Fetch any available work from the OpenSubmit server and
    return an according job object.

    Returns None if no work is available.

    Errors are reported by this function directly.

def fetch_job(config):
    '''
    Fetch any available work from the OpenSubmit server and
    return an according job object.

    Returns None if no work is available.

    Errors are reported by this function directly.
    '''
    url = "%s/jobs/?Secret=%s&UUID=%s" % (config.get("Server", "url"),
                                          config.get("Server", "secret"),
                                          config.get("Server", "uuid"))

    try:
        # Fetch information from server
        result = urlopen(url)
        headers = result.info()
        logger.debug("Raw job data: " + str(result.headers).replace('\n', ', '))
        if not compatible_api_version(headers["APIVersion"]):
            # No proper reporting possible, so only logging.
            logger.error("Incompatible API version. Please update OpenSubmit.")
            return None

        if headers["Action"] == "get_config":
            # The server does not know us,
            # so it demands registration before hand.
            logger.info("Machine unknown on server, sending registration ...")
            send_hostinfo(config)
            return None

        # Create job object with information we got
        from .job import Job
        job = Job(config)

        job.submitter_name = headers['SubmitterName']
        job.author_names = headers['AuthorNames']
        job.submitter_studyprogram = headers['SubmitterStudyProgram']
        job.course = headers['Course']
        job.assignment = headers['Assignment']
        job.action = headers["Action"]
        job.file_id = headers["SubmissionFileId"]
        job.sub_id = headers["SubmissionId"]
        job.file_name = headers["SubmissionOriginalFilename"]
        job.submitter_student_id = headers["SubmitterStudentId"]
        if "Timeout" in headers:
            job.timeout = int(headers["Timeout"])
        if "PostRunValidation" in headers:
            # Ignore server-given host + port and use the configured one instead
            # This fixes problems with the arbitrary Django LiveServer port choice
            # It would be better to return relative URLs only for this property,
            # but this is a Bernhard-incompatible API change
            from urllib.parse import urlparse
            relative_path = urlparse(headers["PostRunValidation"]).path
            job.validator_url = config.get("Server", "url") + relative_path
        job.working_dir = create_working_dir(config, job.sub_id)

        # Store submission in working directory
        submission_fname = job.working_dir + job.file_name
        with open(submission_fname, 'wb') as target:
            target.write(result.read())
        assert(os.path.exists(submission_fname))

        # Store validator package in working directory
        validator_fname = job.working_dir + 'download.validator'
        fetch(job.validator_url, validator_fname)

        try:
            prepare_working_directory(job, submission_fname, validator_fname)
        except JobException as e:
            job.send_fail_result(e.info_student, e.info_tutor)
            return None
        logger.debug("Got job: " + str(job))
        return job
    except HTTPError as e:
        if e.code == 404:
            logger.debug("Nothing to do.")
            return None
    except URLError as e:
        logger.error("Error while contacting {0}: {1}".format(url, str(e)))
        return None