tinyows 1.2.2
cgi_request.c
Go to the documentation of this file.
1/*
2 Copyright (c) <2007-2012> <Barbara Philippot - Olivier Courtin>
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 IN THE SOFTWARE.
21*/
22
23
24#include <stdlib.h>
25#include <stdio.h>
26#include <assert.h>
27#include <string.h>
28#include <ctype.h>
29
30#include "../ows/ows.h"
31
32
33/*
34 * Max query string length send via QUERY_STRING CGI
35 */
36#define CGI_QUERY_MAX 1000000
37
38
39/*
40 * Return true if this cgi call was using a GET request, false otherwise
41 */
43{
44 char *method;
45
46 method = getenv("REQUEST_METHOD");
47 if (method && !strcmp(method, "GET")) return true;
48 return false;
49}
50
51
52/*
53 * Return true if this cgi call was using a POST request, false otherwise
54 */
56{
57 char *method;
58
59 method = getenv("REQUEST_METHOD");
60 if (method && !strcmp(method, "POST")) return true;
61 return false;
62}
63
64
65/*
66 * Return the string sent by CGI
67 */
69{
70 char *query;
71 int query_size = 0;
72 size_t s;
73
74 if (cgi_method_get()) query = getenv("QUERY_STRING");
75 else if (cgi_method_post()) {
76 query_size = atoi(getenv("CONTENT_LENGTH"));
77
78 query = malloc(sizeof(char) * query_size + 1);
79 if (!query) {
80 ows_error(o, OWS_ERROR_REQUEST_HTTP, "Error on QUERY input - Memory allocation", "request");
81 return NULL;
82 }
83 s = fread(query, query_size, 1, stdin);
84 (void)s;
85 if (ferror(stdin)) {
86 ows_error(o, OWS_ERROR_REQUEST_HTTP, "Error on QUERY input", "request");
87 return NULL;
88 }
89 query[query_size] = '\0';
90 }
91 /* local tests */
92 else query = getenv("QUERY_STRING");
93
94 return query;
95}
96
97
98/*
99 * Transform an hexadecimal string into ASCII string
100 * Source : http://hoohoo.ncsa.uiuc.edu/docs/
101 */
102static char cgi_hexatochar(char *what)
103{
104 char digit;
105
106 digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
107 digit *= 16;
108 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
109 return (digit);
110}
111
112
113/*
114 * Unescape an url
115 * Source : http://hoohoo.ncsa.uiuc.edu/docs/
116 */
117static void cgi_unescape_url(char *url)
118{
119 int x, y;
120
121 for (x = 0, y = 0; url[y]; ++x, ++y) {
122 if ((url[x] = url[y]) == '%') {
123 url[x] = cgi_hexatochar(&url[y + 1]);
124 y += 2;
125 }
126 }
127
128 url[x] = '\0';
129}
130
131
132/*
133 * Transform url's plus into spaces
134 */
135static void cgi_plustospace(char *str)
136{
137 int x;
138
139 for (x=0 ; str[x] ; x++)
140 if (str[x] == '+') str[x] = ' ';
141}
142
143/*
144 * Remove CR or LF in URL
145 */
146static void cgi_remove_crlf(char *str)
147{
148 int x;
149
150 for (x=0 ; str[x] ; x++)
151 if (str[x] == '\n' || str[x] == '\r')
152 str[x] = ' ';
153}
154
155/*
156 * Parse char by char QUERY_STRING request and return an array key/value
157 * (key are all lowercase)
158 */
159array *cgi_parse_kvp(ows * o, char *query)
160{
161 int i;
162 bool in_key;
163 buffer *key;
164 buffer *val;
165 array *arr;
166 char string[2];
167
168 assert(o);
169 assert(query);
170
171 key = buffer_init();
172 val = buffer_init();
173 arr = array_init();
174 in_key = true;
175
176 cgi_unescape_url(query);
177 cgi_remove_crlf(query);
178 cgi_plustospace(query);
179
180 for (i = 0; i < CGI_QUERY_MAX && query[i] ; i++) {
181
182 if (query[i] == '&') {
183
184 in_key = true;
185
186 array_add(arr, key, val);
187 key = buffer_init();
188 val = buffer_init();
189
190 } else if (query[i] == '=') {
191 /* char '=' inside filter key mustn't be taken into account */
192 if ((!buffer_case_cmp(key, "filter") || !buffer_case_cmp(key, "outputformat")) && buffer_cmp(val, ""))
193 in_key = false;
194 else buffer_add(val, query[i]);
195 }
196 /* Check characters'CGI request */
197 else {
198 /* to check the regular expression, argument must be a string, not a char */
199 string[0] = query[i];
200 string[1] = '\0';
201
202 if (in_key) {
203
204 /* if word is key, only letters are allowed */
205 if (check_regexp(string, "[A-Za-zà-ÿ]"))
206 buffer_add(key, tolower(query[i]));
207 else {
208 buffer_free(key);
209 buffer_free(val);
210 array_free(arr);
212 "QUERY_STRING contains forbidden characters", "request");
213 return NULL;
214 }
215 } else {
216 /* if word is filter key, more characters are allowed */
217 if ( check_regexp(string, "[A-Za-zà-ÿ0-9.\\=;,():/\\*_ \\-]")
218 || (buffer_cmp(key, "filter") && check_regexp(string, "[A-Za-zà-ÿ0-9.#\\,():/_<> %\"\'=\\*!\\-]|\\[|\\]")))
219 buffer_add(val, query[i]);
220 else {
221 buffer_free(key);
222 buffer_free(val);
223 array_free(arr);
225 "QUERY_STRING contains forbidden characters", "request");
226 return NULL;
227 }
228 }
229 }
230 }
231
232 if (i == CGI_QUERY_MAX) {
233 buffer_free(key);
234 buffer_free(val);
235 array_free(arr);
236 ows_error(o, OWS_ERROR_REQUEST_HTTP, "QUERY_STRING too long", "request");
237 return NULL;
238 }
239
240 array_add(arr, key, val);
241
242 return arr;
243}
244
245
246/*
247 * Add to the array : node name and content element
248 */
249static array *cgi_add_node(array * arr, xmlNodePtr n)
250{
251 buffer *key, *val;
252 xmlChar *content;
253
254 assert(arr);
255 assert(n);
256
257 key = buffer_init();
258 val = buffer_init();
259
260 buffer_add_str(key, (char *) n->name);
261 content = xmlNodeGetContent(n);
262 buffer_add_str(val, (char *) content);
263 xmlFree(content);
264
265 array_add(arr, key, val);
266
267 return arr;
268}
269
270
271/*
272 * Add to the array : attribute name and content attribute
273 */
274static array *cgi_add_att(array * arr, xmlAttr * att)
275{
276 buffer *key, *val;
277 xmlChar *content;
278
279 assert(arr);
280 assert(att);
281
282 key = buffer_init();
283 val = buffer_init();
284
285 buffer_add_str(key, (char *) att->name);
286 content = xmlNodeGetContent(att->children);
287 buffer_add_str(val, (char *) content);
288 xmlFree(content);
289
290 array_add(arr, key, val);
291
292 return arr;
293}
294
295
296/*
297 * Add to the array the sortby element
298 */
299static array *cgi_add_sortby(array * arr, xmlNodePtr n)
300{
301 buffer *key, *val;
302 xmlNodePtr elemt, node;
303 xmlChar *content;
304
305 assert(arr);
306 assert(n);
307
308 content = NULL;
309 key = buffer_init();
310 buffer_add_str(key, (char *) n->name);
311 val = buffer_init();
312
313 elemt = n->children;
314
315 if (elemt->type != XML_ELEMENT_NODE) elemt = elemt->next;
316
317 /* parse the properties to sort */
318 for ( /* empty */ ; elemt ; elemt = elemt->next) {
319 if (elemt->type == XML_ELEMENT_NODE) {
320 node = elemt->children;
321 if (node->type != XML_ELEMENT_NODE) node = node->next;
322
323 /* add the property name */
324 content = xmlNodeGetContent(node);
325 buffer_add_str(val, (char *) content);
326 xmlFree(content);
327
328 buffer_add_str(val, " ");
329
330 node = node->next;
331 if (node->type != XML_ELEMENT_NODE) node = node->next;
332
333 /* add the order */
334 content = xmlNodeGetContent(node);
335 buffer_add_str(val, (char *) content);
336 xmlFree(content);
337
338 }
339
340 if (elemt->next && elemt->next->type == XML_ELEMENT_NODE)
341 buffer_add_str(val, ",");
342 }
343
344 array_add(arr, key, val);
345
346 return arr;
347}
348
349
350/*
351 * Add to the array a buffer
352 */
353static array *cgi_add_buffer(array * arr, buffer * b, char *name)
354{
355 buffer *key, *val;
356
357 assert(arr);
358 assert(b);
359 assert(name);
360
361 key = buffer_init();
362 buffer_add_str(key, name);
363
364 /* if there is only one element in brackets, brackets are useless so delete them */
365 if (check_regexp(b->buf, "^\\([^\\)]*\\)$") || check_regexp(b->buf, "^\\(.*position\\(\\).*\\)$")) {
366 buffer_shift(b, 1);
367 buffer_pop(b, 1);
368 }
369
370 val = buffer_init();
371 buffer_copy(val, b);
372 array_add(arr, key, val);
373
374 return arr;
375}
376
377
378/*
379 * Add element into the buffer
380 */
381static buffer *cgi_add_into_buffer(buffer * b, xmlNodePtr n, bool need_comma)
382{
383 xmlChar *content;
384
385 assert(b);
386 assert(n);
387
388 if (need_comma) buffer_add_str(b, ",");
389
390 content = xmlNodeGetContent(n);
391 buffer_add_str(b, (char *) content);
392 xmlFree(content);
393
394 return b;
395}
396
397
398/*
399 * Add the whole xml element into the buffer
400 */
401buffer *cgi_add_xml_into_buffer(buffer * element, xmlNodePtr n)
402{
403 xmlBufferPtr buf;
404 xmlNsPtr * ns;
405 int i;
406
407 assert(element);
408 assert(n);
409
410 ns = xmlGetNsList(n->doc, n);
411
412 for (i = 0 ; ns[i] ; i++)
413 xmlNewNs(n, ns[i]->href, ns[i]->prefix);
414
415 buf = xmlBufferCreate();
416 xmlNodeDump(buf, n->doc, n, 0, 0);
417 buffer_add_str(element, (char *) buf->content);
418
419 xmlBufferFree(buf);
420 xmlFree(ns);
421
422 return element;
423}
424
425
426static bool is_node_ns_wfs(xmlNodePtr n)
427{
428 if (n->ns && n->ns->href
429 && (!strcmp("http://www.opengis.net/wfs", (char *) n->ns->href)
430 || !strcmp("http://www.opengis.net/ogc", (char *) n->ns->href))) return true;
431 return false;
432}
433
434/*
435 * Parse the XML request and return an array key/value
436 */
437array *cgi_parse_xml(ows * o, char *query)
438{
439 buffer *key, *val, *operations, *prop, *filter, *typename;
440 bool prop_need_comma, typ_need_comma;
441 xmlDocPtr xmldoc;
442 xmlAttr *att;
443 array *arr, *o_ns;
444 bool lock_error, unknown_error;
445 xmlNodePtr node, n = NULL;
446
447 assert(o);
448 assert(query);
449
450 prop_need_comma = typ_need_comma = false;
451 lock_error = unknown_error = false;
452
453 xmldoc = xmlParseMemory(query, strlen(query));
454
455 if (!xmldoc || !(n = xmlDocGetRootElement(xmldoc))) {
456 xmlFreeDoc(xmldoc);
457 ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "XML isn't valid", "request");
458 return NULL;
459 }
460
461 arr = array_init();
462
463 operations = buffer_init();
464 prop = buffer_init();;
465 filter = buffer_init();
466 typename = buffer_init();
467
469
470 /* First child processed aside because name node match value array instead of key array */
471 key = buffer_from_str("request");
472 val = buffer_from_str((char *) n->name);
473 array_add(arr, key, val);
474
475 /* Retrieve the namespace linked to the first node */
476 if (n->ns && n->ns->href) {
477 key = buffer_from_str("xmlns");
478 val = buffer_from_str((char *) n->ns->href);
479 array_add(arr, key, val);
480 }
481
482 /* Name element is put in key, and content element in value */
483 for (att = n->properties ; att ; att = att->next)
484 arr = cgi_add_att(arr, att);
485
486 for (n = n->children; n; n = n->next) {
487 if (n->type != XML_ELEMENT_NODE) continue; /* Eat spaces */
488 if (!is_node_ns_wfs(n)) continue; /* NS check */
489
490 for (att = n->properties ; att ; att = att->next) {
491
492 /* Add typename to the matching global buffer */
493 if (!strcmp((char *) att->name, "typeName")) {
494
495 typename = cgi_add_into_buffer(typename, att->children, typ_need_comma);
496 typ_need_comma = true;
497
498 /* Handle case when ns_prefix don't match but ns_uri does */
499 /* FIXME is this still work with several ns_uri ? */
500 if (n->nsDef && n->nsDef->href && array_is_value(o_ns, (char *) n->nsDef->href)) {
501 buffer_shift(typename, strlen((char *) n->nsDef->prefix));
502 buffer_add_head_str(typename, (array_get_key(o_ns, (char *) n->nsDef->href))->buf);
503 }
504
505 } else arr = cgi_add_att(arr, att); /* Add name and content element in array */
506 }
507
508 if (is_node_ns_wfs(n) && !strcmp((char *) n->name, "TypeName")) {
509 /* Add typename to the matching global buffer */
510 typename = cgi_add_into_buffer(typename, n, typ_need_comma);
511 typ_need_comma = true;
512 } else if (is_node_ns_wfs(n) && !strcmp((char *) n->name, "LockID")) lock_error = true;
513 /* If it's an operation, keep the xml to analyze it later */
514 else if ( is_node_ns_wfs(n)
515 && ( !strcmp((char *) n->name, "Insert")
516 || !strcmp((char *) n->name, "Delete")
517 || !strcmp((char *) n->name, "Update"))) {
518
519 if (!operations->use) buffer_add_str(operations, "<operations>");
520 operations = cgi_add_xml_into_buffer(operations, n); /* Add the whole xml operation to the buffer */
521 }
522 /* if node name match 'Query', parse the children elements */
523 else if (is_node_ns_wfs(n) && !strcmp((char *) n->name, "Query")) {
524 /* each query's propertynames and filter must be in brackets */
525 buffer_add_str(prop, "(");
526 buffer_add_str(filter, "(");
527
528 for (node = n->children; node; node = node->next) {
529 /*execute the process only if n is an element and not spaces for instance */
530 if (node->type != XML_ELEMENT_NODE) continue;
531
532 if (is_node_ns_wfs(node) && !strcmp((char *) node->name, "PropertyName")) {
533 /* add propertyname to the matching global buffer */
534 prop = cgi_add_into_buffer(prop, node, prop_need_comma);
535 prop_need_comma = true;
536 } else if (is_node_ns_wfs(node) && !strcmp((char *) node->name, "Filter")) {
537 /* add the whole xml filter to the matching global buffer */
538 filter = cgi_add_xml_into_buffer(filter, node);
539 } else if (is_node_ns_wfs(node) && !strcmp((char *) node->name, "SortBy")) {
540 /* add sortby element to the array */
541 arr = cgi_add_sortby(arr, node);
542 } else {
543 /* add element to the array */
544 arr = cgi_add_node(arr, node);
545 }
546 }
547
548 /* when there aren't any propertynames, an '*' must be included into
549 the buffer to be sur that propertyname's size and typename's size are similar */
550 if (prop_need_comma == false) buffer_add_str(prop, "*");
551
552 buffer_add_str(prop, ")");
553 prop_need_comma = false;
554 /* when there is no filter, an empty element must be included into
555 the list to be sur that filter's size and typename's size are similar */
556 buffer_add_str(filter, ")");
557
558 } else unknown_error = true;
559 }
560
561 /* operations */
562 if (operations->use) {
563 buffer_add_str(operations, "</operations>");
564 arr = cgi_add_buffer(arr, operations, "operations");
565 }
566
567 /* propertyname */
568 if (prop->use) {
569 /* if buffer just contains a series of (*), propertyname not useful */
570 if (!check_regexp(prop->buf, "^[\\(\\)\\*]+$")) {
571 arr = cgi_add_buffer(arr, prop, "propertyname");
572 }
573 }
574
575 /* filter */
576 if (filter->use) {
577 /* if buffer just contains a series of (), filter not useful */
578 if (!check_regexp(filter->buf, "^[\\(\\)]+$")) {
579 arr = cgi_add_buffer(arr, filter, "filter");
580 }
581 }
582
583 /* typename */
584 if (typename->use) arr = cgi_add_buffer(arr, typename, "typename");
585
586 buffer_free(prop);
587 buffer_free(operations);
588 buffer_free(filter);
589 buffer_free(typename);
590 array_free(o_ns);
591
592 xmlFreeDoc(xmldoc);
593
594 if (lock_error) {
595 array_free(arr);
596 ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "LockID is not implemented", "request");
597 return NULL;
598 }
599 if (unknown_error) {
600 array_free(arr);
601 ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "Unknown or invalid Query", "request");
602 return NULL;
603 }
604
605 return arr;
606}
607
608
609/*
610 * vim: expandtab sw=4 ts=4
611 */
static array * cgi_add_node(array *arr, xmlNodePtr n)
array * cgi_parse_xml(ows *o, char *query)
array * cgi_parse_kvp(ows *o, char *query)
bool cgi_method_get()
Definition cgi_request.c:42
bool cgi_method_post()
Definition cgi_request.c:55
static void cgi_unescape_url(char *url)
char * cgi_getback_query(ows *o)
Definition cgi_request.c:68
buffer * cgi_add_xml_into_buffer(buffer *element, xmlNodePtr n)
static array * cgi_add_sortby(array *arr, xmlNodePtr n)
static void cgi_remove_crlf(char *str)
static bool is_node_ns_wfs(xmlNodePtr n)
#define CGI_QUERY_MAX
Definition cgi_request.c:36
static array * cgi_add_att(array *arr, xmlAttr *att)
static buffer * cgi_add_into_buffer(buffer *b, xmlNodePtr n, bool need_comma)
static array * cgi_add_buffer(array *arr, buffer *b, char *name)
static char cgi_hexatochar(char *what)
static void cgi_plustospace(char *str)
void buffer_add(buffer *buf, char c)
Definition buffer.c:123
void buffer_copy(buffer *dest, const buffer *src)
Definition buffer.c:350
bool array_is_value(const array *a, const char *value)
Definition array.c:125
bool buffer_cmp(const buffer *buf, const char *str)
Definition buffer.c:290
buffer * array_get_key(const array *a, const char *value)
Definition array.c:170
void buffer_add_str(buffer *buf, const char *str)
Definition buffer.c:254
bool buffer_case_cmp(const buffer *buf, const char *str)
Definition buffer.c:330
array * ows_layer_list_namespaces(ows_layer_list *ll)
Definition ows_layer.c:228
void ows_error(ows *o, enum ows_error_code code, char *message, char *locator)
Definition ows_error.c:71
void array_free(array *a)
Definition array.c:53
void buffer_add_head_str(buffer *buf, char *str)
Definition buffer.c:239
buffer * buffer_from_str(const char *str)
Definition buffer.c:202
void buffer_free(buffer *buf)
Definition buffer.c:83
void buffer_shift(buffer *buf, size_t len)
Definition buffer.c:392
void buffer_pop(buffer *buf, size_t len)
Definition buffer.c:379
buffer * buffer_init()
Definition buffer.c:61
void array_add(array *a, buffer *key, buffer *value)
Definition array.c:80
bool check_regexp(const char *str_request, const char *str_regex)
Definition regexp.c:36
array * array_init()
Definition array.c:36
@ OWS_ERROR_REQUEST_HTTP
Definition ows_struct.h:167
@ OWS_ERROR_MISSING_PARAMETER_VALUE
Definition ows_struct.h:158
@ OWS_ERROR_INVALID_PARAMETER_VALUE
Definition ows_struct.h:159
char * buf
size to next realloc
Definition ows_struct.h:39
size_t use
Definition ows_struct.h:36
ows_layer_list * layers
Definition ows_struct.h:402

Generated for tinyows by doxygen 1.10.0