DELETE command
[rxpd] / src / rxpd_connection_cmd.c
1 /*
2     rxpd_connection_cmd.c - regex policy daemon
3
4   Copyright (C)
5     2007,               Christian Thaeter <ct@pipapo.org>
6
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "rxpd.h"
23
24 void
25 rxpd_connection_cmd_CHECK (int fd, short event, void* ptr)
26 {
27   (void) fd;
28   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
29
30   if (event == EV_READ)
31     {
32       int again = -1;
33       char* line;
34       while ((line = rxpd_buffer_readline (&self->in, ++again)))
35         {
36           if (*line == '\0')
37             {
38               rxpd_buffer_printf (&self->out, "#OK:\n");
39             }
40           else
41             {
42               LLIST_FOREACH (&self->file->rules, n)
43                 {
44                   struct rxpd_rule* rule = (struct rxpd_rule*)n;
45                   if (rule->string[0] != '#')
46                     {
47                       if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
48                         {
49                           if (rule->atime != (time_t) -1)
50                             time (&rule->atime);
51
52                           rxpd_buffer_printf (&self->out, "%s\n", rule->string);
53                           break;
54                         }
55                     }
56                 }
57             }
58         }
59     }
60   else if (!self->file)
61     {
62       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
63       rxpd_connection_delete (self);
64       return;
65     }
66
67   if (rxpd_buffer_state (&self->in) == RXPD_OK)
68     rxpd_connection_schedule (self);
69   else
70     {
71       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
72         rxpd_buffer_printf (&self->out, "#ERROR:\n");
73       rxpd_connection_delete (self);
74     }
75 }
76
77
78
79 static void
80 rxpd_connection_APPEND_PREPEND_helper (short event, void* ptr, int do_append)
81 {
82   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
83
84   if (event == EV_READ)
85     {
86       int again = -1;
87       char* line;
88
89       while ((line = rxpd_buffer_readline (&self->in, ++again)))
90         {
91           if (*line)
92             {
93               struct rxpd_rule* rule;
94               rule = rxpd_rule_new (line);
95               if (!rule)
96                 abort();
97
98               llist_insert_tail (&self->tmp_list, &rule->node);
99             }
100           else goto finish;     /* move along, look elsewhere! This goto is not harmful and saves some code. */
101         }
102     }
103   else if (!event && !self->file)
104     {
105       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
106       rxpd_connection_delete (self);
107       return;
108     }
109
110   if (rxpd_buffer_state (&self->in) == RXPD_OK)
111     rxpd_connection_schedule (self);
112   else
113     {
114       // TODO should also print error when any rule compilation failed, use tmp_str to save case?
115       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
116         rxpd_buffer_printf (&self->out, "#ERROR:\n");
117       else
118         {
119         finish:
120           rxpd_buffer_printf (&self->out, "#OK:\n");
121         }
122
123       if (do_append)
124         llist_insertlist_prev (&self->file->rules, &self->tmp_list);
125       else
126         llist_insertlist_next (&self->file->rules, &self->tmp_list);
127       rxpd_connection_delete (self);
128     }
129 }
130
131 void
132 rxpd_connection_cmd_APPEND (int fd, short event, void* ptr)
133 {
134   (void) fd;
135   rxpd_connection_APPEND_PREPEND_helper (event, ptr, 1);
136 }
137
138 void
139 rxpd_connection_cmd_PREPEND (int fd, short event, void* ptr)
140 {
141   (void) fd;
142   rxpd_connection_APPEND_PREPEND_helper (event, ptr, 0);
143 }
144
145
146 void
147 rxpd_connection_cmd_REMOVE (int fd, short event, void* ptr)
148 {
149   (void) fd;
150   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
151
152   if (event == EV_READ)
153     {
154       int again = -1;
155       char* line;
156       while ((line = rxpd_buffer_readline (&self->in, ++again)))
157         {
158           LLIST_FOREACH (&self->file->rules, n)
159             {
160               struct rxpd_rule* rule = (struct rxpd_rule*)n;
161               if (strcmp (rule->string, line) == 0)
162                 {
163                   LList tmp = llist_prev (n);
164                   rxpd_rule_delete (rule);
165                   n = tmp;
166                   rxpd_buffer_printf (&self->out, "#OK:\n");
167                   goto done;
168                 }
169             }
170           rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
171         done:
172           ;
173         }
174     }
175   else if (!self->file)
176     {
177       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
178       rxpd_connection_delete (self);
179       return;
180     }
181
182   if (rxpd_buffer_state (&self->in) == RXPD_OK)
183     rxpd_connection_schedule (self);
184   else
185     {
186       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
187         rxpd_buffer_printf (&self->out, "#ERROR:\n");
188       rxpd_connection_delete (self);
189     }
190 }
191
192
193 static int
194 rxpd_connection_do_REPLACE (struct rxpd_connection* self)
195 {
196   struct rxpd_rule* rule;
197
198   LLIST_FOREACH (&self->file->rules, n)
199     {
200       rule = (struct rxpd_rule*)n;
201       if (strcmp (rule->string, self->tmp_str) == 0)
202         goto found;
203     }
204   return 0;
205  found:
206   llist_insertlist_next (&rule->node, &self->tmp_list);
207   rxpd_rule_delete (rule);
208   free (self->tmp_str);
209   self->tmp_str = NULL;
210   return 1;
211 }
212
213 void
214 rxpd_connection_cmd_REPLACE (int fd, short event, void* ptr)
215 {
216   (void) fd;
217   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
218
219   if (event == EV_READ)
220     {
221       int again = -1;
222       char* line;
223       while ((line = rxpd_buffer_readline (&self->in, ++again)))
224         {
225           if (self->tmp_str)
226             {
227               if (*line)
228                 {
229                   struct rxpd_rule* rule;
230                   rule = rxpd_rule_new (line);
231                   if (!rule)
232                     abort();
233
234                   llist_insert_tail (&self->tmp_list, &rule->node);
235                 }
236               /* TODO handle empty lines? */
237             }
238           else
239             self->tmp_str = rxpd_strdup (line);
240         }
241     }
242   else if (!self->file)
243     {
244       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
245       rxpd_connection_delete (self);
246       return;
247     }
248
249   if (rxpd_buffer_state (&self->in) == RXPD_OK)
250     rxpd_connection_schedule (self);
251   else
252     {
253       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
254         rxpd_buffer_printf (&self->out, "#ERROR:\n");
255       else
256         {
257           if (rxpd_connection_do_REPLACE (self))
258             rxpd_buffer_printf (&self->out, "#OK:\n");
259           else
260             rxpd_buffer_printf (&self->out, "#ERROR: rule matching '%s'\n", self->tmp_str);
261         }
262
263       rxpd_connection_delete (self);
264     }
265 }
266
267 void
268 rxpd_connection_cmd_LOAD (int fd, short event, void* ptr)
269 {
270   (void) fd;
271   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
272
273   if (!event)
274     {
275       if (self->file)
276         {
277           if (rxpd_file_load (self->file))
278             {
279               rxpd_buffer_printf (&self->out, "#OK:\n");
280             }
281           else
282             {
283               rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
284             }
285         }
286       else
287         rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
288       rxpd_connection_delete (self);
289     }
290 }
291
292 void
293 rxpd_connection_cmd_SAVE (int fd, short event, void* ptr)
294 {
295   (void) fd;
296   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
297
298   if (!event)
299     {
300       if (self->file)
301         {
302           if (rxpd_file_save (self->file))
303             {
304               rxpd_buffer_printf (&self->out, "#OK:\n");
305             }
306           else
307             {
308               rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
309             }
310         }
311       else
312         rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
313       rxpd_connection_delete (self);
314     }
315 }
316
317 void
318 rxpd_connection_cmd_DELETE (int fd, short event, void* ptr)
319 {
320   (void) fd;
321   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
322
323   if (!event)
324     {
325       if (self->file)
326         {
327           rxpd_file_delete (self->file);
328           rxpd_buffer_printf (&self->out, "#OK:\n");
329         }
330       rxpd_connection_delete (self);
331     }
332 }
333
334 void
335 rxpd_connection_cmd_DUMP (int fd, short event, void* ptr)
336 {
337   (void) fd;
338   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
339
340   if (!event && !self->file)
341     {
342       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
343       rxpd_connection_delete (self);
344       return;
345     }
346
347   if (llist_is_empty (&self->file->rules))
348     rxpd_buffer_printf (&self->out, "#OK:\n");
349   else
350     {
351       LLIST_FOREACH (&self->file->rules, n)
352         {
353           struct rxpd_rule* rule = (struct rxpd_rule*)n;
354           if (rule->atime != (time_t)-1)
355             rxpd_buffer_printf (&self->out, "%ld:%s\n", rule->atime, rule->string);
356           else if (*rule->string != '#')
357             rxpd_buffer_printf (&self->out, ":%s\n", rule->string);
358           else
359             rxpd_buffer_printf (&self->out, "%s\n", rule->string);
360         }
361     }
362
363   rxpd_connection_delete (self);
364 }
365
366
367 static psplay_delete_t
368 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
369 {
370   (void) level;
371   struct rxpd_file* file = (struct rxpd_file*) node;
372   struct rxpd_connection* conn = (struct rxpd_connection*) data;
373
374   if (which == PSPLAY_INORDER)
375     rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
376
377   return PSPLAY_CONT;
378 }
379
380
381 void
382 rxpd_connection_cmd_LIST (int fd, short event, void* ptr)
383 {
384   (void) fd;
385   (void) event;
386   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
387   struct rxpd_base* base = self->socket->base;
388
389   if (psplay_isempty_root (&base->files))
390     rxpd_buffer_printf (&self->out, "#OK:\n");
391   else
392     psplay_walk (&base->files, NULL, walk_LIST, 0, ptr);
393
394   rxpd_connection_delete (self);
395 }
396
397 void
398 rxpd_connection_cmd_SHUTDOWN (int fd, short event, void* ptr)
399 {
400   (void) fd;
401   (void) event;
402   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
403   struct rxpd_base* base = self->socket->base;
404   // destroy all sockets
405   LLIST_WHILE_HEAD (&base->sockets, n)
406     {
407       struct rxpd_socket* socket = (struct rxpd_socket*)n;
408       rxpd_socket_delete (socket);
409     }
410   rxpd_buffer_printf (&self->out, "#OK:\n");
411   rxpd_connection_delete (self);
412 }
413
414
415 void
416 rxpd_connection_cmd_VERSION (int fd, short event, void* ptr)
417 {
418   (void) fd;
419   (void) event;
420   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
421   rxpd_buffer_printf (&self->out, PACKAGE_STRING "\n#\n"
422                       "# Copyright (C)\n"
423                       "#   2007,               Christian Thaeter <ct@pipapo.org>\n#\n"
424                       "# This is free software.  You may redistribute copies of it under the terms of\n"
425                       "# the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
426                       "# There is NO WARRANTY, to the extent permitted by law.\n#\n"
427                       "# http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
428   rxpd_connection_delete (self);
429 }
430
431 void
432 rxpd_connection_cmd_HELP (int fd, short event, void* ptr)
433 {
434   (void) fd;
435   (void) event;
436   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
437   //struct rxpd_base* base = self->socket->base;
438   rxpd_buffer_printf (&self->out, "# Available commands:\n#\n");
439 #define RXPD_CMD(cmd, help)     rxpd_buffer_printf (&self->out, "# %s %s.\n", #cmd, help);
440   RXPD_COMMANDS
441 #undef RXPD_CMD
442   rxpd_buffer_printf (&self->out, "#\n# general syntax is: 'COMMAND:listname\\n..data..'\n");
443   rxpd_connection_delete (self);
444 }
445
446 void
447 rxpd_connection_cmd_EXPIRE (int fd, short event, void* ptr)
448 {
449   (void) fd;
450   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
451   struct rxpd_base* base = self->socket->base;
452
453   if (event == EV_READ)
454     {
455       int again = -1;
456       char* line;
457       while ((line = rxpd_buffer_readline (&self->in, ++again)))
458         {
459           if (*line)
460             {
461               time_t since = time (NULL) - atoi (line);
462               rxpd_log (base, LOG_INFO, "expire all entries in '%s' since %ld\n",
463                         (const char*) self->file->node.key, since);
464
465               LLIST_FOREACH (&self->file->rules, n)
466                 {
467                   struct rxpd_rule* rule = (struct rxpd_rule*)n;
468                   if (rule->atime != -1 && rule->atime < since)
469                     {
470                       n = llist_prev (n);
471                       rxpd_log (base, LOG_DEBUG, "expiring %ld:%s\n", rule->atime, rule->string);
472                       rxpd_buffer_printf (&self->out, "#OK: expiring '%s'\n", rule->string);
473                       rxpd_rule_delete (rule);
474                     }
475                 }
476             }
477           else
478             rxpd_buffer_printf (&self->out, "#OK:\n");
479         }
480     }
481   else if (!self->file)
482     {
483       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
484       rxpd_connection_delete (self);
485       return;
486     }
487
488   if (rxpd_buffer_state (&self->in) == RXPD_OK)
489     rxpd_connection_schedule (self);
490   else
491     {
492       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
493         rxpd_buffer_printf (&self->out, "#ERROR:\n");
494       rxpd_connection_delete (self);
495     }
496 }
497
498 /* Template
499 void
500 rxpd_connection_cmd_ (int fd, short event, void* ptr)
501 {
502   (void) fd;
503   (void) event;
504   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
505   struct rxpd_base* base = self->socket->base;
506   rxpd_die ("Unimplemented\n");
507 }
508 */