]> mj.ucw.cz Git - eval.git/blob - submit/commands.c
6abd935f0f30cd9f3bbf4ed2d491b74df1e4ae16
[eval.git] / submit / commands.c
1 /*
2  *  The Submit Daemon: High-Level Part of the Protocol
3  *
4  *  (c) 2007 Martin Mares <mj@ucw.cz>
5  */
6
7 #include "lib/lib.h"
8 #include "lib/mempool.h"
9 #include "sherlock/object.h"
10 #include "sherlock/objread.h"
11
12 #include "submitd.h"
13
14 /*** REQUESTS AND REPLIES ***/
15
16 static void NONRET
17 read_error_cb(struct obj_read_state *st UNUSED, byte *msg)
18 {
19   client_error("Request parse error: %s", msg);
20 }
21
22 static int
23 read_request(struct conn *c)
24 {
25   if (c->pool)
26     mp_flush(c->pool);
27   else
28     c->pool = mp_new(1024);
29   c->request = obj_new(c->pool);
30   c->reply = obj_new(c->pool);
31
32   struct obj_read_state st;
33   obj_read_start(&st, c->request);
34   st.error_callback = read_error_cb;
35   byte line[1024];
36   uns size = 0;
37   for (;;)
38     {
39       int l = bgets_nodie(&c->rx_fb, line, sizeof(line));
40       if (l < 0)
41         client_error("Request line too long");
42       if (!l)
43         {
44           if (!size)
45             return 0;
46           else
47             client_error("Truncated request");
48         }
49       if (l == 1)
50         break;
51       size += l;
52       if (size >= max_request_size)
53         client_error("Request too long");
54       obj_read_attr(&st, line[0], line+1);
55     }
56   obj_read_end(&st);
57   return 1;
58 }
59
60 static void
61 write_reply(struct conn *c)
62 {
63   if (!obj_find_attr(c->reply, '-') && !obj_find_attr(c->reply, '+'))
64     obj_set_attr(c->reply, '+', "OK");
65   if (trace_commands)
66     {
67       byte *msg;
68       if (msg = obj_find_aval(c->reply, '-'))
69         log(L_DEBUG, ">> -%s", msg);
70       else if (msg = obj_find_aval(c->reply, '+'))
71         log(L_DEBUG, ">> +%s", msg);
72       else
73         log(L_DEBUG, ">> ???");
74     }
75   obj_write(&c->tx_fb, c->reply, BUCKET_TYPE_PLAIN);
76   bputc(&c->tx_fb, '\n');
77   bflush(&c->tx_fb);
78 }
79
80 static void
81 err(struct conn *c, byte *msg)
82 {
83   obj_set_attr(c->reply, '-', msg);
84 }
85
86 /*** SUBMIT ***/
87
88 static struct fastbuf *
89 read_attachment(struct conn *c)
90 {
91   uns size = obj_find_anum(c->request, 'S', 0);
92   if (size > max_attachment_size)
93     {
94       err(c, "Submission too large");
95       return NULL;
96     }
97   obj_set_attr(c->reply, '+', "Go on");
98   write_reply(c);
99   obj_set_attr(c->reply, '+', NULL);
100
101   // This is less efficient than bbcopy(), but we want our own error handling.
102   struct fastbuf *fb = bopen_tmp(4096);
103   byte buf[4096];
104   uns remains = size;
105   while (remains)
106     {
107       uns cnt = bread(&c->rx_fb, buf, MIN(remains, (uns)sizeof(buf)));
108       if (!cnt)
109         {
110           bclose(fb);
111           client_error("Truncated attachment");
112         }
113       bwrite(fb, buf, cnt);
114       remains -= cnt;
115     }
116   brewind(fb);
117   return fb;
118 }
119
120 static void
121 cmd_submit(struct conn *c)
122 {
123   byte *tname = obj_find_aval(c->request, 'T');
124   if (!tname)
125     {
126       err(c, "No task specified");
127       return;
128     }
129   struct task *task = task_find(tname);
130   if (!task)
131     {
132       err(c, "No such task");
133       return;
134     }
135   struct fastbuf *fb = read_attachment(c);
136   if (!fb)
137     return;
138
139   // FIXME: Check contest time
140   // FIXME: Keep history of submitted tasks
141   // FIXME: File names
142
143   task_lock_status(c);
144   struct odes *o = task_status_find_task(c, task);
145   task_submit(c, task, fb, task->name);
146   log(L_INFO, "User %s submitted task %s", c->user, task->name);
147   task_unlock_status(c, 1);
148 }
149
150 /*** COMMAND MUX ***/
151
152 static void
153 execute_command(struct conn *c)
154 {
155   byte *cmd = obj_find_aval(c->request, '!');
156   if (!cmd)
157     {
158       err(c, "Missing command");
159       return;
160     }
161   if (trace_commands)
162     log(L_DEBUG, "<< %s", cmd);
163   if (!strcasecmp(cmd, "SUBMIT"))
164     cmd_submit(c);
165   else
166     err(c, "Unknown command");
167 }
168
169 int
170 process_command(struct conn *c)
171 {
172   if (!read_request(c))
173     return 0;
174   execute_command(c);
175   write_reply(c);
176   return 1;
177 }
178
179 /*** INITIAL HANDSHAKE ***/
180
181 static void
182 execute_init(struct conn *c)
183 {
184   byte *user = obj_find_aval(c->request, 'U');
185   if (!user)
186     {
187       err(c, "Missing user");
188       return;
189     }
190   if (!c->cert_name ||
191       !strcmp(user, c->cert_name) ||
192       c->rule->allow_admin && !strcmp(c->cert_name, "admin"))
193     {
194       if (!user_exists_p(user))
195         {
196           err(c, "Unknown user");
197           return;
198         }
199       log(L_INFO, "Logged in %s", user);
200     }
201   else
202     {
203       err(c, "Permission denied");
204       log(L_ERROR, "Unauthorized attempt to log in as %s", user);
205       return;
206     }
207   c->user = xstrdup(user);
208 }
209
210 int
211 process_init(struct conn *c)
212 {
213   if (!read_request(c))
214     return 0;
215   execute_init(c);
216   write_reply(c);
217   return !obj_find_attr(c->reply, '-');
218 }