2dc69b05df27dfbc6107b80097818e93d511b67b
[middleman.git] / src / mime.c
1 /*
2     MiddleMan filtering proxy server
3     Copyright (C) 2002  Jason McLaughlin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 #include <string.h>
21 #include "proto.h"
22
23 /*
24 load <mime> section from XML file into MIME_LIST-type struct
25 */
26 MIME_LIST *mime_load(MIME_LIST * mime_list, XML_LIST * xml_list)
27 {
28         MIME_LIST *tmp_list = mime_list;
29         struct MIME_LIST_LIST *allow = NULL, *deny = NULL;
30
31         if (tmp_list == NULL) {
32                 tmp_list = xmalloc(sizeof(MIME_LIST));
33                 tmp_list->dtemplate = NULL;
34                 tmp_list->allow = NULL;
35                 tmp_list->deny = NULL;
36                 tmp_list->empty = NULL;
37                 tmp_list->policy = POLICY_ALLOW;
38                 tmp_list->empty = mime_ll_new(NULL);
39                 tmp_list->id = 0;
40                 tmp_list->enabled = TRUE;
41
42                 mime_list = tmp_list;
43
44                 pthread_rwlock_init(&tmp_list->lock, NULL);
45         } else {
46                 allow = tmp_list->allow;
47                 deny = tmp_list->deny;
48         }
49
50         while ((xml_list = xml_section(xml_list, "<mime>"))) {
51                 XML_LIST_LOOP(xml_list, "<mime>") {
52                         XML_LIST_CMP(xml_list, "<allow>") {
53                                 allow = mime_ll_new(allow);
54                                 allow->id = mime_list->id++;
55
56                                 if (tmp_list->allow == NULL)
57                                         tmp_list->allow = allow;
58                                 XML_LIST_LOOP(xml_list, "<allow>") {
59                                         XML_LIST_CMP(xml_list, "<enabled>") {
60                                                 xml_list = xml_list->next;
61                                                 if (xml_list->type == XML_VALUE) {
62                                                         if (!strcasecmp(xml_list->item, "false"))
63                                                                 allow->enabled = FALSE;
64                                                         else
65                                                                 allow->enabled = TRUE;
66                                                 }
67                                         }
68                                         XML_LIST_CMP(xml_list, "<comment>") {
69                                                 xml_list = xml_list->next;
70                                                 if (xml_list->type == XML_VALUE)
71                                                         mime_ll_insert(allow, xml_list->item, NULL, NULL, NULL, NULL);
72                                         }
73                                         XML_LIST_CMP(xml_list, "<template>") {
74                                                 xml_list = xml_list->next;
75                                                 if (xml_list->type == XML_VALUE)
76                                                         mime_ll_insert(allow, NULL, xml_list->item, NULL, NULL, NULL);
77                                         }
78                                         XML_LIST_CMP(xml_list, "<host>") {
79                                                 xml_list = xml_list->next;
80                                                 if (xml_list->type == XML_VALUE)
81                                                         mime_ll_insert(allow, NULL, NULL, xml_list->item, NULL, NULL);
82                                         }
83                                         XML_LIST_CMP(xml_list, "<mime>") {
84                                                 xml_list = xml_list->next;
85                                                 if (xml_list->type == XML_VALUE)
86                                                         mime_ll_insert(allow, NULL, NULL, NULL, xml_list->item, NULL);
87                                         }
88                                         XML_LIST_CMP(xml_list, "<file>") {
89                                                 xml_list = xml_list->next;
90                                                 if (xml_list->type == XML_VALUE)
91                                                         mime_ll_insert(allow, NULL, NULL, NULL, NULL, xml_list->item);
92                                         }
93                                 }
94                         }
95                         XML_LIST_CMP(xml_list, "<deny>") {
96                                 deny = mime_ll_new(deny);
97                                 deny->id = mime_list->id++;
98
99                                 if (tmp_list->deny == NULL)
100                                         tmp_list->deny = deny;
101                                 XML_LIST_LOOP(xml_list, "<deny>") {
102                                         XML_LIST_CMP(xml_list, "<enabled>") {
103                                                 xml_list = xml_list->next;
104                                                 if (xml_list->type == XML_VALUE) {
105                                                         if (!strcasecmp(xml_list->item, "false"))
106                                                                 deny->enabled = FALSE;
107                                                         else
108                                                                 deny->enabled = TRUE;
109                                                 }
110                                         }
111                                         XML_LIST_CMP(xml_list, "<comment.") {
112                                                 xml_list = xml_list->next;
113                                                 if (xml_list->type == XML_VALUE)
114                                                         mime_ll_insert(deny, xml_list->item, NULL, NULL, NULL, NULL);
115                                         }
116                                         XML_LIST_CMP(xml_list, "<template>") {
117                                                 xml_list = xml_list->next;
118                                                 if (xml_list->type == XML_VALUE)
119                                                         mime_ll_insert(deny, NULL, xml_list->item, NULL, NULL, NULL);
120                                         }
121                                         XML_LIST_CMP(xml_list, "<host>") {
122                                                 xml_list = xml_list->next;
123                                                 if (xml_list->type == XML_VALUE)
124                                                         mime_ll_insert(deny, NULL, NULL, xml_list->item, NULL, NULL);
125                                         }
126                                         XML_LIST_CMP(xml_list, "<mime>") {
127                                                 xml_list = xml_list->next;
128                                                 if (xml_list->type == XML_VALUE)
129                                                         mime_ll_insert(deny, NULL, NULL, NULL, xml_list->item, NULL);
130                                         }
131                                         XML_LIST_CMP(xml_list, "<file>") {
132                                                 xml_list = xml_list->next;
133                                                 if (xml_list->type == XML_VALUE)
134                                                         mime_ll_insert(deny, NULL, NULL, NULL, NULL, xml_list->item);
135                                         }
136                                 }
137                         }
138                         XML_LIST_CMP(xml_list, "<policy>") {
139                                 xml_list = xml_list->next;
140                                 if (xml_list->type == XML_VALUE) {
141                                         if (!strcasecmp(xml_list->item, "allow"))
142                                                 tmp_list->policy = POLICY_ALLOW;
143                                         else if (!strcasecmp(xml_list->item, "deny"))
144                                                 tmp_list->policy = POLICY_DENY;
145                                 }
146                         }
147                         XML_LIST_CMP(xml_list, "<default>") {
148                                 xml_list = xml_list->next;
149                                 if (xml_list->type == XML_VALUE) {
150                                         FREE_AND_NULL(tmp_list->dtemplate);
151                                         tmp_list->dtemplate = xstrdup(xml_list->item);
152                                 }
153                         }
154                         XML_LIST_CMP(xml_list, "<enabled>") {
155                                 xml_list = xml_list->next;
156                                 if (xml_list->type == XML_VALUE) {
157                                         if (!strcasecmp(xml_list->item, "false"))
158                                                 tmp_list->enabled = FALSE;
159                                         else
160                                                 tmp_list->enabled = TRUE;
161                                 }
162                         }
163                 }
164         }
165
166         return mime_list;
167 }
168
169 XML_LIST *mime_xml(MIME_LIST * mime_list, XML_LIST * xml_list)
170 {
171         int i;
172         char *ptr;
173         struct MIME_LIST_LIST *ml = NULL;
174
175         if (mime_list == NULL)
176                 return xml_list;
177
178         pthread_rwlock_rdlock(&mime_list->lock);
179
180         xml_list = xml_list_add(xml_list, "<mime>", XML_TAG);
181
182         xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
183         xml_list = xml_list_add(xml_list, (mime_list->enabled == TRUE) ? "true" : "false", XML_VALUE);
184         xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
185
186         xml_list = xml_list_add(xml_list, "<policy>", XML_TAG);
187         xml_list = xml_list_add(xml_list, (mime_list->policy == POLICY_ALLOW) ? "allow" : "deny", XML_VALUE);
188         xml_list = xml_list_add(xml_list, "</policy>", XML_TAG);
189
190         if (mime_list->dtemplate != NULL) {
191                 xml_list = xml_list_add(xml_list, "<default>", XML_TAG);
192                 ptr = string_to_xml(mime_list->dtemplate);
193                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
194                 xfree(ptr);
195                 xml_list = xml_list_add(xml_list, "</default>", XML_TAG);
196         }
197
198         for (i = 0; i < 2; i++) {
199                 switch (i) {
200                 case 0:
201                         ml = mime_list->allow;
202                         break;
203                 case 1:
204                         ml = mime_list->deny;
205                         break;
206                 }
207
208                 for (; ml; ml = ml->next) {
209                         xml_list = xml_list_add(xml_list, (i == 0) ? "<allow>" : "<deny>", XML_TAG);
210
211                         xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
212                         xml_list = xml_list_add(xml_list, (ml->enabled == TRUE) ? "true" : "false", XML_VALUE);
213                         xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
214
215                         if (ml->comment != NULL) {
216                                 xml_list = xml_list_add(xml_list, "<comment>", XML_TAG);
217                                 ptr = string_to_xml(ml->comment);
218                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
219                                 xfree(ptr);
220                                 xml_list = xml_list_add(xml_list, "</comment>", XML_TAG);
221                         }
222
223                         if (ml->mime != NULL) {
224                                 xml_list = xml_list_add(xml_list, "<mime>", XML_TAG);
225                                 ptr = string_to_xml(ml->mime);
226                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
227                                 xfree(ptr);
228                                 xml_list = xml_list_add(xml_list, "</mime>", XML_TAG);
229                         }
230
231                         if (ml->host != NULL) {
232                                 xml_list = xml_list_add(xml_list, "<host>", XML_TAG);
233                                 ptr = string_to_xml(ml->host);
234                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
235                                 xfree(ptr);
236                                 xml_list = xml_list_add(xml_list, "</host>", XML_TAG);
237                         }
238
239                         if (ml->file != NULL) {
240                                 xml_list = xml_list_add(xml_list, "<file>", XML_TAG);
241                                 ptr = string_to_xml(ml->file);
242                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
243                                 xfree(ptr);
244                                 xml_list = xml_list_add(xml_list, "</file>", XML_TAG);
245                         }
246
247                         if (ml->template != NULL) {
248                                 xml_list = xml_list_add(xml_list, "<template>", XML_TAG);
249                                 ptr = string_to_xml(ml->template);
250                                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
251                                 xfree(ptr);
252                                 xml_list = xml_list_add(xml_list, "</template>", XML_TAG);
253                         }
254
255                         xml_list = xml_list_add(xml_list, (i == 0) ? "</allow>" : "</deny>", XML_TAG);
256                 }
257         }
258
259         xml_list = xml_list_add(xml_list, "</mime>", XML_TAG);
260
261         pthread_rwlock_unlock(&mime_list->lock);
262
263         return xml_list;
264 }
265
266 void mime_ll_insert(struct MIME_LIST_LIST *x, char *a, char *b, char *c, char *d, char *e)
267 {
268         if (a != NULL) {
269                 FREE_AND_NULL(x->comment);
270
271                 if (strcmp(a, ""))
272                         x->comment = xstrdup(a);
273         }
274         if (b != NULL) {
275                 FREE_AND_NULL(x->template);
276
277                 if (strcmp(b, ""))
278                         x->template = xstrdup(b);
279         }
280         if (c != NULL) {
281                 if (x->he != NULL)
282                         reg_free(x->he);
283                 FREE_AND_NULL(x->host);
284
285                 if (strcmp(c, "")) {
286                         x->host = xstrdup(c);
287                         x->he = reg_compile(c, REGFLAGS);
288                 } else
289                         x->he = NULL;
290         }
291         if (d != NULL) {
292                 if (x->me != NULL)
293                         reg_free(x->me);
294                 FREE_AND_NULL(x->mime);
295
296                 if (strcmp(d, "")) {
297                         x->mime = xstrdup(d);
298                         x->me = reg_compile(d, REGFLAGS);
299                 } else
300                         x->me = NULL;
301         }
302         if (e != NULL) {
303                 if (x->fe != NULL)
304                         reg_free(x->fe);
305
306                 if (strcmp(e, "")) {
307                         x->file = xstrdup(e);
308                         x->fe = reg_compile(e, REGFLAGS);
309                 } else
310                         x->fe = NULL;
311         }
312 }
313
314 struct MIME_LIST_LIST *mime_ll_delete(struct MIME_LIST_LIST *x)
315 {
316         struct MIME_LIST_LIST *start = x;
317
318         while (start->prev != NULL)
319                 start = start->prev;
320
321         if (x->next != NULL)
322                 x->next->prev = x->prev;
323         if (x->prev != NULL)
324                 x->prev->next = x->next;
325         else
326                 start = start->next;
327
328         if (x->he != NULL)
329                 reg_free(x->he);
330         if (x->fe != NULL)
331                 reg_free(x->fe);
332         if (x->me != NULL)
333                 reg_free(x->me);
334         FREE_AND_NULL(x->comment);
335         FREE_AND_NULL(x->template);
336         FREE_AND_NULL(x->host);
337         FREE_AND_NULL(x->file);
338         FREE_AND_NULL(x->mime);
339
340         xfree(x);
341
342         return start;
343 }
344
345 struct MIME_LIST_LIST *mime_ll_new(struct MIME_LIST_LIST *x)
346 {
347         if (x == NULL) {
348                 x = xmalloc(sizeof(MIME_LIST));
349                 x->prev = NULL;
350         } else {
351                 while (x->next != NULL)
352                         x = x->next;
353                 x->next = xmalloc(sizeof(MIME_LIST));
354                 x->next->prev = x;
355                 x = x->next;
356         }
357
358         x->enabled = TRUE;
359         x->comment = NULL;
360         x->template = NULL;
361         x->he = NULL;
362         x->fe = NULL;
363         x->me = NULL;
364         x->host = NULL;
365         x->file = NULL;
366         x->mime = NULL;
367         x->next = NULL;
368
369         return x;
370 }
371
372 /*
373 free memory used by a MIME_LIST-type struct
374 */
375 void mime_free(MIME_LIST * mime_list)
376 {
377         if (!mime_list)
378                 return;
379
380         mime_ll_free(mime_list->allow);
381         mime_ll_free(mime_list->deny);
382
383         FREE_AND_NULL(mime_list->dtemplate);
384
385         pthread_rwlock_destroy(&mime_list->lock);
386
387         xfree(mime_list);
388 }
389
390 void mime_ll_free(struct MIME_LIST_LIST *ml)
391 {
392         struct MIME_LIST_LIST *tmp;
393
394         while (ml != NULL) {
395                 tmp = ml->next;
396
397                 if (ml->he != NULL)
398                         reg_free(ml->he);
399                 if (ml->fe != NULL)
400                         reg_free(ml->fe);
401                 if (ml->me != NULL)
402                         reg_free(ml->me);
403                 FREE_AND_NULL(ml->comment);
404                 FREE_AND_NULL(ml->host);
405                 FREE_AND_NULL(ml->file);
406                 FREE_AND_NULL(ml->mime);
407                 FREE_AND_NULL(ml->template);
408
409                 xfree(ml);
410                 ml = tmp;
411         }
412 }
413
414
415 /*
416 check if a specific mime-type is allowed, and return pointer to list node
417 if not
418 */
419 struct MIME_LIST_LIST *mime_check(MIME_LIST * mime_list, CONNECTION * connection)
420 {
421         int action = FALSE, result = TRUE, ret, i;
422         struct MIME_LIST_LIST *current, *match = NULL;
423
424         pthread_rwlock_rdlock(&mime_list->lock);
425
426         if (mime_list->enabled == FALSE || connection->bypass & FEATURE_MIME)
427                 return NULL;
428
429         for (i = 0; i < 2; i++) {
430                 if (i == 0) {
431                         if (mime_list->policy == POLICY_ALLOW) {
432                                 current = mime_list->deny;
433                                 action = FALSE;
434                                 result = TRUE;
435                         } else {
436                                 current = mime_list->allow;
437                                 action = TRUE;
438                                 result = FALSE;
439                         }
440                 } else if (result == action) {
441                         if (mime_list->policy == POLICY_ALLOW) {
442                                 current = mime_list->allow;
443                                 action = TRUE;
444                         } else {
445                                 current = mime_list->deny;
446                                 action = FALSE;
447                         }
448                 } else
449                         break;
450
451                 for (; current != NULL; current = current->next) {
452                         if (current->enabled == FALSE)
453                                 continue;
454
455                         if (current->he != NULL) {
456                                 ret = reg_exec(current->he, connection->header->host);
457                                 if (ret)
458                                         continue;
459                         }
460
461                         if (current->fe != NULL) {
462                                 ret = reg_exec(current->fe, connection->header->file);
463                                 if (ret)
464                                         continue;
465                         }
466
467                         if (current->me != NULL && connection->rheader->content_type != NULL) {
468                                 ret = reg_exec(current->me, connection->rheader->content_type);
469                                 if (ret)
470                                         continue;
471                         } else if (current->me != NULL)
472                                 continue;
473
474                         match = current;
475                         result = action;
476                         break;
477                 }
478         }
479
480         return (!result) ? (match != NULL) ? match : mime_list->empty : NULL;
481 }