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