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 #include <libxml/xmlschemas.h>
30 #include <libxml/xmlschemastypes.h>
31 
32 #include "ows.h"
33 #include "../ows_define.h"
34 
35 
36 /*
37  * Initialize ows_request structure
38  */
ows_request_init()39 ows_request *ows_request_init()
40 {
41   ows_request *or;
42   or = malloc(sizeof(ows_request));
43   assert(or);
44 
45   or->version = NULL;
46   or->service = OWS_SERVICE_UNKNOWN;
47   or->method = OWS_METHOD_UNKNOWN;
48   or->request.wfs = NULL;
49 
50   return or;
51 }
52 
53 
54 /*
55  * Release ows_request structure
56  */
ows_request_free(ows_request * or)57 void ows_request_free(ows_request * or)
58 {
59   assert(or);
60 
61   if (or->version) ows_version_free(or->version);
62 
63   switch (or->service) {
64     case WFS:
65       if (or->request.wfs) wfs_request_free(or->request.wfs);
66       break;
67     case OWS_SERVICE_UNKNOWN:
68       break;
69     default:
70       assert(0); /* Should not happen */
71   }
72 
73   free(or);
74   or = NULL;
75 }
76 
77 
78 #ifdef OWS_DEBUG
79 /*
80  * Flush an ows_request structure
81  * Used for debug purpose
82  */
ows_request_flush(ows_request * or,FILE * output)83 void ows_request_flush(ows_request * or, FILE * output)
84 {
85   assert(or);
86   assert(output);
87 
88   fprintf(output, "method:%d\n", or->method);
89   fprintf(output, "service:%d\n", or->service);
90 
91   if (or->version) {
92     fprintf(output, "version:");
93     ows_version_flush(or->version, output);
94     fprintf(output, "\n");
95   }
96 }
97 #endif
98 
99 
100 /*
101  * Simple callback used to catch error and warning from libxml2 schema
102  * validation
103  */
libxml2_callback(void * ctx,const char * msg,...)104 static void libxml2_callback  (void * ctx, const char * msg, ...)
105 {
106   va_list varg;
107   char * str;
108   ows *o;
109 
110   o = (ows *) ctx;
111   va_start(varg, msg);
112   str = (char *)va_arg( varg, char *);
113   ows_log(o, 1, str);
114 
115 #ifdef OWS_DEBUG
116   fprintf(stderr, "%s", str);
117 #endif
118 }
119 
120 
ows_generate_schema(const ows * o,buffer * xml_schema,bool schema_is_file)121 static xmlSchemaPtr ows_generate_schema(const ows *o, buffer * xml_schema, bool schema_is_file)
122 {
123   xmlSchemaParserCtxtPtr ctxt;
124   xmlSchemaPtr schema = NULL;
125 
126   assert(o);
127   assert(xml_schema);
128 
129   /* Open XML Schema File */
130   if (schema_is_file) ctxt = xmlSchemaNewParserCtxt(xml_schema->buf);
131   else                ctxt = xmlSchemaNewMemParserCtxt(xml_schema->buf, xml_schema->use);
132 
133   xmlSchemaSetParserErrors(ctxt,
134                            (xmlSchemaValidityErrorFunc) libxml2_callback,
135                            (xmlSchemaValidityWarningFunc) libxml2_callback,
136                            (void *) o);
137 
138   schema = xmlSchemaParse(ctxt);
139   xmlSchemaFreeParserCtxt(ctxt);
140 
141   /* If XML Schema hasn't been rightly loaded */
142   if (!schema) {
143     xmlSchemaCleanupTypes();
144     return NULL;
145   }
146 
147   return schema;
148 }
149 
150 
151 /*
152  * Valid an xml string against an XML schema
153  */
ows_schema_validation(ows * o,buffer * xml_schema,buffer * xml,bool schema_is_file,enum ows_schema_type schema_type)154 int ows_schema_validation(ows *o, buffer *xml_schema, buffer *xml, bool schema_is_file, enum ows_schema_type schema_type)
155 {
156   xmlSchemaPtr schema;
157   xmlSchemaValidCtxtPtr schema_ctx;
158   xmlDocPtr doc;
159   int ret = -1;
160   bool schema_generate = true;
161 
162   assert(o);
163   assert(xml);
164   assert(xml_schema);
165 
166   doc = xmlParseMemory(xml->buf, xml->use);
167   if (!doc) return ret;
168   if (!ows_libxml_check_namespace(o, doc->children)) {
169     xmlFreeDoc(doc);
170     return ret;
171   }
172 
173   if (schema_type == WFS_SCHEMA_TYPE_100 && o->schema_wfs_100) {
174     schema_generate = false;
175     schema = o->schema_wfs_100;
176   } else if (schema_type == WFS_SCHEMA_TYPE_110 && o->schema_wfs_110) {
177     schema_generate = false;
178     schema = o->schema_wfs_110;
179   }
180 
181   if (schema_generate) schema = ows_generate_schema(o, xml_schema, schema_is_file);
182   if (!schema) {
183     xmlFreeDoc(doc);
184     return ret;
185   }
186 
187   schema_ctx = xmlSchemaNewValidCtxt(schema);
188   xmlSchemaSetValidErrors(schema_ctx,
189                           (xmlSchemaValidityErrorFunc) libxml2_callback,
190                           (xmlSchemaValidityWarningFunc) libxml2_callback, (void *) o);
191   if (schema_ctx) {
192     ret = xmlSchemaValidateDoc(schema_ctx, doc); /* validation */
193     xmlSchemaFreeValidCtxt(schema_ctx);
194   }
195   xmlFreeDoc(doc);
196 
197   if (schema_generate) {
198     if (schema_type == WFS_SCHEMA_TYPE_100) o->schema_wfs_100 = schema;
199     else if (schema_type == WFS_SCHEMA_TYPE_110) o->schema_wfs_110 = schema;
200   }
201 
202   return ret;
203 }
204 
205 
206 /*
207  * Check and fill version
208  */
ows_request_check_version(ows * o,ows_request * or,const array * cgi)209 static ows_version *ows_request_check_version(ows * o, ows_request * or, const array * cgi)
210 {
211   buffer *b;
212   ows_version *v;
213   list *l = NULL;
214 
215   assert(o);
216   assert(or);
217   assert(cgi);
218 
219   v = or->version;
220   b = array_get(cgi, "version");
221 
222   if (buffer_cmp(b, "")) ows_version_set(o->request->version, 0, 0, 0);
223   else {
224     /* check if version format is x.y.z */
225     if (b->use < 5) {
226       ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE,
227                 "VERSION parameter is not valid (use x.y.z)", "version");
228       return v;
229     }
230 
231     l = list_explode('.', b);
232     if (l->size != 3) {
233       list_free(l);
234       ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE,
235                 "VERSION parameter is not valid (use x.y.z)", "version");
236       return v;
237     }
238 
239     if (    check_regexp(l->first->value->buf, "^[0-9]+$")
240          && check_regexp(l->first->next->value->buf, "^[0-9]+$")
241          && check_regexp(l->first->next->next->value->buf, "^[0-9]+$")) {
242       v->major = atoi(l->first->value->buf);
243       v->minor = atoi(l->first->next->value->buf);
244       v->release = atoi(l->first->next->next->value->buf);
245     } else {
246       list_free(l);
247       ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE,
248                 "VERSION parameter is not valid (use x.y.z)", "version");
249       return v;
250     }
251 
252     list_free(l);
253   }
254 
255   return v;
256 }
257 
258 
259 /*
260  * Check common OWS parameters's validity
261  *(service, version, request and layers definition)
262  */
ows_request_check(ows * o,ows_request * or,const array * cgi,const char * query)263 void ows_request_check(ows * o, ows_request * or, const array * cgi, const char *query)
264 {
265   list_node *srid;
266   buffer *typename, *xmlstring, *schema, *b=NULL;
267   ows_layer_node *ln = NULL;
268   bool srsname = false;
269   int valid = 0;
270 
271   assert(o && or && cgi && query);
272 
273   /* check if SERVICE is set */
274   if (!array_is_key(cgi, "service")) {
275     /* Tests WFS 1.1.0 require a default value for requests
276        encoded in XML if service is not set */
277     if (cgi_method_get()) {
278       ows_error(o, OWS_ERROR_MISSING_PARAMETER_VALUE, "SERVICE is not set", "SERVICE");
279       return;
280     } else {
281       if (buffer_case_cmp(o->metadata->type, "WFS"))
282         or->service = WFS;
283       else {
284         ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "SERVICE Unknown", "SERVICE");
285         return;
286       }
287     }
288   } else {
289     b = array_get(cgi, "service");
290 
291     if (buffer_case_cmp(b, "WFS"))
292       or->service = WFS;
293     else if (buffer_case_cmp(b, "")) {
294       if (buffer_case_cmp(o->metadata->type, "WFS"))
295         or->service = WFS;
296       else {
297         ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "SERVICE Unknown", "SERVICE");
298         return;
299       }
300     } else {
301       ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "SERVICE unknown", "SERVICE");
302       return;
303     }
304   }
305 
306 
307   /* check if REQUEST is set */
308   if (!array_is_key(cgi, "request")) {
309     ows_error(o, OWS_ERROR_MISSING_PARAMETER_VALUE, "REQUEST is not set", "REQUEST");
310     return;
311   } else b = array_get(cgi, "request");
312 
313   /* check if VERSION is set and init Version */
314   or->version = ows_version_init();
315 
316   if (!array_is_key(cgi, "version") || buffer_cmp(array_get(cgi, "version"), "")) {
317     if (!buffer_case_cmp(b, "GetCapabilities")) {
318       /* WFS 1.1.0 with KVP need a version set */
319       if (o->request->method == OWS_METHOD_KVP) {
320         ows_error(o, OWS_ERROR_MISSING_PARAMETER_VALUE, "VERSION is not set", "VERSION");
321         return;
322       }
323       /* WFS 1.1.0 require a default value for requests
324          encoded in XML if version is not set */
325       else if (o->request->method == OWS_METHOD_XML) {
326         if (or->service == WFS) ows_version_set(o->request->version, 1, 1, 0);
327       }
328     }
329   } else or->version = ows_request_check_version(o, or, cgi);
330 
331   /* check if layers have name or title and srs */
332   for (ln = o->layers->first ; ln ; ln = ln->next) {
333 
334     if (!ln->layer->name) {
335       ows_error(o, OWS_ERROR_CONFIG_FILE, "No layer name defined", "config_file");
336       return;
337     }
338 
339     if (!ows_layer_match_table(o, ln->layer->name)) continue;
340 
341     if (!ln->layer->title) {
342       ows_error(o, OWS_ERROR_CONFIG_FILE, "No layer title defined", "config_file");
343       return;
344     }
345 
346     if (or->service == WFS) {
347       if (!ln->layer->ns_prefix->use) {
348         ows_error(o, OWS_ERROR_CONFIG_FILE, "No layer NameSpace prefix defined", "config_file");
349         return;
350       }
351 
352       if (!ln->layer->ns_uri->use) {
353         ows_error(o, OWS_ERROR_CONFIG_FILE, "No layer NameSpace URI defined", "config_file");
354         return;
355       }
356     }
357 
358     if (ln->layer->srid) {
359       if (array_is_key(cgi, "srsname") && array_is_key(cgi, "typename")) {
360         b = array_get(cgi, "srsname");
361         typename = array_get(cgi, "typename");
362 
363         if (buffer_cmp(typename, ln->layer->name->buf)) {
364           if (    !check_regexp(b->buf, "^http://www.opengis.net")
365                && !check_regexp(b->buf, "^EPSG")
366                && !check_regexp(b->buf, "^urn:")
367                && !check_regexp(b->buf, "^spatialreferencing.org")) {
368             ows_error(o, OWS_ERROR_CONFIG_FILE, "srsname isn't valid", "srsName");
369             return;
370           }
371 
372           for (srid = ln->layer->srid->first ; srid ; srid = srid->next) {
373             if (check_regexp(b->buf, srid->value->buf)) srsname = true;
374           }
375 
376           if (srsname == false) {
377             ows_error(o, OWS_ERROR_CONFIG_FILE, "srsname doesn't match srid", "config_file");
378             return;
379           }
380         }
381       }
382     }
383   }
384 
385   /* check XML Validity */
386   if ( (cgi_method_post() && (    !strcmp(getenv("CONTENT_TYPE"), "application/xml; charset=UTF-8")
387                                || !strcmp(getenv("CONTENT_TYPE"), "application/xml")
388                                || !strcmp(getenv("CONTENT_TYPE"), "text/xml")
389                                || !strcmp(getenv("CONTENT_TYPE"), "text/plain")))
390        || (!cgi_method_post() && !cgi_method_get() && query[0] == '<') /* Unit test command line use case */ ) {
391 
392     if (or->service == WFS && o->check_schema) {
393       xmlstring = buffer_from_str(query);
394 
395       if (ows_version_get(or->version) == 100) {
396         schema = wfs_generate_schema(o, or->version);
397         valid = ows_schema_validation(o, schema, xmlstring, false, WFS_SCHEMA_TYPE_100);
398       } else {
399         schema = wfs_generate_schema(o, or->version);
400         valid = ows_schema_validation(o, schema, xmlstring, false, WFS_SCHEMA_TYPE_110);
401       }
402 
403       buffer_free(schema);
404       buffer_free(xmlstring);
405 
406       if (valid != 0) {
407         ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "XML request isn't valid", "request");
408         return;
409       }
410     }
411   }
412 }
413