Removed threading
[middleman.git] / src / xml.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 <ctype.h>
22 #include <string.h>
23 #include "flags.h"
24 #include "proto.h"
25 #include "macros.h"
26
27 /*
28 load an XML-ish file into a linked list
29 note: this is only a partial xml implementation, just enough to suit the needs of this program
30 */
31 XML_LIST *xml_load(XML_LIST * xml_list, char *filename)
32 {
33         int i, line = 0, inside = FALSE, label = FALSE;
34         char buf[8096], tag[64], value[8096] = "", *bufpos, *vpos = value;
35         FILE *fptr;
36         STACK *stack = NULL;
37         XML_LIST *new_list = xml_list;
38
39         if ((fptr = fopen(filename, "r")) == NULL)
40                 return NULL;
41
42         while (fgets(buf, sizeof(buf), fptr) != NULL) {
43                 line++;
44                 bufpos = buf;
45
46                 while (*bufpos) {
47                         if (!strncmp(bufpos, "<!--", 4))
48                                 label++;
49                         else if (!strncmp(bufpos, "-->", 3))
50                                 label--;
51
52                         if (!label && *bufpos == '<') {
53
54                                 s_strncpy(tag, bufpos, sizeof(tag));
55                                 for (i = 0; tag[i] != '>' && tag[i]; i++);
56
57                                 if (i < 2 || !tag[i])
58                                         goto error;
59                                 else
60                                         i++;
61
62                                 tag[i] = '\0';
63
64                                 bufpos += i;
65
66                                 if (tag[1] == '/') {
67                                         if (stack == NULL || strcasecmp(&tag[2], &stack->data[1]))
68                                                 goto error;
69
70                                         if (!strcasecmp(stack->data, "<include>")) {
71                                                 *vpos = '\0';
72                                                 if (!strcasecmp(value, filename))
73                                                         goto error;
74
75                                                 new_list = xml_load(new_list, value);
76
77                                                 value[0] = '\0';
78                                                 vpos = value;
79                                         }
80
81                                         inside = FALSE;
82
83                                         POP(stack);
84
85                                         if (*value) {
86                                                 *vpos = '\0';
87                                                 new_list = xml_list_add(new_list, value, XML_VALUE);
88                                                 *value = '\0';
89                                         }
90                                 } else {
91                                         PUSH(stack, tag);
92
93                                         vpos = value;
94                                         inside = TRUE;
95                                 }
96
97                                 if (strcasecmp(tag, "<include>") && strcasecmp(tag, "</include>"))
98                                         new_list = xml_list_add(new_list, tag, XML_TAG);
99                         } else {
100                                 if (!label && inside) {
101                                         if (*bufpos == '\\' && (bufpos[1] == '\\' || bufpos[1] == '<' || bufpos[1] == '>'))
102                                                 bufpos++;
103
104                                         if (vpos - value < sizeof(value))
105                                                 *(vpos++) = *bufpos;
106
107                                 }
108
109                                 bufpos++;
110                         }
111
112                         if (xml_list == NULL)
113                                 xml_list = new_list;
114                 }
115         }
116
117         if (stack != NULL)
118                 goto error;
119
120         fclose(fptr);
121
122         return xml_list;
123       error:
124
125         fclose(fptr);
126
127         putlog(MMLOG_ERROR, "Parse error in %s on line %d", filename, line);
128
129         while (stack != NULL) {
130                 /* close off all open tags */
131                 s_strncpy(tag, stack->data, sizeof(tag) - 1);
132                 memmove(&tag[2], &tag[1], strlen(&tag[1]) + 1);
133                 tag[1] = '/';
134                 xml_list_add(new_list, tag, XML_TAG);
135                 POP(stack);
136         }
137
138         return xml_list;
139 }
140
141 /*
142 insert an XML tag or item at the end of a linked list
143 */
144 XML_LIST *xml_list_add(XML_LIST * list, char *item, int type)
145 {
146         if (list == NULL) {
147                 list = xmalloc(sizeof(XML_LIST));
148                 list->prev = NULL;
149         } else {
150                 while (list->next != NULL)
151                         list = list->next;
152                 list->next = xmalloc(sizeof(XML_LIST));
153                 list->next->prev = list;
154                 list = list->next;
155         }
156
157         list->item = xstrdup(item);
158         list->type = type;
159         list->next = NULL;
160
161         return list;
162 }
163
164 /*
165 free memory used by an XML linked list
166 */
167 void xml_list_free(XML_LIST * xml_list)
168 {
169         XML_LIST *xml_list_temp;
170
171         while (xml_list != NULL) {
172                 xml_list_temp = xml_list->next;
173                 xfree(xml_list->item);
174                 xfree(xml_list);
175                 xml_list = xml_list_temp;
176         }
177 }
178
179 /*
180 return pointer where section begins
181 */
182 XML_LIST *xml_section(XML_LIST * xml_list, char *section)
183 {
184         int i = 0;
185
186         while (xml_list != NULL) {
187                 if (xml_list->type == XML_TAG) {
188                         if (!i && !strcasecmp(xml_list->item, section))
189                                 return xml_list;
190                         if (xml_list->item[1] == '/')
191                                 i--;
192                         else
193                                 i++;
194                 }
195
196                 xml_list = xml_list->next;
197         }
198
199         return NULL;
200 }
201
202 /*
203 save formatted xml-ish file from XML_LIST struct
204 */
205 int xml_save(XML_LIST * xml_list, char *filename)
206 {
207         FILE *file;
208         int i, ind = 0;
209         char buf[8096];
210
211         file = fopen(filename, "w");
212         if (file == NULL)
213                 return FALSE;
214
215         for (; xml_list; xml_list = xml_list->next) {
216                 for (i = 0; i < ind; i++)
217                         buf[i] = '\t';
218
219
220                 if (ind == 0 && xml_list->prev != NULL) fprintf(file, "\n");
221
222                 if (xml_list->next != NULL && xml_list->next->type == XML_VALUE) {
223                         /* put the value on the same line as the opening and closing tags.. i.e. <foo>bar</foo> */
224                         snprintf(&buf[ind], sizeof(buf) - ind, "%s%s%s", xml_list->item, xml_list->next->item, xml_list->next->next->item);
225                         xml_list = xml_list->next->next;
226                 } else {
227
228                         if (xml_list->type == XML_TAG) {
229                                 if (xml_list->item[1] == '/') {
230                                         ind--;
231                                         snprintf(&buf[ind], sizeof(buf) - ind, "%s", xml_list->item);
232                                 } else {
233                                         snprintf(&buf[ind], sizeof(buf) - ind, "%s", xml_list->item);
234                                         ind++;
235                                 }
236                         } else
237                                 snprintf(&buf[ind], sizeof(buf) - ind, "%s", xml_list->item);
238
239                 }
240
241                 fprintf(file, "%s\n", buf);
242         }
243
244         fclose(file);
245
246         return TRUE;
247 }