Esempio n. 1
0
def getAssignmentsHelper(courseId, currentUser, strout):
    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
    except:
        traceback.print_exc(file = strout)
        return json.dumps({'errorType': ERR_EXCEPTION,
            'errorMessage':"Unable to load course config",
            'errorTrace':strout.get()})

    assignments = vmcfg.assignments()
    sorted_assg = sorted(assignments, lambda x, y: int(assignments.get(x, "OrderNumber")) -
                                                   int(assignments.get(y, "OrderNumber")))
    assg_arr = []

    vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
    with opening_course_db(vmpaths.db_file()) as course_db:
        for key in sorted_assg:
            if assignments.is_hidden(key) and not currentUser in vmcfg.admin_list():
                continue
            a = {}
            a['assignmentId'] = key
            a['assignmentTitle'] = assignments.get(key, "AssignmentTitle")
            a['assignmentStorage'] = assignments.getd(key, "AssignmentStorage", "")
            if a['assignmentStorage'].lower() == "large":
                a['assignmentStorageHost'] = assignments.get(key, "AssignmentStorageHost")
                a['assignmentStorageBasepath'] = assignments.storage_basepath( \
                    assignments.get(key, "AssignmentStorageBasepath"), currentUser)
            a['deadline'] = assignments.get(key, "Deadline")
            a['statementLink'] = assignments.get(key, "StatementLink")
            team = course_db.get_user_team_for_assignment(key, currentUser)
            if team is not None:
                a['team'] = team
            assg_arr.append(a)
    return json.dumps(assg_arr)
Esempio n. 2
0
def getUserUploadedMd5Helper(courseId, assignmentId, username, strout):
    """Get the current MD5 sum submitted for a given username on a given assignment"""
    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
    except:
        traceback.print_exc(file = strout)
        return json.dumps({'errorType' : ERR_EXCEPTION,
                           'errorMessage' : "",
                           'errorTrace' : strout.get()})

    (_, account) = getAssignmentAccountName(courseId, assignmentId, username, strout)
    vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
    submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account)
    md5_fpath = paths.submission_md5_file(submission_dir)

    md5_result = {}
    try:
        if os.path.exists(paths.submission_config_file(submission_dir)) and os.path.isfile(md5_fpath):
            sss = submissions.Submissions(vmpaths)
            upload_time_str = sss.get_upload_time_str(assignmentId, account)
            md5_result['fileExists'] = True

            with open(md5_fpath, 'r') as f:
                md5_result['md5Sum'] = f.read(32)

            md5_result['uploadTime'] = upload_time_str
        else:
            md5_result['fileExists'] = False

        return json.dumps(md5_result)
    except:
        traceback.print_exc(file = strout)
        return json.dumps({'errorType' : ERR_EXCEPTION,
                           'errorMessage' : "",
                           'errorTrace' : strout.get()})
Esempio n. 3
0
def getAssignmentAccountName(courseId, assignmentId, username, strout):
    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
    except:
        traceback.print_exc(file = strout)
        return json.dumps({'errorType' : ERR_EXCEPTION,
                           'errorMessage' : "",
                           'errorTrace' : strout.get()})

    vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
    with opening_course_db(vmpaths.db_file()) as course_db:
        return course_db.get_assignment_account(assignmentId, username)
Esempio n. 4
0
def getAssignmentAccountName(courseId, assignmentId, username, strout):
    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
    except:
        traceback.print_exc(file=strout)
        return json.dumps({
            'errorType': ERR_EXCEPTION,
            'errorMessage': "",
            'errorTrace': strout.get()
        })

    vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
    with opening_course_db(vmpaths.db_file()) as course_db:
        return course_db.get_assignment_account(assignmentId, username)
Esempio n. 5
0
def getUserUploadedMd5Helper(courseId, assignmentId, username, strout):
    """Get the current MD5 sum submitted for a given username on a given assignment"""
    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
    except:
        traceback.print_exc(file=strout)
        return json.dumps({
            'errorType': ERR_EXCEPTION,
            'errorMessage': "",
            'errorTrace': strout.get()
        })

    (_, account) = getAssignmentAccountName(courseId, assignmentId, username,
                                            strout)
    vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
    submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account)
    md5_fpath = paths.submission_md5_file(submission_dir)

    md5_result = {}
    try:
        if os.path.exists(paths.submission_config_file(
                submission_dir)) and os.path.isfile(md5_fpath):
            sss = submissions.Submissions(vmpaths)
            upload_time_str = sss.get_upload_time_str(assignmentId, account)
            md5_result['fileExists'] = True

            with open(md5_fpath, 'r') as f:
                md5_result['md5Sum'] = f.read(32)

            md5_result['uploadTime'] = upload_time_str
        else:
            md5_result['fileExists'] = False

        return json.dumps(md5_result)
    except:
        traceback.print_exc(file=strout)
        return json.dumps({
            'errorType': ERR_EXCEPTION,
            'errorMessage': "",
            'errorTrace': strout.get()
        })
Esempio n. 6
0
def getAssignmentsHelper(courseId, currentUser, strout):
    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
    except:
        traceback.print_exc(file=strout)
        return json.dumps({
            'errorType': ERR_EXCEPTION,
            'errorMessage': "Unable to load course config",
            'errorTrace': strout.get()
        })

    assignments = vmcfg.assignments()
    sorted_assg = sorted(
        assignments, lambda x, y: int(assignments.get(x, "OrderNumber")) - int(
            assignments.get(y, "OrderNumber")))
    assg_arr = []

    vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
    with opening_course_db(vmpaths.db_file()) as course_db:
        for key in sorted_assg:
            if assignments.is_hidden(
                    key) and not currentUser in vmcfg.admin_list():
                continue
            a = {}
            a['assignmentId'] = key
            a['assignmentTitle'] = assignments.get(key, "AssignmentTitle")
            a['assignmentStorage'] = assignments.getd(key, "AssignmentStorage",
                                                      "")
            if a['assignmentStorage'].lower() == "large":
                a['assignmentStorageHost'] = assignments.get(
                    key, "AssignmentStorageHost")
                a['assignmentStorageBasepath'] = assignments.storage_basepath( \
                    assignments.get(key, "AssignmentStorageBasepath"), currentUser)
            a['deadline'] = assignments.get(key, "Deadline")
            a['statementLink'] = assignments.get(key, "StatementLink")
            team = course_db.get_user_team_for_assignment(key, currentUser)
            if team is not None:
                a['team'] = team
            assg_arr.append(a)
    return json.dumps(assg_arr)
Esempio n. 7
0
def getAllGradesHelper(courseId, username, strout):
    try:
        # XXX: DON'T DO THIS: performance degrades very much!
        #update_db.update_grades(courseId)
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
        vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
        assignments = vmcfg.assignments()
        sorted_assg = sorted(assignments, lambda x, y: int(assignments.get(x, "OrderNumber")) -
                                                       int(assignments.get(y, "OrderNumber")))

        # Check if the current user is allowed to view all the grades
        # TODO: This should be implemented neater using some group
        # and permission model.

        user_can_view_all = False
        if vmcfg.public_results() or username in vmcfg.admin_list():
            user_can_view_all = True

        user_grade_rows = None
        team_grade_rows = None
        with opening_course_db(vmpaths.db_file()) as course_db:
            if user_can_view_all:
                user_grade_rows = course_db.get_user_grades()
                team_grade_rows = course_db.get_team_grades()
            else:
                # Get all the individual grades that the user is allowed to see
                user_grade_rows = course_db.get_user_and_teammates_grades(username)
                # Get all the team grades that the user is allowed to see
                team_grade_rows = course_db.get_user_team_grades(user = username)

        ret = []
        grades = {}
        for row in user_grade_rows:
            user, assignment, grade = row
            if not assignment in vmcfg.assignments():
                continue
            if not vmcfg.assignments().show_grades_before_deadline(assignment):
                deadline = time.strptime(vmcfg.assignments().get(assignment, 'Deadline'), DATE_FORMAT)
                deadtime = time.mktime(deadline)
                if time.time() < deadtime:
                    continue
            if vmcfg.assignments().is_hidden(assignment) and username not in vmcfg.admin_list():
                continue

            grades.setdefault(user, {})[assignment] = grade

        for user in sorted(grades.keys()):
            ret.append({'gradeOwner' : 'user',
                        'name'       : user,
                        'results'    : grades.get(user)})

        grades = {}
        for row in team_grade_rows:
            team, assignment, grade = row
            if not assignment in vmcfg.assignments():
                continue
            if not vmcfg.assignments().show_grades_before_deadline(assignment):
                deadline = time.strptime(vmcfg.assignments().get(assignment, 'Deadline'), DATE_FORMAT)
                deadtime = time.mktime(deadline)
                if time.time() < deadtime:
                    continue
            grades.setdefault(team, {})[assignment] = grade

        for team in sorted(grades.keys()):
            ret.append({'gradeOwner' : 'team',
                        'name'       : team,
                        'results'    : grades.get(team)})
        return json.dumps(ret)
    except:
        traceback.print_exc(file = strout)
        return json.dumps({'errorType' : ERR_EXCEPTION,
                           'errorMessage' : "",
                           'errorTrace' : strout.get()})
Esempio n. 8
0
def getResultsHelper(courseId, assignmentId, currentUser, strout, username = None, teamname = None, currentTeam = None):
    # assume that the session was already checked

    if username != None and teamname != None:
        return json.dumps({'errorType' : ERR_OTHER,
                           'errorMessage' : "Can't query both user and team results at the same time.",
                           'errorTrace' : ""})


    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
    except:
        traceback.print_exc(file = strout)
        return json.dumps({'errorType' : ERR_EXCEPTION,
                           'errorMessage' : "",
                           'errorTrace' : strout.get()})

    # Check if the current user is allowed to view any other user's grade.
    # TODO: This should be implemented neater using some group
    # and permission model.

    is_authorized = vmcfg.public_results() or \
                    currentUser in vmcfg.admin_list() or \
                    username == currentUser or \
                    teamname == currentTeam

    if not is_authorized:
        traceback.print_exc(file = strout)
        return json.dumps({'errorType' : ERR_EXCEPTION,
                           'errorMessage' : "User is not authorized to view results.",
                           'errorTrace' : strout.get()})

    vmpaths = paths.VmcheckerPaths(vmcfg.root_path())

    account = None
    if username != None:
        # Get the individual results for this user
        account = username
        isTeamAccount = False
    elif teamname != None:
        # Get the team results for this team
        account = teamname
        isTeamAccount = True
    else:
        # Check if the user is part of a team with a mutual account for this submission
        (isTeamAccount, account) = getAssignmentAccountName(courseId, assignmentId, currentUser, strout)


    submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account)

    r_path = paths.dir_submission_results(submission_dir)

    assignments = vmcfg.assignments()
    ignored_vmrs = assignments.ignored_vmrs(assignmentId)
    try:
        isGraded = False
        result_files = []
        if os.path.isdir(r_path):
            update_db.update_grades(courseId, account=account, assignment=assignmentId)
            for fname in os.listdir(r_path):
                # skill all files not ending in '.vmr'
                if not fname.endswith('.vmr'):
                    continue
                if fname in ignored_vmrs:
                    continue
                f_path = os.path.join(r_path, fname)
                if os.path.isfile(f_path):
                    overflow_msg = u""
                    f_size = os.path.getsize(f_path)
                    if f_size > MAX_VMR_FILE_SIZE:
                        overflow_msg = '\n\n' + _('File truncated! Actual size') + ': ' + str(f_size) + ' ' + _('bytes') + '\n'
                    # decode as utf-8 and ignore any errors, because
                    # characters will be badly encoded as json.
                    with codecs.open(f_path, 'r', encoding='utf-8', errors='ignore') as f:
                        content = f.read(MAX_VMR_FILE_SIZE) + overflow_msg.decode("utf-8")
                        content = xssescape(content)
                        result_files.append({fname : content})
                        if fname == 'grade.vmr' and \
                                "".join(content.split()) not in submissions.GENERATED_STATUSES:
                            isGraded = True
        if (len(result_files) == 1 and result_files[0].keys()[0] == "grade.vmr") and \
                not vmcfg.assignments().submit_only(assignmentId):
            msg = _("In the meantime have a fortune cookie") + ": <blockquote>"
            try:
                process = subprocess.Popen('/usr/games/fortune',
                                       shell=False,
                                       stdout=subprocess.PIPE)
                msg += process.communicate()[0] + "</blockquote>"
            except:
                msg += "Knock knock. Who's there? [Silence] </blockquote>"
            result_files = [ {'fortune.vmr' :  msg } ]
            result_files.append({'queue-contents.vmr' :  get_test_queue_contents(vmcfg, courseId) })
        if 'submission.vmr' not in ignored_vmrs:
            result_files.append({'submission.vmr' :
                                 submission_upload_info(vmcfg, courseId, assignmentId, account, isTeamAccount, isGraded)})
        result_files = sortResultFiles(result_files)
        return json.dumps(result_files)
    except:
        traceback.print_exc(file = strout)
        return json.dumps({'errorType' : ERR_EXCEPTION,
                           'errorMessage' : "",
                           'errorTrace' : strout.get()})
Esempio n. 9
0
def validate_md5_submission(courseId, assignmentId, account, archiveFileName):
    """Checks whether a MD5Submission is valid:
       * checks that the uploaded md5 corresponds to the one of the machine
       * checks that the archive uploaded by the student is a zip file

       On success returns (True,).
       On failure reports the source of the failure:
       - (False, 'md5') - the uploaded md5 does not match the one computed on the archive
       - (False, 'zip') - the uploaded archive is not zip.
    """

    md5_calculated = ""
    md5_uploaded = ""
    archive_file_type = ""

    client = paramiko.SSHClient()
    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
        assignments = vmcfg.assignments()
        storage_hostname = assignments.get(assignmentId, 'AssignmentStorageHost')
        storage_username = assignments.get(assignmentId, 'AssignmentStorageQueryUser')
        storage_basepath = assignments.storage_basepath( \
            assignments.get(assignmentId, 'AssignmentStorageBasepath'), account)

        client.load_system_host_keys(vmcfg.known_hosts_file())
        client.connect(storage_hostname,
                       username=storage_username,
                       key_filename=vmcfg.storer_sshid(),
                       look_for_keys=False)

        archive_abs = os.path.join(storage_basepath, account, archiveFileName)

        # XXX: This will take ages to compute! I wonder how many
        # connections will Apache hold.
        stdin, stdout, stderr = client.exec_command("md5sum " + QuoteForPOSIX(archive_abs))
        md5_calculated = stdout.readline().split()[0]
        for f in [stdin, stdout, stderr]: f.close()

        stdin, stdout, stderr = client.exec_command("file " + QuoteForPOSIX(archive_abs))
        archive_file_type = stdout.readline()[len(archive_abs):].split()[1].lower()
        for f in [stdin, stdout, stderr]: f.close()


        vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
        submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account)
        md5_fpath = paths.submission_md5_file(submission_dir)

        if os.path.isfile(md5_fpath):
            with open(md5_fpath, 'r') as f:
                md5_uploaded = f.read(32)
    except:
        strout = OutputString()
        traceback.print_exc(file = strout)
        return json.dumps({'errorTrace' : strout.get()}, indent=4)
    finally:
        client.close()

    if not md5_calculated == md5_uploaded:
        return (False, MD5_ERR_BAD_MD5) # report the type of the problem

    if not archive_file_type == "zip":
        return (False, MD5_ERR_BAD_ZIP) # report the type of the problem


    return (True,) # no problemo
Esempio n. 10
0
def getAllGradesHelper(courseId, username, strout):
    try:
        # XXX: DON'T DO THIS: performance degrades very much!
        #update_db.update_grades(courseId)
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
        vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
        assignments = vmcfg.assignments()
        sorted_assg = sorted(
            assignments,
            lambda x, y: int(assignments.get(x, "OrderNumber")) - int(
                assignments.get(y, "OrderNumber")))

        # Check if the current user is allowed to view all the grades
        # TODO: This should be implemented neater using some group
        # and permission model.

        user_can_view_all = False
        if vmcfg.public_results() or username in vmcfg.admin_list():
            user_can_view_all = True

        user_grade_rows = None
        team_grade_rows = None
        with opening_course_db(vmpaths.db_file()) as course_db:
            if user_can_view_all:
                user_grade_rows = course_db.get_user_grades()
                team_grade_rows = course_db.get_team_grades()
            else:
                # Get all the individual grades that the user is allowed to see
                user_grade_rows = course_db.get_user_and_teammates_grades(
                    username)
                # Get all the team grades that the user is allowed to see
                team_grade_rows = course_db.get_user_team_grades(user=username)

        ret = []
        grades = {}
        for row in user_grade_rows:
            user, assignment, grade = row
            if not assignment in vmcfg.assignments():
                continue
            if not vmcfg.assignments().show_grades_before_deadline(assignment):
                deadline = time.strptime(
                    vmcfg.assignments().get(assignment, 'Deadline'),
                    DATE_FORMAT)
                deadtime = time.mktime(deadline)
                if time.time() < deadtime:
                    continue
            if vmcfg.assignments().is_hidden(
                    assignment) and username not in vmcfg.admin_list():
                continue

            grades.setdefault(user, {})[assignment] = grade

        for user in sorted(grades.keys()):
            ret.append({
                'gradeOwner': 'user',
                'name': user,
                'results': grades.get(user)
            })

        grades = {}
        for row in team_grade_rows:
            team, assignment, grade = row
            if not assignment in vmcfg.assignments():
                continue
            if not vmcfg.assignments().show_grades_before_deadline(assignment):
                deadline = time.strptime(
                    vmcfg.assignments().get(assignment, 'Deadline'),
                    DATE_FORMAT)
                deadtime = time.mktime(deadline)
                if time.time() < deadtime:
                    continue
            grades.setdefault(team, {})[assignment] = grade

        for team in sorted(grades.keys()):
            ret.append({
                'gradeOwner': 'team',
                'name': team,
                'results': grades.get(team)
            })
        return json.dumps(ret)
    except:
        traceback.print_exc(file=strout)
        return json.dumps({
            'errorType': ERR_EXCEPTION,
            'errorMessage': "",
            'errorTrace': strout.get()
        })
Esempio n. 11
0
def getResultsHelper(courseId,
                     assignmentId,
                     currentUser,
                     strout,
                     username=None,
                     teamname=None,
                     currentTeam=None):
    # assume that the session was already checked

    if username != None and teamname != None:
        return json.dumps({
            'errorType': ERR_OTHER,
            'errorMessage':
            "Can't query both user and team results at the same time.",
            'errorTrace': ""
        })

    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
    except:
        traceback.print_exc(file=strout)
        return json.dumps({
            'errorType': ERR_EXCEPTION,
            'errorMessage': "",
            'errorTrace': strout.get()
        })

    # Check if the current user is allowed to view any other user's grade.
    # TODO: This should be implemented neater using some group
    # and permission model.

    is_authorized = vmcfg.public_results() or \
                    currentUser in vmcfg.admin_list() or \
                    username == currentUser or \
                    teamname == currentTeam

    if not is_authorized:
        traceback.print_exc(file=strout)
        return json.dumps({
            'errorType': ERR_EXCEPTION,
            'errorMessage': "User is not authorized to view results.",
            'errorTrace': strout.get()
        })

    vmpaths = paths.VmcheckerPaths(vmcfg.root_path())

    account = None
    if username != None:
        # Get the individual results for this user
        account = username
        isTeamAccount = False
    elif teamname != None:
        # Get the team results for this team
        account = teamname
        isTeamAccount = True
    else:
        # Check if the user is part of a team with a mutual account for this submission
        (isTeamAccount,
         account) = getAssignmentAccountName(courseId, assignmentId,
                                             currentUser, strout)

    submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account)

    r_path = paths.dir_submission_results(submission_dir)

    assignments = vmcfg.assignments()
    ignored_vmrs = assignments.ignored_vmrs(assignmentId)
    try:
        isGraded = False
        result_files = []
        if os.path.isdir(r_path):
            update_db.update_grades(courseId,
                                    account=account,
                                    assignment=assignmentId)
            for fname in os.listdir(r_path):
                # skill all files not ending in '.vmr'
                if not fname.endswith('.vmr'):
                    continue
                if fname in ignored_vmrs:
                    continue
                f_path = os.path.join(r_path, fname)
                if os.path.isfile(f_path):
                    overflow_msg = ''
                    f_size = os.path.getsize(f_path)
                    if f_size > MAX_VMR_FILE_SIZE:
                        overflow_msg = '\n\n' + _(
                            'File truncated! Actual size') + ': ' + str(
                                f_size) + ' ' + _('bytes') + '\n'
                    # decode as utf-8 and ignore any errors, because
                    # characters will be badly encoded as json.
                    with codecs.open(f_path,
                                     'r',
                                     encoding='utf-8',
                                     errors='ignore') as f:
                        content = f.read(MAX_VMR_FILE_SIZE) + overflow_msg
                        content = xssescape(content)
                        result_files.append({fname: content})
                        if fname == 'grade.vmr' and \
                                "".join(content.split()) not in submissions.GENERATED_STATUSES:
                            isGraded = True
        if (len(result_files) == 1 and result_files[0].keys()[0] == "grade.vmr") and \
                not vmcfg.assignments().submit_only(assignmentId):
            msg = _("In the meantime have a fortune cookie") + ": <blockquote>"
            try:
                process = subprocess.Popen('/usr/games/fortune',
                                           shell=False,
                                           stdout=subprocess.PIPE)
                msg += process.communicate()[0] + "</blockquote>"
            except:
                msg += "Knock knock. Who's there? [Silence] </blockquote>"
            result_files = [{'fortune.vmr': msg}]
            result_files.append({
                'queue-contents.vmr':
                get_test_queue_contents(vmcfg, courseId)
            })
        if 'submission.vmr' not in ignored_vmrs:
            result_files.append({
                'submission.vmr':
                submission_upload_info(vmcfg, courseId, assignmentId, account,
                                       isTeamAccount, isGraded)
            })
        result_files = sortResultFiles(result_files)
        return json.dumps(result_files)
    except:
        traceback.print_exc(file=strout)
        return json.dumps({
            'errorType': ERR_EXCEPTION,
            'errorMessage': "",
            'errorTrace': strout.get()
        })
Esempio n. 12
0
def validate_md5_submission(courseId, assignmentId, account, archiveFileName):
    """Checks whether a MD5Submission is valid:
       * checks that the uploaded md5 corresponds to the one of the machine
       * checks that the archive uploaded by the student is a zip file

       On success returns (True,).
       On failure reports the source of the failure:
       - (False, 'md5') - the uploaded md5 does not match the one computed on the archive
       - (False, 'zip') - the uploaded archive is not zip.
    """

    md5_calculated = ""
    md5_uploaded = ""
    archive_file_type = ""

    client = paramiko.SSHClient()
    try:
        vmcfg = StorerCourseConfig(CourseList().course_config(courseId))
        assignments = vmcfg.assignments()
        storage_hostname = assignments.get(assignmentId,
                                           'AssignmentStorageHost')
        storage_username = assignments.get(assignmentId,
                                           'AssignmentStorageQueryUser')
        storage_basepath = assignments.storage_basepath( \
            assignments.get(assignmentId, 'AssignmentStorageBasepath'), account)

        client.load_system_host_keys(vmcfg.known_hosts_file())
        client.connect(storage_hostname,
                       username=storage_username,
                       key_filename=vmcfg.storer_sshid(),
                       look_for_keys=False)

        archive_abs = os.path.join(storage_basepath, account, archiveFileName)

        # XXX: This will take ages to compute! I wonder how many
        # connections will Apache hold.
        stdin, stdout, stderr = client.exec_command("md5sum " +
                                                    QuoteForPOSIX(archive_abs))
        md5_calculated = stdout.readline().split()[0]
        for f in [stdin, stdout, stderr]:
            f.close()

        stdin, stdout, stderr = client.exec_command("file " +
                                                    QuoteForPOSIX(archive_abs))
        archive_file_type = stdout.readline()[len(archive_abs):].split(
        )[1].lower()
        for f in [stdin, stdout, stderr]:
            f.close()

        vmpaths = paths.VmcheckerPaths(vmcfg.root_path())
        submission_dir = vmpaths.dir_cur_submission_root(assignmentId, account)
        md5_fpath = paths.submission_md5_file(submission_dir)

        if os.path.isfile(md5_fpath):
            with open(md5_fpath, 'r') as f:
                md5_uploaded = f.read(32)
    except:
        strout = OutputString()
        traceback.print_exc(file=strout)
        return json.dumps({'errorTrace': strout.get()}, indent=4)
    finally:
        client.close()

    if not md5_calculated == md5_uploaded:
        return (False, MD5_ERR_BAD_MD5)  # report the type of the problem

    if not archive_file_type == "zip":
        return (False, MD5_ERR_BAD_ZIP)  # report the type of the problem

    return (True, )  # no problemo