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&version=",
205 o->online_resource->buf);
206
207 if (wfs_version == 100)
208 fprintf(o->output, "1.0.0&request=DescribeFeatureType&typename=");
209 else
210 fprintf(o->output, "1.1.0&request=DescribeFeatureType&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&request=DescribeFeatureType");
306
307 if (elemt->next || elemt != ns_prefix->first) {
308 buffer_add_str(schema, "&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, "&version=1.0.0");
319 else buffer_add_str(schema, "&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