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 <string.h>
27 #include <assert.h>
28 
29 #include "../ows/ows.h"
30 
31 
32 /*
33  * Describe the layer_name in GML according
34  * with PostGIS table definition
35  */
wfs_complex_type(ows * o,wfs_request * wr,buffer * layer_name)36 static void wfs_complex_type(ows * o, wfs_request * wr, buffer * layer_name)
37 {
38   buffer *id_name;
39   array *table;
40   array_node *an;
41   list *mandatory_prop;
42   char * xsd_type;
43   buffer *table_name;
44   buffer *character_maximum_length;
45   buffer *constraint_name;
46   list *check_constraints;
47   list_node *ln;
48 
49   assert(o);
50   assert(wr);
51   assert(layer_name);
52 
53   layer_name = ows_layer_prefix_to_uri(o->layers, layer_name);
54   mandatory_prop = ows_psql_not_null_properties(o, layer_name);
55 
56   fprintf(o->output, "<xs:complexType name='");
57   buffer_flush(ows_layer_no_uri(o->layers, layer_name), o->output);
58   fprintf(o->output, "Type'>\n");
59   fprintf(o->output, " <xs:complexContent>\n");
60   fprintf(o->output, "  <xs:extension base='gml:AbstractFeatureType'>\n");
61   fprintf(o->output, "   <xs:sequence>\n");
62 
63   table = ows_psql_describe_table(o, layer_name);
64   id_name = ows_psql_id_column(o, layer_name);
65 
66   assert(table);
67 
68   /* Output the description of the layer_name */
69   for (an = table->first ; an ; an = an->next) {
70 
71     /* Handle GML namespace */
72     if (            (ows_layer_get(o->layers, layer_name))->gml_ns
73                     && in_list((ows_layer_get(o->layers, layer_name))->gml_ns, an->key)) {
74 
75       if (    !strcmp("name", an->key->buf)
76               || !strcmp("description", an->key->buf)
77               || !strcmp("boundedBy", an->key->buf)) {
78         continue;
79       }
80 
81       fprintf(o->output, "    <xs:element ref='gml:%s'/>\n", an->key->buf);
82       continue;
83     }
84 
85     /* Avoid to expose PK if not specificaly wanted */
86     if (id_name && buffer_cmp(an->key, id_name->buf) && !o->expose_pk) {
87       continue;
88     }
89     /* Avoid to expose elements in mapfile gml_exclude_items */
90     if ( (ows_layer_get(o->layers, layer_name))->exclude_items
91          && in_list((ows_layer_get(o->layers, layer_name))->exclude_items, an->key)) {
92       continue;
93     }
94 
95     xsd_type = ows_psql_to_xsd(an->value, o->request->request.wfs->format);
96 
97     if(!strcmp(xsd_type, "string")) {
98       table_name = ows_psql_table_name(o, layer_name);
99       /* Read string constraint from database and convert to gml restrictions*/
100       constraint_name = ows_psql_column_constraint_name(o, an->key, table_name);
101       if(strcmp(constraint_name->buf, "")) {
102         fprintf(o->output, "    <xs:element name ='%s' ", an->key->buf);
103         if (mandatory_prop && in_list(mandatory_prop, an->key))
104           fprintf(o->output, "nillable='false' minOccurs='1' ");
105         else
106           fprintf(o->output, "nillable='true' minOccurs='0' ");
107         fprintf(o->output, "maxOccurs='1'>\n");
108 
109         fprintf(o->output, "<xs:simpleType><xs:restriction base='string'>");
110         check_constraints = ows_psql_column_check_constraint(o, constraint_name);
111         for (ln = check_constraints->first ; ln ; ln = ln->next) {
112           fprintf(o->output, "<xs:enumeration value='%s'/>", ln->value->buf);
113         }
114         fprintf(o->output, "</xs:restriction></xs:simpleType></xs:element>");
115       } else {
116         character_maximum_length = ows_psql_column_character_maximum_length(o, an->key, table_name);
117         if(strcmp(character_maximum_length->buf, "")) {
118           fprintf(o->output, "    <xs:element name ='%s' ", an->key->buf);
119           if (mandatory_prop && in_list(mandatory_prop, an->key))
120             fprintf(o->output, "nillable='false' minOccurs='1' ");
121           else
122             fprintf(o->output, "nillable='true' minOccurs='0' ");
123           fprintf(o->output, "maxOccurs='1'>\n");
124           fprintf(o->output, "<xs:simpleType><xs:restriction base='string'>");
125           fprintf(o->output, "<xs:maxLength value='%s'/>", character_maximum_length->buf);
126           fprintf(o->output, "</xs:restriction></xs:simpleType></xs:element>");
127         } else {
128           fprintf(o->output, "    <xs:element name ='%s' type='%s' ",
129                   an->key->buf, ows_psql_to_xsd(an->value, o->request->request.wfs->format));
130 
131           if (mandatory_prop && in_list(mandatory_prop, an->key))
132             fprintf(o->output, "nillable='false' minOccurs='1' ");
133           else
134             fprintf(o->output, "nillable='true' minOccurs='0' ");
135           fprintf(o->output, "maxOccurs='1'/>\n");
136         }
137         buffer_free(character_maximum_length);
138       }
139       buffer_free(constraint_name);
140     } else {
141       fprintf(o->output, "    <xs:element name ='%s' type='%s' ",
142               an->key->buf, ows_psql_to_xsd(an->value, o->request->request.wfs->format));
143 
144       if (mandatory_prop && in_list(mandatory_prop, an->key))
145         fprintf(o->output, "nillable='false' minOccurs='1' ");
146       else
147         fprintf(o->output, "nillable='true' minOccurs='0' ");
148 
149       fprintf(o->output, "maxOccurs='1'/>\n");
150     }
151   }
152 
153   fprintf(o->output, "   </xs:sequence>\n");
154   fprintf(o->output, "  </xs:extension>\n");
155   fprintf(o->output, " </xs:complexContent>\n");
156   fprintf(o->output, "</xs:complexType>\n");
157 }
158 
159 
160 /*
161  * Execute the DescribeFeatureType request according to version
162  * (GML version differ between WFS 1.0.0 and WFS 1.1.0)
163  */
wfs_describe_feature_type(ows * o,wfs_request * wr)164 void wfs_describe_feature_type(ows * o, wfs_request * wr)
165 {
166   int wfs_version;
167   buffer *namespace;
168   list_node *elemt, *ln;
169   list *ns_prefix, *typ, *layer_name;
170 
171   assert(o && wr);
172 
173   wfs_version = ows_version_get(o->request->version);
174   layer_name = ows_layer_list_prefix_to_uri(o->layers, wr->typename);
175   ns_prefix = ows_layer_list_ns_prefix(o->layers, layer_name);
176   list_free(layer_name);
177   if (!ns_prefix || !ns_prefix->first) {
178     list_free(ns_prefix);
179     ows_error(o, OWS_ERROR_CONFIG_FILE,
180               "Not a single layer is available. Check config file", "describe");
181     return;
182   }
183 
184   if (wr->format == WFS_GML212 || wr->format == WFS_XML_SCHEMA)
185     fprintf(o->output, "Content-Type: text/xml; subtype=gml/2.1.2;\n\n");
186   else if (wr->format == WFS_GML311)
187     fprintf(o->output, "Content-Type: text/xml; subtype=gml/3.1.1;\n\n");
188 
189   fprintf(o->output, "<?xml version='1.0' encoding='%s'?>\n", o->encoding->buf);
190 
191   if (buffer_cmp(ns_prefix->last->value, "gml"))
192     list_pop(ns_prefix);
193 
194 
195   /* if all layers belong to different prefixes, import the matching namespaces */
196   if (ns_prefix->first->next) {
197     fprintf(o->output, "<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'");
198     fprintf(o->output, " xmlns='http://www.w3.org/2001/XMLSchema'");
199     fprintf(o->output, " elementFormDefault='qualified'> ");
200 
201     for (elemt = ns_prefix->first ; elemt ; elemt = elemt->next) {
202       namespace = ows_layer_ns_prefix_to_ns_uri(o->layers, elemt->value);
203       fprintf(o->output, "<xs:import namespace='%s' ", namespace->buf);
204       fprintf(o->output, "schemaLocation='%s?service=WFS&amp;version=",
205               o->online_resource->buf);
206 
207       if (wfs_version == 100)
208         fprintf(o->output, "1.0.0&amp;request=DescribeFeatureType&amp;typename=");
209       else
210         fprintf(o->output, "1.1.0&amp;request=DescribeFeatureType&amp;typename=");
211 
212       /* print the describeFeatureType request with typenames for each prefix */
213       typ = ows_layer_list_by_ns_prefix(o->layers, wr->typename, elemt->value);
214 
215       for (ln = typ->first ; ln ; ln = ln->next) {
216         fprintf(o->output, "%s", ln->value->buf);
217 
218         if (ln->next) fprintf(o->output, ",");
219       }
220 
221       list_free(typ);
222       fprintf(o->output, "' />\n\n");
223     }
224 
225     fprintf(o->output, "</xs:schema>\n");
226   }
227   /* if all layers belong to the same prefix, print the xsd schema describing features */
228   else {
229     namespace = ows_layer_ns_prefix_to_ns_uri(o->layers, ns_prefix->first->value);
230     fprintf(o->output, "<xs:schema targetNamespace='%s' ", namespace->buf);
231     fprintf(o->output, "xmlns:%s='%s' ", ns_prefix->first->value->buf, namespace->buf);
232     fprintf(o->output, "xmlns:ogc='http://www.opengis.net/ogc' ");
233     fprintf(o->output, "xmlns:xs='http://www.w3.org/2001/XMLSchema' ");
234     fprintf(o->output, "xmlns='http://www.w3.org/2001/XMLSchema' ");
235     fprintf(o->output, "xmlns:gml='http://www.opengis.net/gml' ");
236     fprintf(o->output, "elementFormDefault='qualified' ");
237 
238     if (wfs_version == 100) fprintf(o->output, "version='1.0'>\n");
239     else                    fprintf(o->output, "version='1.1'>\n");
240 
241     fprintf(o->output, "<xs:import namespace='http://www.opengis.net/gml'");
242 
243     if (wfs_version == 100)
244       fprintf(o->output, " schemaLocation='http://schemas.opengis.net/gml/2.1.2/feature.xsd'/>\n");
245     else
246       fprintf(o->output, " schemaLocation='http://schemas.opengis.net/gml/3.1.1/base/gml.xsd'/>\n");
247 
248     /* Describe each feature type specified in the request */
249     for (elemt = wr->typename->first ; elemt ; elemt = elemt->next) {
250       fprintf(o->output, "<xs:element name='");
251       buffer_flush(ows_layer_no_uri(o->layers, ows_layer_prefix_to_uri(o->layers, elemt->value)), o->output);
252       fprintf(o->output, "' type='");
253       buffer_flush(elemt->value, o->output);
254       fprintf(o->output, "Type' substitutionGroup='gml:_Feature' />\n");
255       wfs_complex_type(o, wr, elemt->value);
256     }
257 
258     fprintf(o->output, "</xs:schema>");
259   }
260 
261   list_free(ns_prefix);
262 }
263 
264 
265 /*
266  * Generate a WFS Schema related to current Server (all valid layers)
267  * with GML XSD import
268  * This is needed by WFS Insert operation validation as libxml2 only handle
269  * a single schema validation at a time
270  */
wfs_generate_schema(ows * o,ows_version * version)271 buffer * wfs_generate_schema(ows * o, ows_version * version)
272 {
273   list *ns_prefix, *typename, *layers;
274   buffer *namespace, *schema;
275   list_node *elemt, *t;
276   int wfs_version;
277 
278   assert(o && version);
279 
280   schema = buffer_init();
281   wfs_version = ows_version_get(version);
282   layers = ows_layer_list_having_storage(o->layers);
283 
284   buffer_add_str(schema, "<?xml version='1.0' encoding='utf-8'?>\n");
285 
286   ns_prefix = ows_layer_list_ns_prefix(o->layers, layers);
287 
288   buffer_add_str(schema, "<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'");
289   buffer_add_str(schema, " xmlns='http://www.w3.org/2001/XMLSchema' elementFormDefault='qualified'>\n");
290   buffer_add_str(schema, "<xs:import namespace='http://www.opengis.net/wfs' schemaLocation='");
291   buffer_copy(schema, o->schema_dir);
292 
293   if (wfs_version == 100) buffer_add_str(schema, WFS_SCHEMA_100);
294   else                    buffer_add_str(schema, WFS_SCHEMA_110);
295 
296   buffer_add_str(schema, "'/>\n");
297 
298   for (elemt = ns_prefix->first ; elemt ; elemt = elemt->next) {
299     namespace = ows_layer_ns_prefix_to_ns_uri(o->layers, elemt->value);
300     buffer_add_str(schema, "<xs:import namespace='");
301     buffer_copy(schema, namespace);
302     buffer_add_str(schema, "' schemaLocation='");
303 
304     buffer_copy(schema, o->online_resource);
305     buffer_add_str(schema, "?service=WFS&amp;request=DescribeFeatureType");
306 
307     if (elemt->next || elemt != ns_prefix->first) {
308       buffer_add_str(schema, "&amp;Typename=");
309 
310       typename = ows_layer_list_by_ns_prefix(o->layers, layers, elemt->value);
311       for (t = typename->first ; t ; t = t->next) {
312         buffer_copy(schema, t->value);
313         if (t->next) buffer_add(schema, ',');
314       }
315       list_free(typename);
316     }
317 
318     if (wfs_version == 100) buffer_add_str(schema, "&amp;version=1.0.0");
319     else                    buffer_add_str(schema, "&amp;version=1.1.0");
320 
321     buffer_add_str(schema, "'/>\n");
322   }
323 
324   buffer_add_str(schema, "</xs:schema>");
325   list_free(ns_prefix);
326   list_free(layers);
327 
328   return schema;
329 }
330