fix frequently segfault on clean shutdown
[enigma2.git] / lib / base / nxml.cpp
1 #include <lib/base/nconfig.h>
2 #include <string.h>
3
4 #ifdef HAVE_CONFIG_H
5 #include <config.h>
6 #endif
7
8 #ifdef HAVE_TIME_H
9 #include <time.h>
10 #endif
11
12 #ifdef HAVE_LIBXML2
13 #include <libxml/tree.h>
14 #include <libxml/parser.h>
15 #include <libxml/parserInternals.h>
16 #else
17 #define xmlChar char
18 #endif /* HAVE_LIBXML2 */
19
20 #define DE(x)           ((struct nc_de_s *) (data+(x)))
21 #define IDE(x, y)       (DE(((unsigned *) (data+(x)->offset))[(y)]))
22 #define XML_DE          ((const xmlChar *) "dirEntry")
23 #define XML_NS          ((const xmlChar *) "http://hq.alert.sk/projects/nconfig")
24 #define XML_ROOT        ((const xmlChar *) "NConfigExport")
25
26 static char *encodeXml(const char *what)
27 {
28         unsigned p = 0, size = 6*strlen(what)+1;
29         char *ret = (char *)malloc(size);
30         for (; *what; what++) {
31                 switch (*what) {
32                 case '"':
33                         ret[p++] = '&';
34                         ret[p++] = 'q';
35                         ret[p++] = 'u';
36                         ret[p++] = 'o';
37                         ret[p++] = 't';
38                         ret[p++] = ';';
39                         continue;
40                 case '>':
41                         ret[p++] = '&';
42                         ret[p++] = 'q';
43                         ret[p++] = 't';
44                         ret[p++] = ';';
45                         continue;
46                 case '<':
47                         ret[p++] = '&';
48                         ret[p++] = 'l';
49                         ret[p++] = 't';
50                         ret[p++] = ';';
51                         continue;
52                 case '&':
53                         ret[p++] = '&';
54                         ret[p++] = 'a';
55                         ret[p++] = 'm';
56                         ret[p++] = 'p';
57                         ret[p++] = ';';
58                         continue;
59                 }
60                 if (*what >= 0x20 || *what == '\n' || *what == '\r' || *what == '\t')
61                         ret[p++] = *what;
62                 else
63                         p += sprintf(ret+p, "&#%d;", *what);
64         }
65         ret[p] = '\0';
66         return ret;
67 }
68
69 void NConfig::store(nc_de_s *de, FILE *f)
70 {
71         struct nc_de_s *cc;
72         for (unsigned i=0; i<de->pages; i++)
73                 if ((cc = IDE(de, i))->type) {
74                         char *encname = encodeXml(data+cc->name);
75                         fprintf(f, "<nc:%s name=\"%s\" type=\"%d\" value=\"", XML_DE, encname, cc->type);
76                         free(encname);
77                         switch (cc->type) {
78                         case NC_DIR:
79                                 fprintf(f, "%u\">\n", cc->pages);
80                                 store(cc, f);
81                                 fprintf(f, "</nc:%s>\n", XML_DE);
82                                 break;
83                         case NC_STRING:
84                                 fprintf(f, "%s\"/>\n", encname = encodeXml(data+cc->offset));
85                                 free(encname);
86                                 break;
87                         case NC_INT:
88                                 fprintf(f, "%lld\"/>\n", *((signed long long *) (data+cc->offset)));
89                                 break;
90                         case NC_UINT:
91                                 fprintf(f, "%llu\"/>\n", *((unsigned long long *) (data+cc->offset)));
92                                 break;
93                         case NC_DOUBLE:
94                                 fprintf(f, "%La\"/>\n", *((long double *) (data+cc->offset)));
95                                 break;
96                         case NC_RAW:
97                                 {
98                                         const char *raw = data+cc->offset;
99                                         for (unsigned j=0; j<cc->pages; j++)
100                                                 fprintf(f, "%d%d%d", raw[j] / 100, (raw[j] % 100) / 10, raw[j] % 10);
101                                         fprintf(f, "\"/>\n");
102                                 }
103                         }
104                 }
105 }
106
107 int NConfig::toXML(const char *filename)
108 {
109         if (fd < 0)
110                 return NC_ERR_NFILE;
111
112         FILE *f = fopen(filename, "w");
113         if (!f)
114                 return NC_ERR_PERM;
115         
116         fprintf(f, "%s", "<?xml version=\"1.0\"?>\n");
117         fprintf(f, "<nc:%s xmlns:nc=\"%s\" libVersion=\"%s\"", XML_ROOT, XML_NS, VERSION);
118 #ifdef HAVE_TIME_H
119     time_t t = time(NULL);
120     char *tim = ctime(&t);
121     tim[strlen(tim)-1] = 0;
122         fprintf(f, " time=\"%s\"", tim);
123 #endif /* HAVE_TIME_H */
124         fprintf(f, ">\n");
125         lockFile(NC_L_RO);
126
127         store(rdir, f);
128
129         unLockFile();
130         fprintf(f, "</nc:%s>\n", XML_ROOT);
131         fclose(f);
132         return NC_ERR_OK;
133 }
134
135 #ifdef HAVE_LIBXML2
136 static xmlSAXHandler sh;
137 enum stateEnum {noRoot = 0, inRoot, inDir, inEnt, unknown};
138
139 struct ncParseState {
140         stateEnum state, pState;
141         xmlChar *ns;
142         unsigned depth;
143         unsigned unDepth;
144         unsigned force;
145         NConfig *which;
146 };
147
148 static int ncXmlSAXParseFile(xmlSAXHandlerPtr sax, void *user_data, const char *filename)
149 {
150         int ret = 0;
151         xmlParserCtxtPtr ctxt = xmlCreateFileParserCtxt(filename);
152         if (!ctxt)
153                 return -1;
154         ctxt->sax = sax;
155         ctxt->userData = user_data;
156         xmlParseDocument(ctxt);
157         ret = ctxt->wellFormed ? 0 : -1;
158         if (sax)
159                 ctxt->sax = NULL;
160         xmlFreeParserCtxt(ctxt);
161         return ret;
162 }
163
164 static xmlEntityPtr ncXmlGetEntity(void *user_data, const CHAR *name)
165 {
166         return xmlGetPredefinedEntity(name);
167 }
168
169 static void ncXmlStartElement(void *user_data, const CHAR *name, const CHAR **attrs)
170 {
171         struct ncParseState *p = (struct ncParseState *)user_data;
172 #ifdef NC_DEBUG_XML
173         fprintf(stderr, "New element %s state=%d %s\n", name, p->state, p->ns);
174 #endif
175         if (p->state == unknown) {
176                 p->unDepth++;
177                 return;
178         }
179         if (p->state == noRoot) {
180                 while (*attrs) {
181                         if (!xmlStrncmp(*attrs, (const xmlChar *) "xmlns:", 6)) {
182                                 if (!xmlStrcmp(attrs[1], XML_NS)) {
183                                         p->ns = xmlStrdup((*attrs)+6);
184                                         break;
185                                 }
186                         }
187                         attrs += 2;
188                 }
189                 char *b = (char *) malloc(xmlStrlen(p->ns)+xmlStrlen(XML_ROOT)+2);
190                 sprintf(b, "%s:%s", p->ns, XML_ROOT);
191                 if (xmlStrcmp(name, (xmlChar *)b)) {
192 #ifdef NC_DEBUG_XML
193                         fprintf(stderr, "NewElement, entering unknown %s\n", name);
194 #endif
195                         p->pState = p->state;
196                         p->state = unknown;
197                 } else
198                         p->state = inRoot;
199                 free(b);
200                 return;
201         }
202         if (p->state == inRoot || p->state == inDir) {
203                 const xmlChar *value = NULL, *n = NULL;
204                 int type = 0;
205                 while (*attrs) {
206                         if (!xmlStrcmp(*attrs, (const xmlChar *)"value"))
207                                 value = attrs[1];
208                         if (!xmlStrcmp(*attrs, (const xmlChar *)"name"))
209                                 n = attrs[1];
210                         if (!xmlStrcmp(*attrs, (const xmlChar *)"type"))
211                                 type = atoi(attrs[1]);
212                         attrs += 2;
213                 }
214 #ifdef NC_DEBUG_XML
215                 fprintf(stderr, "%s %s %s %d %d\n", name, n, value, type, p->state);
216 #endif
217                 char *b = (char *) malloc(xmlStrlen(p->ns)+xmlStrlen(XML_DE)+2);
218                 sprintf(b, "%s:%s", p->ns, XML_DE);
219                 if (xmlStrcmp(name, (xmlChar *)b) || !type || !value || !n) {
220 #ifdef NC_DEBUG_XML
221                         fprintf(stderr, "NewElement, entering unknown on mismatch\n");
222 #endif
223                         p->pState = p->state;
224                         p->state = unknown;
225                         free(b);
226                         return;
227                 }
228                 free(b);
229                 if (p->force)
230                         p->which->delKey((const char *)n);
231
232                 switch (type) {
233                 case NC_DIR:
234                         if (p->which->createDir((const char *)n, strtoul((const char *)value, NULL, 0)) != NC_ERR_OK) {
235                                 p->pState = p->state;
236                                 p->state = unknown;
237 #ifdef NC_DEBUG_XML
238                                 fprintf(stderr, "NewElement, entering unknown on failed mkdir\n");
239 #endif
240                                 return;
241                         }
242                         p->which->chDir((const char *)n);
243                         break;
244                 case NC_STRING:
245                         p->which->setKey((const char *)n, (const char *)value);
246                         break;
247                 case NC_INT:
248                         p->which->setKey((const char *)n, strtoll((const char *)value, NULL, 0));
249                         break;
250                 case NC_UINT:
251                         p->which->setKey((const char *)n, strtoull((const char *)value, NULL, 0));
252                         break;
253                 case NC_DOUBLE:
254                         {
255                                 long double c;
256                                 sscanf((const char *)value, "%La", &c);
257                                 p->which->setKey((const char *)n, c);
258                         }
259                         break;
260                 case NC_RAW:
261                         {
262                                 unsigned size = xmlStrlen(value) / 3;
263                                 char *dec = NULL;
264                                 if (size) {
265                                         dec = (char *)malloc(size);
266                                         for (unsigned i=0, k=0; i<size; i++, k += 3)
267                                                 dec[i] = value[k] * 100 + value[k+1] * 10 + value[k+2];
268                                 }
269                                 p->which->setKey((const char *)n, dec, size);
270                                 free(dec);
271                         }
272                 }
273                 if (type == NC_DIR) {
274                         p->state = inDir;
275                         p->depth++;
276                 } else {
277                         p->pState = p->state;
278                         p->state = inEnt;
279                 }
280                 return;
281         }
282 }
283
284 static void ncXmlEndElement(void *user_data, const CHAR *name)
285 {
286         struct ncParseState *p = (struct ncParseState *)user_data;
287 #ifdef NC_DEBUG_XML
288         fprintf(stderr, "EndElement %s %s %d\n", name, p->ns, p->state);
289 #endif
290         if (p->state == inEnt) {
291                 p->state = p->pState;
292                 return;
293         }
294         if (p->state == unknown) {
295                 if (p->unDepth)
296                         p->unDepth--;
297                 else
298                         p->state = p->pState;
299                 return;
300         }
301         if (p->state == inRoot) {
302                 p->state = noRoot;
303                 free(p->ns);
304                 p->ns = NULL;
305                 return;
306         }
307         if (p->state == inDir) {
308                 p->depth--;
309                 if (!p->depth)
310                         p->state = inRoot;
311                 p->which->chDir("..");
312         }
313 }
314 #endif /* HAVE_LIBXML2 */
315
316 int NConfig::fromXML(const char *filename, int force)
317 {
318         if (fd < 0)
319                 return NC_ERR_NFILE;
320         if (omode != NC_O_RW)
321                 return NC_ERR_PERM;
322 #ifndef HAVE_LIBXML2
323         return NC_ERR_NOSUPPORT;
324 #else
325         struct ncParseState state = { noRoot, noRoot, NULL, 0, 0, force, this };
326         sh.getEntity = ncXmlGetEntity;
327         sh.startElement = ncXmlStartElement;
328         sh.endElement = ncXmlEndElement;
329
330         lockFile(NC_L_RW);
331         cdir = rdir;
332         int ret = ncXmlSAXParseFile(&sh, &state, filename);
333         cdir = rdir;
334         unLockFile();
335
336         return ret < 0 ? NC_ERR_NVAL : NC_ERR_OK;
337 #endif /* HAVE_LIBXML2 */
338 }
339