tinyows 1.2.2
fe_filter.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 <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <assert.h>
28
29#include "../ows/ows.h"
30
31
32/*
33 * Initialize filter_encoding structure
34 */
36{
38
39 fe = malloc(sizeof(filter_encoding));
40 assert(fe);
41
42 fe->sql = buffer_init();
44 fe->in_not = false;
45 fe->is_numeric = false;
46
47 return fe;
48}
49
50
51/*
52 * Release filter_encoding structure
53 */
55{
56 assert(fe);
57
58 buffer_free(fe->sql);
59 free(fe);
60 fe = NULL;
61}
62
63
64#ifdef OWS_DEBUG
65/*
66 * Print filter_encoding structure
67 */
68void filter_encoding_flush(filter_encoding * fe, FILE * output)
69{
70 assert(fe);
71 assert(output);
72
73 fprintf(output, "[\n");
74
75 if (fe->sql) {
76 fprintf(output, "sql -> ");
77 buffer_flush(fe->sql, output);
78 fprintf(output, "\n");
79 }
80
81 fprintf(output, " error code -> %d\n]\n", fe->error_code);
82}
83
84
85/*
86 * Parse an XML node, used for debug purposes
87 */
88void fe_node_flush(xmlNodePtr node, FILE * output)
89{
90 xmlNodePtr n;
91 xmlAttr *att;
92 xmlChar *content = NULL;
93
94 assert(node);
95 assert(output);
96
97 if (node->type == XML_ELEMENT_NODE) {
98 for (n = node; n; n = n->next) {
99 fprintf(output, "name:%s\n", n->name);
100
101 for (att = n->properties; att ; att = att->next)
102 fprintf(output, "%s=%s\n", att->name, (char *) xmlNodeGetContent(att->children));
103
104 if (n->children) fe_node_flush(n->children, output);
105 }
106 } else if ((node->type == XML_CDATA_SECTION_NODE) || (node->type == XML_TEXT_NODE)) {
107 content = xmlNodeGetContent(node);
108 fprintf(output, "%s\n", content);
109 xmlFree(content);
110 }
111}
112#endif
113
114
115/*
116 * Recursive function which eval a prefixed expression and
117 * return the matching string
118 */
119buffer * fe_expression(ows * o, buffer * typename, filter_encoding * fe, buffer * sql, xmlNodePtr n)
120{
121 char *escaped;
122 xmlChar *content;
123
124 assert(o && typename && fe && sql);
125
126 if (!n) return sql;
127 if (!strcmp((char *) n->name, "Function")) return fe_function(o, typename, fe, sql, n);
128
129 /* Open a bracket when there is a 'grandchildren' elements */
130 if (n->children) {
131 if (n->children->type == XML_ELEMENT_NODE && n->children->children) buffer_add_str(sql, "(");
132 else if (n->children->next && (n->children->next->children)) buffer_add_str(sql, "(");
133 }
134
135 /* Eval the left part of the expression */
136 sql = fe_expression(o, typename, fe, sql, n->children);
137
138 content = xmlNodeGetContent(n);
139
140 /* Order here is meaningfull (i.e Arithmetic then PropertyName then Literal) */
141 if (!strcmp((char *) n->name, "Add")) buffer_add_str(sql, " + ");
142 else if (!strcmp((char *) n->name, "Sub")) buffer_add_str(sql, " - ");
143 else if (!strcmp((char *) n->name, "Mul")) buffer_add_str(sql, " * ");
144 else if (!strcmp((char *) n->name, "Div")) buffer_add_str(sql, " / ");
145 else if (!strcmp((char *) n->name, "PropertyName")) {
146 buffer_add(sql, '"');
147 sql = fe_property_name(o, typename, fe, sql, n, false, true);
148 buffer_add(sql, '"');
149 } else if (!strcmp((char *) n->name, "Literal")) {
150 if (!fe->is_numeric) buffer_add_str(sql, "'");
151 escaped = ows_psql_escape_string(o, (char *) content);
152 if (escaped) {
153 buffer_add_str(sql, escaped);
154 free(escaped);
155 }
156 if (!fe->is_numeric) buffer_add_str(sql, "'");
157 } else if (n->type != XML_ELEMENT_NODE) {
158 sql = fe_expression(o, typename, fe, sql, n->next);
159 }
160
161 xmlFree(content);
162
163 /* Eval the right part of the expression */
164 if (n->children && n->children->next) {
165 if (n->children->type == XML_ELEMENT_NODE && n->children->next->type == XML_ELEMENT_NODE)
166 sql = fe_expression(o, typename, fe, sql, n->children->next);
167 else sql = fe_expression(o, typename, fe, sql, n->children->next->next);
168
169 content = xmlNodeGetContent(n->children->next);
170
171 /* If children element is empty */
172 if (check_regexp((char *) content, "^$") == 1) buffer_add_str(sql, "''");
173 /* Close a bracket when there are not empty children elements */
174 else if (check_regexp((char *) content, " +") != 1) buffer_add_str(sql, ")");
175
176 xmlFree(content);
177 }
178
179 return sql;
180}
181
182
183/*
184 * Transform an Xpath expression into a string propertyname
185 */
186buffer *fe_xpath_property_name(ows * o, buffer * typename, buffer * property)
187{
188 buffer *prop_name;
189
190 assert(o && typename && property);
191
192 if (check_regexp(property->buf, "\\*\\[position")) buffer_shift(property, 13); /* Remove '*[position()=' */
193 else buffer_shift(property, 2); /* Remove '*[' */
194 buffer_pop(property, 1); /* Remove ']' */
195
196 /* Retrieve the matching column name from the number of the column */
197 prop_name = ows_psql_column_name(o, typename, atoi(property->buf));
198 buffer_empty(property);
199 buffer_copy(property, prop_name);
200
201 buffer_free(prop_name);
202
203 return property;
204}
205
206
207/*
208 * Check if propertyName is valid and return the appropriate string
209 */
210buffer *fe_property_name(ows * o, buffer * typename, filter_encoding * fe, buffer * sql, xmlNodePtr n, bool check_geom_column, bool mandatory)
211{
212 xmlChar *content;
213 array *prop_table;
214 buffer *tmp, *layer_name;
215 list *l, *ll;
216
217 assert(o && typename && n && fe && sql);
218
219 while (n->type != XML_ELEMENT_NODE) n = n->next; /* eat spaces */
220
221 layer_name = ows_layer_prefix_to_uri(o->layers, typename);
222 prop_table = ows_psql_describe_table(o, layer_name);
223 assert(prop_table); /* should never happens as Typename checked previsously */
224
225 content = xmlNodeGetContent(n);
226 tmp = buffer_from_str((char *) content);
227 /* Check if propertyname is an Xpath expression */
228 if (check_regexp(tmp->buf, "\\*\\[")) tmp = fe_xpath_property_name(o, typename, tmp);
229
230 /* If propertyname have an Xpath suffix */
231 /* FIXME i just can't understand meaning of this use case */
232 if (check_regexp(tmp->buf, ".*\\[[0-9]+\\]")) {
233 l = list_explode('[', tmp);
234 buffer_empty(tmp);
235 buffer_copy(tmp, l->first->value);
236 list_free(l);
237 }
238
239 /* Check if propertyname is available */
240 ll = list_init();
241 list_add_by_copy(ll, layer_name);
242 tmp = wfs_request_remove_prop_ns_prefix(o, tmp, ll);
243 list_free(ll);
244
245 if (array_is_key(prop_table, tmp->buf)) {
246 buffer_copy(sql, tmp);
247 fe->is_numeric = ows_psql_is_numeric(array_get(prop_table, tmp->buf));
248 if (buffer_cmp(tmp, "intProperty")) fe->is_numeric = true;
249 } else if (mandatory) fe->error_code = FE_ERROR_PROPERTYNAME;
250
251 if (mandatory && check_geom_column && !ows_psql_is_geometry_column(o, layer_name, tmp))
253
254 buffer_free(tmp);
255 xmlFree(content);
256
257 return sql;
258}
259
260
261/*
262 * Check if featureId or GmlObjectId are valid and return the appropriate string
263 */
264buffer *fe_feature_id(ows * o, buffer * typename, filter_encoding * fe, xmlNodePtr n)
265{
266 char *escaped;
267 list *fe_list;
268 bool feature_id, gid;
269 xmlChar *fid = NULL;
270 buffer *buf_fid, *id_name = NULL;
271
272 assert(o && typename && n && fe);
273
274 for (feature_id = gid = false ; n ; n = n->next) {
275 if (n->type == XML_ELEMENT_NODE) {
276 buf_fid = buffer_init();
277
278 /* retrieve the property fid */
279 if (!strcmp((char *) n->name, "FeatureId")) {
280 feature_id = true;
281
282 /* only one type of identifier element must be included */
283 if (!gid) fid = xmlGetProp(n, (xmlChar *) "fid");
284 else {
286 buffer_free(buf_fid);
287 return fe->sql;
288 }
289 }
290
291 /* retrieve the property gml:id */
292 if (!strcmp((char *) n->name, "GmlObjectId")) {
293 gid = true;
294
295 /* only one type of identifier element must be included */
296 if (feature_id == false) fid = xmlGetProp(n, (xmlChar *) "id");
297 else {
299 buffer_free(buf_fid);
300 return fe->sql;
301 }
302 }
303 buffer_add_str(buf_fid, (char *) fid);
304 fe_list = list_explode('.', buf_fid);
305 xmlFree(fid);
306
307 /* Check if the layer_name match the typename queried */
308 if (fe_list->first && !buffer_cmp(fe_list->first->value,
310 ows_layer_prefix_to_uri(o->layers, typename)))->buf)) {
311 buffer_add_str(fe->sql, " FALSE"); /* We still execute the query */
312 list_free(fe_list);
313 buffer_free(buf_fid);
314 continue;
315 }
316
317 /* If there is no id column, raise an error */
318 id_name = ows_psql_id_column(o, ows_layer_prefix_to_uri(o->layers, typename));
319 if (!id_name || !id_name->use) {
321 list_free(fe_list);
322 buffer_free(buf_fid);
323 return fe->sql;
324 }
325
326 buffer_copy(fe->sql, id_name);
327 buffer_add_str(fe->sql, " = \'");
328 if (fe_list->last) escaped = ows_psql_escape_string(o, fe_list->last->value->buf);
329 else escaped = ows_psql_escape_string(o, buf_fid->buf);
330
331 if (escaped) {
332 buffer_add_str(fe->sql, escaped);
333 free(escaped);
334 }
335
336 buffer_add_str(fe->sql, "\'");
337 list_free(fe_list);
338 buffer_free(buf_fid);
339 }
340
341 if (n->next && n->next->type == XML_ELEMENT_NODE) buffer_add_str(fe->sql, " OR ");
342 }
343
344 return fe->sql;
345}
346
347
348/*
349 * Translate an XML filter to a filter encoding structure with a buffer
350 * containing a where condition of a SQL request usable into PostGis
351 * and an error code if an error occured
352 */
353filter_encoding *fe_filter(ows * o, filter_encoding * fe, buffer * typename, buffer * xmlchar)
354{
355 buffer * schema_path;
356 xmlDocPtr xmldoc;
357 xmlNodePtr n;
358 int ret = -1;
359
360 assert(o && fe && typename && xmlchar);
361
362 /* No validation if Filter came from KVP method
363 FIXME: really, but why ? */
364 if (o->check_schema && o->request->method == OWS_METHOD_XML) {
365 schema_path = buffer_init();
366 buffer_copy(schema_path, o->schema_dir);
367
368 if (ows_version_get(o->request->version) == 100) {
369 buffer_add_str(schema_path, WFS_SCHEMA_100);
370 ret = ows_schema_validation(o, schema_path, xmlchar, true, WFS_SCHEMA_TYPE_100);
371 } else {
372 buffer_add_str(schema_path, WFS_SCHEMA_110);
373 ret = ows_schema_validation(o, schema_path, xmlchar, true, WFS_SCHEMA_TYPE_110);
374 }
375
376 if (ret != 0) {
377 buffer_free(schema_path);
379 return fe;
380 }
381
382 buffer_free(schema_path);
383 }
384
385 xmldoc = xmlParseMemory(xmlchar->buf, xmlchar->use);
386
387 if (!xmldoc) {
389 xmlFreeDoc(xmldoc);
390 return fe;
391 }
392
393 if (!ows_libxml_check_namespace(o, xmldoc->children)) {
395 xmlFreeDoc(xmldoc);
396 return fe;
397 }
398
399 n = xmldoc->children->children;
400
401 /* jump to the next element if there are spaces */
402 while (n->type != XML_ELEMENT_NODE) n = n->next;
403
404 if (fe_is_comparison_op((char *) n->name)) fe->sql = fe_comparison_op(o, typename, fe, n);
405 if (fe_is_spatial_op((char *) n->name)) fe->sql = fe_spatial_op(o, typename, fe, n);
406 if (fe_is_logical_op((char *) n->name)) fe->sql = fe_logical_op(o, typename, fe, n);
407 if (!strcmp((char *) n->name, "FeatureId")) fe->sql = fe_feature_id(o, typename, fe, n);
408 else if (!strcmp((char *) n->name, "GmlObjectId") && ows_version_get(o->request->version) == 110)
409 fe->sql = fe_feature_id(o, typename, fe, n); /* FIXME Is FeatureId should really have priority ? */
410
411 xmlFreeDoc(xmldoc);
412
413 return fe;
414}
415
416
417/*
418 * Transform bbox parameter into WHERE statement of a FE->SQL request
419 */
420buffer *fe_kvp_bbox(ows * o, wfs_request * wr, buffer * layer_name, ows_bbox * bbox)
421{
422 buffer *where;
423 list *geom;
424 list_node *ln;
425 int srid = -1;
426 bool transform = false;
427
428 assert(o && wr && layer_name && bbox);
429
430 where = buffer_init();
431 geom = ows_psql_geometry_column(o, layer_name);
432 buffer_add_str(where, " WHERE");
433
434 if (wr->srs) {
435 srid = ows_srs_get_srid_from_layer(o, layer_name);
436 transform = true;
437 }
438 /* BBOX optional crsuri parameter since WFS 1.1 */
439 if (wr->bbox->srs && (wr->bbox->srs->srid != wr->srs->srid)) {
440 srid = wr->srs->srid;
441 transform = true;
442 }
443
444 for (ln = geom->first ; ln ; ln = ln->next) {
445
446 /* We use _ST_Intersects and && operator rather than ST_Intersects for performances issues */
447 buffer_add_str(where, " (_ST_Intersects(");
448 if (transform) buffer_add_str(where, "ST_Transform(");
449 buffer_add_str(where, "\"");
450 buffer_copy(where, ln->value);
451 buffer_add_str(where, "\",");
452 if (transform) {
453 buffer_add_int(where, srid);
454 buffer_add_str(where, "),");
455 }
456
457 if (transform) buffer_add_str(where, "ST_Transform(");
458 ows_bbox_to_query(o, wr->bbox, where);
459 if (transform) {
460 buffer_add_str(where, ",");
461 buffer_add_int(where, srid);
462 buffer_add_str(where, ")");
463 }
464 buffer_add_str(where, ") AND ");
465 if (transform) buffer_add_str(where, "ST_Transform(");
466 buffer_add_str(where, "\"");
467 buffer_copy(where, ln->value);
468 buffer_add_str(where, "\"");
469 if (transform) {
470 buffer_add_str(where, ",");
471 buffer_add_int(where, srid);
472 buffer_add_str(where, ")");
473 }
474 buffer_add_str(where, " && ");
475 if (transform) buffer_add_str(where, "ST_Transform(");
476 ows_bbox_to_query(o, wr->bbox, where);
477 if (transform) {
478 buffer_add_str(where, ",");
479 buffer_add_int(where, srid);
480 buffer_add_str(where, ")");
481 }
482
483 if (ln->next) buffer_add_str(where, ") OR ");
484 else buffer_add_str(where, ")");
485 }
486
487 return where;
488}
489
490
491/*
492 * Transform featureid parameter into WHERE statement of a FE->SQL request
493 */
494buffer *fe_kvp_featureid(ows * o, wfs_request * wr, buffer * layer_name, list * fid)
495{
496 buffer *id_name, *where;
497 char *escaped;
498 list *fe;
499 list_node *ln;
500
501 assert(o && wr && layer_name && fid);
502
503 where = buffer_init();
504
505 id_name = ows_psql_id_column(o, layer_name);
506 if (!id_name || id_name->use == 0) return where;
507
508 buffer_add_str(where, " WHERE ");
509
510 for (ln = fid->first ; ln ; ln = ln->next) {
511 fe = list_explode('.', ln->value);
512 buffer_copy(where, id_name);
513 buffer_add_str(where, " = '");
514 escaped = ows_psql_escape_string(o, fe->last->value->buf);
515 if (escaped) {
516 buffer_add_str(where, escaped);
517 free(escaped);
518 }
519 buffer_add_str(where, "'");
520 list_free(fe);
521
522 if (ln->next) buffer_add_str(where, " OR ");
523 }
524
525 return where;
526}
filter_encoding * fe_filter(ows *o, filter_encoding *fe, buffer *typename, buffer *xmlchar)
Definition fe_filter.c:353
filter_encoding * filter_encoding_init()
Definition fe_filter.c:35
buffer * fe_xpath_property_name(ows *o, buffer *typename, buffer *property)
Definition fe_filter.c:186
buffer * fe_expression(ows *o, buffer *typename, filter_encoding *fe, buffer *sql, xmlNodePtr n)
Definition fe_filter.c:119
void filter_encoding_free(filter_encoding *fe)
Definition fe_filter.c:54
buffer * fe_feature_id(ows *o, buffer *typename, filter_encoding *fe, xmlNodePtr n)
Definition fe_filter.c:264
buffer * fe_property_name(ows *o, buffer *typename, filter_encoding *fe, buffer *sql, xmlNodePtr n, bool check_geom_column, bool mandatory)
Definition fe_filter.c:210
buffer * fe_kvp_featureid(ows *o, wfs_request *wr, buffer *layer_name, list *fid)
Definition fe_filter.c:494
buffer * fe_kvp_bbox(ows *o, wfs_request *wr, buffer *layer_name, ows_bbox *bbox)
Definition fe_filter.c:420
void buffer_add(buffer *buf, char c)
Definition buffer.c:123
int ows_version_get(ows_version *v)
buffer * ows_psql_column_name(ows *o, buffer *layer_name, int number)
Definition ows_psql.c:299
void list_add_by_copy(list *l, buffer *value)
Definition list.c:187
void buffer_empty(buffer *buf)
Definition buffer.c:100
buffer * wfs_request_remove_prop_ns_prefix(ows *o, buffer *prop, list *layer_name)
buffer * fe_function(ows *o, buffer *typename, filter_encoding *fe, buffer *sql, xmlNodePtr n)
void buffer_copy(buffer *dest, const buffer *src)
Definition buffer.c:350
bool buffer_cmp(const buffer *buf, const char *str)
Definition buffer.c:290
list * ows_psql_geometry_column(ows *o, buffer *layer_name)
Definition ows_psql.c:76
void list_free(list *l)
Definition list.c:54
bool fe_is_comparison_op(char *name)
void buffer_add_str(buffer *buf, const char *str)
Definition buffer.c:254
list * list_init()
Definition list.c:36
void buffer_flush(buffer *buf, FILE *output)
Definition buffer.c:112
buffer * fe_logical_op(ows *o, buffer *typename, filter_encoding *fe, xmlNodePtr n)
bool fe_is_spatial_op(char *name)
bool fe_is_logical_op(char *name)
char * ows_psql_escape_string(ows *o, const char *content)
Definition ows_psql.c:840
void ows_bbox_to_query(ows *o, ows_bbox *bbox, buffer *query)
Definition ows_bbox.c:285
buffer * array_get(const array *a, const char *key)
Definition array.c:147
buffer * ows_psql_id_column(ows *o, buffer *layer_name)
Definition ows_psql.c:36
bool ows_libxml_check_namespace(ows *o, xmlNodePtr n)
Definition ows_libxml.c:67
bool array_is_key(const array *a, const char *key)
Definition array.c:105
array * ows_psql_describe_table(ows *o, buffer *layer_name)
Definition ows_psql.c:373
buffer * buffer_from_str(const char *str)
Definition buffer.c:202
void buffer_free(buffer *buf)
Definition buffer.c:83
buffer * fe_comparison_op(ows *o, buffer *typename, filter_encoding *fe, xmlNodePtr n)
void buffer_add_int(buffer *buf, int i)
Definition buffer.c:173
void buffer_shift(buffer *buf, size_t len)
Definition buffer.c:392
bool ows_psql_is_numeric(buffer *type)
Definition ows_psql.c:426
buffer * ows_layer_no_uri(ows_layer_list *ll, buffer *layer_name)
Definition ows_layer.c:376
int ows_srs_get_srid_from_layer(ows *o, buffer *layer_name)
Definition ows_srs.c:380
bool ows_psql_is_geometry_column(ows *o, buffer *layer_name, buffer *column)
Definition ows_psql.c:164
void fe_node_flush(xmlNodePtr node, FILE *output)
buffer * fe_spatial_op(ows *o, buffer *typename, filter_encoding *fe, xmlNodePtr n)
void buffer_pop(buffer *buf, size_t len)
Definition buffer.c:379
buffer * buffer_init()
Definition buffer.c:61
list * list_explode(char separator, const buffer *value)
Definition list.c:296
int ows_schema_validation(ows *o, buffer *xml_schema, buffer *xml, bool schema_is_file, enum ows_schema_type schema_type)
bool check_regexp(const char *str_request, const char *str_regex)
Definition regexp.c:36
buffer * ows_layer_prefix_to_uri(ows_layer_list *ll, buffer *layer_name_prefix)
Definition ows_layer.c:343
void filter_encoding_flush(filter_encoding *fe, FILE *output)
#define WFS_SCHEMA_100
Definition ows_define.h:36
#define WFS_SCHEMA_110
Definition ows_define.h:37
@ FE_NO_ERROR
Definition ows_struct.h:328
@ FE_ERROR_NAMESPACE
Definition ows_struct.h:339
@ FE_ERROR_FILTER
Definition ows_struct.h:330
@ FE_ERROR_PROPERTYNAME
Definition ows_struct.h:332
@ FE_ERROR_GEOM_PROPERTYNAME
Definition ows_struct.h:333
@ FE_ERROR_FEATUREID
Definition ows_struct.h:329
@ FE_ERROR_FID
Definition ows_struct.h:336
@ OWS_METHOD_XML
Definition ows_struct.h:249
wfs_request
Definition ows_struct.h:269
@ WFS_SCHEMA_TYPE_110
Definition ows_struct.h:297
@ WFS_SCHEMA_TYPE_100
Definition ows_struct.h:296
char * buf
size to next realloc
Definition ows_struct.h:39
size_t use
Definition ows_struct.h:36
enum fe_error_code error_code
Definition ows_struct.h:346
struct List_node * next
Definition ows_struct.h:45
buffer * value
Definition ows_struct.h:44
list_node * last
Definition ows_struct.h:51
list_node * first
Definition ows_struct.h:50
ows_version * version
Definition ows_struct.h:353
enum ows_method method
Definition ows_struct.h:354
ows_request * request
Definition ows_struct.h:403
buffer * schema_dir
Definition ows_struct.h:372
bool check_schema
Definition ows_struct.h:397
ows_layer_list * layers
Definition ows_struct.h:402

Generated for tinyows by doxygen 1.12.0