]> mj.ucw.cz Git - moe.git/blob - submit/commands.c
Implemented a limit on number of submits of a single task part.
[moe.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 "lib/simple-lists.h"
10 #include "lib/stkstring.h"
11 #include "sherlock/object.h"
12 #include "sherlock/objread.h"
13
14 #include <time.h>
15
16 #include "submitd.h"
17
18 /*** REQUESTS AND REPLIES ***/
19
20 static void NONRET
21 read_error_cb(struct obj_read_state *st UNUSED, char *msg)
22 {
23   client_error("Request parse error: %s", msg);
24 }
25
26 static int
27 read_request(struct conn *c)
28 {
29   if (c->pool)
30     mp_flush(c->pool);
31   else
32     c->pool = mp_new(1024);
33   c->request = obj_new(c->pool);
34   c->reply = obj_new(c->pool);
35
36   struct obj_read_state st;
37   obj_read_start(&st, c->request);
38   st.error_callback = read_error_cb;
39   byte line[1024];
40   uns size = 0;
41   for (;;)
42     {
43       int l = bgets_nodie(&c->rx_fb, line, sizeof(line));
44       if (l < 0)
45         client_error("Request line too long");
46       if (!l)
47         {
48           if (!size)
49             return 0;
50           else
51             client_error("Truncated request");
52         }
53       if (l == 1)
54         break;
55       size += l;
56       if (size >= max_request_size)
57         client_error("Request too long");
58       obj_read_attr(&st, line[0], line+1);
59     }
60   obj_read_end(&st);
61   return 1;
62 }
63
64 static void
65 write_reply(struct conn *c)
66 {
67   if (!obj_find_attr(c->reply, '-') && !obj_find_attr(c->reply, '+'))
68     obj_set_attr(c->reply, '+', "OK");
69   if (trace_commands)
70     {
71       byte *m;
72       if (m = obj_find_aval(c->reply, '-'))
73         msg(L_DEBUG, ">> -%s", m);
74       else if (m = obj_find_aval(c->reply, '+'))
75         msg(L_DEBUG, ">> +%s", m);
76       else
77         msg(L_DEBUG, ">> ???");
78     }
79   obj_write(&c->tx_fb, c->reply, BUCKET_TYPE_PLAIN);
80   bputc(&c->tx_fb, '\n');
81   bflush(&c->tx_fb);
82 }
83
84 static void
85 err(struct conn *c, byte *msg)
86 {
87   obj_set_attr(c->reply, '-', msg);
88 }
89
90 /*** STATUS ***/
91
92 static void
93 copy_attrs(struct odes *dest, struct odes *src)
94 {
95   for (struct oattr *a = src->attrs ; a; a=a->next)
96     if (a->attr < OBJ_ATTR_SON)
97       for (struct oattr *aa = a; aa; aa=aa->same)
98         obj_add_attr(dest, aa->attr, aa->val);
99 }
100
101 static void
102 cmd_status(struct conn *c)
103 {
104   uns verbose = obj_find_anum(c->request, 'V', 0);
105   task_load_status(c);
106
107   CLIST_FOR_EACH(struct task *, t, task_list)
108     {
109       struct odes *to = task_status_find_task(c, t, 1);
110       struct odes *tr = obj_add_son(c->reply, 'T' + OBJ_ATTR_SON);
111       copy_attrs(tr, to);
112       CLIST_FOR_EACH(simp_node *, x, *t->extensions)
113         obj_add_attr(tr, 'A', x->s);
114       CLIST_FOR_EACH(simp_node *, p, t->parts)
115         {
116           struct odes *po = task_status_find_part(to, p->s, 1);
117           struct odes *pr = obj_add_son(tr, 'P' + OBJ_ATTR_SON);
118           copy_attrs(pr, po);
119           uns current_ver = obj_find_anum(po, 'V', 0);
120           for (struct oattr *v = obj_find_attr(po, 'V' + OBJ_ATTR_SON); v; v=v->same)
121             {
122               struct odes *vo = v->son;
123               uns ver = obj_find_anum(vo, 'V', 0);
124               if (ver == current_ver || verbose)
125                 obj_add_son_ref(pr, 'V' + OBJ_ATTR_SON, vo);
126             }
127         }
128     }
129 }
130
131 /*** SUBMIT ***/
132
133 static struct fastbuf *
134 read_attachment(struct conn *c, uns max_size)
135 {
136   uns size = obj_find_anum(c->request, 'S', 0);
137   if (size > max_size)
138     {
139       err(c, "Submission too large");
140       return NULL;
141     }
142   obj_set_attr(c->reply, '+', "Go on");
143   write_reply(c);
144   obj_set_attr(c->reply, '+', NULL);
145
146   // This is less efficient than bbcopy(), but we want our own error handling.
147   struct fastbuf *fb = bopen_tmp(4096);
148   byte buf[4096];
149   uns remains = size;
150   while (remains)
151     {
152       uns cnt = bread(&c->rx_fb, buf, MIN(remains, (uns)sizeof(buf)));
153       if (!cnt)
154         {
155           bclose(fb);
156           client_error("Truncated attachment");
157         }
158       bwrite(fb, buf, cnt);
159       remains -= cnt;
160     }
161   brewind(fb);
162   return fb;
163 }
164
165 static void
166 cmd_submit(struct conn *c)
167 {
168   byte *tname = obj_find_aval(c->request, 'T');
169   if (!tname)
170     {
171       err(c, "No task specified");
172       return;
173     }
174   struct task *task = task_find(tname);
175   if (!task)
176     {
177       err(c, "No such task");
178       return;
179     }
180
181   byte *pname = obj_find_aval(c->request, 'P');
182   if (!pname)
183     {
184       simp_node *s = clist_head(&task->parts);
185       ASSERT(s);
186       pname = s->s;
187     }
188   else if (!part_exists_p(task, pname))
189     {
190       err(c, "No such task part");
191       return;
192     }
193
194   byte *ext = obj_find_aval(c->request, 'X');
195   if (!ext || !ext_exists_p(task, ext))
196     {
197       err(c, "Missing or invalid extension");
198       return;
199     }
200
201   uns max_size = task->max_size ? : max_attachment_size;
202   struct fastbuf *fb = read_attachment(c, max_size);
203   if (!fb)
204     return;
205
206   task_lock_status(c);
207   struct odes *tasko = task_status_find_task(c, task, 1);
208   struct odes *parto = task_status_find_part(tasko, pname, 1);
209   uns current_ver = obj_find_anum(parto, 'V', 0);
210   if (current_ver >= max_versions)
211     {
212       err(c, "Maximum number of submits of this task exceeded");
213       bclose(fb);
214       task_unlock_status(c, 0);
215       return;
216     }
217   uns last_ver = 0;
218   uns replaced_ver = 0;
219   for (struct oattr *a = obj_find_attr(parto, 'V' + OBJ_ATTR_SON); a; a=a->same)
220     {
221       uns ver = obj_find_anum(a->son, 'V', 0);
222       byte *ext = obj_find_aval(a->son, 'X');
223       ASSERT(ver && ext);
224       last_ver = MAX(last_ver, ver);
225       if (ver == current_ver)
226         {
227           task_delete_part(c->user, tname, pname, ext, ver);
228           obj_set_attr(a->son, 'S', "replaced");
229           replaced_ver = current_ver;
230         }
231     }
232   struct odes *vero = obj_add_son(parto, 'V' + OBJ_ATTR_SON);
233   obj_set_attr_num(vero, 'V', ++last_ver);
234   obj_set_attr_num(vero, 'T', time(NULL));
235   obj_set_attr_num(vero, 'L', obj_find_anum(c->request, 'S', 0));
236   obj_set_attr(vero, 'S', "submitted");
237   obj_set_attr(vero, 'X', ext);
238   task_submit_part(c->user, tname, pname, ext, last_ver, fb);
239   obj_set_attr_num(parto, 'V', last_ver);
240   task_unlock_status(c, 1);
241
242   msg(L_INFO, "User %s submitted task %s%s (version %d%s)",
243         c->user, tname,
244         (strcmp(tname, pname) ? stk_printf("/%s", pname) : ""),
245         last_ver,
246         (replaced_ver ? stk_printf(", replaced %d", replaced_ver) : ""));
247 }
248
249 /*** COMMAND MUX ***/
250
251 static void
252 execute_command(struct conn *c)
253 {
254   byte *cmd = obj_find_aval(c->request, '!');
255   if (!cmd)
256     {
257       err(c, "Missing command");
258       return;
259     }
260   if (trace_commands)
261     msg(L_DEBUG, "<< %s", cmd);
262   if (!strcasecmp(cmd, "SUBMIT"))
263     cmd_submit(c);
264   else if (!strcasecmp(cmd, "STATUS"))
265     cmd_status(c);
266   else
267     err(c, "Unknown command");
268 }
269
270 int
271 process_command(struct conn *c)
272 {
273   if (!read_request(c))
274     return 0;
275   execute_command(c);
276   write_reply(c);
277   return 1;
278 }
279
280 /*** INITIAL HANDSHAKE ***/
281
282 static void
283 execute_init(struct conn *c)
284 {
285   byte *user = obj_find_aval(c->request, 'U');
286   if (!user)
287     {
288       err(c, "Missing user");
289       return;
290     }
291   if (!c->cert_name ||
292       !strcmp(user, c->cert_name) ||
293       c->rule->allow_admin && !strcmp(c->cert_name, "admin"))
294     {
295       if (!user_exists_p(user))
296         {
297           err(c, "Unknown user");
298           return;
299         }
300       msg(L_INFO, "Logged in %s", user);
301     }
302   else
303     {
304       err(c, "Permission denied");
305       msg(L_ERROR, "Unauthorized attempt to log in as %s", user);
306       return;
307     }
308   c->user = xstrdup(user);
309 }
310
311 int
312 process_init(struct conn *c)
313 {
314   if (!read_request(c))
315     return 0;
316   execute_init(c);
317   write_reply(c);
318   return !obj_find_attr(c->reply, '-');
319 }