WIP: pth, cleanup, transistion mostly done
[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 #define RXPD_FILENAME_REQUIRED                                          \
25   do {                                                                  \
26     if (!self->file)                                                    \
27       {                                                                 \
28         rxpd_buffer_printf (&self->out, "#ERROR: missing filename\n");  \
29         return;                                                         \
30       }                                                                 \
31   } while (0)
32
33 void
34 rxpd_connection_cmd_CHECK (struct rxpd_connection* self)
35 {
36   RXPD_FILENAME_REQUIRED;
37
38   char* line;
39   while ((line = rxpd_buffer_readline (&self->in)))
40     {
41       if (*line == '\0')
42         {
43           rxpd_buffer_printf (&self->out, "#OK:\n");
44         }
45       else
46         {
47           // TODO lock the list, printf schedules!
48           // TODO implement RXPD_YIELD_EVERY
49           LLIST_FOREACH (&self->file->rules, n)
50             {
51               struct rxpd_rule* rule = (struct rxpd_rule*)n;
52               if (rule->string[0] != '#')
53                 {
54                   if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
55                     {
56                       if (rule->atime != (time_t) -1)
57                         time (&rule->atime);
58
59                       rxpd_buffer_printf (&self->out, "%s\n", rule->string);
60                       break;
61                     }
62                 }
63             }
64         }
65     }
66
67   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
68     rxpd_buffer_printf (&self->out, "#ERROR:\n");
69 }
70
71
72
73 static void
74 rxpd_connection_APPEND_PREPEND_helper (struct rxpd_connection* self)
75 {
76   char* line;
77
78   while ((line = rxpd_buffer_readline (&self->in)))
79     {
80       if (*line)
81         {
82           struct rxpd_rule* rule;
83           rule = rxpd_rule_new (line);
84           if (!rule)
85             abort();
86
87           llist_insert_tail (&self->tmp_list, &rule->node);
88         }
89     }
90
91   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
92     rxpd_buffer_printf (&self->out, "#ERROR:\n");
93   else
94     rxpd_buffer_printf (&self->out, "#OK:\n");
95 }
96
97 void
98 rxpd_connection_cmd_APPEND (struct rxpd_connection* self)
99 {
100   RXPD_FILENAME_REQUIRED;
101
102   rxpd_connection_APPEND_PREPEND_helper (self);
103   llist_insertlist_prev (&self->file->rules, &self->tmp_list);
104 }
105
106 void
107 rxpd_connection_cmd_PREPEND (struct rxpd_connection* self)
108 {
109   RXPD_FILENAME_REQUIRED;
110
111   rxpd_connection_APPEND_PREPEND_helper (self);
112   llist_insertlist_next (&self->file->rules, &self->tmp_list);
113 }
114
115 void
116 rxpd_connection_cmd_REMOVE (struct rxpd_connection* self)
117 {
118   RXPD_FILENAME_REQUIRED;
119
120   char* line;
121   while ((line = rxpd_buffer_readline (&self->in)))
122     {
123       LLIST_FOREACH (&self->file->rules, n)
124         {
125           struct rxpd_rule* rule = (struct rxpd_rule*)n;
126           if (strcmp (rule->string, line) == 0)
127             {
128               LList tmp = llist_prev (n);
129               rxpd_rule_delete (rule);
130               n = tmp;
131               rxpd_buffer_printf (&self->out, "#OK:\n");
132               goto done;
133             }
134         }
135       rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
136     done:
137       ;
138     }
139
140   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
141     rxpd_buffer_printf (&self->out, "#ERROR:\n");
142 }
143
144
145
146 static int
147 rxpd_connection_do_REPLACE (struct rxpd_connection* self)
148 {
149   struct rxpd_rule* rule;
150   LLIST_FOREACH (&self->file->rules, n)
151     {
152       rule = (struct rxpd_rule*)n;
153       if (strcmp (rule->string, self->tmp_str) == 0)
154         goto found;
155     }
156   return 0;
157
158  found:
159   llist_insertlist_next (&rule->node, &self->tmp_list);
160   rxpd_rule_delete (rule);
161   free (self->tmp_str);
162   self->tmp_str = NULL;
163   return 1;
164 }
165
166 void
167 rxpd_connection_cmd_REPLACE (struct rxpd_connection* self)
168 {
169   RXPD_FILENAME_REQUIRED;
170
171   char* line;
172   while ((line = rxpd_buffer_readline (&self->in)))
173     {
174       if (self->tmp_str)
175         {
176           if (*line)
177             {
178               struct rxpd_rule* rule;
179               rule = rxpd_rule_new (line);
180               if (!rule)
181                 abort();
182
183               llist_insert_tail (&self->tmp_list, &rule->node);
184             }
185           /* TODO handle empty lines? */
186         }
187       else
188         self->tmp_str = rxpd_strdup (line);
189     }
190
191   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
192     rxpd_buffer_printf (&self->out, "#ERROR:\n");
193
194   if (rxpd_connection_do_REPLACE (self))
195     rxpd_buffer_printf (&self->out, "#OK:\n");
196   else
197     rxpd_buffer_printf (&self->out, "#ERROR: no rule matching '%s'\n", self->tmp_str);
198
199 }
200
201 void
202 rxpd_connection_cmd_LOAD (struct rxpd_connection* self)
203 {
204   RXPD_FILENAME_REQUIRED;
205
206   if (rxpd_file_load (self->file))
207     rxpd_buffer_printf (&self->out, "#OK:\n");
208   else
209     rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
210 }
211
212 void
213 rxpd_connection_cmd_SAVE (struct rxpd_connection* self)
214 {
215   RXPD_FILENAME_REQUIRED;
216
217   if (rxpd_file_save (self->file))
218     rxpd_buffer_printf (&self->out, "#OK:\n");
219   else
220     rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
221 }
222
223
224 void
225 rxpd_connection_cmd_DELETE (struct rxpd_connection* self)
226 {
227   RXPD_FILENAME_REQUIRED;
228
229   rxpd_file_delete (self->file);
230   rxpd_buffer_printf (&self->out, "#OK:\n");
231 }
232
233 void
234 rxpd_connection_cmd_FETCH (struct rxpd_connection* self)
235 {
236   RXPD_FILENAME_REQUIRED;
237
238   struct rxpd_base* base = self->socket->base;
239
240   char* line;
241   line = rxpd_buffer_readline (&self->in);
242   if (*line)
243     {
244       rxpd_file_rules_delete (self->file);
245
246       char* list = strrchr (line, '/');
247       // TODO error handling
248       if (!list)
249         rxpd_die ("syntax error");
250
251       *list = '\0';
252       ++ list;
253
254       char* port = strrchr (line, ':');
255       // TODO error handling / unix domain sockets
256       if (!port)
257         rxpd_die ("syntax error");
258
259       *port = '\0';
260       ++ port;
261
262       struct addrinfo* addrs = NULL;
263       int aierr;
264
265       aierr = getaddrinfo (line, port, NULL, &addrs);
266
267       rxpd_log (base, LOG_INFO, "fetching list '%s' from '%s(%s)' at port '%s' to '%s'\n",
268                 list,
269                 line,
270                 inet_ntoa (((struct sockaddr_in*)addrs->ai_addr)->sin_addr),
271                 port,
272                 self->file->node.key);
273
274       if (aierr)
275         rxpd_die ("resolv error %s\n", gai_strerror (aierr));
276
277       // connect
278       // send dump
279       // add recieving event
280
281       freeaddrinfo (addrs);
282     }
283 }
284
285 void
286 rxpd_connection_cmd_DUMP (struct rxpd_connection* self)
287 {
288   RXPD_FILENAME_REQUIRED;
289
290   if (llist_is_empty (&self->file->rules))
291     rxpd_buffer_printf (&self->out, "#OK:\n");
292   else
293     {
294       LLIST_FOREACH (&self->file->rules, n)
295         {
296           struct rxpd_rule* rule = (struct rxpd_rule*)n;
297           if (rule->atime != (time_t)-1)
298             rxpd_buffer_printf (&self->out, "%ld:%s\n", rule->atime, rule->string);
299           else if (*rule->string != '#')
300             rxpd_buffer_printf (&self->out, ":%s\n", rule->string);
301           else
302             rxpd_buffer_printf (&self->out, "%s\n", rule->string);
303         }
304     }
305 }
306
307
308 static psplay_delete_t
309 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
310 {
311   (void) level;
312   struct rxpd_file* file = (struct rxpd_file*) node;
313   struct rxpd_connection* conn = (struct rxpd_connection*) data;
314
315   if (which == PSPLAY_INORDER)
316     rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
317
318   return PSPLAY_CONT;
319 }
320
321
322 void
323 rxpd_connection_cmd_LIST (struct rxpd_connection* self)
324 {
325   struct rxpd_base* base = self->socket->base;
326
327   if (psplay_isempty_root (&base->files))
328     rxpd_buffer_printf (&self->out, "#OK:\n");
329   else
330     psplay_walk (&base->files, NULL, walk_LIST, 0, self);
331 }
332
333 void
334 rxpd_connection_cmd_SHUTDOWN (struct rxpd_connection* self)
335 {
336   (void) self;
337   //struct rxpd_base* base = self->socket->base;
338   // destroy all sockets
339 #if 0
340   LLIST_WHILE_HEAD (&base->sockets, n)
341     {
342       struct rxpd_socket* socket = (struct rxpd_socket*)n;
343       rxpd_socket_delete (socket);
344     }
345   rxpd_buffer_printf (&self->out, "#OK:\n");
346 #endif
347 }
348
349
350 void
351 rxpd_connection_cmd_VERSION (struct rxpd_connection* self)
352 {
353   rxpd_buffer_printf (&self->out, PACKAGE_STRING "\n#\n"
354                       "# Copyright (C)\n"
355                       "#   2007,               Christian Thaeter <ct@pipapo.org>\n#\n"
356                       "# This is free software.  You may redistribute copies of it under the terms of\n"
357                       "# the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
358                       "# There is NO WARRANTY, to the extent permitted by law.\n#\n"
359                       "# http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
360 }
361
362 void
363 rxpd_connection_cmd_HELP (struct rxpd_connection* self)
364 {
365   //struct rxpd_base* base = self->socket->base;
366   rxpd_buffer_printf (&self->out, "# Available commands:\n#\n");
367 #define RXPD_CMD(cmd, help)     rxpd_buffer_printf (&self->out, "# %s %s.\n", #cmd, help);
368   RXPD_COMMANDS
369 #undef RXPD_CMD
370   rxpd_buffer_printf (&self->out, "#\n# general syntax is: 'COMMAND:listname\\n..data..'\n");
371 }
372
373 void
374 rxpd_connection_cmd_EXPIRE (struct rxpd_connection* self)
375 {
376   RXPD_FILENAME_REQUIRED;
377
378   struct rxpd_base* base = self->socket->base;
379
380   char* line = rxpd_buffer_readline (&self->in);
381   if (*line)
382     {
383       // TODO strtol, error handling
384       time_t since = time (NULL) - atoi (line);
385       rxpd_log (base, LOG_INFO, "expire all entries in '%s' since %ld\n",
386                 (const char*) self->file->node.key, since);
387
388       LLIST_FOREACH (&self->file->rules, n)
389         {
390           struct rxpd_rule* rule = (struct rxpd_rule*)n;
391           if (rule->atime != -1 && rule->atime < since)
392             {
393               n = llist_prev (n);
394               rxpd_log (base, LOG_DEBUG, "expiring %ld:%s\n", rule->atime, rule->string);
395               rxpd_buffer_printf (&self->out, "#OK: expiring '%s'\n", rule->string);
396               rxpd_rule_delete (rule);
397             }
398         }
399     }
400   else
401     rxpd_buffer_printf (&self->out, "#ERROR: no age given\n");
402 }
403
404 /* Template
405 void
406 rxpd_connection_cmd_ (struct rxpd_connection* self)
407 {
408   struct rxpd_base* base = self->socket->base;
409   rxpd_die ("Unimplemented\n");
410 }
411 */