Save only existing files automatically, user must use an explicit SAVE for the first...
[rxpd.git] / src / rxpd_connection_cmd.c
blobb4d3c620dd3fdcc6a5d8f54d0c06cd13a5472d53
1 /*
2 rxpd_connection_cmd.c - regex policy daemon
4 Copyright (C)
5 2007, Christian Thaeter <ct@pipapo.org>
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.
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.
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.
22 #include "rxpd.h"
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)
34 static char*
35 rxpd_connection_do_CHECK (struct rxpd_connection* self, char* line, struct rxpd_file* file)
37 char* ret = NULL;
39 // TODO implement RXPD_YIELD_EVERY
40 pth_rwlock_acquire (&file->lock, PTH_RWLOCK_RD, FALSE, NULL);
41 LLIST_FOREACH (&file->rules, n)
43 struct rxpd_rule* rule = (struct rxpd_rule*)n;
44 if (rule->string[0] != '#')
46 if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
48 if (rule->atime != (time_t) -1)
49 time (&rule->atime);
51 ret = rule->string;
53 if (*ret == '>')
55 struct rxpd_base* base = self->socket->base;
57 size_t sublen =
58 (size_t)(strchr (&rule->string[1], ':') - &rule->string[1]);
60 char* subname =
61 alloca (strlen (file->node.key) + sublen + 1);
62 *subname = '\0';
64 strcat (subname, file->node.key);
65 strncat (subname, &rule->string[1], sublen);
67 struct rxpd_file* subfile = (struct rxpd_file*) psplay_find (&base->files, subname);
68 if (subfile)
70 rxpd_log (base, LOG_DEBUG, "going to sublist '%s'\n", subname);
71 ret = rxpd_connection_do_CHECK (self, line, subfile);
73 else
74 rxpd_log (base, LOG_ERR, "sublist '%s' does not exist\n", subname);
77 if (ret)
78 break;
82 pth_rwlock_release (&file->lock);
83 return ret;
87 void
88 rxpd_connection_cmd_CHECK (struct rxpd_connection* self)
90 RXPD_FILENAME_REQUIRED;
92 char* line;
93 while ((line = rxpd_buffer_readline (&self->in)))
95 if (*line == '\0')
97 rxpd_buffer_printf (&self->out, "#OK:\n");
99 else
101 char* match = rxpd_connection_do_CHECK (self, line, self->file);
103 if (match)
104 rxpd_buffer_printf (&self->out, "%s\n", match);
108 if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
109 rxpd_buffer_printf (&self->out, "#ERROR:\n");
113 static void
114 rxpd_connection_APPEND_PREPEND_helper (struct rxpd_connection* self)
116 char* line;
118 while ((line = rxpd_buffer_readline (&self->in)))
120 if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
122 struct rxpd_rule* rule;
124 rule = rxpd_rule_new (line, self->socket->base);
125 llist_insert_tail (&self->tmp_list, &rule->node);
127 else
128 break; /* exit at empty line, error or whatever */
131 if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
132 rxpd_buffer_printf (&self->out, "#ERROR:\n");
133 else
134 rxpd_buffer_printf (&self->out, "#OK:\n");
137 void
138 rxpd_connection_cmd_APPEND (struct rxpd_connection* self)
140 RXPD_FILENAME_REQUIRED;
142 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
144 rxpd_connection_APPEND_PREPEND_helper (self);
145 llist_insertlist_prev (&self->file->rules, &self->tmp_list);
147 pth_rwlock_release (&self->file->lock);
150 void
151 rxpd_connection_cmd_PREPEND (struct rxpd_connection* self)
153 RXPD_FILENAME_REQUIRED;
155 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
157 rxpd_connection_APPEND_PREPEND_helper (self);
158 llist_insertlist_next (&self->file->rules, &self->tmp_list);
160 pth_rwlock_release (&self->file->lock);
163 void
164 rxpd_connection_cmd_REMOVE (struct rxpd_connection* self)
166 RXPD_FILENAME_REQUIRED;
168 char* line;
169 while ((line = rxpd_buffer_readline (&self->in)))
171 if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
173 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
174 LLIST_FOREACH (&self->file->rules, n)
176 struct rxpd_rule* rule = (struct rxpd_rule*)n;
177 if (strcmp (rule->string, line) == 0)
179 LList tmp = llist_prev (n);
180 rxpd_rule_delete (rule);
181 n = tmp;
182 rxpd_buffer_printf (&self->out, "#OK:\n");
183 goto done;
186 rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
187 done:
188 pth_rwlock_release (&self->file->lock);
190 else
191 break; /* exit at empty line, error or whatever */
194 if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
195 rxpd_buffer_printf (&self->out, "#ERROR:\n");
200 static int
201 rxpd_connection_do_REPLACE (struct rxpd_connection* self)
203 struct rxpd_rule* rule;
205 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
207 LLIST_FOREACH (&self->file->rules, n)
209 rule = (struct rxpd_rule*)n;
210 if (strcmp (rule->string, self->tmp_str) == 0)
211 goto found;
213 pth_rwlock_release (&self->file->lock);
214 return 0;
216 found:
217 llist_insertlist_next (&rule->node, &self->tmp_list);
218 rxpd_rule_delete (rule);
219 pth_rwlock_release (&self->file->lock);
221 free (self->tmp_str);
222 self->tmp_str = NULL;
223 return 1;
226 void
227 rxpd_connection_cmd_REPLACE (struct rxpd_connection* self)
229 RXPD_FILENAME_REQUIRED;
231 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
233 char* line;
234 while ((line = rxpd_buffer_readline (&self->in)))
236 if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
238 if (self->tmp_str)
240 struct rxpd_rule* rule;
241 rule = rxpd_rule_new (line, self->socket->base);
242 if (rule)
243 llist_insert_tail (&self->tmp_list, &rule->node);
244 else
245 rxpd_buffer_printf (&self->out, "#ERROR: illegal rule '%s'\n", line);
247 else
248 self->tmp_str = rxpd_strdup (line);
250 else
251 break; /* exit at empty line, error or whatever */
254 pth_rwlock_release (&self->file->lock);
256 if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
257 rxpd_buffer_printf (&self->out, "#ERROR:\n");
259 if (rxpd_connection_do_REPLACE (self))
260 rxpd_buffer_printf (&self->out, "#OK:\n");
261 else
262 rxpd_buffer_printf (&self->out, "#ERROR: no rule matching '%s'\n", self->tmp_str);
265 void
266 rxpd_connection_cmd_LOAD (struct rxpd_connection* self)
268 RXPD_FILENAME_REQUIRED;
270 if (rxpd_file_load (self->file))
271 rxpd_buffer_printf (&self->out, "#OK:\n");
272 else
273 rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
276 void
277 rxpd_connection_cmd_SAVE (struct rxpd_connection* self)
279 RXPD_FILENAME_REQUIRED;
281 if (rxpd_file_save (self->file, 1))
282 rxpd_buffer_printf (&self->out, "#OK:\n");
283 else
284 rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
288 void
289 rxpd_connection_cmd_CLEAR (struct rxpd_connection* self)
291 RXPD_FILENAME_REQUIRED;
293 rxpd_file_rules_delete (self->file);
294 rxpd_buffer_printf (&self->out, "#OK:\n");
297 void
298 rxpd_connection_cmd_DELETE (struct rxpd_connection* self)
300 RXPD_FILENAME_REQUIRED;
302 rxpd_file_delete (self->file);
303 rxpd_buffer_printf (&self->out, "#OK:\n");
306 void
307 rxpd_connection_cmd_FETCH (struct rxpd_connection* self)
309 RXPD_FILENAME_REQUIRED;
311 struct rxpd_base* base = self->socket->base;
313 char* line;
314 line = rxpd_buffer_readline (&self->in);
315 if (*line)
317 /* parse line */
318 char* list = strrchr (line, ':');
319 if (!list)
320 goto esyntax;
322 *list = '\0';
323 ++ list;
325 char* port = strrchr (line, ':');
326 if (!port)
327 goto esyntax;
329 *port = '\0';
330 ++ port;
332 struct addrinfo* addrs = NULL;
333 int aierr;
335 /* resolve peer */
336 aierr = getaddrinfo (line, port, NULL, &addrs);
337 if (aierr)
339 rxpd_buffer_printf (&self->out, "#ERROR: resolving '%s': %s\n", line, gai_strerror (aierr));
340 return;
343 rxpd_log (base, LOG_INFO, "fetching list '%s' from '%s(%s)' at port '%s' to '%s'\n",
344 list,
345 line,
346 inet_ntoa (((struct sockaddr_in*)addrs->ai_addr)->sin_addr),
347 port,
348 self->file->node.key);
350 /* establish connection */
351 int fd = socket (addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
352 if (fd == -1 || connect (fd, addrs->ai_addr, addrs->ai_addrlen))
354 freeaddrinfo (addrs);
355 rxpd_buffer_printf (&self->out, "#ERROR: error connecting '%s': %s\n", line, strerror (errno));
356 return;
359 freeaddrinfo (addrs);
361 /* send DUMP command and fetch data */
362 struct rxpd_buffer in;
363 struct rxpd_buffer out;
364 rxpd_buffer_init (&in, fd);
365 rxpd_buffer_init (&out, fd);
367 rxpd_buffer_printf (&out, "DUMP:%s\n", list);
369 char* line;
370 while ((line = rxpd_buffer_readline (&in)))
372 if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
374 struct rxpd_rule* rule;
375 rule = rxpd_rule_new (line, base);
376 if (!rule)
377 rxpd_die ("rule creation failed on '%s'\n", line);
379 llist_insert_tail (&self->tmp_list, &rule->node);
381 else
382 rxpd_log (base, LOG_INFO, "purge remote side error '%s'\n", line);
384 rxpd_log (base, LOG_DEBUG, "finished dump\n");
386 close (fd);
388 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
389 rxpd_file_rules_delete (self->file);
390 llist_insertlist_next (&self->file->rules, &self->tmp_list);
391 pth_rwlock_release (&self->file->lock);
393 rxpd_buffer_printf (&self->out, "#OK:\n");
395 else
397 esyntax:
398 rxpd_buffer_printf (&self->out, "#ERROR: syntax error\n");
402 void
403 rxpd_connection_cmd_DUMP (struct rxpd_connection* self)
405 RXPD_FILENAME_REQUIRED;
406 rxpd_file_dump (self->file, &self->out);
410 static psplay_delete_t
411 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
413 (void) level;
414 struct rxpd_file* file = (struct rxpd_file*) node;
415 struct rxpd_connection* conn = (struct rxpd_connection*) data;
417 if (which == PSPLAY_INORDER)
418 rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
420 return PSPLAY_CONT;
424 void
425 rxpd_connection_cmd_LIST (struct rxpd_connection* self)
427 struct rxpd_base* base = self->socket->base;
429 if (psplay_isempty_root (&base->files))
430 rxpd_buffer_printf (&self->out, "#OK:\n");
431 else
432 psplay_walk (&base->files, NULL, walk_LIST, 0, self);
435 void
436 rxpd_connection_cmd_SHUTDOWN (struct rxpd_connection* self)
438 /* not pth_raise, we don't want to schedule here! */
439 raise (SIGINT);
440 rxpd_buffer_printf (&self->out, "#OK:\n");
444 void
445 rxpd_connection_cmd_VERSION (struct rxpd_connection* self)
447 rxpd_buffer_printf (&self->out, PACKAGE_STRING "\n#\n"
448 "# Copyright (C)\n"
449 "# 2007, Christian Thaeter <ct@pipapo.org>\n#\n"
450 "# This is free software. You may redistribute copies of it under the terms of\n"
451 "# the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
452 "# There is NO WARRANTY, to the extent permitted by law.\n#\n"
453 "# http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
456 void
457 rxpd_connection_cmd_HELP (struct rxpd_connection* self)
459 rxpd_buffer_printf (&self->out, "# Available commands:\n#\n");
460 #define RXPD_CMD(cmd, help) rxpd_buffer_printf (&self->out, "# %s %s.\n", #cmd, help);
461 RXPD_COMMANDS
462 #undef RXPD_CMD
463 rxpd_buffer_printf (&self->out,
464 "#\n"
465 "# general syntax is: 'COMMAND:listname\\n..data..'\n"
466 "# see http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
469 void
470 rxpd_connection_cmd_EXPIRE (struct rxpd_connection* self)
472 RXPD_FILENAME_REQUIRED;
474 struct rxpd_base* base = self->socket->base;
476 char* line = rxpd_buffer_readline (&self->in);
477 if (*line)
479 // TODO strtol, error handling
480 time_t since = time (NULL) - atoi (line);
481 rxpd_log (base, LOG_INFO, "expire all entries in '%s' since %ld\n",
482 (const char*) self->file->node.key, since);
484 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
486 LLIST_FOREACH (&self->file->rules, n)
488 struct rxpd_rule* rule = (struct rxpd_rule*)n;
489 if (rule->atime != -1 && rule->atime < since)
491 rxpd_log (base, LOG_DEBUG, "expiring %ld:%s\n", rule->atime, rule->string);
492 rxpd_buffer_printf (&self->out, "#OK: expiring '%s'\n", rule->string);
493 if (!rxpd_rule_comment (rule, "EXPIRED"))
494 rxpd_die ("couldn't comment rule out");
498 pth_rwlock_release (&self->file->lock);
500 else
501 rxpd_buffer_printf (&self->out, "#ERROR: no age given\n");
504 void
505 rxpd_connection_cmd_UPDATE (struct rxpd_connection* self)
507 RXPD_FILENAME_REQUIRED;
509 struct rxpd_base* base = self->socket->base;
511 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
513 char* line;
514 while ((line = rxpd_buffer_readline (&self->in)))
516 /* for each line which is a listname */
517 struct rxpd_file* source;
518 source = (struct rxpd_file*) psplay_find (&base->files, line);
519 if (source)
521 pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
523 /* find first line which is in source and self->file, this is our start */
524 LList source_itr = NULL;
525 LList file_itr = NULL;
526 LLIST_FOREACH (&source->rules, n)
528 struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
529 LLIST_FOREACH (&self->file->rules, m)
531 struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
532 if (strcmp (rule_n->string, rule_m->string) == 0)
534 source_itr = &rule_n->node;
535 file_itr = &rule_m->node;
536 rxpd_log (base, LOG_DEBUG, "starting at '%s'\n", rule_n->string);
537 goto leave_loop;
541 leave_loop:
542 if (!source_itr)
544 rxpd_log (base, LOG_DEBUG, "no common line found between '%s' and '%s'\n",
545 (const char*)self->file->node.key, line);
546 pth_rwlock_release (&source->lock);
547 break;
550 /* Now we go for each rule in source which has a atime find the matching rule in self->file and update its atime */
551 /* This algorihm assumes that lists stay ordered (insertions/deletions are supported, but not reordering) */
552 /* advantage is that the algorithm has far less complexity */
553 LLIST_FORRANGE (source_itr, &source->rules, n)
555 struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
556 if (rule_n->atime != (time_t)-1)
558 LLIST_FORRANGE (file_itr, &self->file->rules, m)
560 struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
561 if (strcmp (rule_n->string, rule_m->string) == 0)
563 /* found the rule */
564 if (rule_m->atime != (time_t)-1 && rule_m->atime < rule_n->atime)
566 rxpd_log (base, LOG_DEBUG,
567 "updating atime of rule '%s' from %ld to %ld\n",
568 rule_m->string,
569 rule_m->atime,
570 rule_n->atime
572 rule_m->atime = rule_n->atime;
575 file_itr = llist_next (m);
576 break;
582 pth_rwlock_release (&source->lock);
584 else
585 rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
588 pth_rwlock_release (&self->file->lock);
590 rxpd_buffer_printf (&self->out, "#OK:\n");
594 void
595 rxpd_connection_cmd_MERGE (struct rxpd_connection* self)
597 RXPD_FILENAME_REQUIRED;
599 struct rxpd_base* base = self->socket->base;
601 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
603 char* line;
604 while ((line = rxpd_buffer_readline (&self->in)))
606 /* for each line which is a listname */
607 struct rxpd_file* source;
608 source = (struct rxpd_file*) psplay_find (&base->files, line);
609 if (source)
611 pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
613 LList source_start = llist_head (&source->rules);
614 LList file_start = llist_head (&self->file->rules);
615 LList source_itr = NULL;
616 LList file_itr = NULL;
618 /* find next line which is in source and self->file */
619 while (source_itr != &source->rules)
621 LLIST_FORRANGE (source_start, &source->rules, n)
623 LLIST_FORRANGE (file_start, &self->file->rules, m)
625 struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
626 struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
627 if (strcmp (rule_n->string, rule_m->string) == 0)
629 source_itr = &rule_n->node;
630 file_itr = &rule_m->node;
631 goto leave_loop;
635 /* not found, place the itr's at the end of their lists */
636 source_itr = &source->rules;
637 file_itr = &self->file->rules;
638 leave_loop:
639 /* copy all rules from source_start to source_itr before file_itr */
641 LLIST_FORRANGE (source_start, source_itr, n)
643 struct rxpd_rule* rule = rxpd_rule_copy ((struct rxpd_rule*)n);
644 if (!rule)
645 rxpd_die ("rule copy failed\n");
647 llist_insert_prev (file_itr, &rule->node);
650 /*iterate for mismatch*/
651 file_start = llist_next (file_itr);
652 source_start = llist_next (source_itr);
655 pth_rwlock_release (&source->lock);
657 else
658 rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
661 pth_rwlock_release (&self->file->lock);
663 rxpd_buffer_printf (&self->out, "#OK:\n");
666 void
667 rxpd_connection_cmd_FILTER (struct rxpd_connection* self)
669 RXPD_FILENAME_REQUIRED;
671 /* Filter processes the target list by checking its rules against other lists */
672 /* The outcome of this check is used to decide how to process with a rule */
673 /* DELETE: delete rule from list */
674 /* ACTIVATE: try to reactivate a rule which was commented out */
675 /* any other: comment the rule out by using 'any other' as hint*/
677 struct rxpd_base* base = self->socket->base;
679 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
681 char* line;
682 while ((line = rxpd_buffer_readline (&self->in)))
684 if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
687 /* for each line which is a listname */
688 struct rxpd_file* source;
689 source = (struct rxpd_file*) psplay_find (&base->files, line);
690 if (source)
692 pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
694 LLIST_FOREACH (&self->file->rules, n)
696 LLIST_FOREACH (&source->rules, m)
698 struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
699 struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
700 if (rule_m->string[0] != '#')
702 if (regexec (&rule_m->rx, rule_n->string, 0, NULL, 0) == 0)
704 if (rule_m->atime != (time_t) -1)
705 time (&rule_m->atime);
707 if (RXPD_PREFIXCMP (rule_m->string, "DELETE:"))
709 n = llist_prev (n);
710 rxpd_log (base, LOG_DEBUG, "deleting rule '%s' from list '%s'\n",
711 rule_n->string, (const char*) self->file->node.key);
712 rxpd_rule_delete (rule_n);
714 else if (RXPD_PREFIXCMP (rule_m->string, "ACTIVATE:"))
716 if (!rxpd_rule_activate (rule_n))
717 rxpd_buffer_printf (&self->out, "#ERROR: cant activate rule '%s'\n", rule_n->string);
718 else
719 rxpd_log (base, LOG_DEBUG, "activated rule '%s' in list '%s'\n",
720 rule_n->string, (const char*) self->file->node.key);
722 else
724 char* c = strndupa (rule_m->string, strchr (rule_m->string, ':') - rule_m->string);
725 rxpd_log (base, LOG_DEBUG, "commenting rule '%s' as '%s', in list '%s'\n",
726 rule_n->string, c, (const char*) self->file->node.key);
728 rxpd_rule_comment (rule_n, c);
734 pth_rwlock_release (&source->lock);
736 else
737 rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
739 else
740 break; /* exit at empty line, error or whatever */
743 pth_rwlock_release (&self->file->lock);
745 rxpd_buffer_printf (&self->out, "#OK:\n");
748 /* Template
749 void
750 rxpd_connection_cmd_ (struct rxpd_connection* self)
752 RXPD_FILENAME_REQUIRED;
753 //struct rxpd_base* base = self->socket->base;
754 rxpd_buffer_printf (&self->out, "#ERROR: Unimplemented\n");