cleaned up some code, removed/improved few comments, better logging
[rxpd.git] / src / rxpd_connection_cmd.c
blobfaf1be8f1de5ce7ca7d612954a3ec2bdcf38d9e6
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)
33 void
34 rxpd_connection_cmd_CHECK (struct rxpd_connection* self)
36 RXPD_FILENAME_REQUIRED;
38 char* line;
39 while ((line = rxpd_buffer_readline (&self->in)))
41 if (*line == '\0')
43 rxpd_buffer_printf (&self->out, "#OK:\n");
45 else
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)
51 struct rxpd_rule* rule = (struct rxpd_rule*)n;
52 if (rule->string[0] != '#')
54 if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
56 if (rule->atime != (time_t) -1)
57 time (&rule->atime);
59 rxpd_buffer_printf (&self->out, "%s\n", rule->string);
60 break;
64 pth_rwlock_release (&self->file->lock);
68 if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
69 rxpd_buffer_printf (&self->out, "#ERROR:\n");
74 static void
75 rxpd_connection_APPEND_PREPEND_helper (struct rxpd_connection* self)
77 char* line;
79 while ((line = rxpd_buffer_readline (&self->in)))
81 if (*line && !RXPD_PREFIXCMP (line, "#EXIT:"))
83 struct rxpd_rule* rule;
84 rule = rxpd_rule_new (line);
85 llist_insert_tail (&self->tmp_list, &rule->node);
87 else
88 break; /* exit at empty line, error or whatever */
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");
97 void
98 rxpd_connection_cmd_APPEND (struct rxpd_connection* self)
100 RXPD_FILENAME_REQUIRED;
102 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
104 rxpd_connection_APPEND_PREPEND_helper (self);
105 llist_insertlist_prev (&self->file->rules, &self->tmp_list);
107 pth_rwlock_release (&self->file->lock);
110 void
111 rxpd_connection_cmd_PREPEND (struct rxpd_connection* self)
113 RXPD_FILENAME_REQUIRED;
115 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
117 rxpd_connection_APPEND_PREPEND_helper (self);
118 llist_insertlist_next (&self->file->rules, &self->tmp_list);
120 pth_rwlock_release (&self->file->lock);
123 void
124 rxpd_connection_cmd_REMOVE (struct rxpd_connection* self)
126 RXPD_FILENAME_REQUIRED;
128 char* line;
129 while ((line = rxpd_buffer_readline (&self->in)))
131 if (*line && !RXPD_PREFIXCMP (line, "#EXIT:"))
133 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
134 LLIST_FOREACH (&self->file->rules, n)
136 struct rxpd_rule* rule = (struct rxpd_rule*)n;
137 if (strcmp (rule->string, line) == 0)
139 LList tmp = llist_prev (n);
140 rxpd_rule_delete (rule);
141 n = tmp;
142 rxpd_buffer_printf (&self->out, "#OK:\n");
143 goto done;
146 rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
147 done:
148 pth_rwlock_release (&self->file->lock);
150 else
151 break; /* exit at empty line, error or whatever */
154 if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
155 rxpd_buffer_printf (&self->out, "#ERROR:\n");
160 static int
161 rxpd_connection_do_REPLACE (struct rxpd_connection* self)
163 struct rxpd_rule* rule;
165 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
167 LLIST_FOREACH (&self->file->rules, n)
169 rule = (struct rxpd_rule*)n;
170 if (strcmp (rule->string, self->tmp_str) == 0)
171 goto found;
173 pth_rwlock_release (&self->file->lock);
174 return 0;
176 found:
177 llist_insertlist_next (&rule->node, &self->tmp_list);
178 rxpd_rule_delete (rule);
179 pth_rwlock_release (&self->file->lock);
181 free (self->tmp_str);
182 self->tmp_str = NULL;
183 return 1;
186 void
187 rxpd_connection_cmd_REPLACE (struct rxpd_connection* self)
189 RXPD_FILENAME_REQUIRED;
191 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
193 char* line;
194 while ((line = rxpd_buffer_readline (&self->in)))
196 if (*line && !RXPD_PREFIXCMP (line, "#EXIT:"))
198 if (self->tmp_str)
200 struct rxpd_rule* rule;
201 rule = rxpd_rule_new (line);
202 if (rule)
203 llist_insert_tail (&self->tmp_list, &rule->node);
204 else
205 rxpd_buffer_printf (&self->out, "#ERROR: illegal rule '%s'\n", line);
207 else
208 self->tmp_str = rxpd_strdup (line);
210 else
211 break; /* exit at empty line, error or whatever */
214 pth_rwlock_release (&self->file->lock);
216 if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
217 rxpd_buffer_printf (&self->out, "#ERROR:\n");
219 if (rxpd_connection_do_REPLACE (self))
220 rxpd_buffer_printf (&self->out, "#OK:\n");
221 else
222 rxpd_buffer_printf (&self->out, "#ERROR: no rule matching '%s'\n", self->tmp_str);
225 void
226 rxpd_connection_cmd_LOAD (struct rxpd_connection* self)
228 RXPD_FILENAME_REQUIRED;
230 if (rxpd_file_load (self->file))
231 rxpd_buffer_printf (&self->out, "#OK:\n");
232 else
233 rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
236 void
237 rxpd_connection_cmd_SAVE (struct rxpd_connection* self)
239 RXPD_FILENAME_REQUIRED;
241 if (rxpd_file_save (self->file))
242 rxpd_buffer_printf (&self->out, "#OK:\n");
243 else
244 rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
248 void
249 rxpd_connection_cmd_DELETE (struct rxpd_connection* self)
251 RXPD_FILENAME_REQUIRED;
253 rxpd_file_delete (self->file);
254 rxpd_buffer_printf (&self->out, "#OK:\n");
257 void
258 rxpd_connection_cmd_FETCH (struct rxpd_connection* self)
260 RXPD_FILENAME_REQUIRED;
262 struct rxpd_base* base = self->socket->base;
264 char* line;
265 line = rxpd_buffer_readline (&self->in);
266 if (*line)
268 /* parse line */
269 char* list = strrchr (line, ':');
270 if (!list)
271 goto esyntax;
273 *list = '\0';
274 ++ list;
276 char* port = strrchr (line, ':');
277 if (!port)
278 goto esyntax;
280 *port = '\0';
281 ++ port;
283 struct addrinfo* addrs = NULL;
284 int aierr;
286 /* resolve peer */
287 aierr = getaddrinfo (line, port, NULL, &addrs);
288 if (aierr)
290 rxpd_buffer_printf (&self->out, "#ERROR: resolving '%s': %s\n", line, gai_strerror (aierr));
291 return;
294 rxpd_log (base, LOG_INFO, "fetching list '%s' from '%s(%s)' at port '%s' to '%s'\n",
295 list,
296 line,
297 inet_ntoa (((struct sockaddr_in*)addrs->ai_addr)->sin_addr),
298 port,
299 self->file->node.key);
301 /* establish connection */
302 int fd = socket (addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
303 if (fd == -1 || connect (fd, addrs->ai_addr, addrs->ai_addrlen))
305 freeaddrinfo (addrs);
306 rxpd_buffer_printf (&self->out, "#ERROR: error connecting '%s': %s\n", line, strerror (errno));
307 return;
310 freeaddrinfo (addrs);
312 /* send DUMP command and fetch data */
313 struct rxpd_buffer in;
314 struct rxpd_buffer out;
315 rxpd_buffer_init (&in, fd);
316 rxpd_buffer_init (&out, fd);
318 rxpd_buffer_printf (&out, "DUMP:%s\n", list);
320 char* line;
321 while ((line = rxpd_buffer_readline (&in)))
323 if (*line && !RXPD_PREFIXCMP (line, "#EXIT:"))
325 struct rxpd_rule* rule;
326 rule = rxpd_rule_new (line);
327 if (!rule)
328 rxpd_die ("rule creation failed on '%s'\n", line);
330 llist_insert_tail (&self->tmp_list, &rule->node);
332 else
333 rxpd_log (base, LOG_INFO, "purge remote side error '%s'\n", line);
335 rxpd_log (base, LOG_DEBUG, "finished dump\n");
337 close (fd);
339 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
340 rxpd_file_rules_delete (self->file);
341 llist_insertlist_next (&self->file->rules, &self->tmp_list);
342 pth_rwlock_release (&self->file->lock);
344 rxpd_buffer_printf (&self->out, "#OK:\n");
346 else
348 esyntax:
349 rxpd_buffer_printf (&self->out, "#ERROR: syntax error\n");
353 void
354 rxpd_connection_cmd_DUMP (struct rxpd_connection* self)
356 RXPD_FILENAME_REQUIRED;
357 rxpd_file_dump (self->file, &self->out);
361 static psplay_delete_t
362 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
364 (void) level;
365 struct rxpd_file* file = (struct rxpd_file*) node;
366 struct rxpd_connection* conn = (struct rxpd_connection*) data;
368 if (which == PSPLAY_INORDER)
369 rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
371 return PSPLAY_CONT;
375 void
376 rxpd_connection_cmd_LIST (struct rxpd_connection* self)
378 struct rxpd_base* base = self->socket->base;
380 if (psplay_isempty_root (&base->files))
381 rxpd_buffer_printf (&self->out, "#OK:\n");
382 else
383 psplay_walk (&base->files, NULL, walk_LIST, 0, self);
386 void
387 rxpd_connection_cmd_SHUTDOWN (struct rxpd_connection* self)
389 struct rxpd_base* base = self->socket->base;
391 LLIST_FOREACH (&base->sockets, n)
393 struct rxpd_socket* socket = (struct rxpd_socket*)n;
394 rxpd_socket_cancel (socket);
397 rxpd_buffer_printf (&self->out, "#OK:\n");
401 void
402 rxpd_connection_cmd_VERSION (struct rxpd_connection* self)
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");
413 void
414 rxpd_connection_cmd_HELP (struct rxpd_connection* self)
416 rxpd_buffer_printf (&self->out, "# Available commands:\n#\n");
417 #define RXPD_CMD(cmd, help) rxpd_buffer_printf (&self->out, "# %s %s.\n", #cmd, help);
418 RXPD_COMMANDS
419 #undef RXPD_CMD
420 rxpd_buffer_printf (&self->out,
421 "#\n"
422 "# general syntax is: 'COMMAND:listname\\n..data..'\n"
423 "# see http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
426 void
427 rxpd_connection_cmd_EXPIRE (struct rxpd_connection* self)
429 RXPD_FILENAME_REQUIRED;
431 struct rxpd_base* base = self->socket->base;
433 char* line = rxpd_buffer_readline (&self->in);
434 if (*line)
436 // TODO strtol, error handling
437 time_t since = time (NULL) - atoi (line);
438 rxpd_log (base, LOG_INFO, "expire all entries in '%s' since %ld\n",
439 (const char*) self->file->node.key, since);
441 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
443 LLIST_FOREACH (&self->file->rules, n)
445 struct rxpd_rule* rule = (struct rxpd_rule*)n;
446 if (rule->atime != -1 && rule->atime < since)
448 rxpd_log (base, LOG_DEBUG, "expiring %ld:%s\n", rule->atime, rule->string);
449 rxpd_buffer_printf (&self->out, "#OK: expiring '%s'\n", rule->string);
450 if (!rxpd_rule_comment (rule, "EXPIRED"))
451 rxpd_die ("couldn't comment rule out");
455 pth_rwlock_release (&self->file->lock);
457 else
458 rxpd_buffer_printf (&self->out, "#ERROR: no age given\n");
461 void
462 rxpd_connection_cmd_UPDATE (struct rxpd_connection* self)
464 RXPD_FILENAME_REQUIRED;
466 struct rxpd_base* base = self->socket->base;
468 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
470 char* line;
471 while ((line = rxpd_buffer_readline (&self->in)))
473 /* for each line which is a listname */
474 struct rxpd_file* source;
475 source = (struct rxpd_file*) psplay_find (&base->files, line);
476 if (source)
478 pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
480 /* find first line which is in source and self->file, this is our start */
481 LList source_itr = NULL;
482 LList file_itr = NULL;
483 LLIST_FOREACH (&source->rules, n)
485 struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
486 LLIST_FOREACH (&self->file->rules, m)
488 struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
489 if (strcmp (rule_n->string, rule_m->string) == 0)
491 source_itr = &rule_n->node;
492 file_itr = &rule_m->node;
493 rxpd_log (base, LOG_DEBUG, "starting at '%s'\n", rule_n->string);
494 goto leave_loop;
498 leave_loop:
499 if (!source_itr)
501 rxpd_log (base, LOG_DEBUG, "no common line found between '%s' and '%s'\n",
502 (const char*)self->file->node.key, line);
503 pth_rwlock_release (&source->lock);
504 break;
507 /* Now we go for each rule in source which has a atime find the matching rule in self->file and update its atime */
508 /* This algorihm assumes that lists stay ordered (insertions/deletions are supported, but not reordering) */
509 /* advantage is that the algorithm has far less complexity */
510 LLIST_FORRANGE (source_itr, &source->rules, n)
512 struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
513 if (rule_n->atime != (time_t)-1)
515 LLIST_FORRANGE (file_itr, &self->file->rules, m)
517 struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
518 if (strcmp (rule_n->string, rule_m->string) == 0)
520 /* found the rule */
521 if (rule_m->atime != (time_t)-1 && rule_m->atime < rule_n->atime)
523 rxpd_log (base, LOG_DEBUG,
524 "updating atime of rule '%s' from %ld to %ld\n",
525 rule_m->string,
526 rule_m->atime,
527 rule_n->atime
529 rule_m->atime = rule_n->atime;
532 file_itr = llist_next (m);
533 break;
539 pth_rwlock_release (&source->lock);
541 else
542 rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
545 pth_rwlock_release (&self->file->lock);
547 rxpd_buffer_printf (&self->out, "#OK:\n");
551 void
552 rxpd_connection_cmd_MERGE (struct rxpd_connection* self)
554 RXPD_FILENAME_REQUIRED;
556 struct rxpd_base* base = self->socket->base;
558 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
560 char* line;
561 while ((line = rxpd_buffer_readline (&self->in)))
563 /* for each line which is a listname */
564 struct rxpd_file* source;
565 source = (struct rxpd_file*) psplay_find (&base->files, line);
566 if (source)
568 pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
570 LList source_start = llist_head (&source->rules);
571 LList file_start = llist_head (&self->file->rules);
572 LList source_itr = NULL;
573 LList file_itr = NULL;
575 /* find next line which is in source and self->file */
576 while (source_itr != &source->rules)
578 LLIST_FORRANGE (source_start, &source->rules, n)
580 LLIST_FORRANGE (file_start, &self->file->rules, m)
582 struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
583 struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
584 if (strcmp (rule_n->string, rule_m->string) == 0)
586 source_itr = &rule_n->node;
587 file_itr = &rule_m->node;
588 goto leave_loop;
592 /* not found, place the itr's at the end of their lists */
593 source_itr = &source->rules;
594 file_itr = &self->file->rules;
595 leave_loop:
596 /* copy all rules from source_start to source_itr before file_itr */
598 LLIST_FORRANGE (source_start, source_itr, n)
600 struct rxpd_rule* rule = rxpd_rule_copy ((struct rxpd_rule*)n);
601 if (!rule)
602 rxpd_die ("rule copy failed\n");
604 llist_insert_prev (file_itr, &rule->node);
607 /*iterate for mismatch*/
608 file_start = llist_next (file_itr);
609 source_start = llist_next (source_itr);
612 pth_rwlock_release (&source->lock);
614 else
615 rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
618 pth_rwlock_release (&self->file->lock);
620 rxpd_buffer_printf (&self->out, "#OK:\n");
623 void
624 rxpd_connection_cmd_FILTER (struct rxpd_connection* self)
626 RXPD_FILENAME_REQUIRED;
628 /* Filter processes the target list by checking its rules against other lists */
629 /* The outcome of this check is used to decide how to process with a rule */
630 /* DELETE: delete rule from list */
631 /* ACTIVATE: try to reactivate a rule which was commented out */
632 /* any other: comment the rule out by using 'any other' as hint*/
634 struct rxpd_base* base = self->socket->base;
636 pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
638 char* line;
639 while ((line = rxpd_buffer_readline (&self->in)))
641 if (*line && !RXPD_PREFIXCMP (line, "#EXIT:"))
644 /* for each line which is a listname */
645 struct rxpd_file* source;
646 source = (struct rxpd_file*) psplay_find (&base->files, line);
647 if (source)
649 pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
651 LLIST_FOREACH (&self->file->rules, n)
653 LLIST_FOREACH (&source->rules, m)
655 struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
656 struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
657 if (rule_m->string[0] != '#')
659 if (regexec (&rule_m->rx, rule_n->string, 0, NULL, 0) == 0)
661 if (rule_m->atime != (time_t) -1)
662 time (&rule_m->atime);
664 if (RXPD_PREFIXCMP (rule_m->string, "DELETE:"))
666 n = llist_prev (n);
667 rxpd_log (base, LOG_DEBUG, "deleting rule '%s' from list '%s'\n",
668 rule_n->string, (const char*) self->file->node.key);
669 rxpd_rule_delete (rule_n);
671 else if (RXPD_PREFIXCMP (rule_m->string, "ACTIVATE:"))
673 if (!rxpd_rule_activate (rule_n))
674 rxpd_buffer_printf (&self->out, "#ERROR: cant activate rule '%s'\n", rule_n->string);
675 else
676 rxpd_log (base, LOG_DEBUG, "activated rule '%s' in list '%s'\n",
677 rule_n->string, (const char*) self->file->node.key);
679 else
681 char* c = strndupa (rule_m->string, strchr (rule_m->string, ':') - rule_m->string);
682 rxpd_log (base, LOG_DEBUG, "commenting rule '%s' as '%s', in list '%s'\n",
683 rule_n->string, c, (const char*) self->file->node.key);
685 rxpd_rule_comment (rule_n, c);
691 pth_rwlock_release (&source->lock);
693 else
694 rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
696 else
697 break; /* exit at empty line, error or whatever */
700 pth_rwlock_release (&self->file->lock);
702 rxpd_buffer_printf (&self->out, "#OK:\n");
705 /* Template
706 void
707 rxpd_connection_cmd_ (struct rxpd_connection* self)
709 RXPD_FILENAME_REQUIRED;
710 //struct rxpd_base* base = self->socket->base;
711 rxpd_buffer_printf (&self->out, "#ERROR: Unimplemented\n");