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