Removed threading
[middleman.git] / src / template.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 <stdio.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <fcntl.h>
25 #include "proto.h"
26 #include "pages.h"
27
28 /*
29 parse <templates> section from xml config file
30 */
31 TEMPLATES *templates_load(TEMPLATES * templates, XML_LIST * xml_list)
32 {
33         TEMPLATES *templates_tmp = templates;
34         struct TEMPLATE_LIST *template_list = NULL;
35
36         if (templates_tmp == NULL) {
37                 templates_tmp = xmalloc(sizeof(TEMPLATES));
38                 templates_tmp->template_list = NULL;
39                 templates_tmp->path = NULL;
40                 templates_tmp->id = 0;
41
42                 templates = templates_tmp;
43
44                 pthread_rwlock_init(&templates_tmp->lock, NULL);
45         } else
46                 template_list = templates_tmp->template_list;
47
48         while ((xml_list = xml_section(xml_list, "<templates>"))) {
49                 XML_LIST_LOOP(xml_list, "<templates>") {
50                         XML_LIST_CMP(xml_list, "<template>") {
51                                 template_list = template_list_new(template_list);
52                                 template_list->id = templates->id++;
53
54                                 if (templates_tmp->template_list == NULL)
55                                         templates_tmp->template_list = template_list;
56                                 XML_LIST_LOOP(xml_list, "<template>") {
57                                         XML_LIST_CMP(xml_list, "<enabled>") {
58                                                 xml_list = xml_list->next;
59                                                 if (xml_list->type == XML_VALUE) {
60                                                         if (!strcasecmp(xml_list->item, "false"))
61                                                                 template_list->enabled = FALSE;
62                                                         else
63                                                                 template_list->enabled = TRUE;
64                                                 }
65                                         }
66                                         XML_LIST_CMP(xml_list, "<comment>") {
67                                                 xml_list = xml_list->next;
68                                                 if (xml_list->type == XML_VALUE)
69                                                         template_list_insert(template_list, xml_list->item, NULL, NULL, NULL, NULL, NULL);
70                                         }
71                                         XML_LIST_CMP(xml_list, "<name>") {
72                                                 xml_list = xml_list->next;
73                                                 if (xml_list->type == XML_VALUE)
74                                                         template_list_insert(template_list, NULL, xml_list->item, NULL, NULL, NULL, NULL);
75                                         }
76                                         XML_LIST_CMP(xml_list, "<file>") {
77                                                 xml_list = xml_list->next;
78                                                 if (xml_list->type == XML_VALUE)
79                                                         template_list_insert(template_list, NULL, NULL, xml_list->item, NULL, NULL, NULL);
80                                         }
81                                         XML_LIST_CMP(xml_list, "<type>") {
82                                                 xml_list = xml_list->next;
83                                                 if (xml_list->type == XML_VALUE)
84                                                         template_list_insert(template_list, NULL, NULL, NULL, xml_list->item, NULL, NULL);
85                                         }
86                                         XML_LIST_CMP(xml_list, "<mime>") {
87                                                 xml_list = xml_list->next;
88                                                 if (xml_list->type == XML_VALUE)
89                                                         template_list_insert(template_list, NULL, NULL, NULL, NULL, xml_list->item, NULL);
90                                         }
91                                         XML_LIST_CMP(xml_list, "<code>") {
92                                                 xml_list = xml_list->next;
93                                                 if (xml_list->type == XML_VALUE)
94                                                         template_list_insert(template_list, NULL, NULL, NULL, NULL, NULL, xml_list->item);
95                                         }
96                                 }
97                         }
98                         XML_LIST_CMP(xml_list, "<path>") {
99                                 xml_list = xml_list->next;
100                                 if (xml_list->type == XML_VALUE) {
101                                         FREE_AND_NULL(templates_tmp->path);
102                                         templates_tmp->path = xstrdup(xml_list->item);
103                                 }
104                         }
105                 }
106         }
107
108         return templates;
109 }
110
111 XML_LIST *templates_xml(TEMPLATES * templates, XML_LIST * xml_list)
112 {
113         char *ptr, buf[16];
114         struct TEMPLATE_LIST *tl;
115
116         if (templates == NULL)
117                 return xml_list;
118
119         pthread_rwlock_rdlock(&templates->lock);
120
121         xml_list = xml_list_add(xml_list, "<templates>", XML_TAG);
122
123         if (templates->path != NULL) {
124                 xml_list = xml_list_add(xml_list, "<path>", XML_TAG);
125                 ptr = string_to_xml(templates->path);
126                 xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
127                 xfree(ptr);
128                 xml_list = xml_list_add(xml_list, "</path>", XML_TAG);
129         }
130
131         for (tl = templates->template_list; tl; tl = tl->next) {
132                 xml_list = xml_list_add(xml_list, "<template>", XML_TAG);
133
134                 xml_list = xml_list_add(xml_list, "<enabled>", XML_TAG);
135                 xml_list = xml_list_add(xml_list, (tl->enabled == TRUE) ? "true" : "false", XML_VALUE);
136                 xml_list = xml_list_add(xml_list, "</enabled>", XML_TAG);
137
138                 if (tl->comment != NULL) {
139                         xml_list = xml_list_add(xml_list, "<comment>", XML_TAG);
140                         ptr = string_to_xml(tl->comment);
141                         xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
142                         xfree(ptr);
143                         xml_list = xml_list_add(xml_list, "</comment>", XML_TAG);
144                 }
145
146                 if (tl->name != NULL) {
147                         xml_list = xml_list_add(xml_list, "<name>", XML_TAG);
148                         ptr = string_to_xml(tl->name);
149                         xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
150                         xfree(ptr);
151                         xml_list = xml_list_add(xml_list, "</name>", XML_TAG);
152                 }
153
154                 if (tl->file != NULL) {
155                         xml_list = xml_list_add(xml_list, "<file>", XML_TAG);
156                         ptr = string_to_xml(tl->file);
157                         xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
158                         xfree(ptr);
159                         xml_list = xml_list_add(xml_list, "</file>", XML_TAG);
160                 }
161
162                 if (tl->mime != NULL) {
163                         xml_list = xml_list_add(xml_list, "<mime>", XML_TAG);
164                         ptr = string_to_xml(tl->mime);
165                         xml_list = xml_list_add(xml_list, ptr, XML_VALUE);
166                         xfree(ptr);
167                         xml_list = xml_list_add(xml_list, "</mime>", XML_TAG);
168                 }
169
170                 if (tl->code != -1) {
171                         xml_list = xml_list_add(xml_list, "<code>", XML_TAG);
172                         snprintf(buf, sizeof(buf), "%d", tl->code);
173                         xml_list = xml_list_add(xml_list, buf, XML_VALUE);
174                         xml_list = xml_list_add(xml_list, "</code>", XML_TAG);
175                 }
176
177                 xml_list = xml_list_add(xml_list, "<type>", XML_TAG);
178                 xml_list = xml_list_add(xml_list, (tl->type == TEMPLATE_FILE) ? "file" : "executable", XML_VALUE);
179                 xml_list = xml_list_add(xml_list, "</type>", XML_TAG);
180
181                 xml_list = xml_list_add(xml_list, "</template>", XML_TAG);
182
183         }
184
185         xml_list = xml_list_add(xml_list, "</templates>", XML_TAG);
186
187         pthread_rwlock_unlock(&templates->lock);
188
189         return xml_list;
190 }
191
192 void template_list_insert(struct TEMPLATE_LIST *x, char *a, char *b, char *c, char *d, char *e, char *f)
193 {
194         if (a != NULL) {
195                 FREE_AND_NULL(x->comment);
196
197                 if (strcmp(a, ""))
198                         x->comment = xstrdup(a);
199         }
200         if (b != NULL) {
201                 FREE_AND_NULL(x->name);
202
203                 if (strcmp(b, ""))
204                         x->name = xstrdup(b);
205         }
206         if (c != NULL) {
207                 FREE_AND_NULL(x->file);
208
209                 if (strcmp(c, ""))
210                         x->file = xstrdup(c);
211         }
212         if (d != NULL) {
213                 if (!strcasecmp(d, "file"))
214                         x->type = TEMPLATE_FILE;
215                 else if (!strcasecmp(d, "executable"))
216                         x->type = TEMPLATE_EXECUTABLE;
217         }
218         if (e != NULL) {
219                 FREE_AND_NULL(x->mime);
220
221                 if (strcmp(e, ""))
222                         x->mime = xstrdup(e);
223         }
224         if (f != NULL) {
225                 x->code = -1;
226
227                 if (strcmp(f, ""))
228                         x->code = atoi(f);
229         }
230 }
231
232 struct TEMPLATE_LIST *template_list_new(struct TEMPLATE_LIST *x)
233 {
234         if (x == NULL) {
235                 x = xmalloc(sizeof(struct TEMPLATE_LIST));
236                 x->prev = NULL;
237         } else {
238                 while (x->next != NULL)
239                         x = x->next;
240                 x->next = xmalloc(sizeof(struct TEMPLATE_LIST));
241                 x->next->prev = x;
242                 x = x->next;
243         }
244         x->enabled = TRUE;
245         x->comment = NULL;
246         x->code = -1;
247         x->next = NULL;
248         x->name = NULL;
249         x->file = NULL;
250         x->type = TEMPLATE_FILE;
251         x->mime = NULL;
252
253         return x;
254 }
255
256 struct TEMPLATE_LIST *template_list_delete(struct TEMPLATE_LIST *x)
257 {
258         struct TEMPLATE_LIST *start = x;
259
260         while (start->prev != NULL)
261                 start = start->prev;
262
263         if (x->next != NULL)
264                 x->next->prev = x->prev;
265         if (x->prev != NULL)
266                 x->prev->next = x->next;
267         else
268                 start = start->next;
269
270         FREE_AND_NULL(x->comment);
271         FREE_AND_NULL(x->name);
272         FREE_AND_NULL(x->file);
273         FREE_AND_NULL(x->mime);
274
275         xfree(x);
276
277         return start;
278 }
279
280 /*
281 free templates structure
282 */
283 void templates_free(TEMPLATES * templates)
284 {
285         if (!templates)
286                 return;
287
288         templates_list_free(templates->template_list);
289
290         if (templates->path != NULL)
291                 xfree(templates->path);
292
293         pthread_rwlock_destroy(&templates->lock);
294
295         xfree(templates);
296 }
297
298 void templates_list_free(struct TEMPLATE_LIST *tl)
299 {
300         struct TEMPLATE_LIST *tmp;
301
302         while (tl != NULL) {
303                 tmp = tl->next;
304
305                 FREE_AND_NULL(tl->comment);
306                 FREE_AND_NULL(tl->name);
307                 FREE_AND_NULL(tl->file);
308                 FREE_AND_NULL(tl->mime);
309
310                 xfree(tl);
311                 tl = tmp;
312         }
313 }
314
315 /*
316 load a template from a file, and place in a buffer
317 */
318 FILEBUF *template_get(TEMPLATES * templates, CONNECTION * connection, char *name)
319 {
320         int i = 0, x = -1;
321         char buf[256];
322         struct TEMPLATE_LIST *template_list = NULL;
323         FILEBUF *ret = NULL;
324
325         pthread_rwlock_rdlock(&templates->lock);
326         template_list = templates->template_list;
327
328         while (template_list != NULL) {
329                 /* search through templates from config file first */
330                 if (template_list->name != NULL) {
331                         if (template_list->enabled == TRUE && !strcasecmp(template_list->name, name))
332                                 break;
333                 }
334
335                 template_list = template_list->next;
336         }
337
338         if (template_list == NULL || template_list->file == NULL) {
339                 /* no matching template in config file, search through compiled-in templates */
340                 while (PAGES[i].name != NULL) {
341                         if (!strcasecmp(PAGES[i].name, name)) {
342                                 x = i;
343                                 break;
344                         }
345                         i++;
346                 }
347
348                 /* nope, nothing... oh well */
349                 if (x == -1)
350                         goto out;
351
352                 i = (PAGES[x].len != -1) ? PAGES[x].len : strlen(PAGES[x].content);
353                 ret = filebuf_new();
354
355                 ret->size = i;
356                 ret->data = xmalloc(i);
357                 memcpy(ret->data, PAGES[x].content, i);
358                 ret->type = xstrdup(PAGES[x].mime);
359         } else if (template_list->type == TEMPLATE_FILE) {
360                 if (template_list->file[0] != '/' && templates->path != NULL)
361                         snprintf(buf, sizeof(buf), "%s/%s", templates->path, template_list->file);
362                 else
363                         snprintf(buf, sizeof(buf), "%s", template_list->file);
364
365                 ret = filebuf_new();
366
367                 filebuf_read(ret, buf);
368
369                 if (template_list->mime != NULL)
370                         ret->type = xstrdup(template_list->mime);
371         } else if (template_list->type == TEMPLATE_EXECUTABLE) {
372                 ret = external_exec(connection, template_list->file, NULL, EXTERNAL_PIPE);
373                 if (template_list->mime != NULL && ret != NULL) {
374                         if (!strcasecmp(template_list->mime, "STDIN"))
375                                 ret->type = external_getmime(ret);
376                         else
377                                 ret->type = xstrdup(template_list->mime);
378                 }
379         }
380
381         if (ret != NULL && (ret->type == NULL || !strncasecmp(ret->type, "text/", 5))) {
382                 /* replace variables with actual value, or just remove if value is unknown */
383                 /* note: these can't be escaped... I should fix that */
384                 string_replace(ret, "$HTTP_METHOD", (connection->header != NULL) ? connection->header->method : NULL);
385                 string_replace(ret, "$HTTP_HOST", (connection->header != NULL) ? connection->header->host : NULL);
386                 string_replace(ret, "$HTTP_FILE", (connection->header != NULL) ? connection->header->file : NULL);
387                 snprintf(buf, sizeof(buf), "%d", (connection->header != NULL) ? connection->header->port : 0);
388                 string_replace(ret, "$HTTP_PORT", buf);
389
390                 string_replace(ret, "$IP", connection->ip);
391         }
392
393         if (template_list != NULL && ret != NULL)
394                 ret->code = template_list->code;
395
396       out:
397         pthread_rwlock_unlock(&templates->lock);
398
399         return ret;
400 }
401
402 /*
403 send a template with http headers and proper mime type to web browser
404 */
405 int template_send(TEMPLATES * templates, char *template, CONNECTION * connection, int error)
406 {
407         FILEBUF *filebuf = NULL;
408         HEADER *header;
409
410         filebuf = template_get(templates, connection, template);
411
412         if (!filebuf)
413                 return FALSE;
414
415         header = header_new();
416         header->type = HTTP_RESP;
417         header->code = (filebuf->code != -1) ? filebuf->code : error;
418         header->content_length = filebuf->size;
419         header->content_type = (filebuf->type != NULL) ? xstrdup(filebuf->type) : xstrdup("text/plain");
420
421         header_send(header, connection, CLIENT, HEADER_RESP);
422         http_header_free(header);
423
424         /* don't actually send anything if this is a HEAD request */
425         if (connection->header == NULL || strcasecmp(connection->header->method, "HEAD"))
426                 net_filebuf_send(filebuf, connection, CLIENT);
427
428         putlog(MMLOG_TEMPLATE, "sent %s", template);
429
430         filebuf_free(filebuf);
431
432         return TRUE;
433 }