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 <time.h>
30
31 #include "../ows/ows.h"
32
33 /*
34 * Execute the request sql matching a transaction
35 * Return the result of the request (PGRES_COMMAND_OK or an error message)
36 */
wfs_execute_transaction_request(ows * o,wfs_request * wr,buffer * sql)37 static buffer *wfs_execute_transaction_request(ows * o, wfs_request * wr, buffer * sql)
38 {
39 buffer *result, *cmd_status;
40 PGresult *res;
41
42 assert(o);
43 assert(sql);
44
45 result = buffer_init();
46 cmd_status = buffer_init();
47
48 res = ows_psql_exec(o, sql->buf);
49 if (PQresultStatus(res) != PGRES_COMMAND_OK)
50 buffer_add_str(result, PQresultErrorMessage(res));
51 else
52 buffer_add_str(result, PQresStatus(PQresultStatus(res)));
53 buffer_add_str(cmd_status, (char *) PQcmdStatus(res));
54
55 if (check_regexp(cmd_status->buf, "^DELETE")) {
56 cmd_status = buffer_replace(cmd_status, "DELETE ", "");
57 wr->delete_results += atoi(cmd_status->buf);
58 }
59
60 if (check_regexp(cmd_status->buf, "^UPDATE")) {
61 cmd_status = buffer_replace(cmd_status, "UPDATE ", "");
62 wr->update_results += atoi(cmd_status->buf);
63 }
64
65 buffer_free(cmd_status);
66 PQclear(res);
67
68 return result;
69 }
70
71
72 /*
73 * Summarize overall results of transaction request
74 */
wfs_transaction_summary(ows * o,wfs_request * wr,buffer * result)75 static void wfs_transaction_summary(ows * o, wfs_request * wr, buffer * result)
76 {
77 alist_node *an;
78 int nb = 0;
79
80 assert(o);
81 assert(wr);
82 assert(result);
83
84 fprintf(o->output, "<wfs:TransactionSummary>\n");
85
86 if (buffer_cmp(result, "PGRES_COMMAND_OK")) {
87
88 if (wr->insert_results) {
89 for (an = wr->insert_results->first ; an ; an = an->next) nb += an->value->size;
90 fprintf(o->output, " <wfs:totalInserted>%d</wfs:totalInserted>\n", nb);
91 }
92
93 fprintf(o->output, "<wfs:totalUpdated>%d</wfs:totalUpdated>\n", wr->update_results);
94 fprintf(o->output, " <wfs:totalDeleted>%d</wfs:totalDeleted>\n", wr->delete_results);
95 }
96
97 fprintf(o->output, "</wfs:TransactionSummary>\n");
98 }
99
100
101 /*
102 * Report newly created feature instances
103 */
wfs_transaction_insert_result(ows * o,wfs_request * wr,buffer * result)104 static void wfs_transaction_insert_result(ows * o, wfs_request * wr, buffer * result)
105 {
106 alist_node *an;
107 list_node *ln;
108
109 assert(o);
110 assert(wr);
111 assert(result);
112
113 ln = NULL;
114
115 /* check if there were Insert operations and if the command succeeded */
116 if ((!cgi_method_get()) && (buffer_cmp(result, "PGRES_COMMAND_OK") && (wr->insert_results->first))) {
117
118 if (ows_version_get(o->request->version) == 110)
119 fprintf(o->output, "<wfs:InsertResults>\n");
120
121 for (an = wr->insert_results->first ; an ; an = an->next) {
122
123 if (ows_version_get(o->request->version) == 100) {
124 fprintf(o->output, "<wfs:InsertResult handle=\"%s\">", an->key->buf);
125 for (ln = an->value->first ; ln ; ln = ln->next)
126 fprintf(o->output, "<ogc:FeatureId fid=\"%s\"/>", ln->value->buf);
127 fprintf(o->output, "</wfs:InsertResult>\n");
128 } else {
129 for (ln = an->value->first ; ln ; ln = ln->next) {
130 fprintf(o->output, "<wfs:Feature handle=\"%s\">\n", an->key->buf);
131 fprintf(o->output, " <ogc:FeatureId fid=\"%s\"/>\n", ln->value->buf);
132 fprintf(o->output, "</wfs:Feature>\n");
133 }
134 }
135 }
136
137 if (ows_version_get(o->request->version) == 110)
138 fprintf(o->output, "</wfs:InsertResults>");
139 }
140 }
141
142
143 /*
144 * Report the overall result of the transaction request
145 */
wfs_transaction_result(ows * o,wfs_request * wr,buffer * result,buffer * locator)146 static void wfs_transaction_result(ows * o, wfs_request * wr, buffer * result, buffer * locator)
147 {
148 assert(o);
149 assert(wr);
150 assert(result);
151
152 if (!buffer_cmp(result, "PGRES_COMMAND_OK")) assert(locator);
153
154 /* only if version = 1.0.0 or if command failed */
155 if (ows_version_get(o->request->version) == 100
156 || (ows_version_get(o->request->version) == 110
157 && !buffer_cmp(result, "PGRES_COMMAND_OK"))) {
158
159 if (ows_version_get(o->request->version) == 110)
160 fprintf(o->output, "<wfs:TransactionResults>\n");
161 else {
162 fprintf(o->output, "<wfs:TransactionResult>\n");
163 /* display status transaction only for 1.0.0 version */
164 fprintf(o->output, "<wfs:Status>");
165
166 if (buffer_cmp(result, "PGRES_COMMAND_OK"))
167 fprintf(o->output, "<wfs:SUCCESS/>");
168 else fprintf(o->output, "<wfs:FAILED/>");
169
170 fprintf(o->output, "</wfs:Status>\n");
171 }
172
173
174 if (ows_version_get(o->request->version) == 100)
175 fprintf(o->output, "</wfs:TransactionResult>\n");
176 else {
177 fprintf(o->output, "</wfs:Action>\n");
178 fprintf(o->output, "</wfs:TransactionResults>\n");
179 }
180 }
181 }
182
183
184 /*
185 * Write the transaction response in XML
186 */
wfs_transaction_response(ows * o,wfs_request * wr,buffer * result,buffer * locator)187 static void wfs_transaction_response(ows * o, wfs_request * wr, buffer * result, buffer * locator)
188 {
189 assert(o);
190 assert(wr);
191 assert(result);
192
193 if (!buffer_cmp(result, "PGRES_COMMAND_OK")) {
194 if (buffer_cmp(result, "No FE selector on DELETE statement"))
195 wfs_error(o, wr, WFS_ERROR_MISSING_PARAMETER, result->buf, locator->buf);
196 else
197 wfs_error(o, wr, WFS_ERROR_INVALID_PARAMETER, result->buf, locator->buf);
198 return;
199 }
200
201 fprintf(o->output, "Content-Type: application/xml\n\n");
202 fprintf(o->output, "<?xml version='1.0' encoding='%s'?>\n", o->encoding->buf);
203
204 if (ows_version_get(o->request->version) == 100)
205 fprintf(o->output, "<wfs:WFS_TransactionResponse version=\"1.0.0\"\n");
206 else fprintf(o->output, "<wfs:TransactionResponse version=\"1.1.0\"\n");
207
208 fprintf(o->output, " xmlns:wfs=\"http://www.opengis.net/wfs\"\n");
209 fprintf(o->output, " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
210 fprintf(o->output, " xmlns:ogc=\"http://www.opengis.net/ogc\"\n");
211
212 if (ows_version_get(o->request->version) == 100) {
213 fprintf(o->output, " xsi:schemaLocation='http://www.opengis.net/wfs");
214 fprintf(o->output, " http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd'>\n");
215 } else {
216 fprintf(o->output, " xsi:schemaLocation='http://www.opengis.net/wfs");
217 fprintf(o->output, " http://schemas.opengis.net/wfs/1.1.0/wfs.xsd'>\n");
218 }
219
220 if (ows_version_get(o->request->version) == 100) {
221 wfs_transaction_insert_result(o, wr, result);
222 wfs_transaction_result(o, wr, result, locator);
223 } else {
224 wfs_transaction_summary(o, wr, result);
225 wfs_transaction_result(o, wr, result, locator);
226 wfs_transaction_insert_result(o, wr, result);
227 }
228
229 if (ows_version_get(o->request->version) == 100)
230 fprintf(o->output, "</wfs:WFS_TransactionResponse>\n");
231 else fprintf(o->output, "</wfs:TransactionResponse>\n");
232 }
233
234
235 /*
236 * Add a content node value to a buffer
237 */
wfs_retrieve_value(ows * o,wfs_request * wr,buffer * value,xmlDocPtr xmldoc,xmlNodePtr n)238 static buffer *wfs_retrieve_value(ows * o, wfs_request * wr, buffer * value, xmlDocPtr xmldoc, xmlNodePtr n)
239 {
240 xmlChar *content;
241 char *content_escaped;
242
243 assert(o);
244 assert(wr);
245 assert(value);
246 assert(n);
247
248 content = xmlNodeGetContent(n);
249 content_escaped = ows_psql_escape_string(o, (char *) content);
250
251 if (!content_escaped) {
252 xmlFree(content);
253 buffer_free(value);
254 xmlFreeDoc(xmldoc);
255 ows_error(o, OWS_ERROR_FORBIDDEN_CHARACTER,
256 "Some forbidden character are present into the request", "transaction");
257 }
258
259 buffer_add_str(value, "'");
260 buffer_add_str(value, content_escaped);
261 buffer_add_str(value, "'");
262
263 xmlFree(content);
264 free(content_escaped);
265
266 return value;
267 }
268
269
270 /*
271 * Retrieve the layer's name
272 */
wfs_retrieve_typename(ows * o,wfs_request * wr,xmlNodePtr n)273 static buffer *wfs_retrieve_typename(ows * o, wfs_request * wr, xmlNodePtr n)
274 {
275 xmlAttr *att;
276 xmlChar *content;
277 buffer *typename;
278 array *o_ns;
279
280 content = NULL;
281 typename = buffer_init();
282 o_ns = ows_layer_list_namespaces(o->layers);
283
284 for (att = n->properties ; att ; att = att->next) {
285
286 if (!strcmp((char *) att->name, "typeName")) {
287 content = xmlNodeGetContent(att->children);
288 buffer_add_str(typename, (char *) content);
289
290 /* Handle case when ns_prefix don't match but ns_uri does */
291 /* FIXME is this still work with several ns_uri ? */
292 if (n->nsDef && n->nsDef->href && array_is_value(o_ns, (char *) n->nsDef->href)) {
293 buffer_shift(typename, strlen((char *) n->nsDef->prefix));
294 buffer_add_head_str(typename, (array_get_key(o_ns, (char *) n->nsDef->href))->buf);
295 }
296
297 if (!ows_layer_writable(o->layers, ows_layer_prefix_to_uri(o->layers, typename))) {
298 xmlFree(content);
299 return NULL;
300 }
301
302 xmlFree(content);
303 array_free(o_ns);
304 return typename;
305 }
306 }
307
308 array_free(o_ns);
309 return NULL;
310 }
311
312
313 /*
314 * Insert features into the database
315 * Method POST, XML
316 */
wfs_insert_xml(ows * o,wfs_request * wr,xmlDocPtr xmldoc,xmlNodePtr n)317 static buffer *wfs_insert_xml(ows * o, wfs_request * wr, xmlDocPtr xmldoc, xmlNodePtr n)
318 {
319 buffer *values, *column, *layer_name, *layer_ns_prefix, *result, *sql, *gml;
320 buffer *handle, *id_column, *fid_full_name, *dup_sql, *id;
321 xmlNodePtr node, elemt;
322 filter_encoding *fe;
323 PGresult *res;
324 array * table;
325 char *escaped;
326 list *l;
327 ows_srs * srs_root;
328 int srid_root = 0;
329 xmlChar *attr = NULL;
330 enum wfs_insert_idgen idgen = WFS_GENERATE_NEW;
331 enum wfs_insert_idgen handle_idgen = WFS_GENERATE_NEW;
332
333 assert(o);
334 assert(wr);
335 assert(xmldoc);
336 assert(n);
337
338 sql = buffer_init();
339 handle = buffer_init();
340 result = NULL;
341
342 /* retrieve handle attribute to report it in transaction response */
343 if (xmlHasProp(n, (xmlChar *) "handle")) {
344 attr = xmlGetProp(n, (xmlChar *) "handle");
345 buffer_add_str(handle, (char *) attr);
346 xmlFree(attr);
347 attr = NULL;
348 /* handle is optional in WFS Schema */
349 } else buffer_add_str(handle, "TinyOWS-WFS-default-handle");
350
351 /* idgen appears in WFS 1.1.0, default behaviour is GenerateNew id
352 It is safe to not test WFS version, as schema validation
353 already do the job for us.
354 */
355 if (xmlHasProp(n, (xmlChar *) "idgen")) {
356 attr = xmlGetProp(n, (xmlChar *) "idgen");
357 if (!strcmp((char *) attr, "ReplaceDuplicate")) handle_idgen = WFS_REPLACE_DUPLICATE;
358 else if (!strcmp((char *) attr, "UseExisting")) handle_idgen = WFS_USE_EXISTING;
359 xmlFree(attr);
360 attr = NULL;
361 }
362
363 /*
364 * In WFS 1.1.0 default srsName could be define
365 * TODO: what about for WFS 1.0.0 ?
366 */
367 if (xmlHasProp(n, (xmlChar *) "srsName")) {
368 attr = xmlGetProp(n, (xmlChar *) "srsName");
369 srs_root = ows_srs_init();
370
371 if (!ows_srs_set_from_srsname(o, srs_root, (char *) attr)) {
372 buffer_free(sql);
373 buffer_free(handle);
374 ows_srs_free(srs_root);
375 xmlFree(attr);
376 result = buffer_from_str("Unkwnown or wrong CRS used");
377 return result;
378 }
379
380 srid_root = srs_root->srid;
381 ows_srs_free(srs_root);
382 xmlFree(attr);
383 attr = NULL;
384 }
385
386 n = n->children;
387
388 /* jump to the next element if there are spaces */
389 while (n->type != XML_ELEMENT_NODE) n = n->next;
390
391 /* Create Insert SQL query for each Typename */
392 for ( /* Empty */ ; n ; n = n->next) {
393 if (n->type != XML_ELEMENT_NODE) continue;
394
395 id = buffer_init();
396 values = buffer_init();
397 layer_name = buffer_init();
398
399 /* name of the table in which features must be inserted */
400 buffer_add_str(layer_name, (char *) n->ns->href);
401 buffer_add(layer_name, ':');
402 buffer_add_str(layer_name, (char *) n->name);
403
404 if (!layer_name || !ows_layer_writable(o->layers, layer_name)) {
405 buffer_free(id);
406 buffer_free(sql);
407 buffer_free(values);
408 buffer_free(handle);
409 if (layer_name) buffer_free(layer_name);
410 result = buffer_from_str("Error unknown or not writable Layer Name");
411 return result;
412 }
413
414 idgen = handle_idgen;
415
416 /* In GML 3 GML:id is used, in GML 2.1.2 fid is used.
417 * In both cases no other attribute allowed in this element
418 * and in both cases (f)id is optionnal !
419 */
420 if (xmlHasProp(n, (xmlChar *) "id")) attr = xmlGetProp(n, (xmlChar *) "id");
421 else if (xmlHasProp(n, (xmlChar *) "fid")) attr = xmlGetProp(n, (xmlChar *) "fid");
422
423 if (attr) {
424 buffer_empty(id);
425 buffer_add_str(id, (char *) attr);
426 xmlFree(attr);
427 } else idgen = WFS_GENERATE_NEW;
428 /* FIXME should we end on error if UseExisting or Replace without id set ? */
429
430 id_column = ows_psql_id_column(o, layer_name);
431 if (!id_column) {
432 buffer_free(id);
433 buffer_free(sql);
434 buffer_free(values);
435 buffer_free(layer_name);
436 result = buffer_from_str("Error unknown Layer Name or not id column available");
437 return result;
438 }
439
440 if (id->use) {
441 l = list_explode('.', id);
442 if (l->last) {
443 buffer_empty(id);
444 buffer_copy(id, l->last->value);
445 }
446 list_free(l);
447 }
448
449 layer_ns_prefix = ows_layer_ns_prefix(o->layers, layer_name);
450
451 /* ReplaceDuplicate look if an ID is already used
452 *
453 * May not be safe if another transaction occur between
454 * this select and the related insert !!!
455 */
456 if (idgen == WFS_REPLACE_DUPLICATE) {
457 dup_sql = buffer_init();
458
459 buffer_add_str(dup_sql, "SELECT count(*) FROM \"");
460 buffer_copy(dup_sql, ows_psql_schema_name(o, layer_name));
461 buffer_add_str(dup_sql, "\".\"");
462 buffer_copy(dup_sql, ows_psql_table_name(o, layer_name));
463 buffer_add_str(dup_sql, "\" WHERE ");
464 buffer_copy(dup_sql, id_column);
465 buffer_add_str(dup_sql, "='");
466 escaped = ows_psql_escape_string(o, id->buf);
467 if (escaped) {
468 buffer_add_str(dup_sql, escaped);
469 free(escaped);
470 }
471 buffer_add_str(dup_sql, "';");
472
473 res = ows_psql_exec(o, dup_sql->buf);
474 if (PQresultStatus(res) != PGRES_TUPLES_OK || atoi((char *) PQgetvalue(res, 0, 0)) != 0)
475 idgen = WFS_GENERATE_NEW;
476 else
477 idgen = WFS_USE_EXISTING;
478
479 buffer_free(dup_sql);
480 PQclear(res);
481 }
482
483 if (idgen == WFS_GENERATE_NEW) {
484 buffer_free(id);
485 id = ows_psql_generate_id(o, layer_name);
486 }
487
488 /* Retrieve the id of the inserted feature
489 * to report it in transaction respons
490 */
491 fid_full_name = buffer_init();
492 buffer_add_str(fid_full_name, (char *) n->name);
493 buffer_add(fid_full_name, '.');
494 buffer_copy(fid_full_name, id);
495 alist_add(wr->insert_results, handle, fid_full_name);
496
497 buffer_add_str(sql, "INSERT INTO \"");
498 buffer_copy(sql, ows_psql_schema_name(o, layer_name));
499 buffer_add_str(sql, "\".\"");
500 buffer_copy(sql, ows_psql_table_name(o, layer_name));
501 buffer_add_str(sql, "\" (\"");
502 buffer_copy(sql, id_column);
503 buffer_add_str(sql, "\"");
504
505 node = n->children;
506
507 /* Jump to the next element if spaces */
508 while (node->type != XML_ELEMENT_NODE) node = node->next;
509
510 /* Fill SQL fields and values at once */
511 for ( /* empty */ ; node; node = node->next) {
512
513
514 if (node->type == XML_ELEMENT_NODE &&
515 ( buffer_cmp(ows_layer_ns_uri(o->layers, layer_name), (char *) node->ns->href)
516 || !strcmp("http://www.opengis.net/gml", (char *) node->ns->href)
517 || !strcmp("http://www.opengis.net/gml/3.2", (char *) node->ns->href))) {
518
519 /* We have to ignore if not present in database,
520 gml elements (name, description, boundedBy) */
521 if ( !strcmp("http://www.opengis.net/gml", (char *) node->ns->href)
522 || !strcmp("http://www.opengis.net/gml/3.2", (char *) node->ns->href)) {
523 table = ows_psql_describe_table(o, layer_name);
524 if (!array_is_key(table, (char *) node->name)) continue;
525 }
526 buffer_add(sql, ',');
527 buffer_add(values, ',');
528
529 column = buffer_from_str((char *) node->name);
530
531 buffer_add_str(sql, "\"");
532 escaped = ows_psql_escape_string(o, column->buf);
533 if (escaped) {
534 buffer_add_str(sql, escaped);
535 free(escaped);
536 }
537 buffer_add_str(sql, "\"");
538
539 /* If column's type is a geometry, transform the GML into WKT */
540 if (ows_psql_is_geometry_column(o, layer_name, column)) {
541 elemt = node->children;
542
543 /* Jump to the next element if spaces */
544 while (elemt->type != XML_ELEMENT_NODE) elemt = elemt->next;
545
546 if (!strcmp((char *) elemt->name, "Box") ||
547 !strcmp((char *) elemt->name, "Envelope")) {
548
549 fe = filter_encoding_init();
550 fe->sql = fe_envelope(o, layer_name, fe, fe->sql, elemt);
551 if (fe->error_code != FE_NO_ERROR) {
552 result = fill_fe_error(o, fe);
553 buffer_free(sql);
554 buffer_free(values);
555 buffer_free(column);
556 buffer_free(id);
557 filter_encoding_free(fe);
558 return result;
559 }
560 buffer_copy(values, fe->sql);
561 filter_encoding_free(fe);
562
563 } else if (!strcmp((char *) elemt->name, "Null")) {
564 buffer_add_str(values, "''");
565 } else {
566 gml = ows_psql_gml_to_sql(o, elemt, srid_root);
567 if (gml) {
568 buffer_add_str(values, "'");
569 buffer_copy(values, gml);
570 buffer_add_str(values, "'");
571 buffer_free(gml);
572 } else {
573 buffer_free(sql);
574 buffer_free(values);
575 buffer_free(column);
576 buffer_free(id);
577 buffer_free(layer_name);
578
579 result = buffer_from_str("Error invalid Geometry");
580 return result;
581 }
582 }
583
584 } else values = wfs_retrieve_value(o, wr, values, xmldoc, node);
585
586 buffer_free(column);
587 }
588 }
589
590 /* As 'id' could be NULL in GML */
591 if (id->use) {
592 buffer_add_str(sql, ") VALUES ('");
593 escaped = ows_psql_escape_string(o, id->buf);
594 if (escaped) {
595 buffer_add_str(sql, escaped);
596 free(escaped);
597 }
598 buffer_add_str(sql, "'");
599 } else buffer_add_str(sql, ") VALUES (null");
600
601 buffer_copy(sql, values);
602 buffer_add_str(sql, ") ");
603
604 buffer_free(values);
605 buffer_free(id);
606
607 /* Run the request to insert each feature */
608 if(result) buffer_free(result);
609 result = wfs_execute_transaction_request(o, wr, sql);
610 if (!buffer_cmp(result, "PGRES_COMMAND_OK")) {
611 buffer_free(sql);
612 return result;
613 }
614 buffer_empty(sql);
615 buffer_free(layer_name);
616 }
617
618 buffer_free(sql);
619
620 return result;
621 }
622
623
624 /*
625 * Delete features in database
626 * Method GET / KVP
627 */
wfs_delete(ows * o,wfs_request * wr)628 void wfs_delete(ows * o, wfs_request * wr)
629 {
630 buffer *sql, *result, *where, *layer_name, *locator;
631 int cpt, size;
632 mlist_node *mln_fid;
633 list_node *ln_typename, *ln_filter;
634 list *fe;
635 filter_encoding *filter;
636
637 assert(o);
638 assert(wr);
639
640 sql = buffer_init();
641 ln_typename = NULL;
642 ln_filter = NULL;
643 mln_fid = NULL;
644 where = NULL;
645 size = 0;
646
647 if (wr->typename) {
648 size = wr->typename->size;
649 ln_typename = wr->typename->first;
650 }
651
652 if (wr->filter) ln_filter = wr->filter->first;
653 if (wr->featureid) {
654 size = wr->featureid->size;
655 mln_fid = wr->featureid->first;
656 }
657
658 /* delete elements layer by layer */
659 for (cpt = 0; cpt < size; cpt++) {
660 /* define a layer_name which match typename or featureid */
661 layer_name = buffer_init();
662
663 if (wr->typename) buffer_copy(layer_name, ln_typename->value);
664 else {
665 fe = list_explode('.', mln_fid->value->first->value);
666 buffer_copy(layer_name, fe->first->value);
667 list_free(fe);
668 }
669
670 /* FROM */
671 buffer_add_str(sql, "DELETE FROM \"");
672 buffer_copy(sql, ows_psql_schema_name(o, layer_name));
673 buffer_add_str(sql, "\".\"");
674 buffer_copy(sql, ows_psql_table_name(o, layer_name));
675 buffer_add_str(sql, "\" ");
676
677 /* WHERE : match featureid, bbox or filter */
678
679 /* FeatureId */
680 if (wr->featureid) {
681 where = fe_kvp_featureid(o, wr, layer_name, mln_fid->value);
682
683 if (!where->use) {
684 buffer_free(where);
685 buffer_free(sql);
686 wfs_error(o, wr, WFS_ERROR_NO_MATCHING, "error : an id_column is required to use featureid", "Delete");
687 return;
688 }
689 }
690 /* BBOX */
691 else if (wr->bbox) where = fe_kvp_bbox(o, wr, layer_name, wr->bbox);
692
693 /* Filter */
694 else {
695 if (ln_filter->value->use) {
696 where = buffer_init();
697 buffer_add_str(where, " WHERE ");
698 filter = filter_encoding_init();
699 filter = fe_filter(o, filter, layer_name, ln_filter->value);
700
701 if (filter->error_code != FE_NO_ERROR) {
702 buffer_free(where);
703 buffer_free(sql);
704 fe_error(o, filter);
705 return;
706 }
707
708 buffer_copy(where, filter->sql);
709 filter_encoding_free(filter);
710 }
711 }
712
713 buffer_copy(sql, where);
714 buffer_add_str(sql, "; ");
715 buffer_free(where);
716
717 /*incrementation of the nodes */
718 if (wr->featureid) mln_fid = mln_fid->next;
719 if (wr->typename) ln_typename = ln_typename->next;
720 if (wr->filter) ln_filter = ln_filter->next;
721 }
722
723 result = wfs_execute_transaction_request(o, wr, sql);
724
725 locator = buffer_init();
726 buffer_add_str(locator, "Delete");
727
728 /* display the transaction response directly since
729 there is only one operation using GET method */
730 wfs_transaction_response(o, wr, result, locator);
731
732 buffer_free(locator);
733 buffer_free(result);
734 buffer_free(sql);
735 }
736
737
738 /*
739 * Delete features in database
740 * Method POST / XML
741 */
wfs_delete_xml(ows * o,wfs_request * wr,xmlNodePtr n)742 static buffer *wfs_delete_xml(ows * o, wfs_request * wr, xmlNodePtr n)
743 {
744 buffer *typename, *layer_name, *xmlstring, *result, *sql, *s, *t;
745 filter_encoding *filter;
746
747 assert(o);
748 assert(wr);
749 assert(n);
750
751 sql = buffer_init();
752 s = t = NULL;
753
754 buffer_add_str(sql, "DELETE FROM ");
755
756 /*retrieve the name of the table in which features must be deleted */
757 typename = wfs_retrieve_typename(o, wr, n);
758 layer_name = ows_layer_prefix_to_uri(o->layers, typename);
759 if (layer_name) s = ows_psql_schema_name(o, layer_name);
760 if (layer_name) t = ows_psql_table_name(o, layer_name);
761
762 if (!layer_name || !s || !t) {
763 if (typename) buffer_free(typename);
764 buffer_free(sql);
765 result = buffer_from_str("Typename provided is unknown or not writable");
766
767 return result;
768 }
769
770 buffer_add_str(sql, "\"");
771 buffer_copy(sql, s);
772 buffer_add_str(sql, "\".\"");
773 buffer_copy(sql, t);
774 buffer_add_str(sql, "\"");
775
776 n = n->children;
777 if (!n) {
778 buffer_free(typename);
779 buffer_free(sql);
780 result = buffer_init();
781 buffer_add_str(result, "No FE selector on DELETE statement");
782 return result;
783 }
784
785 /* jump to the next element if there are spaces */
786 while (n->type != XML_ELEMENT_NODE) n = n->next;
787 buffer_add_str(sql, " WHERE ");
788
789 /* put xml filter into a buffer */
790 xmlstring = buffer_init();
791 xmlstring = cgi_add_xml_into_buffer(xmlstring, n);
792
793 filter = filter_encoding_init();
794 filter = fe_filter(o, filter, typename, xmlstring);
795
796 /* check if filter returned an error */
797 if (filter->error_code != FE_NO_ERROR)
798 result = fill_fe_error(o, filter);
799 else {
800 buffer_copy(sql, filter->sql);
801 buffer_add_str(sql, ";");
802 /* run the SQL request to delete all specified features */
803 result = wfs_execute_transaction_request(o, wr, sql);
804 }
805
806 filter_encoding_free(filter);
807 buffer_free(xmlstring);
808 buffer_free(typename);
809 buffer_free(sql);
810
811 return result;
812 }
813
814
815 /*
816 * Update features in database
817 * Method POST / XML
818 */
wfs_update_xml(ows * o,wfs_request * wr,xmlDocPtr xmldoc,xmlNodePtr n)819 static buffer *wfs_update_xml(ows * o, wfs_request * wr, xmlDocPtr xmldoc, xmlNodePtr n)
820 {
821 buffer *typename, *layer_name, *xmlstring, *result, *sql, *property_name, *values, *gml, *s, *t;
822 filter_encoding *filter, *fe;
823 xmlNodePtr node, elemt;
824 xmlChar *content;
825 char *escaped;
826 array *table;
827 ows_srs *srs_root;
828 int srid_root = 0;
829 xmlChar *attr = NULL;
830 list *l;
831
832 assert(o);
833 assert(wr);
834 assert(xmldoc);
835 assert(n);
836
837 sql = buffer_init();
838 content = NULL;
839 s = t = result = layer_name = NULL;
840
841 /*
842 * In WFS 1.1.0 default srsName could be define
843 * FIXME: what about for WFS 1.0.0 ?
844 */
845 if (xmlHasProp(n, (xmlChar *) "srsName")) {
846 attr = xmlGetProp(n, (xmlChar *) "srsName");
847 srs_root = ows_srs_init();
848
849 if (!ows_srs_set_from_srsname(o, srs_root, (char *) attr)) {
850 ows_srs_free(srs_root);
851 xmlFree(attr);
852 buffer_add_str(result, "Unkwnown or wrong CRS used");
853 return result;
854 }
855
856 srid_root = srs_root->srid;
857 ows_srs_free(srs_root);
858 xmlFree(attr);
859 attr = NULL;
860 }
861
862 buffer_add_str(sql, "UPDATE ");
863
864 /*retrieve the name of the table in which features must be updated */
865 typename = wfs_retrieve_typename(o, wr, n);
866 if (typename) layer_name = ows_layer_prefix_to_uri(o->layers, typename);
867 if (layer_name) s = ows_psql_schema_name(o, layer_name);
868 if (layer_name) t = ows_psql_table_name(o, layer_name);
869
870 if (!layer_name || !s || !t) {
871 if (typename) buffer_free(typename);
872 buffer_free(sql);
873 result = buffer_from_str("Typename provided is unknown or not writable");
874 return result;
875 }
876
877 buffer_add_str(sql, "\"");
878 buffer_copy(sql, s);
879 buffer_add_str(sql, "\".\"");
880 buffer_copy(sql, t);
881 buffer_add_str(sql, "\"");
882
883 n = n->children;
884
885 /* jump to the next element if there are spaces */
886 while (n->type != XML_ELEMENT_NODE) n = n->next;
887
888 buffer_add_str(sql, " SET ");
889
890 /* write the fields and the new values of the features to update */
891 for (; n; n = n->next) {
892 values = buffer_init();
893
894 if (n->type == XML_ELEMENT_NODE) {
895 if (!strcmp((char *) n->name, "Property")) {
896 node = n->children;
897
898 while (node->type != XML_ELEMENT_NODE) node = node->next;
899
900 /* We have to ignore if not present in database,
901 gml elements (name, description, boundedBy) */
902 if ( !strcmp("http://www.opengis.net/gml", (char *) node->ns->href)
903 || !strcmp("http://www.opengis.net/gml/3.2", (char *) node->ns->href)) {
904 table = ows_psql_describe_table(o, layer_name);
905 if (!array_is_key(table, (char *) node->name)) continue;
906 }
907
908 property_name = buffer_init();
909
910 /* property name to update */
911 if (!strcmp((char *) node->name, "Name")) {
912 content = xmlNodeGetContent(node);
913 buffer_add_str(property_name, (char *) content);
914 xmlFree(content);
915 buffer_add_str(sql, "\"");
916 l = list_init();
917 list_add_by_copy(l, layer_name);
918 property_name = wfs_request_remove_prop_ns_prefix(o, property_name, l);
919 list_free(l);
920 escaped = ows_psql_escape_string(o, property_name->buf);
921 if (escaped) {
922 buffer_add_str(sql, escaped);
923 free(escaped);
924 }
925 buffer_add_str(sql, "\"");
926 }
927
928 buffer_add_str(sql, " = ");
929 node = node->next;
930
931 /* jump to the next element if there are spaces */
932 if (node && node->type != XML_ELEMENT_NODE) node = node->next;
933
934 /* replacement value is optional, set to NULL if not defined */
935 if (!node) buffer_add_str(sql, "NULL");
936 else if (!strcmp((char *) node->name, "Value")) {
937 if (ows_psql_is_geometry_column(o, layer_name, property_name)) {
938 elemt = node->children;
939
940 /* jump to the next element if there are spaces */
941 while (elemt->type != XML_ELEMENT_NODE) elemt = elemt->next;
942
943 if (!strcmp((char *) elemt->name, "Box") ||
944 !strcmp((char *) elemt->name, "Envelope")) {
945
946 fe = filter_encoding_init();
947 fe->sql = buffer_init();
948 fe->sql = fe_envelope(o, typename, fe, fe->sql, elemt);
949
950 if (fe->error_code != FE_NO_ERROR) {
951 result = fill_fe_error(o, fe);
952 buffer_free(values);
953 buffer_free(sql);
954 buffer_free(typename);
955 buffer_free(property_name);
956 filter_encoding_free(fe);
957 return result;
958 }
959
960 filter_encoding_free(fe);
961 buffer_copy(values, fe->sql);
962
963 } else if (!strcmp((char *) elemt->name, "Null")) {
964 buffer_add_str(values, "''");
965 } else {
966 gml = ows_psql_gml_to_sql(o, elemt, srid_root);
967 if (gml) {
968 buffer_add_str(values, "'");
969 buffer_copy(values, gml);
970 buffer_add_str(values, "'");
971 buffer_free(gml);
972 } else {
973 buffer_free(values);
974 buffer_free(typename);
975 buffer_free(property_name);
976 buffer_free(sql);
977 result = buffer_from_str("Invalid GML Geometry");
978 return result;
979 }
980 }
981 } else values = wfs_retrieve_value(o, wr, values, xmldoc, node);
982
983 buffer_copy(sql, values);
984 }
985 buffer_free(property_name);
986 }
987
988 if (!strcmp((char *) n->name, "Filter")) {
989 buffer_add_str(sql, " WHERE ");
990 xmlstring = buffer_init();
991 xmlstring = cgi_add_xml_into_buffer(xmlstring, n);
992
993 filter = filter_encoding_init();
994 filter = fe_filter(o, filter, typename, xmlstring);
995
996 /* check if filter returned an error */
997 if (filter->error_code != FE_NO_ERROR) {
998 result = fill_fe_error(o, filter);
999 buffer_free(xmlstring);
1000 filter_encoding_free(filter);
1001 buffer_free(values);
1002 buffer_free(sql);
1003 buffer_free(typename);
1004 return result;
1005
1006 } else {
1007 buffer_copy(sql, filter->sql);
1008 buffer_free(xmlstring);
1009 filter_encoding_free(filter);
1010 }
1011 }
1012 }
1013
1014 if (n->next && !strcmp((char *) n->next->name, "Property")) buffer_add_str(sql, ",");
1015
1016 buffer_free(values);
1017 }
1018
1019 buffer_add_str(sql, "; ");
1020 /* run the request to update the specified features */
1021 result = wfs_execute_transaction_request(o, wr, sql);
1022
1023 buffer_free(typename);
1024 buffer_free(sql);
1025
1026 return result;
1027 }
1028
1029
1030 /*
1031 * Parse XML operations to execute each transaction operation
1032 */
wfs_parse_operation(ows * o,wfs_request * wr,buffer * op)1033 void wfs_parse_operation(ows * o, wfs_request * wr, buffer * op)
1034 {
1035 xmlDocPtr xmldoc;
1036 xmlNodePtr n;
1037 xmlAttr *att;
1038 xmlChar *content;
1039
1040 buffer *sql, *result, *end_transaction, *locator;
1041
1042 assert(o);
1043 assert(wr);
1044 assert(op);
1045
1046 sql = buffer_init();
1047 locator = buffer_init();
1048 wr->insert_results = alist_init();
1049 content = NULL;
1050
1051 xmldoc = xmlParseMemory(op->buf, op->use);
1052
1053 if (!xmldoc) {
1054 xmlFreeDoc(xmldoc);
1055 wfs_error(o, wr, WFS_ERROR_NO_MATCHING, "xml isn't valid", "transaction");
1056 return;
1057 }
1058
1059 /* jump to the next element if there are spaces */
1060 n = xmldoc->children;
1061 while (n->type != XML_ELEMENT_NODE) n = n->next;
1062 n = n->children;
1063 while (n->type != XML_ELEMENT_NODE) n = n->next; /* FIXME really ? */
1064
1065 /* initialize the transaction inside postgresql */
1066 buffer_add_str(sql, "BEGIN;");
1067 result = wfs_execute_transaction_request(o, wr, sql);
1068
1069 buffer_empty(result);
1070 buffer_add_str(result, "PGRES_COMMAND_OK");
1071 buffer_empty(sql);
1072
1073 /* go through the operations while transaction is successful */
1074 for ( /* empty */ ; n && (buffer_cmp(result, "PGRES_COMMAND_OK")) ; n = n->next) {
1075 if (n->type != XML_ELEMENT_NODE) continue;
1076
1077 if (!strcmp((char *) n->name, "Insert")) {
1078 buffer_free(result);
1079 result = wfs_insert_xml(o, wr, xmldoc, n);
1080 } else if (!strcmp((char *) n->name, "Delete")) {
1081 buffer_free(result);
1082 result = wfs_delete_xml(o, wr, n);
1083 } else if (!strcmp((char *) n->name, "Update")) {
1084 buffer_free(result);
1085 result = wfs_update_xml(o, wr, xmldoc, n);
1086 }
1087
1088 /* fill locator only if transaction failed */
1089 if (!buffer_cmp(result, "PGRES_COMMAND_OK")) {
1090 /* fill locator with handle attribute if specified
1091 else with transaction name */
1092 if (n->properties) {
1093 att = n->properties;
1094
1095 if (!strcmp((char *) att->name, "handle")) {
1096 content = xmlNodeGetContent(att->children);
1097 buffer_add_str(locator, (char *) content);
1098 xmlFree(content);
1099 } else buffer_add_str(locator, (char *) n->name);
1100 } else buffer_add_str(locator, (char *) n->name);
1101 }
1102 }
1103
1104 /* end the transaction according to the result */
1105 if (buffer_cmp(result, "PGRES_COMMAND_OK")) buffer_add_str(sql, "COMMIT;");
1106 else buffer_add_str(sql, "ROLLBACK;");
1107
1108 end_transaction = wfs_execute_transaction_request(o, wr, sql);
1109 buffer_free(end_transaction);
1110
1111 /* display the xml transaction response */
1112 wfs_transaction_response(o, wr, result, locator);
1113
1114 buffer_free(sql);
1115 buffer_free(result);
1116 buffer_free(locator);
1117
1118 xmlFreeDoc(xmldoc);
1119 }
1120