]> granicus.if.org Git - apache/blob - server/util_xml.c
no need for relative includes. the directories will be added to the
[apache] / server / util_xml.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  */
54
55 /*
56 ** DAV extension module for Apache 2.0.*
57 **  - XML parser for the body of a request
58 */
59
60 /* James Clark's Expat parser */
61 #include "xmlparse.h"
62
63 #include "httpd.h"
64 #include "http_protocol.h"
65 #include "http_log.h"
66
67 /* ### need to fix this... */
68 #include "../modules/dav/main/mod_dav.h"
69
70
71 /* errors related to namespace processing */
72 #define DAV_NS_ERROR_UNKNOWN_PREFIX     (DAV_NS_ERROR_BASE)
73
74 /* test for a namespace prefix that begins with [Xx][Mm][Ll] */
75 #define DAV_NS_IS_RESERVED(name) \
76         ( (name[0] == 'X' || name[0] == 'x') && \
77           (name[1] == 'M' || name[1] == 'm') && \
78           (name[2] == 'L' || name[2] == 'l') )
79
80 /* content for parsing */
81 typedef struct dav_xml_ctx {
82     dav_xml_doc *doc;           /* the doc we're parsing */
83     ap_pool_t *p;               /* the pool we allocate from */
84     dav_xml_elem *cur_elem;     /* current element */
85
86     int error;                  /* an error has occurred */
87     /* errors may be DAV_NS_ERROR_* or other errors defined here (none yet) */
88
89 } dav_xml_ctx;
90
91 /* struct for scoping namespace declarations */
92 typedef struct dav_xml_ns_scope {
93     const char *prefix;         /* prefix used for this ns */
94     int ns;                     /* index into namespace table */
95     int emptyURI;               /* the namespace URI is the empty string */
96     struct dav_xml_ns_scope *next;      /* next scoped namespace */
97 } dav_xml_ns_scope;
98
99 /* ### need a similar mechanism for xml:lang values */
100
101
102 /* return namespace table index for a given prefix */
103 static int dav_find_prefix(dav_xml_ctx *ctx, const char *prefix)
104 {
105     dav_xml_elem *elem = ctx->cur_elem;
106
107     /*
108     ** Walk up the tree, looking for a namespace scope that defines this
109     ** prefix.
110     */
111     for (; elem; elem = elem->parent) {
112         dav_xml_ns_scope *ns_scope = elem->ns_scope;
113
114         for (ns_scope = elem->ns_scope; ns_scope; ns_scope = ns_scope->next) {
115             if (strcmp(prefix, ns_scope->prefix) == 0) {
116                 if (ns_scope->emptyURI) {
117                     /*
118                     ** It is possible to set the default namespace to an
119                     ** empty URI string; this resets the default namespace
120                     ** to mean "no namespace." We just found the prefix
121                     ** refers to an empty URI, so return "no namespace."
122                     */
123                     return DAV_NS_NONE;
124                 }
125
126                 return ns_scope->ns;
127             }
128         }
129     }
130
131     /*
132      * If the prefix is empty (""), this means that a prefix was not
133      * specified in the element/attribute. The search that was performed
134      * just above did not locate a default namespace URI (which is stored
135      * into ns_scope with an empty prefix). This means the element/attribute
136      * has "no namespace". We have a reserved value for this.
137      */
138     if (*prefix == '\0') {
139         return DAV_NS_NONE;
140     }
141
142     /* not found */
143     return DAV_NS_ERROR_UNKNOWN_PREFIX;
144 }
145
146 static void dav_start_handler(void *userdata, const char *name, const char **attrs)
147 {
148     dav_xml_ctx *ctx = userdata;
149     dav_xml_elem *elem;
150     dav_xml_attr *attr;
151     dav_xml_attr *prev;
152     char *colon;
153     const char *quoted;
154
155     /* punt once we find an error */
156     if (ctx->error)
157         return;
158
159     elem = ap_pcalloc(ctx->p, sizeof(*elem));
160
161     /* prep the element */
162     elem->name = ap_pstrdup(ctx->p, name);
163
164     /* fill in the attributes (note: ends up in reverse order) */
165     while (*attrs) {
166         attr = ap_palloc(ctx->p, sizeof(*attr));
167         attr->name = ap_pstrdup(ctx->p, *attrs++);
168         attr->value = ap_pstrdup(ctx->p, *attrs++);
169         attr->next = elem->attr;
170         elem->attr = attr;
171     }
172
173     /* hook the element into the tree */
174     if (ctx->cur_elem == NULL) {
175         /* no current element; this also becomes the root */
176         ctx->cur_elem = ctx->doc->root = elem;
177     }
178     else {
179         /* this element appeared within the current elem */
180         elem->parent = ctx->cur_elem;
181
182         /* set up the child/sibling links */
183         if (elem->parent->last_child == NULL) {
184             /* no first child either */
185             elem->parent->first_child = elem->parent->last_child = elem;
186         }
187         else {
188             /* hook onto the end of the parent's children */
189             elem->parent->last_child->next = elem;
190             elem->parent->last_child = elem;
191         }
192
193         /* this element is now the current element */
194         ctx->cur_elem = elem;
195     }
196
197     /* scan the attributes for namespace declarations */
198     for (prev = NULL, attr = elem->attr;
199          attr;
200          attr = attr->next) {
201         if (strncmp(attr->name, "xmlns", 5) == 0) {
202             const char *prefix = &attr->name[5];
203             dav_xml_ns_scope *ns_scope;
204
205             /* test for xmlns:foo= form and xmlns= form */
206             if (*prefix == ':')
207                 ++prefix;
208             else if (*prefix != '\0') {
209                 /* advance "prev" since "attr" is still present */
210                 prev = attr;
211                 continue;
212             }
213
214             /* quote the URI before we ever start working with it */
215             quoted = dav_quote_string(ctx->p, attr->value, 1);
216
217             /* build and insert the new scope */
218             ns_scope = ap_pcalloc(ctx->p, sizeof(*ns_scope));
219             ns_scope->prefix = prefix;
220             ns_scope->ns = dav_insert_uri(ctx->doc->namespaces, quoted);
221             ns_scope->emptyURI = *quoted == '\0';
222             ns_scope->next = elem->ns_scope;
223             elem->ns_scope = ns_scope;
224
225             /* remove this attribute from the element */
226             if (prev == NULL)
227                 elem->attr = attr->next;
228             else
229                 prev->next = attr->next;
230
231             /* Note: prev will not be advanced since we just removed "attr" */
232         }
233         else if (strcmp(attr->name, "xml:lang") == 0) {
234             /* save away the language (in quoted form) */
235             elem->lang = dav_quote_string(ctx->p, attr->value, 1);
236
237             /* remove this attribute from the element */
238             if (prev == NULL)
239                 elem->attr = attr->next;
240             else
241                 prev->next = attr->next;
242
243             /* Note: prev will not be advanced since we just removed "attr" */
244         }
245         else {
246             /* advance "prev" since "attr" is still present */
247             prev = attr;
248         }
249     }
250
251     /*
252     ** If an xml:lang attribute didn't exist (lang==NULL), then copy the
253     ** language from the parent element (if present).
254     **
255     ** NOTE: dav_elem_size() *depends* upon this pointer equality.
256     */
257     if (elem->lang == NULL && elem->parent != NULL)
258         elem->lang = elem->parent->lang;
259
260     /* adjust the element's namespace */
261     colon = strchr(elem->name, ':');
262     if (colon == NULL) {
263         /*
264          * The element is using the default namespace, which will always
265          * be found. Either it will be "no namespace", or a default
266          * namespace URI has been specified at some point.
267          */
268         elem->ns = dav_find_prefix(ctx, "");
269     }
270     else if (DAV_NS_IS_RESERVED(elem->name)) {
271         elem->ns = DAV_NS_NONE;
272     }
273     else {
274         *colon = '\0';
275         elem->ns = dav_find_prefix(ctx, elem->name);
276         elem->name = colon + 1;
277
278         if (DAV_NS_IS_ERROR(elem->ns)) {
279             ctx->error = elem->ns;
280             return;
281         }
282     }
283
284     /* adjust all remaining attributes' namespaces */
285     for (attr = elem->attr; attr; attr = attr->next) {
286         colon = strchr(attr->name, ':');
287         if (colon == NULL) {
288             /*
289              * Attributes do NOT use the default namespace. Therefore,
290              * we place them into the "no namespace" category.
291              */
292             attr->ns = DAV_NS_NONE;
293         }
294         else if (DAV_NS_IS_RESERVED(attr->name)) {
295             attr->ns = DAV_NS_NONE;
296         }
297         else {
298             *colon = '\0';
299             attr->ns = dav_find_prefix(ctx, attr->name);
300             attr->name = colon + 1;
301
302             if (DAV_NS_IS_ERROR(attr->ns)) {
303                 ctx->error = attr->ns;
304                 return;
305             }
306         }
307     }
308 }
309
310 static void dav_end_handler(void *userdata, const char *name)
311 {
312     dav_xml_ctx *ctx = userdata;
313
314     /* punt once we find an error */
315     if (ctx->error)
316         return;
317
318     /* pop up one level */
319     ctx->cur_elem = ctx->cur_elem->parent;
320 }
321
322 static void dav_cdata_handler(void *userdata, const char *data, int len)
323 {
324     dav_xml_ctx *ctx = userdata;
325     dav_xml_elem *elem;
326     dav_text_header *hdr;
327     const char *s;
328
329     /* punt once we find an error */
330     if (ctx->error)
331         return;
332
333     elem = ctx->cur_elem;
334     s = ap_pstrndup(ctx->p, data, len);
335
336     if (elem->last_child == NULL) {
337         /* no children yet. this cdata follows the start tag */
338         hdr = &elem->first_cdata;
339     }
340     else {
341         /* child elements exist. this cdata follows the last child. */
342         hdr = &elem->last_child->following_cdata;
343     }
344
345     dav_text_append(ctx->p, hdr, s);
346 }
347
348 int dav_parse_input(request_rec * r, dav_xml_doc **pdoc)
349 {
350     int result;
351     dav_xml_ctx ctx =
352     {0};
353     XML_Parser parser;
354
355     if ((result = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != OK)
356         return result;
357
358     if (r->remaining == 0) {
359         *pdoc = NULL;
360         return OK;
361     }
362
363     ctx.p = r->pool;
364     ctx.doc = ap_pcalloc(ctx.p, sizeof(*ctx.doc));
365
366     ctx.doc->namespaces = ap_make_array(ctx.p, 5, sizeof(const char *));
367     dav_insert_uri(ctx.doc->namespaces, "DAV:");
368
369     /* ### we should get the encoding from Content-Encoding */
370     parser = XML_ParserCreate(NULL);
371     if (parser == NULL) {
372         /* ### anything better to do? */
373         fprintf(stderr, "Ouch!  XML_ParserCreate() failed!\n");
374         exit(1);
375     }
376
377     XML_SetUserData(parser, (void *) &ctx);
378     XML_SetElementHandler(parser, dav_start_handler, dav_end_handler);
379     XML_SetCharacterDataHandler(parser, dav_cdata_handler);
380
381     if (ap_should_client_block(r)) {
382         long len;
383         char *buffer;
384         char end;
385         int rv;
386         size_t total_read = 0;
387         size_t limit_xml_body = dav_get_limit_xml_body(r);
388
389         /* allocate our working buffer */
390         buffer = ap_palloc(r->pool, DAV_READ_BLOCKSIZE);
391
392         /* read the body, stuffing it into the parser */
393         while ((len = ap_get_client_block(r, buffer, DAV_READ_BLOCKSIZE)) > 0) {
394             total_read += len;
395             if (limit_xml_body && total_read > limit_xml_body) {
396                 ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
397                               "XML request body is larger than the configured "
398                               "limit of %lu", (unsigned long)limit_xml_body);
399                 goto read_error;
400             }
401
402             rv = XML_Parse(parser, buffer, len, 0);
403             if (rv == 0)
404                 goto parser_error;
405         }
406         if (len == -1) {
407             /* ap_get_client_block() has logged an error */
408             goto read_error;
409         }
410
411         /* tell the parser that we're done */
412         rv = XML_Parse(parser, &end, 0, 1);
413         if (rv == 0)
414             goto parser_error;
415     }
416
417     XML_ParserFree(parser);
418
419     if (ctx.error) {
420         switch (ctx.error) {
421         case DAV_NS_ERROR_UNKNOWN_PREFIX:
422             ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
423                           "An undefined namespace prefix was used.");
424             break;
425
426         default:
427             ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
428                           "There was an error within the XML request body.");
429             break;
430         }
431
432         /* Apache will supply a default error, plus the error log above. */
433         return HTTP_BAD_REQUEST;
434     }
435
436     /* ### assert: ctx.cur_elem == NULL */
437
438     *pdoc = ctx.doc;
439
440     return OK;
441
442   parser_error:
443     {
444         enum XML_Error err = XML_GetErrorCode(parser);
445
446         /* ### fix this error message (default vs special) */
447         ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
448                       "XML parser error code: %s (%d).",
449                       XML_ErrorString(err), err);
450
451         XML_ParserFree(parser);
452
453         /* Apache will supply a default error, plus the error log above. */
454         return HTTP_BAD_REQUEST;
455     }
456
457   read_error:
458     XML_ParserFree(parser);
459
460     /* Apache will supply a default error, plus whatever was logged. */
461     return HTTP_BAD_REQUEST;
462 }