fixed typo
[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
34 static char*
35 rxpd_connection_do_CHECK (struct rxpd_connection* self, char* line, struct rxpd_file* file)
36 {
37   char* ret = NULL;
38
39   // TODO implement RXPD_YIELD_EVERY
40   pth_rwlock_acquire (&file->lock, PTH_RWLOCK_RD, FALSE, NULL);
41   LLIST_FOREACH (&file->rules, n)
42     {
43       struct rxpd_rule* rule = (struct rxpd_rule*)n;
44       if (rule->string[0] != '#')
45         {
46           if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
47             {
48               if (rule->atime != (time_t) -1)
49                 time (&rule->atime);
50
51               ret = rule->string;
52
53               if (*ret == '>')
54                 {
55                   struct rxpd_base* base = self->socket->base;
56
57                   size_t sublen =
58                     (size_t)(strchr (&rule->string[1], ':') - &rule->string[1]);
59
60                   char* subname =
61                     alloca (strlen (file->node.key) + sublen + 1);
62                   *subname = '\0';
63
64                   strcat (subname, file->node.key);
65                   strncat (subname, &rule->string[1], sublen);
66
67                   struct rxpd_file* subfile = (struct rxpd_file*) psplay_find (&base->files, subname);
68                   if (!subfile)
69                     {
70                       subfile = rxpd_file_new (base, subname);
71                       if (!subfile)
72                         {
73                           rxpd_buffer_printf (&self->out, "#ERROR: illegal filename\n");
74                           break;
75                         }
76                       if (rxpd_file_load (subfile))
77                         rxpd_log (base, LOG_INFO, "autoloaded '%s'\n", subname);
78                       else
79                         rxpd_log (base, LOG_WARNING, "could not autoload '%s'\n", subname);
80                     }
81
82                   rxpd_log (base, LOG_DEBUG, "going to sublist '%s'\n", subname);
83                   ret = rxpd_connection_do_CHECK (self, line, subfile);
84                 }
85
86               if (ret)
87                 break;
88             }
89         }
90     }
91   pth_rwlock_release (&file->lock);
92   return ret;
93 }
94
95
96 void
97 rxpd_connection_cmd_CHECK (struct rxpd_connection* self)
98 {
99   RXPD_FILENAME_REQUIRED;
100
101   char* line;
102   while ((line = rxpd_buffer_readline (&self->in)))
103     {
104       if (*line == '\0')
105         {
106           rxpd_buffer_printf (&self->out, "#OK:\n");
107         }
108       else
109         {
110           char* match = rxpd_connection_do_CHECK (self, line, self->file);
111
112           if (match)
113             rxpd_buffer_printf (&self->out, "%s\n", match);
114         }
115     }
116
117   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
118     rxpd_buffer_printf (&self->out, "#ERROR:\n");
119 }
120
121
122 static void
123 rxpd_connection_APPEND_PREPEND_helper (struct rxpd_connection* self)
124 {
125   char* line;
126
127   while ((line = rxpd_buffer_readline (&self->in)))
128     {
129       if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
130         {
131           struct rxpd_rule* rule;
132
133           rule = rxpd_rule_new (line, self->socket->base);
134           llist_insert_tail (&self->tmp_list, &rule->node);
135         }
136       else
137         break;  /* exit at empty line, error or whatever */
138     }
139
140   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
141     rxpd_buffer_printf (&self->out, "#ERROR:\n");
142   else
143     rxpd_buffer_printf (&self->out, "#OK:\n");
144 }
145
146 void
147 rxpd_connection_cmd_APPEND (struct rxpd_connection* self)
148 {
149   RXPD_FILENAME_REQUIRED;
150
151   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
152
153   rxpd_connection_APPEND_PREPEND_helper (self);
154   llist_insertlist_prev (&self->file->rules, &self->tmp_list);
155
156   pth_rwlock_release (&self->file->lock);
157 }
158
159 void
160 rxpd_connection_cmd_PREPEND (struct rxpd_connection* self)
161 {
162   RXPD_FILENAME_REQUIRED;
163
164   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
165
166   rxpd_connection_APPEND_PREPEND_helper (self);
167   llist_insertlist_next (&self->file->rules, &self->tmp_list);
168
169   pth_rwlock_release (&self->file->lock);
170 }
171
172 void
173 rxpd_connection_cmd_REMOVE (struct rxpd_connection* self)
174 {
175   RXPD_FILENAME_REQUIRED;
176
177   char* line;
178   while ((line = rxpd_buffer_readline (&self->in)))
179     {
180       if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
181         {
182           pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
183           LLIST_FOREACH (&self->file->rules, n)
184             {
185               struct rxpd_rule* rule = (struct rxpd_rule*)n;
186               if (strcmp (rule->string, line) == 0)
187                 {
188                   LList tmp = llist_prev (n);
189                   rxpd_rule_delete (rule);
190                   n = tmp;
191                   rxpd_buffer_printf (&self->out, "#OK:\n");
192                   goto done;
193                 }
194             }
195           rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
196         done:
197           pth_rwlock_release (&self->file->lock);
198         }
199       else
200         break;  /* exit at empty line, error or whatever */
201     }
202
203   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
204     rxpd_buffer_printf (&self->out, "#ERROR:\n");
205 }
206
207
208
209 static int
210 rxpd_connection_do_REPLACE (struct rxpd_connection* self)
211 {
212   struct rxpd_rule* rule;
213
214   LLIST_FOREACH (&self->file->rules, n)
215     {
216       rule = (struct rxpd_rule*)n;
217       if (strcmp (rule->string, self->tmp_str) == 0)
218         goto found;
219     }
220   return 0;
221
222  found:
223   llist_insertlist_next (&rule->node, &self->tmp_list);
224   rxpd_rule_delete (rule);
225
226   free (self->tmp_str);
227   self->tmp_str = NULL;
228   return 1;
229 }
230
231 void
232 rxpd_connection_cmd_REPLACE (struct rxpd_connection* self)
233 {
234   RXPD_FILENAME_REQUIRED;
235
236   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
237
238   char* line;
239   while ((line = rxpd_buffer_readline (&self->in)))
240     {
241       if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
242         {
243           if (self->tmp_str)
244             {
245               struct rxpd_rule* rule;
246               rule = rxpd_rule_new (line, self->socket->base);
247               if (rule)
248                 llist_insert_tail (&self->tmp_list, &rule->node);
249               else
250                 rxpd_buffer_printf (&self->out, "#ERROR: illegal rule '%s'\n", line);
251             }
252           else
253             self->tmp_str = rxpd_strdup (line);
254         }
255       else
256         break;  /* exit at empty line, error or whatever */
257     }
258
259   if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
260     rxpd_buffer_printf (&self->out, "#ERROR:\n");
261
262   if (rxpd_connection_do_REPLACE (self))
263     rxpd_buffer_printf (&self->out, "#OK:\n");
264   else
265     rxpd_buffer_printf (&self->out, "#ERROR: no rule matching '%s'\n", self->tmp_str);
266
267   pth_rwlock_release (&self->file->lock);
268 }
269
270 void
271 rxpd_connection_cmd_LOAD (struct rxpd_connection* self)
272 {
273   RXPD_FILENAME_REQUIRED;
274
275   if (rxpd_file_load (self->file))
276     rxpd_buffer_printf (&self->out, "#OK:\n");
277   else
278     rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
279 }
280
281 void
282 rxpd_connection_cmd_SAVE (struct rxpd_connection* self)
283 {
284   RXPD_FILENAME_REQUIRED;
285
286   if (rxpd_file_save (self->file, 1))
287     rxpd_buffer_printf (&self->out, "#OK:\n");
288   else
289     rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
290 }
291
292
293 void
294 rxpd_connection_cmd_CLEAR (struct rxpd_connection* self)
295 {
296   RXPD_FILENAME_REQUIRED;
297
298   rxpd_file_rules_delete (self->file);
299   rxpd_buffer_printf (&self->out, "#OK:\n");
300 }
301
302 void
303 rxpd_connection_cmd_DELETE (struct rxpd_connection* self)
304 {
305   RXPD_FILENAME_REQUIRED;
306
307   rxpd_file_remove (self->file);
308   rxpd_buffer_printf (&self->out, "#OK:\n");
309 }
310
311 void
312 rxpd_connection_cmd_FETCH (struct rxpd_connection* self)
313 {
314   RXPD_FILENAME_REQUIRED;
315
316   struct rxpd_base* base = self->socket->base;
317
318   char* line;
319   line = rxpd_buffer_readline (&self->in);
320   if (*line)
321     {
322       /* parse line */
323       char* list = strrchr (line, ':');
324       if (!list)
325         goto esyntax;
326
327       *list = '\0';
328       ++ list;
329
330       char* port = strrchr (line, ':');
331       if (!port)
332         goto esyntax;
333
334       *port = '\0';
335       ++ port;
336
337       struct addrinfo* addrs = NULL;
338       int aierr;
339
340       /* resolve peer */
341       aierr = getaddrinfo (line, port, NULL, &addrs);
342       if (aierr)
343         {
344           rxpd_buffer_printf (&self->out, "#ERROR: resolving '%s': %s\n", line, gai_strerror (aierr));
345           return;
346         }
347
348       rxpd_log (base, LOG_INFO, "fetching list '%s' from '%s(%s)' at port '%s' to '%s'\n",
349                 list,
350                 line,
351                 inet_ntoa (((struct sockaddr_in*)addrs->ai_addr)->sin_addr),
352                 port,
353                 self->file->node.key);
354
355       /* establish connection */
356       int fd = socket (addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
357       if (fd == -1 || connect (fd, addrs->ai_addr, addrs->ai_addrlen))
358         {
359           freeaddrinfo (addrs);
360           rxpd_buffer_printf (&self->out, "#ERROR: error connecting '%s': %s\n", line, strerror (errno));
361           return;
362         }
363  
364       freeaddrinfo (addrs);
365
366       /* send DUMP command and fetch data */
367       struct rxpd_buffer in;
368       struct rxpd_buffer out;
369       rxpd_buffer_init (&in, fd);
370       rxpd_buffer_init (&out, fd);
371
372       rxpd_buffer_printf (&out, "DUMP:%s\n", list);
373
374       char* line;
375       while ((line = rxpd_buffer_readline (&in)))
376         {
377           if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
378             {
379               struct rxpd_rule* rule;
380               rule = rxpd_rule_new (line, base);
381               if (!rule)
382                 rxpd_die ("rule creation failed on '%s'\n", line);
383
384               llist_insert_tail (&self->tmp_list, &rule->node);
385             }
386           else
387             rxpd_log (base, LOG_INFO, "purge remote side error '%s'\n", line);
388         }
389       rxpd_log (base, LOG_DEBUG, "finished dump\n");
390
391       close (fd);
392
393       pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
394       rxpd_file_rules_delete (self->file);
395       llist_insertlist_next (&self->file->rules, &self->tmp_list);
396       pth_rwlock_release (&self->file->lock);
397
398       rxpd_buffer_printf (&self->out, "#OK:\n");
399     }
400   else
401     {
402     esyntax:
403       rxpd_buffer_printf (&self->out, "#ERROR: syntax error\n");
404     }
405 }
406
407 void
408 rxpd_connection_cmd_DUMP (struct rxpd_connection* self)
409 {
410   RXPD_FILENAME_REQUIRED;
411   rxpd_file_dump (self->file, &self->out, NULL);
412 }
413
414 void
415 rxpd_connection_cmd_DUMPH (struct rxpd_connection* self)
416 {
417   RXPD_FILENAME_REQUIRED;
418   rxpd_file_dump (self->file, &self->out, "%a, %d. %b. %Y, %T");
419 }
420
421
422 /*
423   build a temporary list to make psplay walking lockfree under pth
424 */
425 struct list_record
426 {
427   llist node;
428   char* name;
429 };
430
431 static psplay_delete_t
432 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
433 {
434   (void) level;
435   struct rxpd_file* file = (struct rxpd_file*) node;
436   struct rxpd_connection* conn = (struct rxpd_connection*) data;
437
438   char buf[4096] = "DUMP:";
439   buf[4095] = '\0';
440
441   if (which == PSPLAY_INORDER)
442     {
443       strncpy (buf+sizeof("DUMP:")-1, file->node.key, 4096 - sizeof("DUMP:"));
444
445       if (rxpd_connection_check_policy (conn, buf))
446         {
447           struct list_record* rec = rxpd_malloc (sizeof (struct list_record));
448           rec->name = rxpd_strdup (file->node.key);
449           llist_init (&rec->node);
450           llist_insert_tail (&conn->tmp_list, &rec->node);
451         }
452     }
453   return PSPLAY_CONT;
454 }
455
456
457 void
458 rxpd_connection_cmd_LIST (struct rxpd_connection* self)
459 {
460   struct rxpd_base* base = self->socket->base;
461
462   if (psplay_isempty_root (&base->files))
463     rxpd_buffer_printf (&self->out, "#OK:\n");
464   else
465     {
466       psplay_walk (&base->files, NULL, walk_LIST, 0, self);
467
468       LLIST_WHILE_HEAD (&self->tmp_list, n)
469         {
470           struct list_record* rec = (struct list_record*)n;
471           rxpd_buffer_printf (&self->out, "%s\n", rec->name);
472           llist_unlink (n);
473           free (rec->name);
474           free (n);
475         }
476     }
477 }
478
479 void
480 rxpd_connection_cmd_SHUTDOWN (struct rxpd_connection* self)
481 {
482   /* not pth_raise, we don't want to schedule here! */
483   raise (SIGINT);
484   rxpd_buffer_printf (&self->out, "#OK:\n");
485 }
486
487
488 void
489 rxpd_connection_cmd_VERSION (struct rxpd_connection* self)
490 {
491   rxpd_buffer_printf (&self->out, PACKAGE_STRING "\n#\n"
492                       "# Copyright (C)\n"
493                       "#   2007,               Christian Thaeter <ct@pipapo.org>\n#\n"
494                       "# This is free software.  You may redistribute copies of it under the terms of\n"
495                       "# the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n"
496                       "# There is NO WARRANTY, to the extent permitted by law.\n#\n"
497                       "# http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
498 }
499
500 void
501 rxpd_connection_cmd_HELP (struct rxpd_connection* self)
502 {
503   rxpd_buffer_printf (&self->out, "# Available commands:\n#\n");
504 #define RXPD_CMD(cmd, help)     rxpd_buffer_printf (&self->out, "# %s %s.\n", #cmd, help);
505   RXPD_COMMANDS
506 #undef RXPD_CMD
507     rxpd_buffer_printf (&self->out, 
508                         "#\n"
509                         "# general syntax is: 'COMMAND:listname\\n..data..'\n"
510                         "# see http://www.pipapo.org/pipawiki/RegexPolicyDaemon\n");
511 }
512
513 void
514 rxpd_connection_cmd_EXPIRE (struct rxpd_connection* self)
515 {
516   RXPD_FILENAME_REQUIRED;
517
518   struct rxpd_base* base = self->socket->base;
519
520   char* line = rxpd_buffer_readline (&self->in);
521   if (*line)
522     {
523       // TODO strtol, error handling
524       time_t since = time (NULL) - atoi (line);
525       rxpd_log (base, LOG_INFO, "expire all entries in '%s' since %ld\n",
526                 (const char*) self->file->node.key, since);
527
528       pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
529
530       LLIST_FOREACH (&self->file->rules, n)
531         {
532           struct rxpd_rule* rule = (struct rxpd_rule*)n;
533           if (rule->atime != -1 && rule->atime < since)
534             {
535               rxpd_log (base, LOG_DEBUG, "expiring %ld:%s\n", rule->atime, rule->string);
536               rxpd_buffer_printf (&self->out, "#OK: expiring '%s'\n", rule->string);
537               if (!rxpd_rule_comment (rule, "EXPIRED"))
538                 rxpd_die ("couldn't comment rule out");
539             }
540         }
541
542       pth_rwlock_release (&self->file->lock);
543     }
544   else
545     rxpd_buffer_printf (&self->out, "#ERROR: no age given\n");
546 }
547
548 void
549 rxpd_connection_cmd_UPDATE (struct rxpd_connection* self)
550 {
551   RXPD_FILENAME_REQUIRED;
552
553   struct rxpd_base* base = self->socket->base;
554
555   pth_mutex_acquire (&base->nest_lock, 0, NULL);
556   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
557
558   char* line;
559   while ((line = rxpd_buffer_readline (&self->in)))
560     {
561       /* for each line which is a listname */
562       struct rxpd_file* source;
563       source = (struct rxpd_file*) psplay_find (&base->files, line);
564       if (source)
565         {
566           pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
567
568           /* find first line which is in source and self->file, this is our start */
569           LList source_itr = NULL;
570           LList file_itr = NULL;
571           LLIST_FOREACH (&source->rules, n)
572             {
573               struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
574               LLIST_FOREACH (&self->file->rules, m)
575                 {
576                   struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
577                   if (strcmp (rule_n->string, rule_m->string) == 0)
578                     {
579                       source_itr = &rule_n->node;
580                       file_itr = &rule_m->node;
581                       rxpd_log (base, LOG_DEBUG, "starting at '%s'\n", rule_n->string);
582                       goto leave_loop;
583                     }
584                 }
585             }
586         leave_loop:
587           if (!source_itr)
588             {
589               rxpd_log (base, LOG_DEBUG, "no common line found between '%s' and '%s'\n",
590                         (const char*)self->file->node.key, line);
591               pth_rwlock_release (&source->lock);
592               break;
593             }
594
595           /* Now we go for each rule in source which has a atime find the matching rule in self->file and update its atime */
596           /* This algorihm assumes that lists stay ordered (insertions/deletions are supported, but not reordering) */
597           /* advantage is that the algorithm has far less complexity */
598           LLIST_FORRANGE (source_itr, &source->rules, n)
599             {
600               struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
601               if (rule_n->atime != (time_t)-1)
602                 {
603                   LLIST_FORRANGE (file_itr, &self->file->rules, m)
604                     {
605                       struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
606                       if (strcmp (rule_n->string, rule_m->string) == 0)
607                         {
608                           /* found the rule */
609                           if (rule_m->atime != (time_t)-1 && rule_m->atime < rule_n->atime)
610                             {
611                               rxpd_log (base, LOG_DEBUG,
612                                         "updating atime of rule '%s' from %ld to %ld\n",
613                                         rule_m->string,
614                                         rule_m->atime,
615                                         rule_n->atime
616                                         );
617                               rule_m->atime = rule_n->atime;
618                             }
619
620                           file_itr = llist_next (m);
621                           break;
622                         }
623                     }
624                 }
625             }
626
627           pth_rwlock_release (&source->lock);
628         }
629       else
630         rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
631     }
632
633   pth_rwlock_release (&self->file->lock);
634   pth_mutex_release (&base->nest_lock);
635
636   rxpd_buffer_printf (&self->out, "#OK:\n");
637 }
638
639
640 void
641 rxpd_connection_cmd_MERGE (struct rxpd_connection* self)
642 {
643   RXPD_FILENAME_REQUIRED;
644
645   struct rxpd_base* base = self->socket->base;
646
647   pth_mutex_acquire (&base->nest_lock, 0, NULL);
648   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
649
650   char* line;
651   while ((line = rxpd_buffer_readline (&self->in)))
652     {
653       /* for each line which is a listname */
654       struct rxpd_file* source;
655       source = (struct rxpd_file*) psplay_find (&base->files, line);
656       if (source)
657         {
658           pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
659
660           LList source_start = llist_head (&source->rules);
661           LList file_start = llist_head (&self->file->rules);
662           LList source_itr = NULL;
663           LList file_itr = NULL;
664
665           /* find next line which is in source and self->file */
666           while (source_itr != &source->rules)
667             {
668               LLIST_FORRANGE (source_start, &source->rules, n)
669                 {
670                   LLIST_FORRANGE (file_start, &self->file->rules, m)
671                     {
672                       struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
673                       struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
674                       if (strcmp (rule_n->string, rule_m->string) == 0)
675                         {
676                           source_itr = &rule_n->node;
677                           file_itr = &rule_m->node;
678                           goto leave_loop;
679                         }
680                     }
681                 }
682               /* not found, place the itr's at the end of their lists */
683               source_itr = &source->rules;
684               file_itr = &self->file->rules;
685             leave_loop:
686               /* copy all rules from source_start to source_itr before file_itr */
687
688               LLIST_FORRANGE (source_start, source_itr, n)
689                 {
690                   struct rxpd_rule* rule = rxpd_rule_copy ((struct rxpd_rule*)n);
691                   if (!rule)
692                     rxpd_die ("rule copy failed\n");
693
694                   llist_insert_prev (file_itr, &rule->node);
695                 }
696
697               /*iterate for mismatch*/
698               file_start = llist_next (file_itr);
699               source_start = llist_next (source_itr);
700             }
701
702           pth_rwlock_release (&source->lock);
703         }
704       else
705         rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
706     }
707
708   pth_rwlock_release (&self->file->lock);
709   pth_mutex_release (&base->nest_lock);
710
711   rxpd_buffer_printf (&self->out, "#OK:\n");
712 }
713
714 void
715 rxpd_connection_cmd_FILTER (struct rxpd_connection* self)
716 {
717   RXPD_FILENAME_REQUIRED;
718
719   /* Filter processes the target list by checking its rules against other lists */
720   /* The outcome of this check is used to decide how to process with a rule */
721   /* DELETE: delete rule from list */
722   /* ACTIVATE: try to reactivate a rule which was commented out */
723   /* any other: comment the rule out by using 'any other' as hint*/
724
725   struct rxpd_base* base = self->socket->base;
726
727   pth_mutex_acquire (&base->nest_lock, 0, NULL);
728   pth_rwlock_acquire (&self->file->lock, PTH_RWLOCK_RW, FALSE, NULL);
729
730   char* line;
731   while ((line = rxpd_buffer_readline (&self->in)))
732     {
733       if (*line && !RXPD_PREFIXCMP (line, "!EXIT"))
734         {
735
736           /* for each line which is a listname */
737           struct rxpd_file* source;
738           source = (struct rxpd_file*) psplay_find (&base->files, line);
739           if (source)
740             {
741               pth_rwlock_acquire (&source->lock, PTH_RWLOCK_RD, FALSE, NULL);
742
743               LLIST_FOREACH (&self->file->rules, n)
744                 {
745                   LLIST_FOREACH (&source->rules, m)
746                     {
747                       struct rxpd_rule* rule_n = (struct rxpd_rule*)n;
748                       struct rxpd_rule* rule_m = (struct rxpd_rule*)m;
749                       if (rule_m->string[0] != '#')
750                         {
751                           if (regexec (&rule_m->rx, rule_n->string, 0, NULL, 0) == 0)
752                             {
753                               if (rule_m->atime != (time_t) -1)
754                                 time (&rule_m->atime);
755
756                               if (RXPD_PREFIXCMP (rule_m->string, "DELETE:"))
757                                 {
758                                   n = llist_prev (n);
759                                   rxpd_log (base, LOG_DEBUG, "deleting rule '%s' from list '%s'\n",
760                                             rule_n->string, (const char*) self->file->node.key);
761                                   rxpd_rule_delete (rule_n);
762                                 }
763                               else if (RXPD_PREFIXCMP (rule_m->string, "ACTIVATE:"))
764                                 {
765                                   if (!rxpd_rule_activate (rule_n))
766                                     rxpd_buffer_printf (&self->out, "#ERROR: cant activate rule '%s'\n", rule_n->string);
767                                   else
768                                     rxpd_log (base, LOG_DEBUG, "activated rule '%s' in list '%s'\n",
769                                               rule_n->string, (const char*) self->file->node.key);
770                                 }
771                               else
772                                 {
773                                   char* c = strndupa (rule_m->string, strchr (rule_m->string, ':') - rule_m->string);
774                                   rxpd_log (base, LOG_DEBUG, "commenting rule '%s' as '%s', in list '%s'\n",
775                                             rule_n->string, c, (const char*) self->file->node.key);
776
777                                   rxpd_rule_comment (rule_n, c);
778                                 }
779                             }
780                         }
781                     }
782                 }
783               pth_rwlock_release (&source->lock);
784             }
785           else
786             rxpd_buffer_printf (&self->out, "#ERROR: unknown file '%s'\n", line);
787         }
788       else
789         break;  /* exit at empty line, error or whatever */
790     }
791
792   pth_rwlock_release (&self->file->lock);
793   pth_mutex_release (&base->nest_lock);
794
795   rxpd_buffer_printf (&self->out, "#OK:\n");
796 }
797
798 /* Template
799 void
800 rxpd_connection_cmd_ (struct rxpd_connection* self)
801 {
802   RXPD_FILENAME_REQUIRED;
803   //struct rxpd_base* base = self->socket->base;
804   rxpd_buffer_printf (&self->out, "#ERROR: Unimplemented\n");
805 }
806 */