def upload_source(self, input, file):
        """
        POST /upload_source
        """
        alerts = []
        if get_exeflags(file["content"]):
            alerts.append({
                "severity": "warning",
                "message": "You have submitted an executable! Please send the "
                           "source code."
            })
            Logger.info("UPLOAD",
                        "User %s has uploaded an executable" % input["token"])
        if not alerts:
            alerts.append({
                "severity": "success",
                "message": "Source file uploaded correctly."
            })

        source_id = Database.gen_id()
        try:
            path = StorageManager.new_source_file(source_id, file["name"])
        except ValueError:
            BaseHandler.raise_exc(BadRequest, "INVALID_FILENAME",
                                  "The provided file has an invalid name")

        StorageManager.save_file(path, file["content"])
        file_size = StorageManager.get_file_size(path)

        Database.add_source(source_id, input["id"], path, file_size)
        Logger.info("UPLOAD", "User %s has uploaded the source %s" % (
            input["token"], source_id))
        output = BaseHandler.format_dates(Database.get_source(source_id))
        output["validation"] = {"alerts": alerts}
        return output
 def evaluate_output(task_name, input_path, output_path):
     """
     Given an input of a task, evaluate the correctness of the output
     :param task_name: Name of the task
     :param input_path: Path to the user's input file
     :param output_path: Path to the user's output file
     :return: The stdout of the checker
     """
     try:
         # call the checker and store the output
         start_time = time.monotonic()
         output = gevent.subprocess.check_output([
             ContestManager.tasks[task_name]["checker"],
             StorageManager.get_absolute_path(input_path),
             StorageManager.get_absolute_path(output_path)
         ])
         if time.monotonic() > start_time + 1:
             Logger.warning(
                 "TASK", "Evaluation of output %s "
                 "for task %s, with input %s, took %f "
                 "seconds" % (output_path, task_name, input_path,
                              time.monotonic() - start_time))
     except:
         # TODO log the stdout and stderr of the checker
         Logger.error(
             "TASK", "Error while evaluating output %s "
             "for task %s, with input %s: %s" %
             (output_path, task_name, input_path, traceback.format_exc()))
         raise
     Logger.info(
         "TASK", "Evaluated output %s for task %s, with input %s" %
         (output_path, task_name, input_path))
     return output
예제 #3
0
    def upload_output(self, input, file):
        """
        POST /upload_output
        """
        output_id = Database.gen_id()
        try:
            path = StorageManager.new_output_file(output_id, file["name"])
        except ValueError:
            BaseHandler.raise_exc(BadRequest, "INVALID_FILENAME",
                                  "The provided file has an invalid name")
        StorageManager.save_file(path, file["content"])
        file_size = StorageManager.get_file_size(path)

        try:
            result = ContestManager.evaluate_output(input["task"],
                                                    input["path"], path)
        except:
            BaseHandler.raise_exc(InternalServerError, "INTERNAL_ERROR",
                                  "Failed to evaluate the output")

        Database.add_output(output_id, input["id"], path, file_size, result)
        Logger.info(
            "UPLOAD",
            "User %s has uploaded the output %s" % (input["token"], output_id))
        return InfoHandler.patch_output(Database.get_output(output_id))
    def test_rename_file(self):
        backup = Config.storedir
        Config.storedir = Utils.new_tmp_dir()

        relative_path = 'baz/file.txt'
        new_path = 'baz/txt.elif'
        StorageManager.save_file(relative_path, 'foobar'.encode())
        StorageManager.rename_file(relative_path, new_path)

        with open(StorageManager.get_absolute_path(new_path), 'r') as file:
            lines = file.readlines()
            self.assertEqual('foobar', lines[0])

        Config.storedir = backup
    def test_get_file_size(self):
        filename = Utils.new_tmp_file()

        with open(filename, 'w') as file:
            file.write('This string is 28 chars long')

        self.assertEqual(28, StorageManager.get_file_size(filename))
 def test_new_output_file(self):
     output_id = 'output_id'
     filename = 'filename.foo'
     path = StorageManager.new_output_file(output_id, filename)
     self.assertIn("output", path)
     self.assertTrue(path.find(output_id) >= 0)
     self.assertTrue(path.find(filename) >= 0)
 def test_sanitize_file_too_long(self):
     filename = "file_" + ("a" * 5 * StorageManager.MAX_LENGTH) + ".txt"
     sanitized = StorageManager._sanitize(filename)
     self.assertEqual(
         "file_" + ("a" * (StorageManager.MAX_LENGTH - 9)) + ".txt",
         sanitized)
     self.assertEqual(StorageManager.MAX_LENGTH, len(sanitized))
 def test_new_source_file(self):
     source_id = 'source_id'
     filename = 'filename.foo'
     path = StorageManager.new_source_file(source_id, filename)
     self.assertIn("source", path)
     self.assertTrue(path.find(source_id) >= 0)
     self.assertTrue(path.find(filename) >= 0)
예제 #9
0
    def generate_input(self, task, user):
        """
        POST /generate_input
        """
        token = user["token"]
        if Database.get_user_task(token, task["name"])["current_attempt"]:
            self.raise_exc(Forbidden, "FORBIDDEN",
                           "You already have a ready input!")

        attempt = Database.get_next_attempt(token, task["name"])
        id, path = ContestManager.get_input(task["name"], attempt)
        size = StorageManager.get_file_size(path)

        Database.begin()
        try:
            Database.add_input(id,
                               token,
                               task["name"],
                               attempt,
                               path,
                               size,
                               autocommit=False)
            Database.set_user_attempt(token,
                                      task["name"],
                                      attempt,
                                      autocommit=False)
            Database.commit()
        except:
            Database.rollback()
            raise
        Logger.info(
            "CONTEST", "Generated input %s for user %s on task %s" %
            (id, token, task["name"]))
        return BaseHandler.format_dates(Database.get_input(id=id))
예제 #10
0
 def upload_pack(self, file):
     """
     POST /admin/upload_pack
     """
     if Database.get_meta("admin_token"):
         BaseHandler.raise_exc(Forbidden, "FORBIDDEN",
                               "The pack has already been extracted")
     elif os.path.exists(Config.encrypted_file):
         BaseHandler.raise_exc(Forbidden, "FORBIDDEN",
                               "The pack has already been uploaded")
     if not crypto.validate(file["content"]):
         self.raise_exc(Forbidden, "BAD_FILE", "The uploaded file is "
                        "not valid")
     StorageManager.save_file(os.path.realpath(Config.encrypted_file),
                              file["content"])
     return {}
예제 #11
0
    def get_input(task_name, attempt):
        """
        Fetch an input from the queue and properly rename it
        :param task_name: Name of the task
        :param attempt: Number of the attempt for the user
        :return: A pair, the first element is the id of the input file,
        the second the path
        """
        if ContestManager.input_queue[task_name].empty():
            Logger.warning("TASK", "Empty queue for task %s!" % task_name)

        input = ContestManager.input_queue[task_name].get()
        path = StorageManager.new_input_file(input["id"], task_name, attempt)
        StorageManager.rename_file(input["path"], path)

        return input["id"], path
 def test_new_input_file(self):
     input_id = 'input_id'
     task = 'simple_task'
     attempt = 42
     path = StorageManager.new_input_file(input_id, task, attempt)
     self.assertIn("input", path)
     self.assertTrue(path.find(input_id) >= 0)
     self.assertTrue(path.find(task) >= 0)
     self.assertTrue(path.find(str(attempt)) >= 0)
    def test_save_file(self):
        backup = Config.storedir
        Config.storedir = Utils.new_tmp_dir("new_path")

        relative_path = os.path.join("baz", "file.txt")
        content = 'This is the content of the file'

        try:
            os.remove(Config.storedir)
        except:
            pass

        StorageManager.save_file(relative_path, content.encode())
        with open(StorageManager.get_absolute_path(relative_path),
                  'r') as file:
            file_content = file.readlines()
            self.assertEqual(1, len(file_content))
            self.assertEqual(content, file_content[0])

        Config.storedir = backup
    def test_get_absolute_path(self):
        backup = Config.storedir
        Config.storedir = Utils.new_tmp_dir()

        relative_path = 'path/to/file'
        abs_path = StorageManager.get_absolute_path(relative_path)

        self.assertTrue(abs_path.find(Config.storedir) >= 0)
        self.assertTrue(abs_path.find(relative_path) >= 0)

        Config.storedir = backup
 def test_sanitize_no_extension(self):
     filename = "x" * StorageManager.MAX_LENGTH * 5
     sanitized = StorageManager._sanitize(filename)
     self.assertEqual("x" * StorageManager.MAX_LENGTH, sanitized)
     self.assertEqual(StorageManager.MAX_LENGTH, len(sanitized))
 def test_sanitize_invalid_filename_3(self):
     filename = ".."
     with self.assertRaises(ValueError):
         StorageManager._sanitize(filename)
예제 #17
0
    def worker(task_name):
        """ Method that stays in the background and generates inputs """
        task = ContestManager.tasks[task_name]
        queue = ContestManager.input_queue[task_name]

        while True:
            try:
                id = Database.gen_id()
                path = StorageManager.new_input_file(id, task_name, "invalid")
                seed = int(sha256(id.encode()).hexdigest(), 16) % (2**31)

                stdout = os.open(
                    StorageManager.get_absolute_path(path),
                    os.O_WRONLY | os.O_CREAT,
                    0o644,
                )

                try:
                    start_time = time.monotonic()
                    # generate the input and store the stdout into a file
                    retcode = gevent.subprocess.call(
                        [task["generator"], str(seed), "0"], stdout=stdout)
                    if time.monotonic() > start_time + 1:
                        Logger.warning(
                            "TASK",
                            "Generation of input %s for task %s took %f seconds"
                            % (seed, task_name, time.monotonic() - start_time),
                        )
                finally:
                    os.close(stdout)

                if retcode != 0:
                    Logger.error(
                        "TASK",
                        "Error %d generating input %s (%d) for task %s" %
                        (retcode, id, seed, task_name),
                    )
                    # skip the input
                    continue

                # if there is a validator in the task use it to check if the
                # generated input is valid
                if "validator" in task:
                    stdin = os.open(StorageManager.get_absolute_path(path),
                                    os.O_RDONLY)
                    try:
                        start_time = time.monotonic()
                        # execute the validator piping the input file to stdin
                        retcode = gevent.subprocess.call(
                            [task["validator"], "0"], stdin=stdin)
                        if time.monotonic() > start_time + 1:
                            Logger.warning(
                                "TASK",
                                "Validation of input %s for task %s took %f "
                                "seconds" % (seed, task_name,
                                             time.monotonic() - start_time),
                            )
                    finally:
                        os.close(stdin)

                    if retcode != 0:
                        Logger.error(
                            "TASK",
                            "Error %d validating input %s (%d) for task %s" %
                            (retcode, id, seed, task_name),
                        )
                        # skip the input
                        continue

                Logger.debug(
                    "TASK",
                    "Generated input %s (%d) for task %s" %
                    (id, seed, task_name),
                )
                # this method is blocking if the queue is full
                queue.put({"id": id, "path": path})
            except:
                Logger.error(
                    "TASK",
                    "Exception while creating an input file: " +
                    traceback.format_exc(),
                )
 def test_sanitize_extension_too_long(self):
     filename = "file." + ("x" * 5 * StorageManager.MAX_LENGTH)
     sanitized = StorageManager._sanitize(filename)
     self.assertEqual("file." + ("x" * (StorageManager.MAX_LENGTH - 5)),
                      sanitized)
     self.assertEqual(StorageManager.MAX_LENGTH, len(sanitized))
 def test_sanitize(self):
     filename = " fi!@le n²amжe'.txt  "
     sanitized = StorageManager._sanitize(filename)
     self.assertEqual("file_n²amжe.txt", sanitized)
 def test_sanitize_no_name(self):
     filename = ".hidden"
     sanitized = StorageManager._sanitize(filename)
     self.assertEqual(".hidden", sanitized)