* (c) 2007 Martin Mares <mj@ucw.cz>
*/
-#include "lib/lib.h"
-#include "lib/mempool.h"
+#include "ucw/lib.h"
+#include "ucw/mempool.h"
+#include "ucw/simple-lists.h"
+#include "ucw/stkstring.h"
+#include "ucw/fastbuf.h"
#include "sherlock/object.h"
#include "sherlock/objread.h"
+#include <fcntl.h>
+#include <time.h>
+
#include "submitd.h"
/*** REQUESTS AND REPLIES ***/
static void NONRET
-read_error_cb(struct obj_read_state *st UNUSED, byte *msg)
+read_error_cb(struct obj_read_state *st UNUSED, char *msg)
{
client_error("Request parse error: %s", msg);
}
struct obj_read_state st;
obj_read_start(&st, c->request);
st.error_callback = read_error_cb;
- byte line[1024];
+ char line[1024];
uns size = 0;
for (;;)
{
obj_set_attr(c->reply, '+', "OK");
if (trace_commands)
{
- byte *msg;
- if (msg = obj_find_aval(c->reply, '-'))
- log(L_DEBUG, ">> -%s", msg);
- else if (msg = obj_find_aval(c->reply, '+'))
- log(L_DEBUG, ">> +%s", msg);
+ char *m;
+ if (m = obj_find_aval(c->reply, '-'))
+ msg(L_DEBUG, ">> -%s", m);
+ else if (m = obj_find_aval(c->reply, '+'))
+ msg(L_DEBUG, ">> +%s", m);
else
- log(L_DEBUG, ">> ???");
+ msg(L_DEBUG, ">> ???");
}
obj_write(&c->tx_fb, c->reply, BUCKET_TYPE_PLAIN);
bputc(&c->tx_fb, '\n');
}
static void
-err(struct conn *c, byte *msg)
+err(struct conn *c, char *msg)
{
obj_set_attr(c->reply, '-', msg);
}
+/*** STATUS ***/
+
+static void
+copy_attrs(struct odes *dest, struct odes *src)
+{
+ for (struct oattr *a = src->attrs ; a; a=a->next)
+ if (a->attr < OBJ_ATTR_SON)
+ for (struct oattr *aa = a; aa; aa=aa->same)
+ obj_add_attr(dest, aa->attr, aa->val);
+}
+
+static void
+cmd_status(struct conn *c)
+{
+ uns verbose = obj_find_anum(c->request, 'V', 0);
+ task_load_status(c);
+
+ CLIST_FOR_EACH(struct task *, t, task_list)
+ {
+ struct odes *to = task_status_find_task(c, t, 1);
+ struct odes *tr = obj_add_son(c->reply, 'T' + OBJ_ATTR_SON);
+ copy_attrs(tr, to);
+ CLIST_FOR_EACH(simp_node *, x, *t->extensions)
+ obj_add_attr(tr, 'A', x->s);
+ CLIST_FOR_EACH(simp_node *, p, t->parts)
+ {
+ struct odes *po = task_status_find_part(to, p->s, 1);
+ struct odes *pr = obj_add_son(tr, 'P' + OBJ_ATTR_SON);
+ copy_attrs(pr, po);
+ uns current_ver = obj_find_anum(po, 'V', 0);
+ for (struct oattr *v = obj_find_attr(po, 'V' + OBJ_ATTR_SON); v; v=v->same)
+ {
+ struct odes *vo = v->son;
+ uns ver = obj_find_anum(vo, 'V', 0);
+ if (ver == current_ver || verbose)
+ obj_add_son_ref(pr, 'V' + OBJ_ATTR_SON, vo);
+ }
+ }
+ }
+}
+
+/*** Contest timeout checks ***/
+
+static int
+load_time_limit(char *name)
+{
+ struct fastbuf *f = bopen_try(name, O_RDONLY, 1024);
+ if (!f)
+ return -1;
+ char buf[256];
+ int h, m;
+ if (bgets_nodie(f, buf, sizeof(buf)) < 0 ||
+ sscanf(buf, "%d:%d", &h, &m) != 2 ||
+ h < 0 || h > 23 || m < 0 || m > 59)
+ {
+ msg(L_ERROR, "Invalid timeout in %s", name);
+ bclose(f);
+ return -1;
+ }
+ bclose(f);
+ return 100*h + m;
+}
+
+static int
+contest_over(struct conn *c)
+{
+ time_t now = time(NULL);
+ struct tm *tm = localtime(&now);
+ int tstamp = tm->tm_hour*100 + tm->tm_min;
+ int local_limit = load_time_limit(stk_printf("solutions/%s/TIMEOUT", c->user));
+ int global_limit = load_time_limit("solutions/TIMEOUT");
+ if (trace_commands > 1)
+ msg(L_DEBUG, "Time check: current %d, global limit %d, user limit %d", tstamp, global_limit, local_limit);
+ if (local_limit >= 0)
+ return (tstamp >= local_limit);
+ return (global_limit >= 0 && tstamp >= global_limit);
+}
+
/*** SUBMIT ***/
static struct fastbuf *
-read_attachment(struct conn *c)
+read_attachment(struct conn *c, uns max_size)
{
uns size = obj_find_anum(c->request, 'S', 0);
- if (size > max_attachment_size)
+ if (size > max_size)
{
err(c, "Submission too large");
return NULL;
// This is less efficient than bbcopy(), but we want our own error handling.
struct fastbuf *fb = bopen_tmp(4096);
- byte buf[4096];
+ char buf[4096];
uns remains = size;
while (remains)
{
static void
cmd_submit(struct conn *c)
{
- byte *tname = obj_find_aval(c->request, 'T');
+ if (contest_over(c))
+ {
+ err(c, "The contest is over, no more submits allowed");
+ return;
+ }
+
+ char *tname = obj_find_aval(c->request, 'T');
if (!tname)
{
err(c, "No task specified");
err(c, "No such task");
return;
}
- struct fastbuf *fb = read_attachment(c);
+
+ char *pname = obj_find_aval(c->request, 'P');
+ if (!pname)
+ {
+ simp_node *s = clist_head(&task->parts);
+ ASSERT(s);
+ pname = s->s;
+ }
+ else if (!part_exists_p(task, pname))
+ {
+ err(c, "No such task part");
+ return;
+ }
+
+ char *ext = obj_find_aval(c->request, 'X');
+ if (!ext || !ext_exists_p(task, ext))
+ {
+ err(c, "Missing or invalid extension");
+ return;
+ }
+
+ uns max_size = task->max_size ? : max_attachment_size;
+ struct fastbuf *fb = read_attachment(c, max_size);
if (!fb)
return;
- // FIXME: Check contest time
- // FIXME: Keep history of submitted tasks
- // FIXME: File names
-
task_lock_status(c);
- struct odes *o = task_status_find_task(c, task);
- task_submit(c, task, fb, task->name);
- log(L_INFO, "User %s submitted task %s", c->user, task->name);
+ struct odes *tasko = task_status_find_task(c, task, 1);
+ struct odes *parto = task_status_find_part(tasko, pname, 1);
+ uns current_ver = obj_find_anum(parto, 'V', 0);
+ if (current_ver >= max_versions)
+ {
+ err(c, "Maximum number of submits of this task exceeded");
+ bclose(fb);
+ task_unlock_status(c, 0);
+ return;
+ }
+ uns last_ver = 0;
+ uns replaced_ver = 0;
+ for (struct oattr *a = obj_find_attr(parto, 'V' + OBJ_ATTR_SON); a; a=a->same)
+ {
+ uns ver = obj_find_anum(a->son, 'V', 0);
+ char *ext = obj_find_aval(a->son, 'X');
+ ASSERT(ver && ext);
+ last_ver = MAX(last_ver, ver);
+ if (ver == current_ver)
+ {
+ task_delete_part(c->user, tname, pname, ext, ver);
+ obj_set_attr(a->son, 'S', "replaced");
+ replaced_ver = current_ver;
+ }
+ }
+ struct odes *vero = obj_add_son(parto, 'V' + OBJ_ATTR_SON);
+ obj_set_attr_num(vero, 'V', ++last_ver);
+ obj_set_attr_num(vero, 'T', time(NULL));
+ obj_set_attr_num(vero, 'L', obj_find_anum(c->request, 'S', 0));
+ obj_set_attr(vero, 'S', "submitted");
+ obj_set_attr(vero, 'X', ext);
+ task_submit_part(c->user, tname, pname, ext, last_ver, fb);
+ obj_set_attr_num(parto, 'V', last_ver);
task_unlock_status(c, 1);
+
+ msg(L_INFO, "User %s submitted task %s%s (version %d%s)",
+ c->user, tname,
+ (strcmp(tname, pname) ? stk_printf("/%s", pname) : ""),
+ last_ver,
+ (replaced_ver ? stk_printf(", replaced %d", replaced_ver) : ""));
}
/*** COMMAND MUX ***/
static void
execute_command(struct conn *c)
{
- byte *cmd = obj_find_aval(c->request, '!');
+ char *cmd = obj_find_aval(c->request, '!');
if (!cmd)
{
err(c, "Missing command");
return;
}
if (trace_commands)
- log(L_DEBUG, "<< %s", cmd);
+ msg(L_DEBUG, "<< %s", cmd);
if (!strcasecmp(cmd, "SUBMIT"))
cmd_submit(c);
+ else if (!strcasecmp(cmd, "STATUS"))
+ cmd_status(c);
+ else if (!strcasecmp(cmd, "NOP"))
+ ;
else
err(c, "Unknown command");
}
static void
execute_init(struct conn *c)
{
- byte *user = obj_find_aval(c->request, 'U');
+ char *user = obj_find_aval(c->request, 'U');
if (!user)
{
err(c, "Missing user");
err(c, "Unknown user");
return;
}
- log(L_INFO, "Logged in %s", user);
+ msg(L_INFO, "Logged in %s", user);
}
else
{
err(c, "Permission denied");
- log(L_ERROR, "Unauthorized attempt to log in as %s", user);
+ msg(L_ERROR, "Unauthorized attempt to log in as %s", user);
return;
}
c->user = xstrdup(user);