renamed SYNC to UPDATE
[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 implement RXPD_YIELD_EVERY
48           pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RD, FALSE, NULL);
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           pth_rwlock_release (&self->file->lock);
65         }
66     }
67
68   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
69     rxpd_buffer_printf (&self->out, "#ERROR:\n");
70 }
71
72
73
74 static void
75 rxpd_connection_APPEND_PREPEND_helper (struct rxpd_connection* self)
76 {
77   char* line;
78
79   while ((line = rxpd_buffer_readline (&self->in)))
80     {
81       if (*line && strncmp (line, "#EXIT:", sizeof ("#EXIT:")-1))
82         {
83           struct rxpd_rule* rule;
84           rule = rxpd_rule_new (line);
85           if (!rule)
86             abort();
87
88           llist_insert_tail (&self->tmp_list, &rule->node);
89         }
90       else
91         break;  /* exit at empty line, error or whatever */
92     }
93
94   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
95     rxpd_buffer_printf (&self->out, "#ERROR:\n");
96   else
97     rxpd_buffer_printf (&self->out, "#OK:\n");
98 }
99
100 void
101 rxpd_connection_cmd_APPEND (struct rxpd_connection* self)
102 {
103   RXPD_FILENAME_REQUIRED;
104
105   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
106
107   rxpd_connection_APPEND_PREPEND_helper (self);
108   llist_insertlist_prev (&self->file->rules, &self->tmp_list);
109
110   pth_rwlock_release (&self->file->lock);
111 }
112
113 void
114 rxpd_connection_cmd_PREPEND (struct rxpd_connection* self)
115 {
116   RXPD_FILENAME_REQUIRED;
117
118   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
119
120   rxpd_connection_APPEND_PREPEND_helper (self);
121   llist_insertlist_next (&self->file->rules, &self->tmp_list);
122
123   pth_rwlock_release (&self->file->lock);
124 }
125
126 void
127 rxpd_connection_cmd_REMOVE (struct rxpd_connection* self)
128 {
129   RXPD_FILENAME_REQUIRED;
130
131   char* line;
132   while ((line = rxpd_buffer_readline (&self->in)))
133     {
134       if (*line && strncmp (line, "#EXIT:", sizeof ("#EXIT:")-1))
135         {
136           pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
137           LLIST_FOREACH (&self->file->rules, n)
138             {
139               struct rxpd_rule* rule = (struct rxpd_rule*)n;
140               if (strcmp (rule->string, line) == 0)
141                 {
142                   LList tmp = llist_prev (n);
143                   rxpd_rule_delete (rule);
144                   n = tmp;
145                   rxpd_buffer_printf (&self->out, "#OK:\n");
146                   goto done;
147                 }
148             }
149           rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
150         done:
151           pth_rwlock_release (&self->file->lock);
152         }
153       else
154         break;  /* exit at empty line, error or whatever */
155     }
156
157   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
158     rxpd_buffer_printf (&self->out, "#ERROR:\n");
159 }
160
161
162
163 static int
164 rxpd_connection_do_REPLACE (struct rxpd_connection* self)
165 {
166   struct rxpd_rule* rule;
167
168   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
169
170   LLIST_FOREACH (&self->file->rules, n)
171     {
172       rule = (struct rxpd_rule*)n;
173       if (strcmp (rule->string, self->tmp_str) == 0)
174         goto found;
175     }
176   pth_rwlock_release (&self->file->lock);
177   return 0;
178
179  found:
180   llist_insertlist_next (&rule->node, &self->tmp_list);
181   rxpd_rule_delete (rule);
182   pth_rwlock_release (&self->file->lock);
183
184   free (self->tmp_str);
185   self->tmp_str = NULL;
186   return 1;
187 }
188
189 void
190 rxpd_connection_cmd_REPLACE (struct rxpd_connection* self)
191 {
192   RXPD_FILENAME_REQUIRED;
193
194   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
195
196   char* line;
197   while ((line = rxpd_buffer_readline (&self->in)))
198     {
199       if (*line && strncmp (line, "#EXIT:", sizeof ("#EXIT:")-1))
200         {
201           if (self->tmp_str)
202             {
203               struct rxpd_rule* rule;
204               rule = rxpd_rule_new (line);
205               if (rule)
206                 llist_insert_tail (&self->tmp_list, &rule->node);
207               else
208                 rxpd_buffer_printf (&self->out, "#ERROR: illegal rule '%s'\n", line);
209             }
210           else
211             self->tmp_str = rxpd_strdup (line);
212         }
213       else
214         break;  /* exit at empty line, error or whatever */
215     }
216
217   pth_rwlock_release (&self->file->lock);
218
219   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
220     rxpd_buffer_printf (&self->out, "#ERROR:\n");
221
222   if (rxpd_connection_do_REPLACE (self))
223     rxpd_buffer_printf (&self->out, "#OK:\n");
224   else
225     rxpd_buffer_printf (&self->out, "#ERROR: no rule matching '%s'\n", self->tmp_str);
226 }
227
228 void
229 rxpd_connection_cmd_LOAD (struct rxpd_connection* self)
230 {
231   RXPD_FILENAME_REQUIRED;
232
233   if (rxpd_file_load (self->file))
234     rxpd_buffer_printf (&self->out, "#OK:\n");
235   else
236     rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
237 }
238
239 void
240 rxpd_connection_cmd_SAVE (struct rxpd_connection* self)
241 {
242   RXPD_FILENAME_REQUIRED;
243
244   if (rxpd_file_save (self->file))
245     rxpd_buffer_printf (&self->out, "#OK:\n");
246   else
247     rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
248 }
249
250
251 void
252 rxpd_connection_cmd_DELETE (struct rxpd_connection* self)
253 {
254   RXPD_FILENAME_REQUIRED;
255
256   rxpd_file_delete (self->file);
257   rxpd_buffer_printf (&self->out, "#OK:\n");
258 }
259
260 void
261 rxpd_connection_cmd_FETCH (struct rxpd_connection* self)
262 {
263   RXPD_FILENAME_REQUIRED;
264
265   struct rxpd_base* base = self->socket->base;
266
267   char* line;
268   line = rxpd_buffer_readline (&self->in);
269   if (*line)
270     {
271       /* parse line */
272
273       char* list = strrchr (line, '/');
274       if (!list)
275         goto esyntax;
276
277       *list = '\0';
278       ++ list;
279
280       char* port = strrchr (line, ':');
281       if (!port)
282         goto esyntax;
283
284       *port = '\0';
285       ++ port;
286
287       struct addrinfo* addrs = NULL;
288       int aierr;
289
290       /* resolve peer */
291       aierr = getaddrinfo (line, port, NULL, &addrs);
292       if (aierr)
293         {
294           rxpd_buffer_printf (&self->out, "#ERROR: resolving '%s': %s\n", line, gai_strerror (aierr));
295           return;
296         }
297
298       rxpd_log (base, LOG_INFO, "fetching list '%s' from '%s(%s)' at port '%s' to '%s'\n",
299                 list,
300                 line,
301                 inet_ntoa (((struct sockaddr_in*)addrs->ai_addr)->sin_addr),
302                 port,
303                 self->file->node.key);
304
305       /* establish connection */
306       int fd = socket (addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
307       if (fd == -1 || connect (fd, addrs->ai_addr, addrs->ai_addrlen))
308         {
309           freeaddrinfo (addrs);
310           rxpd_buffer_printf (&self->out, "#ERROR: error connecting '%s': %s\n", line, strerror (errno));
311           return;
312         }
313  
314       freeaddrinfo (addrs);
315
316       /* send DUMP command and fetch data */
317       struct rxpd_buffer in;
318       struct rxpd_buffer out;
319       rxpd_buffer_init (&in, fd);
320       rxpd_buffer_init (&out, fd);
321
322       rxpd_buffer_printf (&out, "DUMP:%s\n", list);
323
324       char* line;
325       while ((line = rxpd_buffer_readline (&in)))
326         {
327           if (*line && strncmp (line, "#ERROR:", sizeof ("#ERROR:")-1))
328             {
329               struct rxpd_rule* rule;
330               rule = rxpd_rule_new (line);
331               if (!rule)
332                 rxpd_die ("rule creation failed on '%s'\n", line);
333
334               llist_insert_tail (&self->tmp_list, &rule->node);
335             }
336           else
337             rxpd_log (base, LOG_INFO, "purge remote side error '%s'\n", line);
338         }
339       rxpd_log (base, LOG_DEBUG, "finished dump\n");
340
341       close (fd);
342
343       pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
344       rxpd_file_rules_delete (self->file);
345       llist_insertlist_next (&self->file->rules, &self->tmp_list);
346       pth_rwlock_release (&self->file->lock);
347
348       rxpd_buffer_printf (&self->out, "#OK:\n");
349     }
350   else
351     {
352     esyntax:
353       rxpd_buffer_printf (&self->out, "#ERROR: syntax error\n");
354     }
355 }
356
357 void
358 rxpd_connection_cmd_DUMP (struct rxpd_connection* self)
359 {
360   RXPD_FILENAME_REQUIRED;
361   rxpd_file_dump (self->file, &self->out);
362 }
363
364
365 static psplay_delete_t
366 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
367 {
368   (void) level;
369   struct rxpd_file* file = (struct rxpd_file*) node;
370   struct rxpd_connection* conn = (struct rxpd_connection*) data;
371
372   if (which == PSPLAY_INORDER)
373     rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
374
375   return PSPLAY_CONT;
376 }
377
378
379 void
380 rxpd_connection_cmd_LIST (struct rxpd_connection* self)
381 {
382   struct rxpd_base* base = self->socket->base;
383
384   if (psplay_isempty_root (&base->files))
385     rxpd_buffer_printf (&self->out, "#OK:\n");
386   else
387     psplay_walk (&base->files, NULL, walk_LIST, 0, self);
388 }
389
390 void
391 rxpd_connection_cmd_SHUTDOWN (struct rxpd_connection* self)
392 {
393   struct rxpd_base* base = self->socket->base;
394
395   LLIST_FOREACH (&base->sockets, n)
396     {
397       struct rxpd_socket* socket = (struct rxpd_socket*)n;
398       rxpd_socket_cancel (socket);
399     }
400
401   rxpd_buffer_printf (&self->out, "#OK:\n");
402 }
403
404
405 void
406 rxpd_connection_cmd_VERSION (struct rxpd_connection* self)
407 {
408   rxpd_buffer_printf (&self->out, PACKAGE_STRING "\n#\n"
409                       "# Copyright (C)\n"
410                       "#   2007,               Christian Thaeter <ct@pipapo.org>\n#\n"
411                       "# This is free software.  You may redistribute copies of it under the terms of\n"
412                       "# the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
413                       "# There is NO WARRANTY, to the extent permitted by law.\n#\n"
414                       "# http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
415 }
416
417 void
418 rxpd_connection_cmd_HELP (struct rxpd_connection* self)
419 {
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 }
427
428 void
429 rxpd_connection_cmd_EXPIRE (struct rxpd_connection* self)
430 {
431   RXPD_FILENAME_REQUIRED;
432
433   struct rxpd_base* base = self->socket->base;
434
435   char* line = rxpd_buffer_readline (&self->in);
436   if (*line)
437     {
438       // TODO strtol, error handling
439       time_t since = time (NULL) - atoi (line);
440       rxpd_log (base, LOG_INFO, "expire all entries in '%s' since %ld\n",
441                 (const char*) self->file->node.key, since);
442
443       pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
444
445       LLIST_FOREACH (&self->file->rules, n)
446         {
447           struct rxpd_rule* rule = (struct rxpd_rule*)n;
448           if (rule->atime != -1 && rule->atime < since)
449             {
450               n = llist_prev (n);
451               rxpd_log (base, LOG_DEBUG, "expiring %ld:%s\n", rule->atime, rule->string);
452               rxpd_buffer_printf (&self->out, "#OK: expiring '%s'\n", rule->string);
453               rxpd_rule_delete (rule);
454             }
455         }
456
457       pth_rwlock_release (&self->file->lock);
458     }
459   else
460     rxpd_buffer_printf (&self->out, "#ERROR: no age given\n");
461 }
462
463 void
464 rxpd_connection_cmd_UPDATE (struct rxpd_connection* self)
465 {
466   RXPD_FILENAME_REQUIRED;
467
468   struct rxpd_base* base = self->socket->base;
469
470
471   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
472
473   char* line;
474   while ((line = rxpd_buffer_readline (&self->in)))
475     {
476       /* for each line which is a listname */
477       struct rxpd_file* source;
478       source = (struct rxpd_file*) psplay_find (&base->files, line);
479       if (source)
480         {
481           pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
482
483           /* find first line which is in source and self->file, this is our start */
484           LList source_itr = NULL;
485           LList file_itr = NULL;
486           LLIST_FOREACH (&source->rules, n)
487             {
488               struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
489               LLIST_FOREACH (&self->file->rules, m)
490                 {
491                   struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
492                   if (strcmp (rule_n->string, rule_m->string) == 0)
493                     {
494                       source_itr = &rule_n->node;
495                       file_itr = &rule_m->node;
496                       rxpd_log (base, LOG_DEBUG, "starting at '%s'\n", rule_n->string);
497                       goto leave_loop;
498                     }
499                 }
500             }
501         leave_loop:
502           if (!source_itr)
503             {
504               rxpd_log (base, LOG_DEBUG, "no common line found between '%s' and '%s'\n",
505                         (const char*)self->file->node.key, line);
506               pth_rwlock_release (&source->lock);
507               break;
508             }
509
510           /* Now we go for each rule in source which has a atime find the matching rule in self->file and update its atime */
511           /* This algorihm assumes that lists stay ordered (insertions/deletions are supported, but not reordering) */
512           /* advantage is that the cost is O(N+M) instead O(N*M) */
513           LLIST_FORRANGE (source_itr, &source->rules, n)
514             {
515               struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
516               if (rule_n->atime != (time_t)-1)
517                 {
518                   LLIST_FORRANGE (file_itr, &self->file->rules, m)
519                     {
520                       struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
521                       if (strcmp (rule_n->string, rule_m->string) == 0)
522                         {
523                           /* found the rule */
524                           if (rule_m->atime != (time_t)-1 && rule_m->atime < rule_n->atime)
525                             {
526                               rxpd_log (base, LOG_DEBUG,
527                                         "updating atime of rule '%s' from %ld to %ld\n",
528                                         rule_m->string,
529                                         rule_m->atime,
530                                         rule_n->atime
531                                         );
532                               rule_m->atime = rule_n->atime;
533                             }
534
535                           file_itr = llist_next (m);
536                           break;
537                         }
538                     }
539                 }
540             }
541
542           pth_rwlock_release (&source->lock);
543         }
544       else
545         rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
546     }
547
548   pth_rwlock_release (&self->file->lock);
549
550   rxpd_buffer_printf (&self->out, "#OK:\n");
551 }
552
553 /* Template
554 void
555 rxpd_connection_cmd_ (struct rxpd_connection* self)
556 {
557   RXPD_FILENAME_REQUIRED;
558   //struct rxpd_base* base = self->socket->base;
559   rxpd_buffer_printf (&self->out, "#ERROR: Unimplemented\n");
560 }
561 */