d22ae0323a077a1d82bbc1a6512f2e25b33cda05
[rxpd] / src / rxpd.c
1 /*
2     rxpd.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 static struct rxpd_base global_base;
25
26 struct rxpd_base*
27 rxpd_init (char* rulesdir)
28 {
29   if (global_base.rulesdir)
30     return NULL;
31
32   global_base.rulesdir = strdup (rulesdir);
33   if (!global_base.rulesdir) abort();
34
35   psplay_init_root (&global_base.files, rxpd_file_cmp, rxpd_file_delete);
36
37   llist_init (&global_base.sockets);
38
39   return &global_base;
40 }
41
42 void
43 rxpd_destroy (void)
44 {
45   if (global_base.rulesdir)
46     {
47       free (global_base.rulesdir);
48       psplay_destroy_root (&global_base.files);
49       LLIST_WHILE_HEAD (&global_base.sockets, n)
50         {
51           struct rxpd_socket* socket = (struct rxpd_socket*)n;
52           rxpd_socket_delete (socket);
53         }
54     }
55 }
56
57
58 //
59 struct rxpd_rule*
60 rxpd_rule_new (const char* buf)
61 {
62   struct rxpd_rule* self = malloc (sizeof (struct rxpd_rule));
63   if (self)
64     {
65       llist_init (&self->node);
66
67       if (*buf != '#')
68         {
69           int err;
70           char* rxstart = strchr (buf, ':');
71
72           if (!rxstart)
73             self->string = strdup ("#ERROR: Syntax error, line was neither a comment nor a rule");
74           else
75             {
76               err = regcomp (&self->rx, rxstart+1, REG_EXTENDED|REG_ICASE|REG_NOSUB);
77
78               if (!err)
79                 {
80                   self->string = strdup (buf);
81                   if (!self->string) abort();
82                 }
83               else
84                 {
85                   regfree (&self->rx);
86                   char ebuf[256];
87                   size_t len = regerror (err, NULL, ebuf, 256);
88                   self->string = malloc(len + strlen(buf) + 14);
89                   if (!self->string) abort();
90                   strcpy (self->string, "#ERROR: ");
91                   strcat (self->string, ebuf);
92                   strcat (self->string, " in '");
93                   strcat (self->string, buf);
94                   strcat (self->string, "'");
95                 }
96             }
97         }
98       else
99         {
100           self->string = strdup (buf);
101           if (!self->string) abort();
102         }
103     }
104   return self;
105 }
106
107 void
108 rxpd_rule_delete (struct rxpd_rule* rule)
109 {
110   if (rule)
111     {
112       llist_unlink (&rule->node);
113       if (rule->string[0] != '#')
114         regfree (&rule->rx);
115       free (rule->string);
116       free(rule);
117     }
118 }
119
120 //
121
122
123 struct rxpd_file*
124 rxpd_file_new (struct rxpd_base* base, const char* filename)
125 {
126   char buf[4096];
127   struct rxpd_file* self = NULL;
128
129   if (!filename ||
130       strcspn(filename, RXPD_FILE_ILG_CHARS) != strlen (filename) ||
131       strlen (filename) + strlen (base->rulesdir) > 4095)
132     return NULL;
133
134   strcpy (buf, base->rulesdir);
135   strcat (buf, filename);
136   filename = strdup (buf);
137   if (filename)
138     {
139       self = malloc (sizeof (struct rxpd_file));
140       if (self)
141         {
142           self->filename = filename;
143           const char* basename = strrchr (filename, '/');
144           if (basename)
145             ++basename;
146           else
147             basename = filename;
148           psplay_init (&self->node, basename);
149           llist_init (&self->rules);
150
151           psplay_insert (&base->files, &self->node);
152         }
153     }
154   return self;
155 }
156
157 void
158 rxpd_file_delete (PSplay f)
159 {
160   if (f)
161     {
162       struct rxpd_file* file = (struct rxpd_file*)f;
163       LLIST_WHILE_HEAD (&file->rules, n)
164         {
165           struct rxpd_rule* node = (struct rxpd_rule*)n;
166           rxpd_rule_delete (node);
167         }
168       free ((void*)file->filename);
169       free (f);
170     }
171 }
172
173 int
174 rxpd_file_load (struct rxpd_file* self)
175 {
176   FILE* f = fopen (self->filename, "r");
177   // TODO error handling
178   if (f)
179     {
180       /* First purge old rules */
181       LLIST_WHILE_HEAD (&self->rules, n)
182         {
183           struct rxpd_rule* node = (struct rxpd_rule*)n;
184           rxpd_rule_delete (node);
185         }
186
187       // TODO test excess line length = error
188       char buf[4096];
189
190       while (fgets (buf, 4096, f))
191         {
192           size_t last = strlen(buf);
193           if (buf[last-1] == '\n')
194             buf[last-1] = '\0';
195
196           struct rxpd_rule* rule;
197           rule = rxpd_rule_new (buf);
198           if (!rule)
199             abort();
200
201           printf("loaded rule '%s'\n", rule->string);
202
203           llist_insert_tail (&self->rules, &rule->node);
204         }
205
206       fclose (f);
207       return 1;
208     }
209   else
210     return 0;
211 }
212
213 int
214 rxpd_file_save (struct rxpd_file* self)
215 {
216   FILE* f = fopen (self->filename, "w");
217   // TODO error handling
218   if (f)
219     {
220       LLIST_FOREACH (&self->rules, n)
221         {
222           struct rxpd_rule* node = (struct rxpd_rule*)n;
223           fprintf (f, "%s\n", node->string);
224         }
225
226       fclose (f);
227       return 1;
228     }
229   else
230     return 0;
231 }
232
233 int
234 rxpd_file_cmp (const void* A, const void* B)
235 {
236   return strcmp (A, B);
237 }
238
239
240 //
241
242 struct rxpd_socket*
243 rxpd_socket_new_tcp4 (struct rxpd_base* base, const char* addr, unsigned short port)
244 {
245   struct rxpd_socket* self = malloc (sizeof (struct rxpd_socket));
246   if (!self)
247     abort();
248
249   self->base = base;
250
251   llist_init (&self->node);
252
253   self->fd = socket (PF_INET, SOCK_STREAM, 0);
254   if (self->fd == -1)
255     abort ();
256
257   struct sockaddr_in listen_addr;
258   memset (&listen_addr, 0, sizeof (listen_addr));
259
260   listen_addr.sin_family = AF_INET;
261   if (addr)
262     {
263       if (inet_aton (addr, &listen_addr.sin_addr) == 0)
264         abort();
265     }
266   else
267     listen_addr.sin_addr.s_addr = INADDR_ANY;
268
269   listen_addr.sin_port = htons(port);
270
271   if (bind (self->fd, (struct sockaddr*)&listen_addr, sizeof (listen_addr)) == -1)
272     abort();
273
274   static int yes = 1;
275   if (setsockopt (self->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
276     abort ();
277
278   if (listen (self->fd, 20) == -1)
279     abort ();
280
281   event_set (&self->ev, self->fd, EV_READ, rxpd_socket_accept, self);
282   llist_insert_tail (&base->sockets, &self->node);
283   return self;
284 }
285
286
287 void
288 rxpd_socket_delete (struct rxpd_socket* self)
289 {
290   if (self)
291     {
292       event_del (&self->ev);
293       llist_unlink (&self->node);
294       close (self->fd);
295     }
296   free (self);
297 }
298
299 struct rxpd_socket*
300 rxpd_socket_schedule (struct rxpd_socket* self)
301 {
302   if (self)
303     {
304       event_add (&self->ev, NULL);
305     }
306   return self;
307 }
308
309 struct rxpd_socket*
310 rxpd_socket_suspend (struct rxpd_socket* self)
311 {
312   if (self)
313     {
314       event_del (&self->ev);
315     }
316   return self;
317 }
318
319 void
320 rxpd_socket_accept (int fd, short event, void* ptr)
321 {
322   printf ("incoming connection\n");
323
324   struct rxpd_socket* self = ptr;
325
326   struct rxpd_connection* conn =
327     rxpd_connection_new (self->base, fd);
328
329   rxpd_connection_schedule (conn);
330   rxpd_socket_schedule (self);
331 }
332
333 ///
334
335 struct rxpd_buffer*
336 rxpd_buffer_init (struct rxpd_buffer* self, struct rxpd_connection* conn)
337 {
338   self->conn = conn;
339   self->state = RXPD_OK;
340   self->eol = self->eob = self->buffer;
341   self->buffer [4095] = '\0';
342   return self;
343 }
344
345
346 char*
347 rxpd_buffer_readline (struct rxpd_buffer* self, int again)
348 {
349   int fd = self->conn->fd;
350
351   if (self->eol < self->eob)
352     {
353       //there was a line pending, shift buffer left
354       memmove (self->buffer, self->eol+1, self->eob - self->eol - 1);
355       self->eob = (char*)(self->eob - (self->eol - self->buffer + 1));
356       self->eol = self->buffer;
357       // TODO handle \r's
358     }
359
360
361   if (!again && self->state == RXPD_OK)   // we only read when again is 0, first iteration
362     {
363       ssize_t r = 0;
364       do
365         {
366           r = read(fd, self->eob, 4095 - (self->eob - self->buffer));
367         }
368       while (r == -1 && errno == EINTR);
369
370       if (r != -1)
371         {
372
373           if (r == 0)
374             {
375               shutdown (fd, SHUT_RD);
376               self->state = RXPD_EOF;
377             }
378
379           self->eob += r;
380         }
381       else
382         self->state = RXPD_ERROR;
383     }
384
385   // find next newline, terminate string there
386   for (char* i = self->buffer; i < self->eob; ++i)
387     {
388       if (*i == '\n')
389         {
390           *i = '\0';
391           self->eol = i;
392           break;
393         }
394     }
395
396   // TODO handle buffer overfulls
397
398   return (self->eob == self->buffer) ? NULL : self->buffer;
399 }
400
401 /*
402 void
403 rxpd_buffer_write(int fd, short event, void* ptr)
404 {
405   struct rxpd_buffer* self = (struct rxpd_buffer*) ptr;
406
407   ssize_t n = write(int fd, const void *buf, size_t count);
408
409 }
410 */
411
412 int
413 rxpd_buffer_printf (struct rxpd_buffer* self, const char* fmt, ...)
414 {
415   // for now we do a blocking write, needs to be fixed some day
416   // add string to buffer
417   va_list ap;
418   va_start(ap, fmt);
419   //int sz = self->buffer+4096 - self->eob;
420   int n = vsnprintf (self->buffer, 4096, fmt, ap);
421   va_end(ap);
422
423   write (self->conn->fd, self->buffer, n);
424
425   if (n>4095)
426     return 0;
427
428   return 1;
429 }
430
431
432
433
434 ///
435
436 struct rxpd_connection*
437 rxpd_connection_new (struct rxpd_base* base, int fd)
438 {
439   struct rxpd_connection* self = malloc (sizeof (struct rxpd_connection));
440   if (!self)
441     abort();
442
443   socklen_t addr_sz = sizeof (self->peer_addr); 
444   self->fd = accept (fd, (struct sockaddr*)&self->peer_addr, &addr_sz);
445   if (self->fd == -1)
446     abort ();
447
448   static int yes = 1;
449   if (setsockopt (self->fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
450     abort ();
451
452   self->base = base;
453   self->file = NULL;
454   self->tmp_str = NULL;
455   llist_init (&self->tmp_list);
456
457   rxpd_buffer_init (&self->in, self);
458   rxpd_buffer_init (&self->out, self);
459   
460   event_set (&self->ev, self->fd, EV_READ, rxpd_connection_parse_cmd, self);
461   return self;
462 }
463
464 void
465 rxpd_connection_delete (struct rxpd_connection* self)
466 {
467   if (self)
468     {
469       event_del (&self->ev);
470       close (self->fd);
471       free (self->tmp_str);
472       LLIST_WHILE_HEAD (&self->tmp_list, n)
473         {
474           struct rxpd_rule* node = (struct rxpd_rule*)n;
475           rxpd_rule_delete (node);
476         }
477     }
478   free (self);
479 }
480
481 struct rxpd_connection*
482 rxpd_connection_schedule (struct rxpd_connection* self)
483 {
484   if (self)
485     {
486       event_add (&self->ev, NULL);
487     }
488   return self;
489 }
490
491 void
492 rxpd_connection_parse_cmd (int fd, short event, void* ptr)
493 {
494   printf ("parse cmd\n");
495   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
496
497   char* line;
498   line = rxpd_buffer_readline (&self->in, 0);
499
500   if (!line)
501     {
502       rxpd_buffer_printf (&self->out, "#ERROR: no data\n");
503       close (fd);
504       return;
505     }
506
507   static const struct cmd_table
508   {
509     enum rxpd_cmd_e nr;
510     const char* cmd;
511     size_t sz;
512   } cmds[] =
513     {
514 #define RXPD_CMD(cmd) {RXPD_CMD_##cmd, #cmd":", sizeof (#cmd)},
515       RXPD_COMMANDS
516 #undef RXPD_CMD
517       {0, NULL, 0}
518     };
519
520   const struct cmd_table* i;
521   for (i = cmds; i->cmd; ++i)
522     if (strncmp (line, i->cmd, i->sz) == 0)
523       break;
524   if (!i->cmd)
525     {
526       rxpd_buffer_printf (&self->out, "#ERROR: no command\n");
527       rxpd_connection_delete (self);
528       return;
529     }
530   // TODO policy check here
531
532   if (line[i->sz])
533     {
534       // rulename provided
535       self->file = (struct rxpd_file*) psplay_find (&self->base->files, &line[i->sz]);
536
537       if (!self->file)
538         {
539           self->file = rxpd_file_new (self->base, &line[i->sz]);
540           if (!self->file)
541             {
542               rxpd_buffer_printf (&self->out, "#ERROR: illegal filename\n");
543               rxpd_connection_delete (self);
544               return;
545             }
546         }
547     }
548
549   // dispatch
550   switch (i->nr)
551     {
552 #define RXPD_CMD(cmd)                                                           \
553 case RXPD_CMD_##cmd:                                                            \
554   event_set (&self->ev, self->fd, EV_READ, rxpd_connection_cmd_##cmd, self);    \
555   rxpd_connection_cmd_##cmd (fd, 0, ptr);                                       \
556   break;
557       RXPD_COMMANDS
558 #undef RXPD_CMD
559     }
560 }
561
562
563
564 void
565 rxpd_connection_cmd_CHECK (int fd, short event, void* ptr)
566 {
567   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
568
569   if (event == EV_READ)
570     {
571       int again = -1;
572       char* line;
573       while (line = rxpd_buffer_readline (&self->in, ++again))
574         {
575           if (*line == '\0')
576             {
577               rxpd_buffer_printf (&self->out, "#OK:\n");
578             }
579           else
580             {
581               LLIST_FOREACH (&self->file->rules, n)
582                 {
583                   struct rxpd_rule* rule = (struct rxpd_rule*)n;
584                   if (rule->string[0] != '#')
585                     {
586                       if (regexec (&rule->rx, line, 0, NULL, 0) == 0)
587                         {
588                           rxpd_buffer_printf (&self->out, "%s\n", rule->string);
589                           break;
590                         }
591                     }
592                 }
593             }
594         }
595     }
596   else if (!self->file)
597     {
598       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
599       rxpd_connection_delete (self);
600       return;
601     }
602
603   if (rxpd_buffer_state (&self->in) == RXPD_OK)
604     rxpd_connection_schedule (self);
605   else
606     {
607       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
608         rxpd_buffer_printf (&self->out, "#ERROR:\n");
609       rxpd_connection_delete (self);
610     }
611 }
612
613
614
615 static void
616 rxpd_connection_APPEND_PREPEND_helper (int fd, short event, void* ptr, int do_append)
617 {
618   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
619
620   if (event == EV_READ)
621     {
622       int again = -1;
623       char* line;
624
625       while (line = rxpd_buffer_readline (&self->in, ++again))
626         {
627           if (*line)
628             {
629               struct rxpd_rule* rule;
630               rule = rxpd_rule_new (line);
631               if (!rule)
632                 abort();
633
634               llist_insert_tail (&self->tmp_list, &rule->node);
635             }
636           else goto finish;     /* move along, look elsewhere! This goto is not harmful and saves some code. */
637         }
638     }
639   else if (!event && !self->file)
640     {
641       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
642       rxpd_connection_delete (self);
643       return;
644     }
645
646   if (rxpd_buffer_state (&self->in) == RXPD_OK)
647     rxpd_connection_schedule (self);
648   else
649     {
650       // TODO should also print error when any rule compilation failed, use tmp_str to save case?
651       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
652         rxpd_buffer_printf (&self->out, "#ERROR:\n");
653       else
654         {
655         finish:
656           rxpd_buffer_printf (&self->out, "#OK:\n");
657         }
658
659       if (do_append)
660         llist_insertlist_prev (&self->file->rules, &self->tmp_list);
661       else
662         llist_insertlist_next (&self->file->rules, &self->tmp_list);
663       rxpd_connection_delete (self);
664     }
665 }
666
667 void
668 rxpd_connection_cmd_APPEND (int fd, short event, void* ptr)
669 {
670   rxpd_connection_APPEND_PREPEND_helper (fd, event, ptr, 1);
671 }
672
673 void
674 rxpd_connection_cmd_PREPEND (int fd, short event, void* ptr)
675 {
676   rxpd_connection_APPEND_PREPEND_helper (fd, event, ptr, 0);
677 }
678
679
680 void
681 rxpd_connection_cmd_REMOVE (int fd, short event, void* ptr)
682 {
683   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
684
685   if (event == EV_READ)
686     {
687       int again = -1;
688       char* line;
689       while (line = rxpd_buffer_readline (&self->in, ++again))
690         {
691           LLIST_FOREACH (&self->file->rules, n)
692             {
693               struct rxpd_rule* rule = (struct rxpd_rule*)n;
694               if (strcmp (rule->string, line) == 0)
695                 {
696                   LList tmp = llist_prev (n);
697                   rxpd_rule_delete (rule);
698                   n = tmp;
699                   rxpd_buffer_printf (&self->out, "#OK:\n");
700                   goto done;
701                 }
702             }
703           rxpd_buffer_printf (&self->out, "#ERROR: line not found\n");
704         done:
705           ;
706         }
707     }
708   else if (!self->file)
709     {
710       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
711       rxpd_connection_delete (self);
712       return;
713     }
714
715   if (rxpd_buffer_state (&self->in) == RXPD_OK)
716     rxpd_connection_schedule (self);
717   else
718     {
719       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
720         rxpd_buffer_printf (&self->out, "#ERROR:\n");
721       rxpd_connection_delete (self);
722     }
723 }
724
725
726 static int
727 rxpd_connection_do_REPLACE (struct rxpd_connection* self)
728 {
729   struct rxpd_rule* rule;
730
731   LLIST_FOREACH (&self->file->rules, n)
732     {
733       rule = (struct rxpd_rule*)n;
734       if (strcmp (rule->string, self->tmp_str) == 0)
735         goto found;
736     }
737   return 0;
738  found:
739   llist_insertlist_next (&rule->node, &self->tmp_list);
740   rxpd_rule_delete (rule);
741   free (self->tmp_str);
742   self->tmp_str = NULL;
743   return 1;
744 }
745
746 void
747 rxpd_connection_cmd_REPLACE (int fd, short event, void* ptr)
748 {
749   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
750
751   if (event == EV_READ)
752     {
753       int again = -1;
754       char* line;
755       while (line = rxpd_buffer_readline (&self->in, ++again))
756         {
757           if (self->tmp_str)
758             {
759               if (*line)
760                 {
761                   struct rxpd_rule* rule;
762                   rule = rxpd_rule_new (line);
763                   if (!rule)
764                     abort();
765
766                   llist_insert_tail (&self->tmp_list, &rule->node);
767                 }
768               /* TODO handle empty lines? */
769             }
770           else
771             {
772               self->tmp_str = strdup (line);
773               if (!self->tmp_str) abort();
774             }
775         }
776     }
777   else if (!self->file)
778     {
779       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
780       rxpd_connection_delete (self);
781       return;
782     }
783
784   if (rxpd_buffer_state (&self->in) == RXPD_OK)
785     rxpd_connection_schedule (self);
786   else
787     {
788       if (rxpd_buffer_state (&self->in) == RXPD_ERROR)
789         rxpd_buffer_printf (&self->out, "#ERROR:\n");
790       else
791         {
792           if (rxpd_connection_do_REPLACE (self))
793             rxpd_buffer_printf (&self->out, "#OK:\n");
794           else
795             rxpd_buffer_printf (&self->out, "#ERROR: rule matching '%s'\n", self->tmp_str);
796         }
797
798       rxpd_connection_delete (self);
799     }
800 }
801
802 void
803 rxpd_connection_cmd_LOAD (int fd, short event, void* ptr)
804 {
805   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
806
807   if (!event)
808     {
809       if (self->file)
810         {
811           if (rxpd_file_load (self->file))
812             {
813               rxpd_buffer_printf (&self->out, "#OK:\n");
814             }
815           else
816             {
817               rxpd_buffer_printf (&self->out, "#ERROR: loading file '%s'\n", (const char*)self->file->node.key);
818             }
819         }
820       else
821         rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
822       rxpd_connection_delete (self);
823     }
824 }
825
826 void
827 rxpd_connection_cmd_SAVE (int fd, short event, void* ptr)
828 {
829   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
830
831   if (!event)
832     {
833       if (self->file)
834         {
835           if (rxpd_file_save (self->file))
836             {
837               rxpd_buffer_printf (&self->out, "#OK:\n");
838             }
839           else
840             {
841               rxpd_buffer_printf (&self->out, "#ERROR: saving file '%s'\n", (const char*)self->file->node.key);
842             }
843         }
844       else
845         rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
846       rxpd_connection_delete (self);
847     }
848 }
849
850 void
851 rxpd_connection_cmd_DUMP (int fd, short event, void* ptr)
852 {
853   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
854
855   if (!event && !self->file)
856     {
857       rxpd_buffer_printf (&self->out, "#ERROR: no such file\n");
858       rxpd_connection_delete (self);
859       return;
860     }
861
862   if (llist_is_empty (&self->file->rules))
863     rxpd_buffer_printf (&self->out, "#OK:\n");
864   else
865     {
866       LLIST_FOREACH (&self->file->rules, n)
867         {
868           struct rxpd_rule* rule = (struct rxpd_rule*)n;
869           rxpd_buffer_printf (&self->out, "%s\n", rule->string);
870         }
871     }
872
873   rxpd_connection_delete (self);
874 }
875
876
877 static psplay_delete_t
878 walk_LIST (PSplay node, const enum psplay_order_e which, int level, void* data)
879 {
880   (void) level;
881   struct rxpd_file* file = (struct rxpd_file*) node;
882   struct rxpd_connection* conn = (struct rxpd_connection*) data;
883
884   if (which == PSPLAY_INORDER)
885     rxpd_buffer_printf (&conn->out, "%s\n", file->node.key);
886
887   return PSPLAY_CONT;
888 }
889
890
891 void
892 rxpd_connection_cmd_LIST (int fd, short event, void* ptr)
893 {
894   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
895
896   if (psplay_isempty_root (&self->base->files))
897     rxpd_buffer_printf (&self->out, "#OK:\n");
898   else
899     psplay_walk (&self->base->files, NULL, walk_LIST, 0, ptr);
900
901   rxpd_connection_delete (self);
902 }
903
904 void
905 rxpd_connection_cmd_SHUTDOWN (int fd, short event, void* ptr)
906 {
907   struct rxpd_connection* self = (struct rxpd_connection*) ptr;
908   // destroy all sockets
909   LLIST_WHILE_HEAD (&self->base->sockets, n)
910     {
911       struct rxpd_socket* socket = (struct rxpd_socket*)n;
912       rxpd_socket_delete (socket);
913     }
914   rxpd_buffer_printf (&self->out, "#OK:\n");
915   rxpd_connection_delete (self);
916 }