new daemonizing code which chdir's and switches user etc
[rxpd] / src / rxpd_file.c
1 /*
2     rxpd_file.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 struct rxpd_file*
25 rxpd_file_new (struct rxpd_base* base, const char* filename)
26 {
27   struct rxpd_file* self = NULL;
28
29   while (*filename == '/')
30     ++filename;
31
32   if (!filename ||
33       strcspn (filename, RXPD_FILE_ILG_CHARS) != strlen (filename) ||
34       strstr (filename, "../") ||
35       strstr (filename, "/./") ||
36       filename[strlen (filename) - 1] == '/' ||
37       strlen (filename) + strlen (base->basedir) > 4095)
38     {
39       rxpd_log (base, LOG_ERR, "illegal filename: '%s'\n", filename?filename:"");
40       return NULL;
41     }
42
43   self = rxpd_malloc (sizeof (struct rxpd_file));
44   self->base = base;
45   pth_rwlock_init (&self->lock);
46
47   psplay_init (&self->node, rxpd_strdup (filename));
48   llist_init (&self->rules);
49
50   psplay_insert (&base->files, &self->node);
51
52   rxpd_log (base, LOG_INFO, "new file: '%s'\n", filename);
53   return self;
54 }
55
56 void
57 rxpd_file_delete (struct rxpd_file* self)
58 {
59   if (self)
60     {
61       rxpd_file_rules_delete (self);
62       psplay_remove (&self->base->files, &self->node);
63       free ((void*)self->node.key);
64       free (self);
65     }
66 }
67
68 struct rxpd_file*
69 rxpd_file_rules_delete (struct rxpd_file* self)
70 {
71   if (self)
72     {
73       pth_rwlock_acquire (&self->lock, PTH_RWLOCK_RW, FALSE, NULL);
74       LLIST_WHILE_HEAD (&self->rules, n)
75         {
76           struct rxpd_rule* node = (struct rxpd_rule*)n;
77           rxpd_rule_delete (node);
78         }
79       pth_rwlock_release (&self->lock);
80     }
81   return self;
82 }
83
84 int
85 rxpd_file_load (struct rxpd_file* self)
86 {
87   FILE* f = fopen (self->node.key, "r");
88   if (f)
89     {
90       pth_rwlock_acquire (&self->lock, PTH_RWLOCK_RW, FALSE, NULL);
91
92       /* First purge old rules */
93       LLIST_WHILE_HEAD (&self->rules, n)
94         {
95           struct rxpd_rule* node = (struct rxpd_rule*)n;
96           rxpd_rule_delete (node);
97         }
98
99       // TODO test excess line length = error
100       char buf[4096];
101
102       rxpd_log (self->base, LOG_NOTICE, "loading '%s'\n", self->node.key);
103
104       while (fgets (buf, 4096, f))
105         {
106           size_t last = strlen(buf);
107           if (buf[last-1] == '\n')
108             buf[last-1] = '\0';
109
110           struct rxpd_rule* rule;
111           rule = rxpd_rule_new (buf);
112           rxpd_log (self->base, LOG_DEBUG, "new rule '%s'\n", rule->string);
113
114           llist_insert_tail (&self->rules, &rule->node);
115         }
116
117       pth_rwlock_release (&self->lock);
118       fclose (f);
119       return 1;
120     }
121   else
122     {
123       rxpd_log (self->base, LOG_ERR, "failed loading '%s'\n", self->node.key);
124       return 0;
125     }
126 }
127
128
129 int
130 rxpd_file_save (struct rxpd_file* self)
131 {
132   char* filename = strdupa (self->node.key);
133
134   char* slash = filename;
135
136   while ((slash = strchr (slash+1, '/')))
137     {
138       *slash = '\0';
139       mkdir (filename, 0777);
140       *slash = '/';
141     }
142
143   FILE* f = fopen (filename, "w");
144   if (f)
145     {
146       pth_rwlock_acquire (&self->lock, PTH_RWLOCK_RD, FALSE, NULL);
147
148       LLIST_FOREACH (&self->rules, n)
149         {
150           struct rxpd_rule* node = (struct rxpd_rule*)n;
151           if (node->atime != (time_t)-1)
152             fprintf (f, "%ld:%s\n", node->atime, node->string);
153           else if (*node->string != '#')
154             fprintf (f, ":%s\n", node->string);
155           else
156             fprintf (f, "%s\n", node->string);
157         }
158
159       pth_rwlock_release (&self->lock);
160       fclose (f);
161       rxpd_log (self->base, LOG_NOTICE, "saved '%s'\n", filename);
162       return 1;
163     }
164   else
165     {
166       rxpd_log (self->base, LOG_ERR, "failed saving '%s'\n", filename);
167       return 0;
168     }
169 }
170
171 struct rxpd_file*
172 rxpd_file_dump (struct rxpd_file* self, struct rxpd_buffer* out)
173 {
174   if (self)
175     {
176       if (llist_is_empty (&self->rules))
177         rxpd_buffer_printf (out, "#OK:\n");
178       else
179         {
180           pth_rwlock_acquire (&self->lock, PTH_RWLOCK_RD, FALSE, NULL);
181
182           LLIST_FOREACH (&self->rules, n)
183             {
184               struct rxpd_rule* rule = (struct rxpd_rule*)n;
185               if (rule->atime != (time_t)-1)
186                 rxpd_buffer_printf (out, "%ld:%s\n", rule->atime, rule->string);
187               else if (*rule->string != '#')
188                 rxpd_buffer_printf (out, ":%s\n", rule->string);
189               else
190                 rxpd_buffer_printf (out, "%s\n", rule->string);
191             }
192           pth_rwlock_release (&self->lock);
193         }
194     }
195
196   return self;
197 }
198
199 int
200 rxpd_file_cmp (const void* A, const void* B)
201 {
202   return strcmp (A, B);
203 }
204
205