EXPIRE 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_DUMP (int fd, short event, void* ptr)
319 {
320   (void) fd;
321   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
322
323   if (!event && !self->file)
324     {
325       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
326       rxpd_connection_delete (self);
327       return;
328     }
329
330   if (llist_is_empty (&self->file->rules))
331     rxpd_buffer_printf (&self->out, "#OK:\n");
332   else
333     {
334       LLIST_FOREACH (&self->file->rules, n)
335         {
336           struct rxpd_rule* rule = (struct rxpd_rule*)n;
337           if (rule->atime != (time_t)-1)
338             rxpd_buffer_printf (&self->out, "%ld:%s\n", rule->atime, rule->string);
339           else if (*rule->string != '#')
340             rxpd_buffer_printf (&self->out, ":%s\n", rule->string);
341           else
342             rxpd_buffer_printf (&self->out, "%s\n", rule->string);
343         }
344     }
345
346   rxpd_connection_delete (self);
347 }
348
349
350 static psplay_delete_t
351 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
352 {
353   (void) level;
354   struct rxpd_file* file = (struct rxpd_file*) node;
355   struct rxpd_connection* conn = (struct rxpd_connection*) data;
356
357   if (which == PSPLAY_INORDER)
358     rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
359
360   return PSPLAY_CONT;
361 }
362
363
364 void
365 rxpd_connection_cmd_LIST (int fd, short event, void* ptr)
366 {
367   (void) fd;
368   (void) event;
369   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
370   struct rxpd_base* base = self->socket->base;
371
372   if (psplay_isempty_root (&base->files))
373     rxpd_buffer_printf (&self->out, "#OK:\n");
374   else
375     psplay_walk (&base->files, NULL, walk_LIST, 0, ptr);
376
377   rxpd_connection_delete (self);
378 }
379
380 void
381 rxpd_connection_cmd_SHUTDOWN (int fd, short event, void* ptr)
382 {
383   (void) fd;
384   (void) event;
385   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
386   struct rxpd_base* base = self->socket->base;
387   // destroy all sockets
388   LLIST_WHILE_HEAD (&base->sockets, n)
389     {
390       struct rxpd_socket* socket = (struct rxpd_socket*)n;
391       rxpd_socket_delete (socket);
392     }
393   rxpd_buffer_printf (&self->out, "#OK:\n");
394   rxpd_connection_delete (self);
395 }
396
397
398 void
399 rxpd_connection_cmd_VERSION (int fd, short event, void* ptr)
400 {
401   (void) fd;
402   (void) event;
403   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
404   rxpd_buffer_printf (&self->out, PACKAGE_STRING "\n#\n"
405                       "# Copyright (C)\n"
406                       "#   2007,               Christian Thaeter <ct@pipapo.org>\n#\n"
407                       "# This is free software.  You may redistribute copies of it under the terms of\n"
408                       "# the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
409                       "# There is NO WARRANTY, to the extent permitted by law.\n#\n"
410                       "# http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
411   rxpd_connection_delete (self);
412 }
413
414 void
415 rxpd_connection_cmd_HELP (int fd, short event, void* ptr)
416 {
417   (void) fd;
418   (void) event;
419   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
420   //struct rxpd_base* base = self->socket->base;
421   rxpd_buffer_printf (&self->out, "# Available commands:\n#\n");
422 #define RXPD_CMD(cmd, help)     rxpd_buffer_printf (&self->out, "# %s %s.\n", #cmd, help);
423   RXPD_COMMANDS
424 #undef RXPD_CMD
425   rxpd_buffer_printf (&self->out, "#\n# general syntax is: 'COMMAND:listname\\n..data..'\n");
426   rxpd_connection_delete (self);
427 }
428
429 void
430 rxpd_connection_cmd_EXPIRE (int fd, short event, void* ptr)
431 {
432   (void) fd;
433   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
434   struct rxpd_base* base = self->socket->base;
435
436   if (event == EV_READ)
437     {
438       int again = -1;
439       char* line;
440       while ((line = rxpd_buffer_readline (&self->in, ++again)))
441         {
442           if (*line)
443             {
444               time_t since = time (NULL) - atoi (line);
445               rxpd_log (base, LOG_INFO, "expire all entries in '%s' since %ld\n",
446                         (const char*) self->file->node.key, since);
447
448               LLIST_FOREACH (&self->file->rules, n)
449                 {
450                   struct rxpd_rule* rule = (struct rxpd_rule*)n;
451                   if (rule->atime != -1 && rule->atime < since)
452                     {
453                       n = llist_prev (n);
454                       rxpd_log (base, LOG_DEBUG, "expiring %ld:%s\n", rule->atime, rule->string);
455                       rxpd_buffer_printf (&self->out, "#OK: expiring '%s'\n", rule->string);
456                       rxpd_rule_delete (rule);
457                     }
458                 }
459             }
460           else
461             rxpd_buffer_printf (&self->out, "#OK:\n");
462         }
463     }
464   else if (!self->file)
465     {
466       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
467       rxpd_connection_delete (self);
468       return;
469     }
470
471   if (rxpd_buffer_state (&self->in) == RXPD_OK)
472     rxpd_connection_schedule (self);
473   else
474     {
475       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
476         rxpd_buffer_printf (&self->out, "#ERROR:\n");
477       rxpd_connection_delete (self);
478     }
479 }
480
481 /* Template
482 void
483 rxpd_connection_cmd_ (int fd, short event, void* ptr)
484 {
485   (void) fd;
486   (void) event;
487   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
488   struct rxpd_base* base = self->socket->base;
489   rxpd_die ("Unimplemented\n");
490 }
491 */