Coverage Report

Created: 2025-06-22 06:59

/src/MapServer/src/mapwfs.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  WFS server implementation
6
 * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7
 *
8
 **********************************************************************
9
 * Copyright (c) 2002, Daniel Morissette, DM Solutions Group Inc
10
 * Copyright (c) 2013, Even Rouault
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a
13
 * copy of this software and associated documentation files (the "Software"),
14
 * to deal in the Software without restriction, including without limitation
15
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
 * and/or sell copies of the Software, and to permit persons to whom the
17
 * Software is furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in
20
 * all copies of this Software or works derived from this Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
 ****************************************************************************/
29
30
#include "mapserver.h"
31
#include "mapows.h"
32
33
#include <string>
34
35
#if defined(USE_WFS_SVR)
36
37
/* There is a dependency to GDAL/OGR for the GML driver and MiniXML parser */
38
#include "cpl_minixml.h"
39
#include "cpl_conv.h"
40
#include "cpl_string.h"
41
42
#include "mapogcfilter.h"
43
#include "mapowscommon.h"
44
#include "maptemplate.h"
45
46
#if defined(USE_LIBXML2)
47
#include "maplibxml2.h"
48
#endif
49
50
#include <string>
51
52
static int msWFSAnalyzeStoredQuery(mapObj *map, wfsParamsObj *wfsparams,
53
                                   const char *id,
54
                                   const char *pszResolvedQuery);
55
static void msWFSSimplifyPropertyNameAndFilter(wfsParamsObj *wfsparams);
56
static void msWFSAnalyzeStartIndexAndFeatureCount(mapObj *map,
57
                                                  const wfsParamsObj *paramsObj,
58
                                                  int bIsHits,
59
                                                  int *pmaxfeatures,
60
                                                  int *pstartindex);
61
static int msWFSRunBasicGetFeature(mapObj *map, layerObj *lp,
62
                                   const wfsParamsObj *paramsObj,
63
                                   int nWFSVersion);
64
65
static int msWFSParseRequest(mapObj *map, cgiRequestObj *request,
66
                             wfsParamsObj *wfsparams, int force_wfs_mode);
67
68
/* Must be sorted from more recent to older one */
69
static const int wfsSupportedVersions[] = {OWS_2_0_0, OWS_1_1_0, OWS_1_0_0};
70
static const char *const wfsSupportedVersionsStr[] = {"2.0.0", "1.1.0",
71
                                                      "1.0.0"};
72
static const int wfsNumSupportedVersions =
73
    (int)(sizeof(wfsSupportedVersions) / sizeof(wfsSupportedVersions[0]));
74
static const char *const wfsUnsupportedOperations[] = {
75
    "GetFeatureWithLock", "LockFeature", "Transaction", "CreateStoredQuery",
76
    "DropStoredQuery"};
77
static const int wfsNumUnsupportedOperations =
78
    (int)(sizeof(wfsUnsupportedOperations) /
79
          sizeof(wfsUnsupportedOperations[0]));
80
81
0
#define WFS_LATEST_VERSION wfsSupportedVersionsStr[0]
82
83
/* Supported DescribeFeature formats */
84
typedef enum {
85
  OWS_DEFAULT_SCHEMA,  /* basically a GML 2.1 schema */
86
  OWS_SFE_SCHEMA,      /* GML for simple feature exchange (formerly GML3L0) */
87
  OWS_GML32_SFE_SCHEMA /* GML 3.2 Simple Features Level 0 */
88
} WFSSchemaVersion;
89
90
/*
91
** msWFSGetIndexUnsupportedOperation()
92
**
93
** Return index of pszOp in wfsUnsupportedOperations, or -1 otherwise
94
*/
95
96
0
static int msWFSGetIndexUnsupportedOperation(const char *pszOp) {
97
0
  int i;
98
0
  for (i = 0; i < wfsNumUnsupportedOperations; i++) {
99
0
    if (strcasecmp(wfsUnsupportedOperations[i], pszOp) == 0)
100
0
      return i;
101
0
  }
102
0
  return -1;
103
0
}
104
105
static const char *msWFSGetDefaultVersion(mapObj *map);
106
107
/*
108
** msWFSException()
109
**
110
** Report current MapServer error in XML exception format.
111
*/
112
113
static int msWFSExceptionInternal(mapObj *map, const char *locator,
114
                                  const char *code, const char *version,
115
0
                                  int locatorShouldBeNull) {
116
0
  char *schemalocation = NULL;
117
  /* In WFS, exceptions are always XML.
118
   */
119
120
0
  if (version == NULL)
121
0
    version = msWFSGetDefaultVersion(map);
122
123
0
  if (msOWSParseVersionString(version) >= OWS_2_0_0)
124
0
    return msWFSException20(map, (locatorShouldBeNull) ? NULL : locator, code);
125
0
  if (msOWSParseVersionString(version) >= OWS_1_1_0)
126
0
    return msWFSException11(map, (locatorShouldBeNull) ? NULL : locator, code,
127
0
                            version);
128
129
0
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
130
0
  msIO_sendHeaders();
131
132
0
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
133
134
0
  msIO_printf("<ServiceExceptionReport ");
135
0
  msIO_printf("version=\"1.2.0\" ");
136
0
  msIO_printf("xmlns=\"http://www.opengis.net/ogc\" ");
137
0
  msIO_printf("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
138
0
  schemalocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
139
0
  msIO_printf("xsi:schemaLocation=\"http://www.opengis.net/ogc "
140
0
              "%s/wfs/1.0.0/OGC-exception.xsd\">\n",
141
0
              schemalocation);
142
0
  free(schemalocation);
143
0
  msIO_printf("  <ServiceException code=\"%s\" locator=\"%s\">\n", code,
144
0
              locator);
145
  /* Optional <Locator> element currently unused. */
146
  /* msIO_printf("    <Message>\n"); */
147
0
  msWriteErrorXML(stdout);
148
  /* msIO_printf("    </Message>\n"); */
149
0
  msIO_printf("  </ServiceException>\n");
150
0
  msIO_printf("</ServiceExceptionReport>\n");
151
152
0
  return MS_FAILURE; /* so we can call 'return msWFSException();' anywhere */
153
0
}
154
155
int msWFSException(mapObj *map, const char *locator, const char *code,
156
0
                   const char *version) {
157
0
  return msWFSExceptionInternal(map, locator, code, version, FALSE);
158
0
}
159
160
/*
161
** msWFSExceptionNoLocator()
162
**
163
** Report current MapServer error in XML exception format.
164
** For WFS >= 1.1.0, the locator will be ignored. It will be just used
165
** for legacy WFS 1.0 exceptions.
166
*/
167
168
static int msWFSExceptionNoLocator(mapObj *map, const char *locator,
169
0
                                   const char *code, const char *version) {
170
0
  return msWFSExceptionInternal(map, locator, code, version, TRUE);
171
0
}
172
173
/*
174
** Helper function to build a list of output formats.
175
**
176
** Given a layer it will return all formats valid for that layer, otherwise
177
** all formats permitted on layers in the map are returned.  The string
178
** returned should be freed by the caller.
179
*/
180
181
0
char *msWFSGetOutputFormatList(mapObj *map, layerObj *layer, int nWFSVersion) {
182
0
  int i, got_map_list = 0;
183
0
  static const int out_list_size = 20000;
184
0
  char *out_list = (char *)msSmallCalloc(1, out_list_size);
185
186
0
  if (nWFSVersion == OWS_1_0_0)
187
0
    strcpy(out_list, "GML2");
188
0
  else if (nWFSVersion == OWS_1_1_0)
189
0
    strcpy(out_list, "text/xml; subtype=gml/3.1.1");
190
0
  else
191
0
    strcpy(out_list, "application/gml+xml; version=3.2,"
192
0
                     "text/xml; subtype=gml/3.2.1,"
193
0
                     "text/xml; subtype=gml/3.1.1,"
194
0
                     "text/xml; subtype=gml/2.1.2");
195
196
0
  for (i = 0; i < map->numlayers; i++) {
197
0
    const char *format_list;
198
0
    layerObj *lp;
199
0
    int j, n;
200
0
    char **tokens;
201
202
0
    lp = GET_LAYER(map, i);
203
0
    if (layer != NULL && layer != lp)
204
0
      continue;
205
206
0
    format_list =
207
0
        msOWSLookupMetadata(&(lp->metadata), "F", "getfeature_formatlist");
208
209
0
    if (format_list == NULL && !got_map_list) {
210
0
      format_list = msOWSLookupMetadata(&(map->web.metadata), "F",
211
0
                                        "getfeature_formatlist");
212
0
      got_map_list = 1;
213
0
    }
214
215
0
    if (format_list == NULL)
216
0
      continue;
217
218
0
    n = 0;
219
0
    tokens = msStringSplit(format_list, ',', &n);
220
221
0
    for (j = 0; j < n; j++) {
222
0
      int iformat;
223
0
      const char *fname, *hit;
224
0
      outputFormatObj *format_obj;
225
226
0
      msStringTrim(tokens[j]);
227
0
      iformat = msGetOutputFormatIndex(map, tokens[j]);
228
0
      if (iformat < 0)
229
0
        continue;
230
231
0
      format_obj = map->outputformatlist[iformat];
232
233
0
      fname = format_obj->name;
234
0
      if (nWFSVersion >= OWS_1_1_0 && format_obj->mimetype != NULL)
235
0
        fname = format_obj->mimetype;
236
237
0
      hit = strstr(out_list, fname);
238
0
      if (hit != NULL &&
239
0
          (hit[strlen(fname)] == '\0' || hit[strlen(fname)] == ','))
240
0
        continue;
241
242
0
      if (strlen(out_list) + strlen(fname) + 3 < out_list_size) {
243
0
        strcat(out_list, ",");
244
0
        strcat(out_list, fname);
245
0
      } else
246
0
        break;
247
0
    }
248
249
0
    msFreeCharArray(tokens, n);
250
0
  }
251
252
0
  return out_list;
253
0
}
254
255
/*
256
**
257
*/
258
static void msWFSPrintRequestCap(const char *request, const char *script_url,
259
                                 const char *format_tag,
260
0
                                 const char *formats_list) {
261
0
  msIO_printf("    <%s>\n", request);
262
263
  /* We expect to receive a NULL-terminated args list of formats */
264
0
  if (format_tag != NULL) {
265
0
    int i, n;
266
0
    char **tokens;
267
268
0
    n = 0;
269
0
    tokens = msStringSplit(formats_list, ',', &n);
270
271
0
    msIO_printf("      <%s>\n", format_tag);
272
273
0
    for (i = 0; i < n; i++) {
274
0
      msIO_printf("        <%s/>\n", tokens[i]);
275
0
    }
276
277
0
    msFreeCharArray(tokens, n);
278
279
0
    msIO_printf("      </%s>\n", format_tag);
280
0
  }
281
282
0
  msIO_printf("      <DCPType>\n");
283
0
  msIO_printf("        <HTTP>\n");
284
0
  msIO_printf("          <Get onlineResource=\"%s\" />\n", script_url);
285
0
  msIO_printf("        </HTTP>\n");
286
0
  msIO_printf("      </DCPType>\n");
287
0
  msIO_printf("      <DCPType>\n");
288
0
  msIO_printf("        <HTTP>\n");
289
0
  msIO_printf("          <Post onlineResource=\"%s\" />\n", script_url);
290
0
  msIO_printf("        </HTTP>\n");
291
0
  msIO_printf("      </DCPType>\n");
292
293
0
  msIO_printf("    </%s>\n", request);
294
0
}
295
296
/* msWFSLocateSRSInList()
297
**
298
** Utility function to check if a space separated list contains  the one passed
299
*in argument.
300
**  The list comes normally from ows_srs metadata, and is expected to use the
301
*simple EPSG notation
302
** (EPSG:4326 ESPG:42304 ...). The srs comes from the query string and can
303
*either
304
** be of simple EPSG format or using gc:def:crs:EPSG:xxx format
305
*/
306
0
int msWFSLocateSRSInList(const char *pszList, const char *srs) {
307
0
  int nTokens, i;
308
0
  char **tokens = NULL;
309
0
  int bFound = MS_FALSE;
310
0
  char epsg_string[100];
311
0
  const char *code;
312
313
0
  if (!pszList || !srs)
314
0
    return MS_FALSE;
315
316
0
  if (strncasecmp(srs, "EPSG:", 5) == 0)
317
0
    code = srs + 5;
318
0
  else if (strncasecmp(srs, "urn:ogc:def:crs:EPSG:", 21) == 0) {
319
0
    if (srs[21] == ':')
320
0
      code = srs + 21;
321
0
    else
322
0
      code = srs + 20;
323
324
0
    while (*code != ':' && *code != '\0')
325
0
      code++;
326
0
    if (*code == ':')
327
0
      code++;
328
0
  } else if (strncasecmp(srs, "urn:EPSG:geographicCRS:", 23) == 0)
329
0
    code = srs + 23;
330
0
  else
331
0
    return MS_FALSE;
332
333
0
  snprintf(epsg_string, sizeof(epsg_string), "EPSG:%s", code);
334
335
0
  tokens = msStringSplit(pszList, ' ', &nTokens);
336
0
  if (tokens && nTokens > 0) {
337
0
    for (i = 0; i < nTokens; i++) {
338
0
      if (strcasecmp(tokens[i], epsg_string) == 0) {
339
0
        bFound = MS_TRUE;
340
0
        break;
341
0
      }
342
0
    }
343
0
  }
344
0
  msFreeCharArray(tokens, nTokens);
345
346
0
  return bFound;
347
0
}
348
349
/* msWFSGetFeatureApplySRS()
350
**
351
** Utility function called from msWFSGetFeature. It is assumed that at this
352
*point
353
** all queried layers are turned ON
354
*/
355
static int msWFSGetFeatureApplySRS(mapObj *map, const char *srs,
356
0
                                   int nWFSVersion) {
357
0
  char *pszMapSRS = NULL;
358
0
  char *pszOutputSRS = NULL;
359
0
  layerObj *lp;
360
0
  int i;
361
362
  /*validation of SRS
363
    - wfs 1.0 does not have an srsname parameter so all queried layers should be
364
    advertized using the same srs. For wfs 1.1.0 an srsName can be passed, we
365
    should validate that It is valid for all queries layers
366
  */
367
368
  /* Start by applying the default service SRS to the mapObj,
369
   * make sure we reproject the map extent if a projection was
370
   * already set
371
   */
372
0
  msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE,
373
0
                   &pszMapSRS);
374
0
  if (pszMapSRS && nWFSVersion > OWS_1_0_0) {
375
0
    projectionObj proj;
376
0
    msInitProjection(&proj);
377
0
    msProjectionInheritContextFrom(&proj, &(map->projection));
378
0
    if (map->projection.numargs > 0 &&
379
0
        msLoadProjectionStringEPSG(&proj, pszMapSRS) == 0) {
380
0
      msProjectRect(&(map->projection), &proj, &map->extent);
381
0
    }
382
0
    msLoadProjectionStringEPSG(&(map->projection), pszMapSRS);
383
0
    msFreeProjection(&proj);
384
0
  }
385
386
0
  if (srs == NULL || nWFSVersion == OWS_1_0_0) {
387
0
    for (i = 0; i < map->numlayers; i++) {
388
0
      char *pszLayerSRS;
389
0
      lp = GET_LAYER(map, i);
390
0
      if (lp->status != MS_ON)
391
0
        continue;
392
393
0
      if (pszMapSRS)
394
0
        pszLayerSRS = pszMapSRS;
395
0
      else
396
0
        msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE,
397
0
                         &pszLayerSRS);
398
399
0
      if (pszLayerSRS == NULL) {
400
0
        msSetError(MS_WFSERR,
401
0
                   "Server config error: SRS must be set at least at the map "
402
0
                   "or at the layer level.",
403
0
                   "msWFSGetFeature()");
404
0
        if (pszOutputSRS)
405
0
          msFree(pszOutputSRS);
406
        /*pszMapSrs would also be NULL, no use freeing*/
407
0
        return MS_FAILURE;
408
0
      }
409
0
      if (pszOutputSRS == NULL)
410
0
        pszOutputSRS = msStrdup(pszLayerSRS);
411
0
      else if (strcasecmp(pszLayerSRS, pszOutputSRS) != 0) {
412
0
        msSetError(
413
0
            MS_WFSERR,
414
0
            "Invalid GetFeature Request: All TYPENAMES in a single GetFeature "
415
0
            "request must have been advertized in the same SRS.  Please check "
416
0
            "the capabilities and reformulate your request.",
417
0
            "msWFSGetFeature()");
418
0
        if (pszOutputSRS)
419
0
          msFree(pszOutputSRS);
420
0
        if (pszLayerSRS != pszMapSRS)
421
0
          msFree(pszLayerSRS);
422
0
        msFree(pszMapSRS);
423
0
        return MS_FAILURE;
424
0
      }
425
0
      if (pszLayerSRS != pszMapSRS)
426
0
        msFree(pszLayerSRS);
427
0
    }
428
0
  } else { /*srs is given so it should be valid for all layers*/
429
    /*get all the srs defined at the map level and check them against the
430
      srsName passed as argument*/
431
0
    msFree(pszMapSRS);
432
0
    msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_FALSE,
433
0
                     &pszMapSRS);
434
0
    if (pszMapSRS) {
435
0
      if (!msWFSLocateSRSInList(pszMapSRS, srs)) {
436
0
        msSetError(MS_WFSERR,
437
0
                   "Invalid GetFeature Request:Invalid SRS.  Please check the "
438
0
                   "capabilities and reformulate your request.",
439
0
                   "msWFSGetFeature()");
440
0
        msFree(pszMapSRS);
441
0
        return MS_FAILURE;
442
0
      }
443
0
      pszOutputSRS = msStrdup(srs);
444
0
    } else {
445
0
      for (i = 0; i < map->numlayers; i++) {
446
0
        char *pszLayerSRS = NULL;
447
0
        lp = GET_LAYER(map, i);
448
0
        if (lp->status != MS_ON)
449
0
          continue;
450
451
0
        msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_FALSE,
452
0
                         &pszLayerSRS);
453
0
        if (!pszLayerSRS) {
454
0
          msSetError(MS_WFSERR,
455
0
                     "Server config error: SRS must be set at least at the map "
456
0
                     "or at the layer level.",
457
0
                     "msWFSGetFeature()");
458
0
          msFree(pszMapSRS);
459
0
          return MS_FAILURE;
460
0
        }
461
0
        if (!msWFSLocateSRSInList(pszLayerSRS, srs)) {
462
0
          msSetError(MS_WFSERR,
463
0
                     "Invalid GetFeature Request:Invalid SRS.  Please check "
464
0
                     "the capabilities and reformulate your request.",
465
0
                     "msWFSGetFeature()");
466
0
          msFree(pszMapSRS);
467
0
          msFree(pszLayerSRS);
468
0
          return MS_FAILURE;
469
0
        }
470
0
        msFree(pszLayerSRS);
471
0
      }
472
0
      pszOutputSRS = msStrdup(srs);
473
0
    }
474
0
  }
475
476
0
  if (pszOutputSRS) {
477
0
    projectionObj sProjTmp;
478
0
    int nTmp = 0;
479
480
0
    msInitProjection(&sProjTmp);
481
0
    msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
482
0
    if (nWFSVersion >= OWS_1_1_0) {
483
0
      nTmp = msLoadProjectionStringEPSG(&(sProjTmp), pszOutputSRS);
484
0
    } else {
485
0
      nTmp = msLoadProjectionString(&(sProjTmp), pszOutputSRS);
486
0
    }
487
0
    if (nTmp == 0) {
488
0
      msProjectRect(&(map->projection), &(sProjTmp), &map->extent);
489
0
    }
490
0
    msFreeProjection(&(sProjTmp));
491
492
0
    if (nTmp != 0) {
493
0
      msSetError(MS_WFSERR, "msLoadProjectionString() failed",
494
0
                 "msWFSGetFeature()");
495
0
      return MS_FAILURE;
496
0
    }
497
498
    /*we load the projection sting in the map and possibly
499
    set the axis order*/
500
0
    if (nWFSVersion >= OWS_1_1_0) {
501
0
      msLoadProjectionStringEPSG(&(map->projection), pszOutputSRS);
502
0
    } else {
503
0
      msLoadProjectionString(&(map->projection), pszOutputSRS);
504
0
    }
505
506
0
    nTmp = GetMapserverUnitUsingProj(&(map->projection));
507
0
    if (nTmp != -1) {
508
0
      map->units = static_cast<MS_UNITS>(nTmp);
509
0
    }
510
0
  }
511
512
0
  msFree(pszOutputSRS);
513
0
  msFree(pszMapSRS);
514
0
  return MS_SUCCESS;
515
0
}
516
517
0
static int msWFSIsLayerAllowed(layerObj *lp, owsRequestObj *ows_request) {
518
0
  return msIsLayerSupportedForWFSOrOAPIF(lp) &&
519
0
         (msIntegerInArray(lp->index, ows_request->enabled_layers,
520
0
                           ows_request->numlayers));
521
0
}
522
523
static layerObj *msWFSGetLayerByName(mapObj *map, owsRequestObj *ows_request,
524
0
                                     const char *name) {
525
0
  int j;
526
0
  for (j = 0; j < map->numlayers; j++) {
527
0
    layerObj *lp;
528
529
0
    lp = GET_LAYER(map, j);
530
531
0
    if (msWFSIsLayerAllowed(lp, ows_request) && lp->name &&
532
0
        (strcasecmp(lp->name, name) == 0)) {
533
0
      return lp;
534
0
    }
535
0
  }
536
0
  return NULL;
537
0
}
538
539
/*
540
** msWFSDumpLayer()
541
*/
542
0
int msWFSDumpLayer(mapObj *map, layerObj *lp, const char *script_url_encoded) {
543
0
  rectObj ext;
544
0
  char *pszWfsSrs = NULL;
545
0
  projectionObj poWfs;
546
547
0
  msIO_printf("    <FeatureType>\n");
548
549
0
  if (lp->name && strlen(lp->name) > 0 &&
550
0
      (msIsXMLTagValid(lp->name) == MS_FALSE || isdigit(lp->name[0])))
551
0
    msIO_fprintf(stdout,
552
0
                 "<!-- WARNING: The layer name '%s' might contain spaces or "
553
0
                 "invalid characters or may start with a number. This could "
554
0
                 "lead to potential problems. -->\n",
555
0
                 lp->name);
556
557
0
  msOWSPrintEncodeParam(stdout, "LAYER.NAME", lp->name, OWS_WARN,
558
0
                        "        <Name>%s</Name>\n", NULL);
559
560
0
  msOWSPrintEncodeMetadata(stdout, &(lp->metadata), "FO", "title", OWS_WARN,
561
0
                           "        <Title>%s</Title>\n", lp->name);
562
563
0
  msOWSPrintEncodeMetadata(stdout, &(lp->metadata), "FO", "abstract", OWS_NOERR,
564
0
                           "        <Abstract>%s</Abstract>\n", NULL);
565
566
0
  msOWSPrintEncodeMetadataList(stdout, &(lp->metadata), "FO", "keywordlist",
567
0
                               "        <Keywords>\n", "        </Keywords>\n",
568
0
                               "          %s\n", NULL);
569
570
  /* In WFS, every layer must have exactly one SRS and there is none at */
571
  /* the top level contrary to WMS */
572
  /*  */
573
  /* So here is the way we'll deal with SRS: */
574
  /* 1- If a top-level map projection (or wfs_srs metadata) is set then */
575
  /* all layers are advertized in the map's projection and they will */
576
  /* be reprojected on the fly in the GetFeature request. */
577
  /* 2- If there is no top-level map projection (or wfs_srs metadata) then */
578
  /* each layer is advertized in its own projection as defined in the */
579
  /* layer's projection object or wfs_srs metadata. */
580
  /*  */
581
582
  /* if Map has a SRS,  Use it for all layers. */
583
0
  msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE,
584
0
                   &pszWfsSrs);
585
0
  if (!pszWfsSrs) {
586
    /* Map has no SRS.  Use layer SRS or produce a warning. */
587
0
    msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE,
588
0
                     &pszWfsSrs);
589
0
  }
590
591
0
  msOWSPrintEncodeParam(
592
0
      stdout,
593
0
      "(at least one of) MAP.PROJECTION, LAYER.PROJECTION or wfs_srs metadata",
594
0
      pszWfsSrs, OWS_WARN, "        <SRS>%s</SRS>\n", NULL);
595
596
  /* If layer has no proj set then use map->proj for bounding box. */
597
0
  if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS) {
598
0
    msInitProjection(&poWfs);
599
0
    msProjectionInheritContextFrom(&poWfs, &(map->projection));
600
0
    if (pszWfsSrs != NULL)
601
0
      msLoadProjectionString(&(poWfs), pszWfsSrs);
602
603
0
    if (lp->projection.numargs > 0) {
604
0
      msOWSPrintLatLonBoundingBox(stdout, "        ", &(ext), &(lp->projection),
605
0
                                  &(poWfs), OWS_WFS);
606
0
    } else {
607
0
      msOWSPrintLatLonBoundingBox(stdout, "        ", &(ext),
608
0
                                  &(map->projection), &(poWfs), OWS_WFS);
609
0
    }
610
0
    msFreeProjection(&poWfs);
611
0
  } else {
612
0
    msIO_printf("<!-- WARNING: Optional LatLongBoundingBox could not be "
613
0
                "established for this layer.  Consider setting the EXTENT in "
614
0
                "the LAYER object, or wfs_extent metadata. Also check that "
615
0
                "your data exists in the DATA statement -->\n");
616
0
  }
617
618
0
  const char *metadataurl_list =
619
0
      msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_list");
620
0
  if (metadataurl_list) {
621
0
    int ntokens = 0;
622
0
    char **tokens = msStringSplit(metadataurl_list, ' ', &ntokens);
623
0
    for (int i = 0; i < ntokens; i++) {
624
0
      std::string key("metadataurl_");
625
0
      key += tokens[i];
626
0
      msOWSPrintURLType(stdout, &(lp->metadata), "FO", key.c_str(), OWS_WARN,
627
0
                        NULL, "MetadataURL", " type=\"%s\"", NULL, NULL,
628
0
                        " format=\"%s\"", "%s", MS_TRUE, MS_FALSE, MS_FALSE,
629
0
                        MS_TRUE, MS_TRUE, NULL, NULL, NULL, NULL, NULL,
630
0
                        "        ");
631
0
    }
632
0
    msFreeCharArray(tokens, ntokens);
633
0
  } else {
634
0
    if (!msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_href"))
635
0
      msMetadataSetGetMetadataURL(lp, script_url_encoded);
636
637
0
    msOWSPrintURLType(stdout, &(lp->metadata), "FO", "metadataurl", OWS_WARN,
638
0
                      NULL, "MetadataURL", " type=\"%s\"", NULL, NULL,
639
0
                      " format=\"%s\"", "%s", MS_TRUE, MS_FALSE, MS_FALSE,
640
0
                      MS_TRUE, MS_TRUE, NULL, NULL, NULL, NULL, NULL,
641
0
                      "        ");
642
0
  }
643
644
0
  if (msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid") == NULL) {
645
0
    msIO_fprintf(
646
0
        stdout,
647
0
        "<!-- WARNING: Required Feature Id attribute (fid) not specified for "
648
0
        "this feature type. Make sure you set one of wfs_featureid, "
649
0
        "ows_featureid or gml_featureid metadata. -->\n");
650
0
  }
651
652
0
  msIO_printf("    </FeatureType>\n");
653
654
0
  msFree(pszWfsSrs);
655
0
  return MS_SUCCESS;
656
0
}
657
658
/*
659
** msWFSHandleUpdateSequence()
660
*/
661
int msWFSHandleUpdateSequence(mapObj *map, wfsParamsObj *params,
662
0
                              const char *pszFunction) {
663
  /* -------------------------------------------------------------------- */
664
  /*      Handle updatesequence                                           */
665
  /* -------------------------------------------------------------------- */
666
667
0
  const char *updatesequence =
668
0
      msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence");
669
670
0
  if (params->pszUpdateSequence != NULL) {
671
0
    int i =
672
0
        msOWSNegotiateUpdateSequence(params->pszUpdateSequence, updatesequence);
673
0
    if (i == 0) { /* current */
674
0
      msSetError(MS_WFSERR,
675
0
                 "UPDATESEQUENCE parameter (%s) is equal to server (%s)",
676
0
                 pszFunction, params->pszUpdateSequence, updatesequence);
677
      /* FIXME? : according to table 7 of OWS 1.1, we should return a service */
678
      /* metadata document with only “version” and “updateSequence” parameters
679
       */
680
0
      return msWFSException(map, "updatesequence", "CurrentUpdateSequence",
681
0
                            params->pszVersion);
682
0
    }
683
0
    if (i > 0) { /* invalid */
684
0
      msSetError(MS_WFSERR,
685
0
                 "UPDATESEQUENCE parameter (%s) is higher than server (%s)",
686
0
                 pszFunction, params->pszUpdateSequence, updatesequence);
687
      /* locator must be NULL. See Table 25 of OWS 1.1 */
688
0
      return msWFSExceptionNoLocator(map, "updatesequence",
689
0
                                     MS_OWS_ERROR_INVALID_UPDATE_SEQUENCE,
690
0
                                     params->pszVersion);
691
0
    }
692
0
  }
693
694
0
  return MS_SUCCESS;
695
0
}
696
697
/*
698
** msWFSGetCapabilitiesNegotiateVersion()
699
*/
700
static int msWFSGetCapabilitiesNegotiateVersion(mapObj *map,
701
0
                                                wfsParamsObj *wfsparams) {
702
0
  int iVersion = -1;
703
0
  char tmpString[OWS_VERSION_MAXLEN];
704
705
  /* acceptversions: do OWS Common style of version negotiation */
706
0
  if (wfsparams->pszAcceptVersions &&
707
0
      strlen(wfsparams->pszAcceptVersions) > 0) {
708
0
    char **tokens;
709
0
    int i, j;
710
711
0
    tokens = msStringSplit(wfsparams->pszAcceptVersions, ',', &j);
712
0
    for (i = 0; i < j; i++) {
713
0
      iVersion = msOWSParseVersionString(tokens[i]);
714
715
0
      if (iVersion < 0) {
716
0
        msSetError(MS_WFSERR, "Invalid version format : %s.",
717
0
                   "msWFSGetCapabilities()", tokens[i]);
718
0
        msFreeCharArray(tokens, j);
719
0
        return msWFSException(map, "acceptversions",
720
0
                              MS_OWS_ERROR_INVALID_PARAMETER_VALUE, NULL);
721
0
      }
722
723
      /* negotiate version */
724
0
      iVersion = msOWSCommonNegotiateVersion(iVersion, wfsSupportedVersions,
725
0
                                             wfsNumSupportedVersions);
726
0
      if (iVersion != -1)
727
0
        break;
728
0
    }
729
0
    msFreeCharArray(tokens, j);
730
0
    if (iVersion == -1) {
731
0
      msSetError(MS_WFSERR,
732
0
                 "ACCEPTVERSIONS list (%s) does not match supported versions",
733
0
                 "msWFSGetCapabilities()", wfsparams->pszAcceptVersions);
734
      /* locator must be NULL. See Table 25 of OWS 1.1 */
735
0
      return msWFSExceptionNoLocator(
736
0
          map, "acceptversions", MS_OWS_ERROR_VERSION_NEGOTIATION_FAILED, NULL);
737
0
    }
738
0
  } else {
739
    /* negotiate version */
740
0
    int tmpInt;
741
0
    iVersion = msOWSParseVersionString(wfsparams->pszVersion);
742
0
    if (iVersion < 0) {
743
0
      return msWFSException(map, "version",
744
0
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE, NULL);
745
0
    }
746
0
    tmpInt = msOWSCommonNegotiateVersion(iVersion, wfsSupportedVersions,
747
0
                                         wfsNumSupportedVersions);
748
    /* Old style negotiation : paragraph D.11 of OWS 1.1.0 spec */
749
0
    if (tmpInt < 0) {
750
0
      int i;
751
0
      for (i = 0; i < wfsNumSupportedVersions; i++) {
752
0
        if (iVersion >= wfsSupportedVersions[i]) {
753
0
          iVersion = wfsSupportedVersions[i];
754
0
          break;
755
0
        }
756
0
      }
757
0
      if (i == wfsNumSupportedVersions)
758
0
        iVersion = wfsSupportedVersions[wfsNumSupportedVersions - 1];
759
0
    }
760
0
  }
761
762
  /* set result as string and carry on */
763
0
  if (wfsparams->pszVersion)
764
0
    msFree(wfsparams->pszVersion);
765
0
  wfsparams->pszVersion = msStrdup(msOWSGetVersionString(iVersion, tmpString));
766
767
0
  return MS_SUCCESS;
768
0
}
769
770
/*
771
** msWFSGetCapabilities()
772
*/
773
int msWFSGetCapabilities(mapObj *map, wfsParamsObj *wfsparams,
774
0
                         cgiRequestObj *req, owsRequestObj *ows_request) {
775
0
  char *script_url = NULL, *script_url_encoded;
776
0
  const char *updatesequence = NULL;
777
0
  const char *wmtver = NULL;
778
0
  char *formats_list;
779
0
  int ret;
780
0
  int iVersion;
781
0
  int i = 0;
782
783
0
  ret = msWFSGetCapabilitiesNegotiateVersion(map, wfsparams);
784
0
  if (ret != MS_SUCCESS)
785
0
    return ret;
786
787
0
  iVersion = msOWSParseVersionString(wfsparams->pszVersion);
788
0
  if (iVersion == OWS_2_0_0)
789
0
    return msWFSGetCapabilities20(map, wfsparams, req, ows_request);
790
0
  if (iVersion == OWS_1_1_0)
791
0
    return msWFSGetCapabilities11(map, wfsparams, req, ows_request);
792
793
  /* Decide which version we're going to return... only 1.0.0 for now */
794
0
  wmtver = "1.0.0";
795
796
  /* We need this server's onlineresource. */
797
0
  if ((script_url = msOWSGetOnlineResource(map, "FO", "onlineresource", req)) ==
798
0
          NULL ||
799
0
      (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) {
800
0
    msSetError(MS_WFSERR, "Server URL not found", "msWFSGetCapabilities()");
801
0
    return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
802
0
                          wmtver);
803
0
  }
804
0
  free(script_url);
805
0
  script_url = NULL;
806
807
0
  ret = msWFSHandleUpdateSequence(map, wfsparams, "msWFSGetCapabilities()");
808
0
  if (ret != MS_SUCCESS) {
809
0
    free(script_url_encoded);
810
0
    return ret;
811
0
  }
812
813
0
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
814
0
  msIO_sendHeaders();
815
816
0
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
817
818
0
  updatesequence =
819
0
      msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence");
820
0
  msIO_printf("<WFS_Capabilities \n"
821
0
              "   version=\"%s\" \n"
822
0
              "   updateSequence=\"%s\" \n"
823
0
              "   xmlns=\"http://www.opengis.net/wfs\" \n"
824
0
              "   xmlns:ogc=\"http://www.opengis.net/ogc\" \n"
825
0
              "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
826
0
              "   xsi:schemaLocation=\"http://www.opengis.net/wfs "
827
0
              "%s/wfs/%s/WFS-capabilities.xsd\">\n",
828
0
              wmtver, updatesequence ? updatesequence : "0",
829
0
              msOWSGetSchemasLocation(map), wmtver);
830
831
  /*
832
  ** SERVICE definition
833
  */
834
0
  msIO_printf("<Service>\n");
835
0
  msIO_printf("  <Name>MapServer WFS</Name>\n");
836
837
  /* the majority of this section is dependent on appropriately named metadata
838
   * in the WEB object */
839
0
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "title",
840
0
                           OWS_WARN, "  <Title>%s</Title>\n", map->name);
841
0
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "abstract",
842
0
                           OWS_NOERR, "  <Abstract>%s</Abstract>\n", NULL);
843
844
0
  msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "FO",
845
0
                               "keywordlist", "  <Keywords>\n",
846
0
                               "  </Keywords>\n", "    %s\n", NULL);
847
848
  /* Service/onlineresource */
849
  /* Defaults to same as request onlineresource if wfs_service_onlineresource */
850
  /* is not set. */
851
0
  msOWSPrintEncodeMetadata(
852
0
      stdout, &(map->web.metadata), "FO", "service_onlineresource", OWS_NOERR,
853
0
      "  <OnlineResource>%s</OnlineResource>\n", script_url_encoded);
854
855
0
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "fees",
856
0
                           OWS_NOERR, "  <Fees>%s</Fees>\n", NULL);
857
858
0
  msOWSPrintEncodeMetadata(
859
0
      stdout, &(map->web.metadata), "FO", "accessconstraints", OWS_NOERR,
860
0
      "  <AccessConstraints>%s</AccessConstraints>\n", NULL);
861
862
0
  msIO_printf("</Service>\n\n");
863
864
  /*
865
  ** CAPABILITY definitions: list of supported requests
866
  */
867
868
0
  msIO_printf("<Capability>\n");
869
870
0
  msIO_printf("  <Request>\n");
871
0
  msWFSPrintRequestCap("GetCapabilities", script_url_encoded, NULL, NULL);
872
  /* msWFSPrintRequestCap("DescribeFeatureType", script_url_encoded,
873
   * "SchemaDescriptionLanguage", "XMLSCHEMA", "SFE_XMLSCHEMA", NULL); */
874
  /* msWFSPrintRequestCap("GetFeature", script_url_encoded, "ResultFormat",
875
   * "GML2", "GML3", NULL); */
876
877
  /* don't advertise the GML3 or GML for SFE support */
878
0
  if (msOWSRequestIsEnabled(map, NULL, "F", "DescribeFeatureType", MS_TRUE))
879
0
    msWFSPrintRequestCap("DescribeFeatureType", script_url_encoded,
880
0
                         "SchemaDescriptionLanguage", "XMLSCHEMA");
881
882
0
  if (msOWSRequestIsEnabled(map, NULL, "F", "GetFeature", MS_TRUE)) {
883
0
    formats_list = msWFSGetOutputFormatList(map, NULL, OWS_1_0_0);
884
0
    msWFSPrintRequestCap("GetFeature", script_url_encoded, "ResultFormat",
885
0
                         formats_list);
886
0
    msFree(formats_list);
887
0
  }
888
889
0
  msIO_printf("  </Request>\n");
890
0
  msIO_printf("</Capability>\n\n");
891
892
  /*
893
  ** FeatureTypeList: layers
894
  */
895
896
0
  msIO_printf("<FeatureTypeList>\n");
897
898
  /* Operations supported... set default at top-level, and more operations */
899
  /* can be added inside each layer... for MapServer only query is supported */
900
0
  msIO_printf("  <Operations>\n");
901
0
  msIO_printf("    <Query/>\n");
902
0
  msIO_printf("  </Operations>\n");
903
904
0
  for (i = 0; i < map->numlayers; i++) {
905
0
    layerObj *lp;
906
0
    lp = GET_LAYER(map, i);
907
908
0
    if (lp->status == MS_DELETE)
909
0
      continue;
910
911
0
    if (msWFSIsLayerAllowed(lp, ows_request)) {
912
0
      msWFSDumpLayer(map, lp, script_url_encoded);
913
0
    }
914
0
  }
915
916
0
  msIO_printf("</FeatureTypeList>\n\n");
917
918
  /*
919
  ** OGC Filter Capabilities ... for now we support only BBOX
920
  */
921
922
0
  msIO_printf("<ogc:Filter_Capabilities>\n");
923
0
  msIO_printf("  <ogc:Spatial_Capabilities>\n");
924
0
  msIO_printf("    <ogc:Spatial_Operators>\n");
925
#ifdef USE_GEOS
926
  msIO_printf("      <ogc:Equals/>\n");
927
  msIO_printf("      <ogc:Disjoint/>\n");
928
  msIO_printf("      <ogc:Touches/>\n");
929
  msIO_printf("      <ogc:Within/>\n");
930
  msIO_printf("      <ogc:Overlaps/>\n");
931
  msIO_printf("      <ogc:Crosses/>\n");
932
  msIO_printf("      <ogc:Intersect/>\n");
933
  msIO_printf("      <ogc:Contains/>\n");
934
  msIO_printf("      <ogc:DWithin/>\n");
935
#endif
936
0
  msIO_printf("      <ogc:BBOX/>\n");
937
0
  msIO_printf("    </ogc:Spatial_Operators>\n");
938
0
  msIO_printf("  </ogc:Spatial_Capabilities>\n");
939
940
0
  msIO_printf("  <ogc:Scalar_Capabilities>\n");
941
0
  msIO_printf("    <ogc:Logical_Operators />\n");
942
0
  msIO_printf("    <ogc:Comparison_Operators>\n");
943
0
  msIO_printf("      <ogc:Simple_Comparisons />\n");
944
0
  msIO_printf("      <ogc:Like />\n");
945
0
  msIO_printf("      <ogc:Between />\n");
946
0
  msIO_printf("    </ogc:Comparison_Operators>\n");
947
0
  msIO_printf("  </ogc:Scalar_Capabilities>\n");
948
949
0
  msIO_printf("</ogc:Filter_Capabilities>\n\n");
950
951
  /*
952
  ** Done!
953
  */
954
0
  msIO_printf("</WFS_Capabilities>\n");
955
956
0
  free(script_url_encoded);
957
958
0
  return MS_SUCCESS;
959
0
}
960
961
/*
962
** Helper functions for producing XML schema.
963
*/
964
965
static const char *msWFSGetGeometryType(const char *type,
966
0
                                        OWSGMLVersion outputformat) {
967
0
  if (!type)
968
0
    return "GeometryPropertyType";
969
970
0
  if (strcasecmp(type, "point") == 0) {
971
0
    switch (outputformat) {
972
0
    case OWS_GML2:
973
0
    case OWS_GML3:
974
0
    case OWS_GML32:
975
0
      return "PointPropertyType";
976
0
    }
977
0
  } else if (strcasecmp(type, "multipoint") == 0) {
978
0
    switch (outputformat) {
979
0
    case OWS_GML2:
980
0
    case OWS_GML3:
981
0
    case OWS_GML32:
982
0
      return "MultiPointPropertyType";
983
0
    }
984
0
  } else if (strcasecmp(type, "line") == 0) {
985
0
    switch (outputformat) {
986
0
    case OWS_GML2:
987
0
      return "LineStringPropertyType";
988
0
    case OWS_GML3:
989
0
    case OWS_GML32:
990
0
      return "CurvePropertyType";
991
0
    }
992
0
  } else if (strcasecmp(type, "multiline") == 0) {
993
0
    switch (outputformat) {
994
0
    case OWS_GML2:
995
0
      return "MultiLineStringPropertyType";
996
0
    case OWS_GML3:
997
0
    case OWS_GML32:
998
0
      return "MultiCurvePropertyType";
999
0
    }
1000
0
  } else if (strcasecmp(type, "polygon") == 0) {
1001
0
    switch (outputformat) {
1002
0
    case OWS_GML2:
1003
0
      return "PolygonPropertyType";
1004
0
    case OWS_GML3:
1005
0
    case OWS_GML32:
1006
0
      return "SurfacePropertyType";
1007
0
    }
1008
0
  } else if (strcasecmp(type, "multipolygon") == 0) {
1009
0
    switch (outputformat) {
1010
0
    case OWS_GML2:
1011
0
      return "MultiPolygonPropertyType";
1012
0
    case OWS_GML3:
1013
0
    case OWS_GML32:
1014
0
      return "MultiSurfacePropertyType";
1015
0
    }
1016
0
  }
1017
1018
0
  return "???unknown???";
1019
0
}
1020
1021
static void msWFSWriteGeometryElement(FILE *stream,
1022
                                      gmlGeometryListObj *geometryList,
1023
                                      OWSGMLVersion outputformat,
1024
0
                                      const char *tab) {
1025
0
  int i;
1026
0
  gmlGeometryObj *geometry = NULL;
1027
1028
0
  if (!stream || !tab)
1029
0
    return;
1030
0
  if (geometryList->numgeometries == 1 &&
1031
0
      strcasecmp(geometryList->geometries[0].name, "none") == 0)
1032
0
    return;
1033
1034
0
  if (geometryList->numgeometries == 1) {
1035
0
    geometry = &(geometryList->geometries[0]);
1036
0
    msIO_fprintf(
1037
0
        stream, "%s<element name=\"%s\" type=\"gml:%s\" minOccurs=\"%d\"", tab,
1038
0
        geometry->name, msWFSGetGeometryType(geometry->type, outputformat),
1039
0
        geometry->occurmin);
1040
0
    if (geometry->occurmax == OWS_GML_OCCUR_UNBOUNDED)
1041
0
      msIO_fprintf(stream, " maxOccurs=\"unbounded\"/>\n");
1042
0
    else
1043
0
      msIO_fprintf(stream, " maxOccurs=\"%d\"/>\n", geometry->occurmax);
1044
0
  } else {
1045
0
    msIO_fprintf(stream, "%s<choice>\n", tab);
1046
0
    for (i = 0; i < geometryList->numgeometries; i++) {
1047
0
      geometry = &(geometryList->geometries[i]);
1048
1049
0
      msIO_fprintf(stream,
1050
0
                   "  %s<element name=\"%s\" type=\"gml:%s\" minOccurs=\"%d\"",
1051
0
                   tab, geometry->name,
1052
0
                   msWFSGetGeometryType(geometry->type, outputformat),
1053
0
                   geometry->occurmin);
1054
0
      if (geometry->occurmax == OWS_GML_OCCUR_UNBOUNDED)
1055
0
        msIO_fprintf(stream, " maxOccurs=\"unbounded\"/>\n");
1056
0
      else
1057
0
        msIO_fprintf(stream, " maxOccurs=\"%d\"/>\n", geometry->occurmax);
1058
0
    }
1059
0
    msIO_fprintf(stream, "%s</choice>\n", tab);
1060
0
  }
1061
1062
0
  return;
1063
0
}
1064
1065
static OWSGMLVersion
1066
0
msWFSGetGMLVersionFromSchemaVersion(WFSSchemaVersion outputformat) {
1067
0
  switch (outputformat) {
1068
0
  case OWS_DEFAULT_SCHEMA:
1069
0
    return OWS_GML2;
1070
0
  case OWS_SFE_SCHEMA:
1071
0
    return OWS_GML3;
1072
0
  case OWS_GML32_SFE_SCHEMA:
1073
0
    return OWS_GML32;
1074
0
  }
1075
0
  return OWS_GML2;
1076
0
}
1077
1078
static void msWFSSchemaWriteGeometryElement(FILE *stream,
1079
                                            gmlGeometryListObj *geometryList,
1080
                                            WFSSchemaVersion outputformat,
1081
0
                                            const char *tab) {
1082
0
  OWSGMLVersion gmlversion = msWFSGetGMLVersionFromSchemaVersion(outputformat);
1083
0
  msWFSWriteGeometryElement(stream, geometryList, gmlversion, tab);
1084
0
}
1085
1086
0
static const char *msWFSMapServTypeToXMLType(const char *type) {
1087
0
  const char *element_type = "string";
1088
  /* Map from MapServer types to XSD types */
1089
0
  if (strcasecmp(type, "Integer") == 0)
1090
0
    element_type = "integer";
1091
  /* Note : xs:int and xs:integer differ */
1092
0
  else if (EQUAL(type, "int"))
1093
0
    element_type = "int";
1094
0
  if (strcasecmp(type, "Long") == 0) /* 64bit integer */
1095
0
    element_type = "long";
1096
0
  else if (
1097
0
      EQUAL(type, "Real") ||
1098
0
      EQUAL(type,
1099
0
            "double") /* just in case someone provided the xsd type directly */)
1100
0
    element_type = "double";
1101
0
  else if (EQUAL(type, "Character"))
1102
0
    element_type = "string";
1103
0
  else if (EQUAL(type, "Date"))
1104
0
    element_type = "date";
1105
0
  else if (EQUAL(type, "Time"))
1106
0
    element_type = "time";
1107
0
  else if (EQUAL(type, "DateTime"))
1108
0
    element_type = "dateTime";
1109
0
  else if (EQUAL(type, "Boolean"))
1110
0
    element_type = "boolean";
1111
0
  return element_type;
1112
0
}
1113
1114
static void msWFSWriteItemElement(FILE *stream, gmlItemObj *item,
1115
                                  const char *tab,
1116
                                  WFSSchemaVersion outputformat,
1117
0
                                  int is_nillable) {
1118
0
  const char *element_name;
1119
0
  const char *element_type = "string";
1120
0
  const char *pszMinOccurs = "";
1121
0
  const char *pszNillable = "";
1122
1123
0
  if (!stream || !item || !tab)
1124
0
    return;
1125
0
  if (!item->visible)
1126
0
    return; /* not exposing this attribute */
1127
0
  if (item->_template)
1128
0
    return; /* can't adequately deal with templated items yet */
1129
1130
0
  if (item->alias) /* TODO: what about name spaces embedded in the alias? */
1131
0
    element_name = item->alias;
1132
0
  else
1133
0
    element_name = item->name;
1134
1135
0
  if (item->type) {
1136
0
    if (outputformat == OWS_GML32_SFE_SCHEMA &&
1137
0
        (EQUAL(item->type, "Date") || EQUAL(item->type, "Time") ||
1138
0
         EQUAL(item->type, "DateTime")))
1139
0
      element_type = "gml:TimeInstantType";
1140
0
    else
1141
0
      element_type = msWFSMapServTypeToXMLType(item->type);
1142
0
  }
1143
1144
0
  if (item->minOccurs == 0)
1145
0
    pszMinOccurs = " minOccurs=\"0\"";
1146
1147
0
  if (is_nillable)
1148
0
    pszNillable = " nillable=\"true\"";
1149
1150
0
  msIO_fprintf(stream, "%s<element name=\"%s\"%s%s type=\"%s\"/>\n", tab,
1151
0
               element_name, pszMinOccurs, pszNillable, element_type);
1152
1153
0
  return;
1154
0
}
1155
1156
static void msWFSWriteConstantElement(FILE *stream, gmlConstantObj *constant,
1157
0
                                      const char *tab) {
1158
0
  const char *element_type = "string";
1159
1160
0
  if (!stream || !constant || !tab)
1161
0
    return;
1162
1163
0
  if (constant->type)
1164
0
    element_type = msWFSMapServTypeToXMLType(constant->type);
1165
1166
0
  msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s\"/>\n", tab,
1167
0
               constant->name, element_type);
1168
1169
0
  return;
1170
0
}
1171
1172
static void msWFSWriteGroupElement(FILE *stream, gmlGroupObj *group,
1173
0
                                   const char *tab, const char *_namespace) {
1174
0
  if (group->type)
1175
0
    msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s:%s\"/>\n", tab,
1176
0
                 group->name, _namespace, group->type);
1177
0
  else
1178
0
    msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s:%sType\"/>\n", tab,
1179
0
                 group->name, _namespace, group->name);
1180
1181
0
  return;
1182
0
}
1183
1184
static void msWFSWriteGroupElementType(FILE *stream, gmlGroupObj *group,
1185
                                       gmlItemListObj *itemList,
1186
                                       gmlConstantListObj *constantList,
1187
                                       const char *tab,
1188
0
                                       WFSSchemaVersion outputformat) {
1189
0
  int i, j;
1190
1191
0
  gmlItemObj *item = NULL;
1192
0
  gmlConstantObj *constant = NULL;
1193
1194
  /* setup the element tab */
1195
0
  std::string element_tab(tab);
1196
0
  element_tab += "    ";
1197
1198
0
  if (group->type)
1199
0
    msIO_fprintf(stream, "%s<complexType name=\"%s\">\n", tab, group->type);
1200
0
  else
1201
0
    msIO_fprintf(stream, "%s<complexType name=\"%sType\">\n", tab, group->name);
1202
1203
0
  msIO_fprintf(stream, "%s  <sequence>\n", tab);
1204
1205
  /* now the items/constants (e.g. elements) in the group */
1206
0
  for (i = 0; i < group->numitems; i++) {
1207
0
    for (j = 0; j < constantList->numconstants;
1208
0
         j++) { /* find the right gmlConstantObj */
1209
0
      constant = &(constantList->constants[j]);
1210
0
      if (strcasecmp(constant->name, group->items[i]) == 0) {
1211
0
        msWFSWriteConstantElement(stream, constant, element_tab.c_str());
1212
0
        break;
1213
0
      }
1214
0
    }
1215
0
    if (j != constantList->numconstants)
1216
0
      continue;                                /* found this item */
1217
0
    for (j = 0; j < itemList->numitems; j++) { /* find the right gmlItemObj */
1218
0
      item = &(itemList->items[j]);
1219
0
      if (strcasecmp(item->name, group->items[i]) == 0) {
1220
0
        msWFSWriteItemElement(stream, item, element_tab.c_str(), outputformat,
1221
0
                              MS_FALSE);
1222
0
        break;
1223
0
      }
1224
0
    }
1225
0
  }
1226
1227
0
  msIO_fprintf(stream, "%s  </sequence>\n", tab);
1228
0
  msIO_fprintf(stream, "%s</complexType>\n", tab);
1229
1230
0
  return;
1231
0
}
1232
1233
0
static const char *msWFSGetGMLSchemaLocation(OWSGMLVersion outputformat) {
1234
0
  switch (outputformat) {
1235
0
  case OWS_GML2:
1236
0
    return MS_OWSCOMMON_GML_212_SCHEMA_LOCATION;
1237
0
  case OWS_GML3:
1238
0
    return MS_OWSCOMMON_GML_311_SCHEMA_LOCATION;
1239
0
  case OWS_GML32:
1240
0
    return MS_OWSCOMMON_GML_321_SCHEMA_LOCATION;
1241
0
  }
1242
0
  return "/unknown.xsd";
1243
0
}
1244
1245
0
static const char *msWFSGetGMLNamespaceURI(WFSSchemaVersion outputformat) {
1246
0
  switch (outputformat) {
1247
0
  case OWS_DEFAULT_SCHEMA:
1248
0
    return MS_OWSCOMMON_GML_NAMESPACE_URI;
1249
0
  case OWS_SFE_SCHEMA:
1250
0
    return MS_OWSCOMMON_GML_NAMESPACE_URI;
1251
0
  case OWS_GML32_SFE_SCHEMA:
1252
0
    return MS_OWSCOMMON_GML_32_NAMESPACE_URI;
1253
0
  }
1254
0
  return "http://unknown";
1255
0
}
1256
1257
static const char *
1258
0
msWFSGetGMLNamespaceURIFromGMLVersion(OWSGMLVersion outputformat) {
1259
0
  switch (outputformat) {
1260
0
  case OWS_GML2:
1261
0
    return MS_OWSCOMMON_GML_NAMESPACE_URI;
1262
0
  case OWS_GML3:
1263
0
    return MS_OWSCOMMON_GML_NAMESPACE_URI;
1264
0
  case OWS_GML32:
1265
0
    return MS_OWSCOMMON_GML_32_NAMESPACE_URI;
1266
0
  }
1267
0
  return "http://unknown";
1268
0
}
1269
1270
0
static void msWFS_NS_printf(const char *prefix, const char *uri) {
1271
0
  if (prefix == NULL)
1272
0
    msIO_printf("   xmlns=\"%s\"\n", uri);
1273
0
  else
1274
0
    msIO_printf("   xmlns:%s=\"%s\"\n", prefix, uri);
1275
0
}
1276
1277
static void
1278
0
msWFSPrintAdditionalNamespaces(const gmlNamespaceListObj *namespaceList) {
1279
0
  int i;
1280
  /* any additional namespaces */
1281
0
  for (i = 0; i < namespaceList->numnamespaces; i++) {
1282
0
    if (namespaceList->namespaces[i].uri) {
1283
0
      char *uri_encoded = NULL;
1284
1285
0
      uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
1286
0
      msWFS_NS_printf(namespaceList->namespaces[i].prefix, uri_encoded);
1287
0
      msFree(uri_encoded);
1288
0
    }
1289
0
  }
1290
0
}
1291
1292
0
static const char *msWFSStripNS(const char *name) {
1293
0
  const char *pszColon = strchr(name, ':');
1294
0
  const char *pszSlash = strchr(name, '/');
1295
0
  if (pszColon && (pszSlash == NULL || pszColon < pszSlash))
1296
0
    return pszColon + 1;
1297
0
  return name;
1298
0
}
1299
1300
/*
1301
** msWFSDescribeFeatureType()
1302
*/
1303
static int msWFSDescribeFeatureType(mapObj *map, wfsParamsObj *paramsObj,
1304
                                    owsRequestObj *ows_request,
1305
0
                                    int nWFSVersion) {
1306
0
  int i, numlayers = 0;
1307
0
  char **layers = NULL;
1308
1309
0
  const char *value;
1310
0
  const char *user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
1311
0
  const char *user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
1312
0
  char *user_namespace_uri_encoded = NULL;
1313
0
  const char *collection_name = OWS_WFS_FEATURE_COLLECTION_NAME;
1314
0
  char *encoded;
1315
1316
0
  WFSSchemaVersion outputformat =
1317
0
      OWS_DEFAULT_SCHEMA; /* default output is GML 2.1 compliant schema*/
1318
1319
0
  gmlNamespaceListObj *namespaceList =
1320
0
      NULL; /* for external application schema support */
1321
0
  char *mimetype = NULL;
1322
1323
0
  if (paramsObj->pszTypeName && numlayers == 0) {
1324
    /* Parse comma-delimited list of type names (layers) */
1325
0
    layers = msStringSplit(paramsObj->pszTypeName, ',', &numlayers);
1326
0
    if (numlayers > 0) {
1327
      /* strip namespace if there is one :ex TYPENAME=cdf:Other */
1328
0
      for (i = 0; i < numlayers; i++) {
1329
0
        char *pszTmp = msStrdup(msWFSStripNS(layers[i]));
1330
0
        free(layers[i]);
1331
0
        layers[i] = pszTmp;
1332
0
      }
1333
0
    }
1334
0
  }
1335
1336
0
  if (paramsObj->pszOutputFormat) {
1337
0
    if (strcasecmp(paramsObj->pszOutputFormat, "XMLSCHEMA") == 0 ||
1338
0
        strstr(paramsObj->pszOutputFormat, "gml/2") != NULL) {
1339
0
      mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/2.1.2");
1340
0
      outputformat = OWS_DEFAULT_SCHEMA;
1341
0
    } else if (strcasecmp(paramsObj->pszOutputFormat, "SFE_XMLSCHEMA") == 0 ||
1342
0
               strstr(paramsObj->pszOutputFormat, "gml/3.1") != NULL) {
1343
0
      mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/3.1.1");
1344
0
      outputformat = OWS_SFE_SCHEMA;
1345
0
    } else if (strstr(paramsObj->pszOutputFormat, "gml/3.2") != NULL ||
1346
0
               strstr(paramsObj->pszOutputFormat,
1347
0
                      "application/gml+xml; version=3.2") != NULL) {
1348
0
      mimetype = msEncodeHTMLEntities("application/gml+xml; version=3.2");
1349
0
      outputformat = OWS_GML32_SFE_SCHEMA;
1350
0
    } else {
1351
0
      msSetError(MS_WFSERR,
1352
0
                 "Unsupported DescribeFeatureType outputFormat (%s).",
1353
0
                 "msWFSDescribeFeatureType()", paramsObj->pszOutputFormat);
1354
0
      if (layers)
1355
0
        msFreeCharArray(layers, numlayers);
1356
0
      msFree(mimetype);
1357
0
      return msWFSException(map, "outputformat",
1358
0
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1359
0
                            paramsObj->pszVersion);
1360
0
    }
1361
0
  }
1362
  /* If no outputFormat explicitly asked, use a sensible default for the WFS
1363
     version */
1364
0
  else {
1365
0
    switch (nWFSVersion) {
1366
0
    case OWS_1_0_0:
1367
0
    default:
1368
0
      mimetype = msEncodeHTMLEntities(
1369
0
          "text/xml"); /* ERO: why not "text/xml; subtype=gml/2.1.2" ? */
1370
0
      break;
1371
1372
0
    case OWS_1_1_0:
1373
0
      mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/3.1.1");
1374
0
      outputformat = OWS_SFE_SCHEMA;
1375
0
      break;
1376
1377
0
    case OWS_2_0_0:
1378
0
      mimetype = msEncodeHTMLEntities("application/gml+xml; version=3.2");
1379
0
      outputformat = OWS_GML32_SFE_SCHEMA;
1380
0
      break;
1381
0
    }
1382
0
  }
1383
1384
  /* Validate layers */
1385
0
  if (numlayers > 0) {
1386
0
    for (i = 0; i < numlayers; i++) {
1387
0
      layerObj *lp = msWFSGetLayerByName(map, ows_request, layers[i]);
1388
0
      if (lp == NULL) {
1389
0
        msSetError(MS_WFSERR,
1390
0
                   "Invalid typename (%s). A layer might be disabled for \
1391
0
this request. Check wfs/ows_enable_request settings.",
1392
0
                   "msWFSDescribeFeatureType()",
1393
0
                   layers[i]); /* paramsObj->pszTypeName); */
1394
0
        msFreeCharArray(layers, numlayers);
1395
0
        msFree(mimetype);
1396
0
        return msWFSException(map, "typename",
1397
0
                              MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1398
0
                              paramsObj->pszVersion);
1399
0
      }
1400
0
    }
1401
0
  }
1402
1403
  /*
1404
  ** retrieve any necessary external namespace/schema configuration information
1405
  */
1406
0
  namespaceList = msGMLGetNamespaces(&(map->web), "G");
1407
0
  if (namespaceList == NULL) {
1408
0
    msSetError(MS_MISCERR, "Unable to populate namespace list",
1409
0
               "msWFSDescribeFeatureType()");
1410
0
    return MS_FAILURE;
1411
0
  }
1412
1413
  /*
1414
  ** DescribeFeatureType response
1415
  */
1416
1417
0
  msIO_setHeader("Content-Type", "%s; charset=UTF-8", mimetype);
1418
0
  msIO_sendHeaders();
1419
1420
0
  if (mimetype)
1421
0
    msFree(mimetype);
1422
1423
0
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
1424
1425
0
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
1426
0
  if (value)
1427
0
    user_namespace_uri = value;
1428
0
  user_namespace_uri_encoded = msEncodeHTMLEntities(user_namespace_uri);
1429
1430
0
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
1431
0
  if (value)
1432
0
    user_namespace_prefix = value;
1433
0
  if (user_namespace_prefix != NULL &&
1434
0
      msIsXMLTagValid(user_namespace_prefix) == MS_FALSE)
1435
0
    msIO_printf(
1436
0
        "<!-- WARNING: The value '%s' is not valid XML namespace. -->\n",
1437
0
        user_namespace_prefix);
1438
1439
0
  msIO_printf("<schema\n"
1440
0
              "   targetNamespace=\"%s\" \n"
1441
0
              "   xmlns:%s=\"%s\" \n",
1442
0
              user_namespace_uri_encoded, user_namespace_prefix,
1443
0
              user_namespace_uri_encoded);
1444
0
  if (nWFSVersion < OWS_2_0_0)
1445
0
    msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
1446
0
                    MS_OWSCOMMON_OGC_NAMESPACE_URI);
1447
0
  msWFS_NS_printf("xsd", MS_OWSCOMMON_W3C_XS_NAMESPACE_URI);
1448
0
  msWFS_NS_printf(NULL, MS_OWSCOMMON_W3C_XS_NAMESPACE_URI);
1449
0
  msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1450
0
                  msWFSGetGMLNamespaceURI(outputformat));
1451
1452
0
  msWFSPrintAdditionalNamespaces(namespaceList);
1453
1454
0
  msIO_printf("   elementFormDefault=\"qualified\" version=\"0.1\" >\n");
1455
1456
0
  encoded = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
1457
1458
0
  msIO_printf("\n  <import namespace=\"%s\"\n"
1459
0
              "          schemaLocation=\"%s%s\" />\n",
1460
0
              msWFSGetGMLNamespaceURI(outputformat), encoded,
1461
0
              msWFSGetGMLSchemaLocation(
1462
0
                  msWFSGetGMLVersionFromSchemaVersion(outputformat)));
1463
1464
0
  msFree(encoded);
1465
1466
  /* any additional namespace includes */
1467
0
  for (i = 0; i < namespaceList->numnamespaces; i++) {
1468
0
    if (namespaceList->namespaces[i].uri &&
1469
0
        namespaceList->namespaces[i].schemalocation) {
1470
0
      char *schema_location_encoded = NULL, *uri_encoded = NULL;
1471
1472
0
      uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
1473
0
      schema_location_encoded =
1474
0
          msEncodeHTMLEntities(namespaceList->namespaces[i].schemalocation);
1475
0
      msIO_printf("\n  <import namespace=\"%s\"\n schemaLocation=\"%s\" />\n",
1476
0
                  uri_encoded, schema_location_encoded);
1477
0
      msFree(uri_encoded);
1478
0
      msFree(schema_location_encoded);
1479
0
    }
1480
0
  }
1481
1482
  /* output definition for the default feature container, can't use
1483
     wfs:FeatureCollection with GML3: kept here so that the behavior with
1484
     wfs1.0 and gml3 output is preserved. We can use the wfs:FeatureCollection
1485
     for wfs1.1*/
1486
0
  if (outputformat == OWS_SFE_SCHEMA && nWFSVersion == OWS_1_0_0) {
1487
0
    value =
1488
0
        msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection");
1489
0
    if (value)
1490
0
      collection_name = value;
1491
1492
0
    msIO_printf("  <element name=\"%s\" type=\"%s:%sType\" "
1493
0
                "substitutionGroup=\"gml:_FeatureCollection\"/>\n",
1494
0
                collection_name, user_namespace_prefix, collection_name);
1495
0
    msIO_printf("  <complexType name=\"%sType\">\n", collection_name);
1496
0
    msIO_printf("    <complexContent>\n");
1497
0
    msIO_printf(
1498
0
        "      <extension base=\"gml:AbstractFeatureCollectionType\">\n");
1499
0
    msIO_printf("        <attribute name=\"version\" type=\"string\" "
1500
0
                "use=\"required\" fixed=\"1.0.0\"/>\n");
1501
0
    msIO_printf("      </extension>\n");
1502
0
    msIO_printf("    </complexContent>\n");
1503
0
    msIO_printf("  </complexType>\n");
1504
0
  }
1505
1506
  /*
1507
  ** loop through layers
1508
  */
1509
0
  for (i = 0; i < map->numlayers; i++) {
1510
0
    layerObj *lp;
1511
0
    int j, bFound = 0;
1512
1513
0
    lp = GET_LAYER(map, i);
1514
1515
0
    for (j = 0; j < numlayers && !bFound; j++) {
1516
0
      if (lp->name && strcasecmp(lp->name, layers[j]) == 0)
1517
0
        bFound = 1;
1518
0
    }
1519
1520
0
    if ((numlayers == 0 || bFound) && msWFSIsLayerAllowed(lp, ows_request)) {
1521
1522
      /*
1523
      ** OK, describe this layer IF you can open it and retrieve items
1524
      */
1525
0
      if (msLayerOpen(lp) == MS_SUCCESS) {
1526
0
        if (msLayerGetItems(lp) == MS_SUCCESS) {
1527
0
          int k;
1528
0
          gmlGroupListObj *groupList = NULL;
1529
0
          gmlItemListObj *itemList = NULL;
1530
0
          gmlConstantListObj *constantList = NULL;
1531
0
          gmlGeometryListObj *geometryList = NULL;
1532
0
          gmlItemObj *item = NULL;
1533
0
          gmlConstantObj *constant = NULL;
1534
0
          char *encoded_name = NULL;
1535
1536
0
          const char *layer_namespace_prefix;
1537
0
          const char *substitution_group;
1538
0
          char *encoded_type = NULL;
1539
1540
0
          itemList = msGMLGetItems(lp, "G"); /* GML-related metadata */
1541
0
          constantList = msGMLGetConstants(lp, "G");
1542
0
          groupList = msGMLGetGroups(lp, "G");
1543
0
          geometryList = msGMLGetGeometries(lp, "GFO", MS_TRUE);
1544
0
          if (itemList == NULL || constantList == NULL || groupList == NULL ||
1545
0
              geometryList == NULL) {
1546
0
            msSetError(MS_MISCERR,
1547
0
                       "Unable to populate item and group metadata structures",
1548
0
                       "msWFSDescribeFeatureType()");
1549
0
            return MS_FAILURE;
1550
0
          }
1551
1552
0
          value =
1553
0
              msOWSLookupMetadata(&(lp->metadata), "OFG", "namespace_prefix");
1554
0
          if (value)
1555
0
            layer_namespace_prefix = value;
1556
0
          else
1557
0
            layer_namespace_prefix = user_namespace_prefix;
1558
1559
0
          encoded_name = msEncodeHTMLEntities(lp->name);
1560
0
          value = msOWSLookupMetadata(&(lp->metadata), "F", "layer_type");
1561
0
          if (value) {
1562
0
            encoded_type = msEncodeHTMLEntities(value);
1563
0
          } else {
1564
0
            size_t sz = strlen(encoded_name) + strlen("Type") + 1;
1565
0
            encoded_type = (char *)msSmallMalloc(sz);
1566
0
            strlcpy(encoded_type, encoded_name, sz);
1567
0
            strlcat(encoded_type, "Type", sz);
1568
0
          }
1569
1570
0
          switch (outputformat) {
1571
0
          case OWS_DEFAULT_SCHEMA: /* default GML 2.1.x schema */
1572
0
          case OWS_SFE_SCHEMA:     /* reference GML 3.1.1 schema */
1573
0
          default:
1574
0
            substitution_group = "gml:_Feature";
1575
0
            break;
1576
1577
0
          case OWS_GML32_SFE_SCHEMA: /* reference GML 3.2.1 schema */
1578
0
            substitution_group = "gml:AbstractFeature";
1579
0
            break;
1580
0
          }
1581
1582
0
          msIO_printf("\n"
1583
0
                      "  <element name=\"%s\" \n"
1584
0
                      "           type=\"%s:%s\" \n"
1585
0
                      "           substitutionGroup=\"%s\" />\n\n",
1586
0
                      encoded_name, layer_namespace_prefix, encoded_type,
1587
0
                      substitution_group);
1588
0
          msFree(encoded_type);
1589
1590
0
          if (strcmp(layer_namespace_prefix, user_namespace_prefix) != 0) {
1591
0
            msFree(encoded_name);
1592
0
            msGMLFreeItems(itemList);
1593
0
            msGMLFreeConstants(constantList);
1594
0
            msGMLFreeGroups(groupList);
1595
0
            msGMLFreeGeometries(geometryList);
1596
0
            continue; /* the rest is defined in an external schema */
1597
0
          }
1598
1599
0
          msIO_printf("  <complexType name=\"%sType\">\n", encoded_name);
1600
0
          msIO_printf("    <complexContent>\n");
1601
0
          msIO_printf("      <extension base=\"gml:AbstractFeatureType\">\n");
1602
0
          msIO_printf("        <sequence>\n");
1603
1604
          /* write the geometry schema element(s) */
1605
0
          msWFSSchemaWriteGeometryElement(stdout, geometryList, outputformat,
1606
0
                                          "          ");
1607
1608
          /* write the constant-based schema elements */
1609
0
          for (k = 0; k < constantList->numconstants; k++) {
1610
0
            constant = &(constantList->constants[k]);
1611
0
            if (msItemInGroups(constant->name, groupList) == MS_FALSE)
1612
0
              msWFSWriteConstantElement(stdout, constant, "          ");
1613
0
          }
1614
1615
          /* write the item-based schema elements */
1616
0
          for (k = 0; k < itemList->numitems; k++) {
1617
0
            item = &(itemList->items[k]);
1618
0
            if (msItemInGroups(item->name, groupList) == MS_FALSE) {
1619
0
              int nillable = MS_FALSE;
1620
0
              char mdname[256];
1621
0
              const char *pszNillable;
1622
0
              snprintf(mdname, sizeof(mdname), "%s_nillable", item->name);
1623
0
              pszNillable = msOWSLookupMetadata(&(lp->metadata), "G", mdname);
1624
0
              if (pszNillable && strcasecmp(pszNillable, "true") == 0)
1625
0
                nillable = MS_TRUE;
1626
0
              msWFSWriteItemElement(stdout, item, "          ", outputformat,
1627
0
                                    nillable);
1628
0
            }
1629
0
          }
1630
1631
0
          for (k = 0; k < groupList->numgroups; k++)
1632
0
            msWFSWriteGroupElement(stdout, &(groupList->groups[k]),
1633
0
                                   "          ", user_namespace_prefix);
1634
1635
0
          msIO_printf("        </sequence>\n");
1636
0
          msIO_printf("      </extension>\n");
1637
0
          msIO_printf("    </complexContent>\n");
1638
0
          msIO_printf("  </complexType>\n");
1639
1640
          /* any group types */
1641
0
          for (k = 0; k < groupList->numgroups; k++)
1642
0
            msWFSWriteGroupElementType(stdout, &(groupList->groups[k]),
1643
0
                                       itemList, constantList, "  ",
1644
0
                                       outputformat);
1645
1646
0
          msGMLFreeItems(itemList);
1647
0
          msGMLFreeConstants(constantList);
1648
0
          msGMLFreeGroups(groupList);
1649
0
          msGMLFreeGeometries(geometryList);
1650
1651
0
          msFree(encoded_name);
1652
0
        }
1653
1654
0
        msLayerClose(lp);
1655
0
      } else {
1656
0
        msIO_printf("\n\n<!-- ERROR: Failed opening layer %s -->\n\n",
1657
0
                    lp->name);
1658
0
      }
1659
0
    }
1660
0
  }
1661
1662
  /*
1663
  ** Done!
1664
  */
1665
0
  msIO_printf("\n</schema>\n");
1666
1667
0
  msFree(user_namespace_uri_encoded);
1668
1669
0
  if (layers)
1670
0
    msFreeCharArray(layers, numlayers);
1671
1672
0
  msGMLFreeNamespaces(namespaceList);
1673
1674
0
  return MS_SUCCESS;
1675
0
}
1676
1677
/*
1678
** msWFSGetFeature_GMLPreamble()
1679
**
1680
** Generate the GML preamble up to the first feature for the builtin
1681
** WFS GML support.
1682
*/
1683
1684
typedef struct {
1685
  const char *user_namespace_prefix;
1686
  const char *user_namespace_uri;
1687
  char *user_namespace_uri_encoded;
1688
  const char *collection_name;
1689
  const char *_typename;
1690
  char *script_url, *script_url_encoded;
1691
  const char *output_mime_type;
1692
  const char *output_schema_format;
1693
} WFSGMLInfo;
1694
1695
0
static void msWFSPrintURLAndXMLEncoded(const char *str) {
1696
0
  char *url_encoded = msEncodeUrl(str);
1697
0
  char *xml_encoded = msEncodeHTMLEntities(url_encoded);
1698
0
  msIO_printf("%s", xml_encoded);
1699
0
  msFree(xml_encoded);
1700
0
  msFree(url_encoded);
1701
0
}
1702
1703
static void msWFSGetFeature_PrintBasePrevNextURI(
1704
    cgiRequestObj *req, WFSGMLInfo *gmlinfo, wfsParamsObj *paramsObj,
1705
    const char *encoded_version, const char *encoded_typename,
1706
0
    const char *encoded_mime_type) {
1707
0
  int i;
1708
0
  int bFirstArg = MS_TRUE;
1709
0
  msIO_printf("%s", gmlinfo->script_url_encoded);
1710
1711
0
  if (req->postrequest != NULL) {
1712
0
    msIO_printf("SERVICE=WFS&amp;VERSION=");
1713
0
    msIO_printf("%s", encoded_version);
1714
0
    msIO_printf("&amp;REQUEST=");
1715
0
    msIO_printf("%s", paramsObj->pszRequest);
1716
0
    msIO_printf("&amp;TYPENAMES=");
1717
0
    msIO_printf("%s", encoded_typename);
1718
0
    msIO_printf("&amp;OUTPUTFORMAT=");
1719
0
    msIO_printf("%s", encoded_mime_type);
1720
0
    if (paramsObj->pszBbox != NULL) {
1721
0
      msIO_printf("&amp;BBOX=");
1722
0
      msWFSPrintURLAndXMLEncoded(paramsObj->pszBbox);
1723
0
    }
1724
0
    if (paramsObj->pszSrs != NULL) {
1725
0
      msIO_printf("&amp;SRSNAME=");
1726
0
      msWFSPrintURLAndXMLEncoded(paramsObj->pszSrs);
1727
0
    }
1728
0
    if (paramsObj->pszFilter != NULL) {
1729
0
      msIO_printf("&amp;FILTER=");
1730
0
      msWFSPrintURLAndXMLEncoded(paramsObj->pszFilter);
1731
0
    }
1732
0
    if (paramsObj->pszPropertyName != NULL) {
1733
0
      msIO_printf("&amp;PROPERTYNAME=");
1734
0
      msWFSPrintURLAndXMLEncoded(paramsObj->pszPropertyName);
1735
0
    }
1736
0
    if (paramsObj->pszValueReference != NULL) {
1737
0
      msIO_printf("&amp;VALUEREFERENCE=");
1738
0
      msWFSPrintURLAndXMLEncoded(paramsObj->pszValueReference);
1739
0
    }
1740
0
    if (paramsObj->pszSortBy != NULL) {
1741
0
      msIO_printf("&amp;SORTBY=");
1742
0
      msWFSPrintURLAndXMLEncoded(paramsObj->pszSortBy);
1743
0
    }
1744
0
    if (paramsObj->nMaxFeatures >= 0)
1745
0
      msIO_printf("&amp;COUNT=%d", paramsObj->nMaxFeatures);
1746
0
  } else {
1747
0
    for (i = 0; i < req->NumParams; i++) {
1748
0
      if (req->ParamNames[i] && req->ParamValues[i] &&
1749
0
          strcasecmp(req->ParamNames[i], "MAP") != 0 &&
1750
0
          strcasecmp(req->ParamNames[i], "STARTINDEX") != 0 &&
1751
0
          strcasecmp(req->ParamNames[i], "RESULTTYPE") != 0) {
1752
0
        if (!bFirstArg)
1753
0
          msIO_printf("&amp;");
1754
0
        bFirstArg = MS_FALSE;
1755
0
        msIO_printf("%s=", req->ParamNames[i]);
1756
0
        msWFSPrintURLAndXMLEncoded(req->ParamValues[i]);
1757
0
      }
1758
0
    }
1759
0
  }
1760
0
}
1761
1762
static void msWFSGetFeature_GetTimeStamp(char *timestring,
1763
0
                                         size_t timestringlen) {
1764
0
  struct tm *now;
1765
0
  time_t tim = time(NULL);
1766
1767
0
  now = localtime(&tim);
1768
1769
0
  snprintf(timestring, timestringlen, "%d-%02d-%02dT%02d:%02d:%02d",
1770
0
           now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour,
1771
0
           now->tm_min, now->tm_sec);
1772
0
}
1773
1774
static int msWFSGetFeature_GMLPreamble(
1775
    mapObj *map, cgiRequestObj *req, WFSGMLInfo *gmlinfo,
1776
    wfsParamsObj *paramsObj, OWSGMLVersion outputformat, int iResultTypeHits,
1777
    int iNumberOfFeatures, int nMatchingFeatures, int maxfeatures,
1778
    int bHasNextFeatures, int nWFSVersion)
1779
1780
0
{
1781
0
  const char *value;
1782
0
  char *encoded_version, *encoded_typename, *encoded_schema;
1783
0
  gmlNamespaceListObj *namespaceList =
1784
0
      NULL; /* for external application schema support */
1785
0
  char timestring[100];
1786
0
  timestring[0] = '\0';
1787
1788
0
  namespaceList = msGMLGetNamespaces(&(map->web), "G");
1789
0
  if (namespaceList == NULL) {
1790
0
    msSetError(MS_MISCERR, "Unable to populate namespace list",
1791
0
               "msWFSGetFeature_GMLPreamble()");
1792
0
    return MS_FAILURE;
1793
0
  }
1794
1795
  /*
1796
  ** Establish script_url.
1797
  */
1798
1799
0
  if ((gmlinfo->script_url =
1800
0
           msOWSGetOnlineResource(map, "FO", "onlineresource", req)) == NULL ||
1801
0
      (gmlinfo->script_url_encoded =
1802
0
           msEncodeHTMLEntities(gmlinfo->script_url)) == NULL) {
1803
0
    msSetError(MS_WFSERR, "Server URL not found", "msWFSGetFeature()");
1804
0
    msGMLFreeNamespaces(namespaceList);
1805
0
    return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
1806
0
                          paramsObj->pszVersion);
1807
0
  }
1808
1809
  /*
1810
  ** Write encoding.
1811
  */
1812
0
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
1813
1814
0
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
1815
0
  if (value)
1816
0
    gmlinfo->user_namespace_uri = value;
1817
0
  gmlinfo->user_namespace_uri_encoded =
1818
0
      msEncodeHTMLEntities(gmlinfo->user_namespace_uri);
1819
1820
0
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
1821
0
  if (value)
1822
0
    gmlinfo->user_namespace_prefix = value;
1823
1824
0
  if (gmlinfo->user_namespace_prefix != NULL &&
1825
0
      msIsXMLTagValid(gmlinfo->user_namespace_prefix) == MS_FALSE)
1826
0
    msIO_printf(
1827
0
        "<!-- WARNING: The value '%s' is not valid XML namespace. -->\n",
1828
0
        gmlinfo->user_namespace_prefix);
1829
1830
0
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection");
1831
0
  if (value)
1832
0
    gmlinfo->collection_name = value;
1833
1834
0
  encoded_version = msEncodeHTMLEntities(paramsObj->pszVersion);
1835
0
  encoded_typename = msEncodeHTMLEntities(gmlinfo->_typename);
1836
0
  encoded_schema = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
1837
1838
0
  if (nWFSVersion >= OWS_2_0_0) {
1839
0
    int nNextStartIndex;
1840
0
    char *tmp;
1841
0
    char *encoded_mime_type;
1842
0
    int bAbleToPrintPreviousOrNext = MS_TRUE;
1843
1844
0
    tmp = msEncodeUrl(gmlinfo->output_mime_type);
1845
0
    encoded_mime_type = msEncodeHTMLEntities(tmp);
1846
0
    msFree(tmp);
1847
1848
0
    msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
1849
1850
0
    if (strcasecmp(paramsObj->pszRequest, "GetFeature") == 0)
1851
0
      msIO_printf("<wfs:FeatureCollection\n");
1852
0
    else
1853
0
      msIO_printf("<wfs:ValueCollection\n");
1854
1855
0
    msWFS_NS_printf(gmlinfo->user_namespace_prefix,
1856
0
                    gmlinfo->user_namespace_uri_encoded);
1857
0
    msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1858
0
                    msWFSGetGMLNamespaceURIFromGMLVersion(outputformat));
1859
0
    msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
1860
0
                    MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
1861
0
    msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
1862
0
                    MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
1863
0
    msWFSPrintAdditionalNamespaces(namespaceList);
1864
1865
0
    msIO_printf("   xsi:schemaLocation=\"%s "
1866
0
                "%sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&"
1867
0
                "amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s "
1868
0
                "%s %s%s "
1869
0
                "%s %s%s\"\n",
1870
0
                gmlinfo->user_namespace_uri_encoded,
1871
0
                gmlinfo->script_url_encoded, encoded_version, encoded_typename,
1872
0
                gmlinfo->output_schema_format,
1873
1874
0
                MS_OWSCOMMON_WFS_20_NAMESPACE_URI, encoded_schema,
1875
0
                MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION,
1876
1877
0
                msWFSGetGMLNamespaceURIFromGMLVersion(outputformat),
1878
0
                encoded_schema, msWFSGetGMLSchemaLocation(outputformat));
1879
1880
0
    msIO_printf("   timeStamp=\"%s\" numberMatched=\"", timestring);
1881
0
    if (nMatchingFeatures < 0) {
1882
      /* If we don't know the exact number, at least return something */
1883
      /* equivalent to what we would return with WFS 1.1, otherwise */
1884
      /* resultType=hits would not return anything useful to the client. */
1885
0
      if (iResultTypeHits == MS_TRUE)
1886
0
        msIO_printf("%d", iNumberOfFeatures);
1887
0
      else
1888
0
        msIO_printf("unknown");
1889
0
    } else
1890
0
      msIO_printf("%d", nMatchingFeatures);
1891
0
    msIO_printf("\" numberReturned=\"%d\"",
1892
0
                (iResultTypeHits == 1) ? 0 : iNumberOfFeatures);
1893
1894
    /* TODO: in case of a multi-layer GetFeature POST, it is difficult to build
1895
     * a */
1896
    /* valid GET KVP GetFeature/GetPropertyValue when some options are
1897
     * specified. So for now just */
1898
    /* avoid those problematic cases */
1899
0
    if (req->postrequest != NULL &&
1900
0
        (paramsObj->bHasPostStoredQuery ||
1901
0
         (strchr(encoded_typename, ',') != NULL &&
1902
0
          (paramsObj->pszFilter != NULL || paramsObj->pszPropertyName != NULL ||
1903
0
           paramsObj->pszSortBy != NULL)))) {
1904
0
      bAbleToPrintPreviousOrNext = MS_FALSE;
1905
0
    }
1906
1907
0
    if (bAbleToPrintPreviousOrNext) {
1908
0
      if (maxfeatures > 0 && iResultTypeHits != 1 &&
1909
0
          paramsObj->nStartIndex > 0 &&
1910
0
          ((nMatchingFeatures < 0 && iNumberOfFeatures > 0) ||
1911
0
           (nMatchingFeatures >= 0 &&
1912
0
            paramsObj->nStartIndex < nMatchingFeatures))) {
1913
0
        int nPrevStartIndex;
1914
1915
0
        msIO_printf("\n");
1916
0
        msIO_printf("   previous=\"");
1917
0
        msWFSGetFeature_PrintBasePrevNextURI(req, gmlinfo, paramsObj,
1918
0
                                             encoded_version, encoded_typename,
1919
0
                                             encoded_mime_type);
1920
1921
0
        nPrevStartIndex = paramsObj->nStartIndex - maxfeatures;
1922
0
        if (nPrevStartIndex > 0)
1923
0
          msIO_printf("&amp;STARTINDEX=%d", nPrevStartIndex);
1924
0
        msIO_printf("\"");
1925
0
      }
1926
1927
0
      if (iResultTypeHits != 1 && paramsObj->nStartIndex >= 0)
1928
0
        nNextStartIndex = paramsObj->nStartIndex;
1929
0
      else
1930
0
        nNextStartIndex = 0;
1931
0
      if (maxfeatures > 0 &&
1932
0
          (bHasNextFeatures ||
1933
0
           (nMatchingFeatures > 0 &&
1934
0
            (iResultTypeHits == 1 ||
1935
0
             iNumberOfFeatures + nNextStartIndex < nMatchingFeatures)))) {
1936
0
        msIO_printf("\n");
1937
0
        msIO_printf("   next=\"");
1938
0
        msWFSGetFeature_PrintBasePrevNextURI(req, gmlinfo, paramsObj,
1939
0
                                             encoded_version, encoded_typename,
1940
0
                                             encoded_mime_type);
1941
1942
0
        if (iResultTypeHits != 1)
1943
0
          nNextStartIndex += iNumberOfFeatures;
1944
1945
0
        if (nNextStartIndex > 0)
1946
0
          msIO_printf("&amp;STARTINDEX=%d", nNextStartIndex);
1947
0
        msIO_printf("\"");
1948
0
      }
1949
0
    }
1950
1951
0
    msIO_printf(">\n");
1952
1953
0
    msFree(encoded_mime_type);
1954
0
  }
1955
  /*
1956
  ** GML 2.x
1957
  */
1958
0
  else if (outputformat == OWS_GML2) { /* use a wfs:FeatureCollection */
1959
0
    msIO_printf("<wfs:FeatureCollection\n");
1960
0
    msWFS_NS_printf(gmlinfo->user_namespace_prefix,
1961
0
                    gmlinfo->user_namespace_uri_encoded);
1962
0
    msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
1963
0
                    MS_OWSCOMMON_WFS_NAMESPACE_URI);
1964
0
    msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1965
0
                    MS_OWSCOMMON_GML_NAMESPACE_URI);
1966
0
    msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
1967
0
                    MS_OWSCOMMON_OGC_NAMESPACE_URI);
1968
0
    msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
1969
0
                    MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
1970
0
    msWFSPrintAdditionalNamespaces(namespaceList);
1971
1972
    /* FIXME ? : the schemaLocation will be only valid for WFS 1.0.0 */
1973
0
    msIO_printf("   xsi:schemaLocation=\"http://www.opengis.net/wfs "
1974
0
                "%s/wfs/%s/WFS-basic.xsd \n"
1975
0
                "                       %s "
1976
0
                "%sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&"
1977
0
                "amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s\">\n",
1978
0
                encoded_schema, encoded_version,
1979
0
                gmlinfo->user_namespace_uri_encoded,
1980
0
                gmlinfo->script_url_encoded, encoded_version, encoded_typename,
1981
0
                gmlinfo->output_schema_format);
1982
0
  }
1983
1984
  /*
1985
  ** GML 3
1986
  */
1987
0
  else if (outputformat == OWS_GML3) {
1988
0
    if (nWFSVersion == OWS_1_1_0) {
1989
0
      msIO_printf("<wfs:FeatureCollection\n");
1990
0
      msWFS_NS_printf(gmlinfo->user_namespace_prefix,
1991
0
                      gmlinfo->user_namespace_uri_encoded);
1992
0
      msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1993
0
                      MS_OWSCOMMON_GML_NAMESPACE_URI);
1994
0
      msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
1995
0
                      MS_OWSCOMMON_WFS_NAMESPACE_URI);
1996
0
      msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
1997
0
                      MS_OWSCOMMON_OGC_NAMESPACE_URI);
1998
0
      msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
1999
0
                      MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
2000
0
    } else {
2001
0
      msIO_printf("<%s:%s\n"
2002
0
                  "   version=\"1.0.0\"\n",
2003
0
                  gmlinfo->user_namespace_prefix, gmlinfo->collection_name);
2004
0
      msWFS_NS_printf(gmlinfo->user_namespace_prefix,
2005
0
                      gmlinfo->user_namespace_uri_encoded);
2006
0
      msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
2007
0
                      MS_OWSCOMMON_GML_NAMESPACE_URI);
2008
0
      msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
2009
0
                      MS_OWSCOMMON_OGC_NAMESPACE_URI);
2010
0
      msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
2011
0
                      MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
2012
0
    }
2013
2014
0
    msWFSPrintAdditionalNamespaces(namespaceList);
2015
2016
0
    msIO_printf("   xsi:schemaLocation=\"%s "
2017
0
                "%sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&"
2018
0
                "amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s",
2019
0
                gmlinfo->user_namespace_uri_encoded,
2020
0
                gmlinfo->script_url_encoded, encoded_version, encoded_typename,
2021
0
                gmlinfo->output_schema_format);
2022
2023
0
    if (nWFSVersion == OWS_1_1_0) {
2024
2025
0
      msIO_printf("  %s %s%s", MS_OWSCOMMON_WFS_NAMESPACE_URI, encoded_schema,
2026
0
                  MS_OWSCOMMON_WFS_11_SCHEMA_LOCATION);
2027
2028
0
      if (iResultTypeHits == 1) {
2029
0
        msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
2030
2031
0
        msIO_printf("\" timeStamp=\"%s\" numberOfFeatures=\"%d", timestring,
2032
0
                    iNumberOfFeatures);
2033
0
      }
2034
0
    }
2035
0
    msIO_printf("\">\n");
2036
0
  }
2037
2038
0
  msFree(encoded_version);
2039
0
  msFree(encoded_schema);
2040
0
  msFree(encoded_typename);
2041
2042
0
  msGMLFreeNamespaces(namespaceList);
2043
2044
0
  return MS_SUCCESS;
2045
0
}
2046
2047
/*
2048
** msWFSGetFeature_GMLPostfix()
2049
**
2050
** Generate the GML file tail closing the collection and cleanup a bit.
2051
*/
2052
2053
static int msWFSGetFeature_GMLPostfix(WFSGMLInfo *gmlinfo,
2054
                                      OWSGMLVersion outputformat,
2055
                                      int maxfeatures, int iResultTypeHits,
2056
                                      int iNumberOfFeatures, int nWFSVersion)
2057
2058
0
{
2059
0
  if (((iNumberOfFeatures == 0) || (maxfeatures == 0)) &&
2060
0
      iResultTypeHits == 0) {
2061
2062
0
    if (nWFSVersion < OWS_2_0_0) {
2063
0
      msIO_printf("   <gml:boundedBy>\n");
2064
0
      if (outputformat == OWS_GML3 || outputformat == OWS_GML32)
2065
0
        msIO_printf("      <gml:Null>missing</gml:Null>\n");
2066
0
      else
2067
0
        msIO_printf("      <gml:null>missing</gml:null>\n");
2068
0
      msIO_printf("   </gml:boundedBy>\n");
2069
0
    }
2070
0
  }
2071
2072
0
  if (outputformat == OWS_GML2)
2073
0
    msIO_printf("</wfs:FeatureCollection>\n\n");
2074
0
  else {
2075
0
    if (nWFSVersion >= OWS_1_1_0)
2076
0
      msIO_printf("</wfs:FeatureCollection>\n\n");
2077
0
    else
2078
0
      msIO_printf("</%s:%s>\n\n", gmlinfo->user_namespace_prefix,
2079
0
                  gmlinfo->collection_name);
2080
0
  }
2081
2082
0
  return MS_SUCCESS;
2083
0
}
2084
2085
/*
2086
** msWFSBuildParamList()
2087
*/
2088
static void msWFSBuildParamList(char **ppszStrList, const char *pszValue,
2089
0
                                const char *pszSep) {
2090
0
  if (*ppszStrList == NULL)
2091
0
    *ppszStrList = msStrdup(pszValue);
2092
0
  else {
2093
0
    char *pszTmp = msStrdup(*ppszStrList);
2094
0
    size_t nSize = strlen(pszTmp) + strlen(pszSep) + strlen(pszValue) + 1;
2095
0
    *ppszStrList = (char *)msSmallRealloc(*ppszStrList, nSize);
2096
2097
0
    snprintf(*ppszStrList, nSize, "%s%s%s", pszTmp, pszSep, pszValue);
2098
0
    free(pszTmp);
2099
0
  }
2100
0
}
2101
2102
/*
2103
** msWFSTurnOffAllLayer()
2104
*/
2105
0
static void msWFSTurnOffAllLayer(mapObj *map) {
2106
0
  int j;
2107
0
  for (j = 0; j < map->numlayers; j++) {
2108
0
    layerObj *lp;
2109
0
    lp = GET_LAYER(map, j);
2110
0
    lp->status = MS_OFF;
2111
0
  }
2112
0
}
2113
2114
/*
2115
** msWFSRunFilter()
2116
*/
2117
static int msWFSRunFilter(mapObj *map, layerObj *lp,
2118
                          const wfsParamsObj *paramsObj, const char *pszFilter,
2119
0
                          int nWFSVersion) {
2120
0
  int layerWasOpened;
2121
0
  FilterEncodingNode *psNode = NULL;
2122
2123
0
  psNode = FLTParseFilterEncoding(pszFilter);
2124
2125
0
  if (!psNode) {
2126
0
    msSetError(MS_WFSERR, "Invalid or Unsupported FILTER in GetFeature : %s",
2127
0
               "msWFSGetFeature()", pszFilter);
2128
0
    return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2129
0
                          paramsObj->pszVersion);
2130
0
  }
2131
2132
  /* Starting with WFS 1.1, we need to swap coordinates of BBOX or geometry */
2133
  /* parameters in some circumstances */
2134
0
  if (nWFSVersion >= OWS_1_1_0) {
2135
0
    int bDefaultSRSNeedsAxisSwapping = MS_FALSE;
2136
0
    char *srs;
2137
0
    msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE,
2138
0
                     &srs);
2139
0
    if (!srs) {
2140
0
      msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE, &srs);
2141
0
    }
2142
0
    if (srs && strncasecmp(srs, "EPSG:", 5) == 0) {
2143
0
      bDefaultSRSNeedsAxisSwapping = msIsAxisInverted(atoi(srs + 5));
2144
0
    }
2145
0
    msFree(srs);
2146
0
    FLTDoAxisSwappingIfNecessary(map, psNode, bDefaultSRSNeedsAxisSwapping);
2147
0
  }
2148
2149
0
  layerWasOpened = msLayerIsOpen(lp);
2150
0
  if (!layerWasOpened && msLayerOpen(lp) != MS_SUCCESS) {
2151
0
    FLTFreeFilterEncodingNode(psNode);
2152
0
    return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
2153
0
                          paramsObj->pszVersion);
2154
0
  }
2155
2156
0
  FLTProcessPropertyIsNull(psNode, map, lp->index);
2157
2158
  /*preparse the filter for gml aliases*/
2159
0
  FLTPreParseFilterForAliasAndGroup(psNode, map, lp->index, "G");
2160
2161
  /* Check that FeatureId filters are consistent with the active layer */
2162
0
  if (FLTCheckFeatureIdFilters(psNode, map, lp->index) == MS_FAILURE) {
2163
0
    FLTFreeFilterEncodingNode(psNode);
2164
0
    return msWFSException(map, "resourceid",
2165
0
                          MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2166
0
                          paramsObj->pszVersion);
2167
0
  }
2168
2169
  /* FIXME?: could probably apply to WFS 1.1 too */
2170
0
  if (nWFSVersion >= OWS_2_0_0) {
2171
0
    int nEvaluation;
2172
2173
0
    if (FLTCheckInvalidOperand(psNode) == MS_FAILURE) {
2174
0
      FLTFreeFilterEncodingNode(psNode);
2175
0
      return msWFSException(map, "filter",
2176
0
                            MS_WFS_ERROR_OPERATION_PROCESSING_FAILED,
2177
0
                            paramsObj->pszVersion);
2178
0
    }
2179
2180
0
    if (FLTCheckInvalidProperty(psNode, map, lp->index) == MS_FAILURE) {
2181
0
      FLTFreeFilterEncodingNode(psNode);
2182
0
      return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2183
0
                            paramsObj->pszVersion);
2184
0
    }
2185
2186
0
    psNode = FLTSimplify(psNode, &nEvaluation);
2187
0
    if (psNode == NULL) {
2188
0
      FLTFreeFilterEncodingNode(psNode);
2189
0
      if (nEvaluation == 1) {
2190
        /* return full layer */
2191
0
        return msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
2192
0
      } else {
2193
        /* return empty result set */
2194
0
        return MS_SUCCESS;
2195
0
      }
2196
0
    }
2197
0
  }
2198
2199
  // ignore any classes used by the WFS layer to avoid selecting fields we don't
2200
  // need
2201
0
  int numclasses = lp->numclasses;
2202
0
  lp->numclasses = 0;
2203
2204
  /* run filter.  If no results are found, do not throw exception */
2205
  /* this is a null result */
2206
0
  int status = FLTApplyFilterToLayer(psNode, map, lp->index);
2207
2208
  // set the class count to the original value
2209
0
  lp->numclasses = numclasses;
2210
2211
0
  if (status != MS_SUCCESS) {
2212
0
    errorObj *ms_error = msGetErrorObj();
2213
2214
0
    if (ms_error->code != MS_NOTFOUND) {
2215
0
      msSetError(MS_WFSERR, "FLTApplyFilterToLayer() failed",
2216
0
                 "msWFSGetFeature()");
2217
0
      FLTFreeFilterEncodingNode(psNode);
2218
0
      return msWFSException(map, "mapserv",
2219
0
                            (nWFSVersion >= OWS_2_0_0)
2220
0
                                ? MS_WFS_ERROR_OPERATION_PROCESSING_FAILED
2221
0
                                : MS_OWS_ERROR_NO_APPLICABLE_CODE,
2222
0
                            paramsObj->pszVersion);
2223
0
    }
2224
0
  }
2225
2226
0
  FLTFreeFilterEncodingNode(psNode);
2227
2228
0
  return MS_SUCCESS;
2229
0
}
2230
2231
/*
2232
** msWFSRunBasicGetFeature()
2233
*/
2234
static int msWFSRunBasicGetFeature(mapObj *map, layerObj *lp,
2235
                                   const wfsParamsObj *paramsObj,
2236
0
                                   int nWFSVersion) {
2237
0
  rectObj ext;
2238
0
  int status;
2239
2240
0
  map->query.type = MS_QUERY_BY_RECT; /* setup the query */
2241
0
  map->query.mode = MS_QUERY_MULTIPLE;
2242
0
  map->query.rect = map->extent;
2243
0
  map->query.layer = lp->index;
2244
2245
0
  if (FLTLayerSetInvalidRectIfSupported(lp, &(map->query.rect), "FO")) {
2246
    /* do nothing */
2247
0
  } else if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS) {
2248
0
    char *pszMapSRS = NULL;
2249
2250
    /*if srsName was given for wfs 1.1.0, It is at this point loaded into the
2251
    map object and should be used*/
2252
0
    if (!paramsObj->pszSrs)
2253
0
      msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE,
2254
0
                       &pszMapSRS);
2255
2256
    /* For a single point layer, to avoid numerical precision issues */
2257
    /* when reprojection is involved */
2258
0
    ext.minx -= 1e-5;
2259
0
    ext.miny -= 1e-5;
2260
0
    ext.maxx += 1e-5;
2261
0
    ext.maxy += 1e-5;
2262
2263
0
    if (pszMapSRS != NULL && strncmp(pszMapSRS, "EPSG:", 5) == 0) {
2264
2265
0
      if (nWFSVersion >= OWS_1_1_0)
2266
0
        status = msLoadProjectionStringEPSG(&(map->projection), pszMapSRS);
2267
0
      else
2268
0
        status = msLoadProjectionString(&(map->projection), pszMapSRS);
2269
2270
0
      if (status != 0) {
2271
0
        msSetError(MS_WFSERR, "msLoadProjectionString() failed: %s",
2272
0
                   "msWFSGetFeature()", pszMapSRS);
2273
0
        msFree(pszMapSRS);
2274
0
        return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
2275
0
                              paramsObj->pszVersion);
2276
0
      }
2277
0
    }
2278
0
    msFree(pszMapSRS);
2279
2280
    /*make sure that the layer projection is loaded.
2281
        It could come from a ows/wfs_srs metadata*/
2282
0
    if (lp->projection.numargs == 0) {
2283
0
      char *pszLayerSRS;
2284
0
      msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE,
2285
0
                       &pszLayerSRS);
2286
0
      if (pszLayerSRS) {
2287
0
        if (strncmp(pszLayerSRS, "EPSG:", 5) == 0) {
2288
0
          if (nWFSVersion >= OWS_1_1_0)
2289
0
            msLoadProjectionStringEPSG(&(lp->projection), pszLayerSRS);
2290
0
          else
2291
0
            msLoadProjectionString(&(lp->projection), pszLayerSRS);
2292
0
        }
2293
0
      }
2294
0
      msFree(pszLayerSRS);
2295
0
    }
2296
2297
0
    if (msProjectionsDiffer(&map->projection, &lp->projection) == MS_TRUE) {
2298
0
      msProjectRect(&lp->projection, &map->projection, &(ext));
2299
0
    }
2300
0
    map->query.rect = ext;
2301
0
  }
2302
2303
0
  if (msQueryByRect(map) != MS_SUCCESS) {
2304
0
    errorObj *ms_error;
2305
0
    ms_error = msGetErrorObj();
2306
2307
0
    if (ms_error->code != MS_NOTFOUND) {
2308
0
      msSetError(MS_WFSERR, "ms_error->code not found", "msWFSGetFeature()");
2309
0
      return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
2310
0
                            paramsObj->pszVersion);
2311
0
    }
2312
0
  }
2313
2314
0
  return MS_SUCCESS;
2315
0
}
2316
2317
/*
2318
** msWFSRetrieveFeatures()
2319
*/
2320
static int msWFSRetrieveFeatures(
2321
    mapObj *map, owsRequestObj *ows_request, const wfsParamsObj *paramsObj,
2322
    const WFSGMLInfo *gmlinfo, const char *pszFilter, int bBBOXSet,
2323
    const char *pszBBOXSRS, rectObj bbox, const char *pszFeatureId,
2324
    char **layers, int numlayers, int maxfeatures, int nWFSVersion,
2325
0
    int *pnOutTotalFeatures, int *pnHasNext) {
2326
0
  int i, j;
2327
0
  int iNumberOfFeatures = 0;
2328
2329
0
  if (pszFilter && strlen(pszFilter) > 0) {
2330
0
    int nFilters;
2331
0
    char **paszFilter = NULL;
2332
2333
    /* -------------------------------------------------------------------- */
2334
    /*      Validate the parameters. When a FILTER parameter is given,      */
2335
    /*      It needs the TYPENAME parameter for the layers. Also Filter     */
2336
    /*      is Mutually exclusive with FEATUREID and BBOX (see wfs specs    */
2337
    /*      1.0 section 13.7.3 on GetFeature)                               */
2338
    /*                                                                      */
2339
    /* -------------------------------------------------------------------- */
2340
2341
    /* WFS 2.0 */
2342
0
    if (nWFSVersion >= OWS_2_0_0 && paramsObj->pszFilterLanguage != NULL &&
2343
0
        strcasecmp(paramsObj->pszFilterLanguage,
2344
0
                   "urn:ogc:def:query Language:OGC-FES:Filter") != 0) {
2345
0
      msSetError(MS_WFSERR,
2346
0
                 "Unhandled value for FILTER_LANGUAGE parameter. Only "
2347
0
                 "\"urn:ogc:def:query Language:OGC-FES:Filter\" accepted.",
2348
0
                 "msWFSGetFeature()");
2349
0
      return msWFSException(map, "filter_language",
2350
0
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2351
0
                            paramsObj->pszVersion);
2352
0
    }
2353
2354
0
    if (gmlinfo->_typename == NULL || strlen(gmlinfo->_typename) <= 0 ||
2355
0
        layers == NULL || numlayers <= 0) {
2356
0
      msSetError(MS_WFSERR,
2357
0
                 "Required %s parameter missing for GetFeature with a FILTER "
2358
0
                 "parameter.",
2359
0
                 "msWFSGetFeature()",
2360
0
                 (nWFSVersion >= OWS_2_0_0) ? "TYPENAMES" : "TYPENAME");
2361
0
      return msWFSException(
2362
0
          map, (nWFSVersion >= OWS_2_0_0) ? "typenames" : "typename",
2363
0
          MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
2364
0
    }
2365
2366
0
    if (bBBOXSet) {
2367
0
      msSetError(MS_WFSERR,
2368
0
                 "BBOX parameter and FILTER parameter are mutually exclusive "
2369
0
                 "in GetFeature.",
2370
0
                 "msWFSGetFeature()");
2371
0
      return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
2372
0
                            paramsObj->pszVersion);
2373
0
    }
2374
2375
0
    if (pszFeatureId != NULL) {
2376
0
      msSetError(MS_WFSERR,
2377
0
                 "%s parameter and FILTER parameter are mutually exclusive in "
2378
0
                 "GetFeature.",
2379
0
                 "msWFSGetFeature()",
2380
0
                 (nWFSVersion < OWS_2_0_0) ? "FEATUREID" : "RESOURCEID");
2381
0
      return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
2382
0
                            paramsObj->pszVersion);
2383
0
    }
2384
2385
0
    if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) ==
2386
0
        MS_FAILURE)
2387
0
      return msWFSException(map, "srsname",
2388
0
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2389
0
                            paramsObj->pszVersion);
2390
2391
    /* -------------------------------------------------------------------- */
2392
    /*      Parse the Filter parameter. If there are several Filter         */
2393
    /*      parameters, each Filter is inside a parentheses. Eg :           */
2394
    /*      FILTER=(<Filter><Within><PropertyName>                          */
2395
    /*      INWATERA_1M/WKB_GEOM|INWATERA_1M/WKB_GEOM                       */
2396
    /*      <PropertyName><gml:Box><gml:coordinates>10,10
2397
     * 20,20</gml:coordinates>*/
2398
    /*      </gml:Box></Within></Filter>)(<Filter><Within><PropertyName>    */
2399
    /*      INWATERA_1M/WKB_GEOM<PropertyName><gml:Box><gml:coordinates>10,10*/
2400
    /*      20,20</gml:coordinates></gml:Box></Within></Filter>)            */
2401
    /* -------------------------------------------------------------------- */
2402
0
    nFilters = 0;
2403
0
    if (strlen(pszFilter) > 0 && pszFilter[0] == '(') {
2404
0
      paszFilter = FLTSplitFilters(pszFilter, &nFilters);
2405
0
    } else if (numlayers == 1) {
2406
0
      nFilters = 1;
2407
0
      paszFilter = (char **)msSmallMalloc(sizeof(char *) * nFilters);
2408
0
      paszFilter[0] = msStrdup(pszFilter);
2409
0
    }
2410
2411
0
    if (numlayers != nFilters) {
2412
0
      msSetError(MS_WFSERR,
2413
0
                 "Wrong number of filter elements, one filter must be "
2414
0
                 "specified for each feature type listed in the %s parameter.",
2415
0
                 "msWFSGetFeature()",
2416
0
                 (nWFSVersion >= OWS_2_0_0) ? "TYPENAMES" : "TYPENAME");
2417
0
      msFreeCharArray(paszFilter, nFilters);
2418
0
      return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2419
0
                            paramsObj->pszVersion);
2420
0
    }
2421
2422
    /* -------------------------------------------------------------------- */
2423
    /*      run through the filters and build the class expressions.        */
2424
    /* -------------------------------------------------------------------- */
2425
0
    for (i = 0; i < nFilters; i++) {
2426
0
      int status;
2427
0
      layerObj *lp;
2428
2429
0
      lp = msWFSGetLayerByName(map, ows_request, layers[i]);
2430
2431
      /* Special value set when parsing XML Post when there is a mix of */
2432
      /* Query with and without Filter */
2433
0
      if (strcmp(paszFilter[i], "!") == 0)
2434
0
        status = msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
2435
0
      else
2436
0
        status = msWFSRunFilter(map, lp, paramsObj, paszFilter[i], nWFSVersion);
2437
2438
0
      if (status != MS_SUCCESS) {
2439
0
        msFreeCharArray(paszFilter, nFilters);
2440
0
        return status;
2441
0
      }
2442
2443
      /* Decrement the total maxfeatures */
2444
0
      if (map->query.maxfeatures >= 0) {
2445
0
        if (lp->resultcache && lp->resultcache->numresults > 0) {
2446
0
          map->query.maxfeatures -= lp->resultcache->numresults;
2447
0
          if (map->query.maxfeatures <= 0)
2448
0
            break;
2449
0
        }
2450
0
      }
2451
0
    }
2452
2453
0
    msFreeCharArray(paszFilter, nFilters);
2454
0
  } /* end if filter set */
2455
2456
0
  if (pszFeatureId != NULL) {
2457
0
    char **tokens = NULL;
2458
0
    int nTokens = 0, j = 0, k = 0;
2459
0
    char **aFIDLayers = NULL;
2460
0
    char **aFIDValues = NULL;
2461
0
    int iFIDLayers = 0;
2462
2463
    /* Keep only selected layers, set to OFF by default. */
2464
0
    msWFSTurnOffAllLayer(map);
2465
2466
    /*featureid can be a list INWATERA_1M.1234, INWATERA_1M.1235
2467
    We will keep all the feature id from the same layer together
2468
    so that an OR would be applied if several of them are present
2469
    */
2470
0
    tokens = msStringSplit(pszFeatureId, ',', &nTokens);
2471
0
    iFIDLayers = 0;
2472
0
    if (tokens && nTokens >= 1) {
2473
0
      aFIDLayers = (char **)msSmallMalloc(sizeof(char *) * nTokens);
2474
0
      aFIDValues = (char **)msSmallMalloc(sizeof(char *) * nTokens);
2475
0
      for (j = 0; j < nTokens; j++) {
2476
0
        aFIDLayers[j] = NULL;
2477
0
        aFIDValues[j] = NULL;
2478
0
      }
2479
0
      for (j = 0; j < nTokens; j++) {
2480
0
        const char *pszLastDot = strrchr(tokens[j], '.');
2481
0
        if (pszLastDot != NULL) {
2482
0
          char *pszLayerInFID = msStrdup(tokens[j]);
2483
0
          pszLayerInFID[pszLastDot - tokens[j]] = '\0';
2484
          /* Find if the layer is already requested */
2485
0
          for (k = 0; k < iFIDLayers; k++) {
2486
0
            if (strcasecmp(aFIDLayers[k], pszLayerInFID) == 0)
2487
0
              break;
2488
0
          }
2489
          /* If not, add it to the list of requested layers */
2490
0
          if (k == iFIDLayers) {
2491
0
            aFIDLayers[iFIDLayers] = msStrdup(pszLayerInFID);
2492
0
            iFIDLayers++;
2493
0
          }
2494
          /* Add the id to the list of search identifiers of the layer */
2495
0
          if (aFIDValues[k] != NULL)
2496
0
            aFIDValues[k] = msStringConcatenate(aFIDValues[k], ",");
2497
0
          aFIDValues[k] = msStringConcatenate(aFIDValues[k], pszLastDot + 1);
2498
0
          msFree(pszLayerInFID);
2499
0
        } else {
2500
          /* In WFS 20, an unknown/invalid feature id shouldn't trigger an */
2501
          /* exception. Tested by CITE */
2502
0
          if (nWFSVersion < OWS_2_0_0) {
2503
0
            msSetError(MS_WFSERR,
2504
0
                       "Invalid FeatureId in GetFeature. Expecting "
2505
0
                       "layername.value : %s",
2506
0
                       "msWFSGetFeature()", tokens[j]);
2507
0
            if (tokens)
2508
0
              msFreeCharArray(tokens, nTokens);
2509
0
            msFreeCharArray(aFIDLayers, iFIDLayers);
2510
0
            msFreeCharArray(aFIDValues, iFIDLayers);
2511
0
            return msWFSException(map, "featureid",
2512
0
                                  MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2513
0
                                  paramsObj->pszVersion);
2514
0
          }
2515
0
        }
2516
0
      }
2517
0
    }
2518
0
    if (tokens)
2519
0
      msFreeCharArray(tokens, nTokens);
2520
2521
    /*turn on the layers and make sure projections are set properly*/
2522
0
    for (j = 0; j < iFIDLayers; j++) {
2523
0
      layerObj *lp;
2524
0
      lp = msWFSGetLayerByName(map, ows_request, aFIDLayers[j]);
2525
0
      if (lp == NULL) {
2526
0
        msSetError(
2527
0
            MS_WFSERR,
2528
0
            "Invalid typename given with FeatureId in GetFeature : %s. A layer might be disabled for \
2529
0
this request. Check wfs/ows_enable_request settings.",
2530
0
            "msWFSGetFeature()", aFIDLayers[j]);
2531
2532
0
        msFreeCharArray(aFIDLayers, iFIDLayers);
2533
0
        msFreeCharArray(aFIDValues, iFIDLayers);
2534
0
        return msWFSException(map, "featureid",
2535
0
                              MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2536
0
                              paramsObj->pszVersion);
2537
0
      }
2538
2539
0
      lp->status = MS_ON;
2540
0
    }
2541
2542
0
    if (map->query.only_cache_result_count == MS_FALSE)
2543
0
      msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, FALSE, NULL, NULL);
2544
2545
0
    if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) ==
2546
0
        MS_FAILURE)
2547
0
      return msWFSException(map, "srsname",
2548
0
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2549
0
                            paramsObj->pszVersion);
2550
2551
0
    for (j = 0; j < iFIDLayers; j++) {
2552
0
      layerObj *lp;
2553
0
      lp = msWFSGetLayerByName(map, ows_request, aFIDLayers[j]);
2554
0
      if (lp->_template == NULL) {
2555
        /* Force setting a template to enable query. */
2556
0
        lp->_template = msStrdup("ttt.html");
2557
0
      }
2558
0
      FilterEncodingNode *psNode =
2559
0
          FLTCreateFeatureIdFilterEncoding(aFIDValues[j]);
2560
2561
0
      if (FLTApplyFilterToLayer(psNode, map, lp->index) != MS_SUCCESS) {
2562
0
        msSetError(MS_WFSERR, "FLTApplyFilterToLayer() failed",
2563
0
                   "msWFSGetFeature");
2564
0
        FLTFreeFilterEncodingNode(psNode);
2565
0
        msFreeCharArray(aFIDLayers, iFIDLayers);
2566
0
        msFreeCharArray(aFIDValues, iFIDLayers);
2567
0
        return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
2568
0
                              paramsObj->pszVersion);
2569
0
      }
2570
2571
0
      FLTFreeFilterEncodingNode(psNode);
2572
2573
      /* Decrement the total maxfeatures */
2574
0
      if (map->query.maxfeatures >= 0) {
2575
0
        if (lp->resultcache && lp->resultcache->numresults > 0) {
2576
0
          map->query.maxfeatures -= lp->resultcache->numresults;
2577
0
          if (map->query.maxfeatures <= 0)
2578
0
            break;
2579
0
        }
2580
0
      }
2581
0
    }
2582
2583
0
    msFreeCharArray(aFIDLayers, iFIDLayers);
2584
0
    msFreeCharArray(aFIDValues, iFIDLayers);
2585
0
  }
2586
2587
  /*
2588
  ** Perform Query (only BBOX for now)
2589
  */
2590
  /* __TODO__ Using a rectangle query may not be the most efficient way */
2591
  /* to do things here. */
2592
0
  if (pszFilter == NULL && pszFeatureId == NULL) {
2593
2594
    /* Apply the requested SRS */
2595
0
    if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) ==
2596
0
        MS_FAILURE)
2597
0
      return msWFSException(map, "srsname",
2598
0
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2599
0
                            paramsObj->pszVersion);
2600
2601
0
    if (!bBBOXSet) {
2602
0
      for (j = 0; j < map->numlayers; j++) {
2603
0
        layerObj *lp;
2604
0
        lp = GET_LAYER(map, j);
2605
0
        if (lp->status == MS_ON) {
2606
          // classes don't affect retrieval of features, so set the count to 0
2607
          // when the query is executed this avoids selecting any fields
2608
          // referenced in a CLASS from the data source
2609
0
          int numclasses = lp->numclasses;
2610
0
          lp->numclasses = 0;
2611
0
          int status = msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
2612
          // set the class count back to its original value once the query is
2613
          // run
2614
0
          lp->numclasses = numclasses;
2615
0
          if (status != MS_SUCCESS)
2616
0
            return status;
2617
0
        }
2618
0
      }
2619
0
    } else {
2620
2621
0
      char *sBBoxSrs = NULL;
2622
2623
0
      if (pszBBOXSRS != NULL)
2624
0
        sBBoxSrs = msStrdup(pszBBOXSRS);
2625
2626
      /* On WFS 2.0.0, if the BBOX has no explicit SRS, use the map SRS */
2627
      /* Should likely be used for WFS 1.1.0 too, but don't want to cause */
2628
      /* issue at that point, since it can influence axis ordering */
2629
0
      if (nWFSVersion >= OWS_2_0_0 && sBBoxSrs == NULL) {
2630
0
        projectionObj sProjTmp;
2631
2632
0
        msInitProjection(&sProjTmp);
2633
0
        msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
2634
0
        msOWSGetEPSGProj(&sProjTmp, &(map->web.metadata), "FO", MS_TRUE,
2635
0
                         &sBBoxSrs);
2636
0
        msFreeProjection(&sProjTmp);
2637
0
      }
2638
2639
0
      if (sBBoxSrs) {
2640
0
        int status;
2641
0
        projectionObj sProjTmp;
2642
2643
0
        msInitProjection(&sProjTmp);
2644
0
        msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
2645
        /*do the axis order for now. It is unclear if the bbox are expected
2646
          ro respect the axis order defined in the projectsion #3296*/
2647
2648
0
        if (nWFSVersion >= OWS_1_1_0) {
2649
0
          if ((status = msLoadProjectionStringEPSG(&sProjTmp, sBBoxSrs)) == 0) {
2650
0
            msAxisNormalizePoints(&sProjTmp, 1, &bbox.minx, &bbox.miny);
2651
0
            msAxisNormalizePoints(&sProjTmp, 1, &bbox.maxx, &bbox.maxy);
2652
0
          }
2653
0
        } else
2654
0
          status = msLoadProjectionString(&sProjTmp, sBBoxSrs);
2655
2656
0
        if (status == 0 && map->projection.numargs > 0)
2657
0
          msProjectRect(&sProjTmp, &map->projection, &bbox);
2658
0
        msFreeProjection(&sProjTmp);
2659
2660
0
        msFree(sBBoxSrs);
2661
0
        sBBoxSrs = NULL;
2662
0
      }
2663
0
      map->query.type = MS_QUERY_BY_RECT; /* setup the query */
2664
0
      map->query.mode = MS_QUERY_MULTIPLE;
2665
0
      map->query.rect = bbox;
2666
2667
      /*
2668
        Retaining this block of code in case we want to setup a rendering path
2669
        through the query code at some point.
2670
      */
2671
      /* if(map->outputformat && MS_DRIVER_MVT(map->outputformat)) {
2672
        const char *mvt_buffer = msGetOutputFormatOption(map->outputformat,
2673
      "EDGE_BUFFER", "10"); int buffer = MS_ABS(atoi(mvt_buffer)); double res =
2674
      (bbox.maxx - bbox.minx)/(double)map->width; bbox.minx -= buffer * res;
2675
        bbox.maxx += buffer * res;
2676
        res = (bbox.maxy - bbox.miny)/(double)map->height;
2677
        bbox.miny -= buffer * res;
2678
        bbox.maxy += buffer * res;
2679
        map->width += buffer*2;
2680
        map->height += buffer*2;
2681
        msCalculateScale(bbox,map->units,map->width,map->height,
2682
      map->resolution, &map->scaledenom); map->query.rect = bbox;
2683
      } */
2684
2685
0
      if (msQueryByRect(map) != MS_SUCCESS) {
2686
0
        errorObj *ms_error;
2687
0
        ms_error = msGetErrorObj();
2688
2689
0
        if (ms_error->code != MS_NOTFOUND) {
2690
0
          msSetError(MS_WFSERR, "ms_error->code not found",
2691
0
                     "msWFSGetFeature()");
2692
0
          return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
2693
0
                                paramsObj->pszVersion);
2694
0
        }
2695
0
      }
2696
0
    }
2697
0
  }
2698
2699
  /* Count total number of features retrieved */
2700
0
  for (j = 0; j < map->numlayers; j++) {
2701
0
    if (GET_LAYER(map, j)->resultcache &&
2702
0
        GET_LAYER(map, j)->resultcache->numresults > 0) {
2703
0
      iNumberOfFeatures += GET_LAYER(map, j)->resultcache->numresults;
2704
0
    }
2705
0
  }
2706
2707
  /* If maxfeatures is set, we have actually asked for 1 more feature than asked
2708
   */
2709
  /* to be able to know if there are next features. So it is now time to */
2710
  /* remove this extra unasked feature from the result cache */
2711
0
  if (maxfeatures >= 0 && iNumberOfFeatures == maxfeatures + 1) {
2712
0
    int lastJ = -1;
2713
0
    for (j = 0; j < map->numlayers; j++) {
2714
0
      if (GET_LAYER(map, j)->resultcache &&
2715
0
          GET_LAYER(map, j)->resultcache->numresults > 0) {
2716
0
        lastJ = j;
2717
0
      }
2718
0
    }
2719
0
    GET_LAYER(map, lastJ)->resultcache->numresults--;
2720
0
    GET_LAYER(map, lastJ)->resultcache->bounds =
2721
0
        GET_LAYER(map, lastJ)->resultcache->previousBounds;
2722
0
    iNumberOfFeatures--;
2723
0
    if (pnHasNext)
2724
0
      *pnHasNext = MS_TRUE;
2725
0
  } else if (pnHasNext)
2726
0
    *pnHasNext = MS_FALSE;
2727
2728
0
  *pnOutTotalFeatures = iNumberOfFeatures;
2729
2730
0
  return MS_SUCCESS;
2731
0
}
2732
2733
/*
2734
** msWFSParsePropertyNameOrSortBy()
2735
*/
2736
static char **msWFSParsePropertyNameOrSortBy(const char *pszPropertyName,
2737
0
                                             int numlayers) {
2738
0
  char **tokens = NULL;
2739
0
  int nPropertyNames = 0;
2740
0
  char **papszPropertyName = NULL;
2741
0
  int i;
2742
2743
0
  if (pszPropertyName == NULL) {
2744
0
    papszPropertyName = (char **)msSmallMalloc(sizeof(char *) * numlayers);
2745
0
    for (i = 0; i < numlayers; i++) {
2746
0
      papszPropertyName[i] = msStrdup("!");
2747
0
    }
2748
0
    return papszPropertyName;
2749
0
  }
2750
2751
0
  if (!(pszPropertyName[0] == '(' || numlayers == 1)) {
2752
0
    return NULL;
2753
0
  }
2754
2755
0
  if (numlayers == 1 && pszPropertyName[0] != '(') {
2756
    /* Accept PROPERTYNAME without () when there is a single TYPENAME */
2757
0
    std::string osTmpPropertyName;
2758
0
    osTmpPropertyName = '(';
2759
0
    osTmpPropertyName += pszPropertyName;
2760
0
    osTmpPropertyName += ')';
2761
0
    tokens = msStringSplit(osTmpPropertyName.c_str() + 1, '(', &nPropertyNames);
2762
0
  } else
2763
0
    tokens = msStringSplit(pszPropertyName + 1, '(', &nPropertyNames);
2764
2765
  /*expecting to always have a list of property names equal to
2766
      the number of layers(typename)*/
2767
0
  if (nPropertyNames != numlayers) {
2768
0
    msFreeCharArray(tokens, nPropertyNames);
2769
0
    return NULL;
2770
0
  }
2771
2772
0
  papszPropertyName = (char **)msSmallMalloc(sizeof(char *) * nPropertyNames);
2773
0
  for (i = 0; i < nPropertyNames; i++) {
2774
0
    if (strlen(tokens[i]) > 0) {
2775
0
      papszPropertyName[i] = msStrdup(tokens[i]);
2776
      /* remove trailing ) */
2777
0
      papszPropertyName[i][strlen(papszPropertyName[i]) - 1] = '\0';
2778
0
    } else {
2779
0
      for (i--; i >= 0; i--)
2780
0
        msFree(papszPropertyName[i]);
2781
0
      msFree(papszPropertyName);
2782
0
      msFreeCharArray(tokens, nPropertyNames);
2783
0
      return NULL;
2784
0
    }
2785
0
  }
2786
2787
0
  msFreeCharArray(tokens, nPropertyNames);
2788
2789
0
  return papszPropertyName;
2790
0
}
2791
2792
0
static void msWFSInitGMLInfo(WFSGMLInfo *pgmlinfo) {
2793
0
  memset(pgmlinfo, 0, sizeof(WFSGMLInfo));
2794
0
  pgmlinfo->user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
2795
0
  pgmlinfo->user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
2796
0
  pgmlinfo->collection_name = OWS_WFS_FEATURE_COLLECTION_NAME;
2797
0
  pgmlinfo->_typename = "";
2798
0
  pgmlinfo->output_mime_type = "text/xml";
2799
0
  pgmlinfo->output_schema_format = "XMLSCHEMA";
2800
0
}
2801
2802
0
static void msWFSCleanupGMLInfo(WFSGMLInfo *pgmlinfo) {
2803
0
  free(pgmlinfo->script_url);
2804
0
  free(pgmlinfo->script_url_encoded);
2805
0
  msFree(pgmlinfo->user_namespace_uri_encoded);
2806
0
}
2807
2808
static int msWFSGetGMLOutputFormat(wfsParamsObj *paramsObj,
2809
0
                                   WFSGMLInfo *pgmlinfo, int nWFSVersion) {
2810
0
  OWSGMLVersion outputformat = OWS_GML2;
2811
2812
  /* Validate outputformat */
2813
0
  if (paramsObj->pszOutputFormat) {
2814
    /* We support GML2, GML3, GML3.2 for now. */
2815
0
    if (strcasecmp(paramsObj->pszOutputFormat, "GML2") == 0 ||
2816
0
        strcasecmp(paramsObj->pszOutputFormat, "text/xml; subtype=gml/2.1.2") ==
2817
0
            0) {
2818
0
      outputformat = OWS_GML2;
2819
0
      pgmlinfo->output_schema_format = "XMLSCHEMA";
2820
      /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE
2821
       * WFS 2.0 */
2822
0
      pgmlinfo->output_mime_type = "text/xml; subtype=gml/2.1.2";
2823
0
    } else if (strcasecmp(paramsObj->pszOutputFormat, "GML3") == 0 ||
2824
0
               strcasecmp(paramsObj->pszOutputFormat,
2825
0
                          "text/xml; subtype=gml/3.1.1") == 0) {
2826
0
      outputformat = OWS_GML3;
2827
0
      pgmlinfo->output_schema_format = "SFE_XMLSCHEMA";
2828
      /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE
2829
       * WFS 2.0 */
2830
0
      pgmlinfo->output_mime_type = "text/xml; subtype=gml/3.1.1";
2831
0
    } else if (strcasecmp(paramsObj->pszOutputFormat, "GML32") == 0 ||
2832
0
               strcasecmp(paramsObj->pszOutputFormat,
2833
0
                          "text/xml; subtype=gml/3.2.1") == 0 ||
2834
0
               strcasecmp(paramsObj->pszOutputFormat,
2835
0
                          "text/xml; subtype=\"gml/3.2.1\"") == 0 ||
2836
0
               strcasecmp(paramsObj->pszOutputFormat,
2837
0
                          "application/gml+xml; version=3.2") == 0) {
2838
0
      outputformat = OWS_GML32;
2839
0
      pgmlinfo->output_schema_format =
2840
0
          "application%2Fgml%2Bxml%3B%20version%3D3.2";
2841
0
      pgmlinfo->output_mime_type = "text/xml; subtype=\"gml/3.2.1\"";
2842
0
    } else {
2843
0
      return -1;
2844
0
    }
2845
2846
    /* If OUTPUTFORMAT not set, default to gml */
2847
0
  } else {
2848
    /*set the output format using the version if available*/
2849
0
    if (nWFSVersion == OWS_1_1_0) {
2850
0
      outputformat = OWS_GML3;
2851
0
      pgmlinfo->output_schema_format = "text/xml;%20subtype=gml/3.1.1";
2852
      /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE
2853
       * WFS 2.0 */
2854
0
      pgmlinfo->output_mime_type = "text/xml; subtype=gml/3.1.1";
2855
0
    } else if (nWFSVersion >= OWS_2_0_0) {
2856
0
      outputformat = OWS_GML32;
2857
0
      pgmlinfo->output_schema_format =
2858
0
          "application%2Fgml%2Bxml%3B%20version%3D3.2";
2859
0
      pgmlinfo->output_mime_type = "text/xml; subtype=\"gml/3.2.1\"";
2860
0
    }
2861
0
  }
2862
2863
0
  if (nWFSVersion == OWS_1_0_0) {
2864
0
    pgmlinfo->output_mime_type = "text/xml";
2865
0
  }
2866
2867
0
  return outputformat;
2868
0
}
2869
2870
static outputFormatObj *msWFSGetOtherOutputFormat(mapObj *map,
2871
0
                                                  wfsParamsObj *paramsObj) {
2872
0
  const char *format_list;
2873
0
  hashTableObj *md;
2874
0
  outputFormatObj *psFormat = NULL;
2875
0
  int j;
2876
2877
  /* validate selected format against all selected layers. */
2878
0
  for (j = 0; j < map->numlayers; j++) {
2879
0
    layerObj *lp;
2880
2881
0
    lp = GET_LAYER(map, j);
2882
2883
0
    if (lp->status != MS_ON)
2884
0
      continue;
2885
2886
0
    md = &(lp->metadata);
2887
0
    format_list = msOWSLookupMetadata(md, "F", "getfeature_formatlist");
2888
0
    if (format_list == NULL) {
2889
0
      md = &(map->web.metadata);
2890
0
      format_list = msOWSLookupMetadata(md, "F", "getfeature_formatlist");
2891
0
    }
2892
0
    if (format_list) {
2893
0
      psFormat = msOwsIsOutputFormatValid(map, paramsObj->pszOutputFormat, md,
2894
0
                                          "F", "getfeature_formatlist");
2895
0
    }
2896
0
    if (psFormat == NULL) {
2897
0
      msSetError(MS_WFSERR,
2898
0
                 "'%s' is not a permitted output format for layer '%s', review "
2899
0
                 "wfs_getfeature_formatlist setting.",
2900
0
                 "msWFSGetFeature()", paramsObj->pszOutputFormat, lp->name);
2901
0
      return NULL;
2902
0
    }
2903
2904
0
    if (psFormat->imagemode != MS_IMAGEMODE_FEATURE) {
2905
0
      msSetError(MS_WFSERR,
2906
0
                 "OUTPUTFORMAT '%s' does not have IMAGEMODE FEATURE, and is "
2907
0
                 "not permitted for WFS output.",
2908
0
                 "msWFSGetFeature()", paramsObj->pszOutputFormat);
2909
0
      return NULL;
2910
0
    }
2911
0
  }
2912
2913
0
  return psFormat;
2914
0
}
2915
2916
static void msWFSAnalyzeStartIndexAndFeatureCount(mapObj *map,
2917
                                                  const wfsParamsObj *paramsObj,
2918
                                                  int bIsHits,
2919
                                                  int *pmaxfeatures,
2920
0
                                                  int *pstartindex) {
2921
0
  const char *tmpmaxfeatures = NULL;
2922
0
  int maxfeatures = -1, startindex = -1;
2923
0
  int j;
2924
2925
0
  int nQueriedLayers = 0;
2926
0
  layerObj *lpQueried = NULL;
2927
2928
0
  tmpmaxfeatures =
2929
0
      msOWSLookupMetadata(&(map->web.metadata), "FO", "maxfeatures");
2930
0
  if (tmpmaxfeatures)
2931
0
    maxfeatures = atoi(tmpmaxfeatures);
2932
2933
0
  if (bIsHits) {
2934
0
    const char *ignoreMaxFeaturesForHits = msOWSLookupMetadata(
2935
0
        &(map->web.metadata), "FO", "maxfeatures_ignore_for_resulttype_hits");
2936
2937
    /* For PostGIS where we have an efficient implementation of hits, default to
2938
     * true */
2939
0
    if (ignoreMaxFeaturesForHits == NULL) {
2940
0
      int bHasEfficientHits = MS_TRUE;
2941
0
      for (j = 0; j < map->numlayers; j++) {
2942
0
        layerObj *lp;
2943
0
        lp = GET_LAYER(map, j);
2944
0
        if (lp->status == MS_ON) {
2945
0
          if (lp->connectiontype != MS_POSTGIS) {
2946
0
            bHasEfficientHits = MS_FALSE;
2947
0
            break;
2948
0
          }
2949
0
        }
2950
0
      }
2951
2952
0
      if (bHasEfficientHits)
2953
0
        ignoreMaxFeaturesForHits = "true";
2954
0
    }
2955
2956
0
    if (ignoreMaxFeaturesForHits != NULL &&
2957
0
        strcasecmp(ignoreMaxFeaturesForHits, "true") == 0)
2958
0
      maxfeatures = -1;
2959
0
  }
2960
2961
0
  if (paramsObj->nMaxFeatures >= 0) {
2962
0
    if (maxfeatures < 0 ||
2963
0
        (maxfeatures > 0 && paramsObj->nMaxFeatures < maxfeatures))
2964
0
      maxfeatures = paramsObj->nMaxFeatures;
2965
0
  }
2966
2967
0
  nQueriedLayers = 0;
2968
0
  for (j = 0; j < map->numlayers; j++) {
2969
0
    layerObj *lp;
2970
0
    lp = GET_LAYER(map, j);
2971
0
    if (lp->status == MS_ON) {
2972
      /* No reason to handle tolerances for WFS GetFeature */
2973
0
      lp->tolerance = 0;
2974
0
      lpQueried = GET_LAYER(map, j);
2975
0
      nQueriedLayers++;
2976
0
    }
2977
0
  }
2978
2979
  /* WFS convention is that STARTINDEX=0 means the first feature, whereas */
2980
  /* in MapServer internals we used another interpretation with STARTINDEX=1 */
2981
  /* being the first index. This was before that the SWG WFS clarified things */
2982
  /* hence the mess. Ultimately we should likely fix our internals to avoid */
2983
  /* possible confusion */
2984
0
  if (paramsObj->nStartIndex >= 0) {
2985
0
    startindex = 1 + paramsObj->nStartIndex;
2986
0
    map->query.startindex = startindex;
2987
0
  }
2988
2989
  /* maxfeatures set */
2990
0
  if (maxfeatures >= 0) {
2991
0
    int extrafeature = 1;
2992
0
    for (j = 0; j < map->numlayers; j++) {
2993
0
      layerObj *lp;
2994
0
      lp = GET_LAYER(map, j);
2995
0
      if (lp->status == MS_ON) {
2996
        /*over-ride the value only if it is unset or wfs maxfeatures is
2997
          lower that what is currently set*/
2998
0
        if (lp->maxfeatures <= 0 ||
2999
0
            (lp->maxfeatures > 0 && maxfeatures < lp->maxfeatures)) {
3000
          /* TODO: We don't handle DESC sorting, as it would mean to be */
3001
          /* able to evict the first property, which mapquery cannot handle */
3002
          /* This would have impact on the resultcache.bounds */
3003
0
          if (lp->sortBy.nProperties > 0) {
3004
0
            int k;
3005
0
            int countDesc = 1;
3006
0
            for (k = 0; k < lp->sortBy.nProperties; k++) {
3007
0
              if (lp->sortBy.properties[k].sortOrder == SORT_DESC)
3008
0
                countDesc++;
3009
0
            }
3010
0
            if (countDesc > 0)
3011
0
              extrafeature = 0;
3012
0
          }
3013
3014
          /* + 1 to be able to detect if there are more features for a next
3015
           * request */
3016
0
          lp->maxfeatures = maxfeatures + extrafeature;
3017
0
        }
3018
0
      }
3019
0
    }
3020
    /* + 1 to be able to detect if there are more features for a next request */
3021
0
    map->query.maxfeatures = maxfeatures + extrafeature;
3022
0
  }
3023
3024
  /* startindex set */
3025
0
  if (startindex > 0 && nQueriedLayers > 1) {
3026
0
    for (j = 0; j < map->numlayers; j++) {
3027
0
      layerObj *lp;
3028
0
      lp = GET_LAYER(map, j);
3029
0
      if (lp->status == MS_ON) {
3030
0
        msLayerEnablePaging(lp, MS_FALSE);
3031
0
      }
3032
0
    }
3033
0
  } else if (startindex > 0 && lpQueried) {
3034
0
    lpQueried->startindex = startindex;
3035
0
  }
3036
3037
0
  if (pmaxfeatures)
3038
0
    *pmaxfeatures = maxfeatures;
3039
0
  if (pstartindex)
3040
0
    *pstartindex = startindex;
3041
0
}
3042
3043
static int msWFSAnalyzeBBOX(mapObj *map, wfsParamsObj *paramsObj,
3044
0
                            rectObj *pbbox, char **pszBBoxSRS) {
3045
  /* Default filter is map extents */
3046
0
  *pbbox = map->extent;
3047
3048
0
  if (paramsObj->pszBbox) {
3049
0
    char **tokens;
3050
0
    int n;
3051
3052
0
    tokens = msStringSplit(paramsObj->pszBbox, ',', &n);
3053
0
    if (tokens == NULL || (n != 4 && n != 5)) {
3054
0
      msSetError(MS_WFSERR, "Wrong number of arguments for BBOX.",
3055
0
                 "msWFSGetFeature()");
3056
0
      msFreeCharArray(tokens, n);
3057
0
      return msWFSException(map, "bbox", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3058
0
                            paramsObj->pszVersion);
3059
0
    }
3060
0
    pbbox->minx = atof(tokens[0]);
3061
0
    pbbox->miny = atof(tokens[1]);
3062
0
    pbbox->maxx = atof(tokens[2]);
3063
0
    pbbox->maxy = atof(tokens[3]);
3064
    /*5th aregument is assumed to be projection*/
3065
0
    if (n == 5)
3066
0
      *pszBBoxSRS = msStrdup(tokens[4]);
3067
3068
0
    msFreeCharArray(tokens, n);
3069
0
  }
3070
0
  return 0;
3071
0
}
3072
3073
static int msWFSComputeMatchingFeatures(mapObj *map, owsRequestObj *ows_request,
3074
                                        wfsParamsObj *paramsObj,
3075
                                        int iNumberOfFeatures, int maxfeatures,
3076
                                        WFSGMLInfo *pgmlinfo, rectObj bbox,
3077
                                        const char *sBBoxSrs, char **layers,
3078
0
                                        int numlayers, int nWFSVersion) {
3079
0
  int nMatchingFeatures = -1;
3080
3081
0
  if (nWFSVersion >= OWS_2_0_0) {
3082
    /* If no features have been retrieved and there was no feature count limit,
3083
     * then */
3084
    /* it is obvious. We must test maxfeatures because if startindex is defined,
3085
     */
3086
    /* and above the number of matched features, iNumberOfFeatures can be 0. */
3087
0
    if (iNumberOfFeatures == 0 && maxfeatures < 0) {
3088
0
      nMatchingFeatures = 0;
3089
0
    }
3090
3091
    /* In the case we had limited the query by a maximum number of features, and
3092
     */
3093
    /* that the query has returned less features than the limit, or if there was
3094
     */
3095
    /* no limit in feature count, we are able to determine the number of
3096
     * matching
3097
     */
3098
    /* features */
3099
0
    else if (iNumberOfFeatures > 0 &&
3100
0
             (maxfeatures < 0 || iNumberOfFeatures < maxfeatures)) {
3101
0
      if (paramsObj->nStartIndex >= 0)
3102
0
        nMatchingFeatures = iNumberOfFeatures + paramsObj->nStartIndex;
3103
0
      else
3104
0
        nMatchingFeatures = iNumberOfFeatures;
3105
0
    }
3106
3107
    /* Otherwise big hammer ! */
3108
0
    else {
3109
      /* The WFS 2.0 spec is not really clear on numberMatched behavior */
3110
      /* and there are interesting logic in server implementations. */
3111
      /* */
3112
      /* Deegree
3113
       * (http://deegree3-demo.deegree.org:80/inspire-workspace/services/wfs?)
3114
       */
3115
      /* will return numberMatched="19005" on the cp:CadastralParcel layer for a
3116
       */
3117
      /* resultType=hits request, whereas the server limitation is set to 15000
3118
       */
3119
      /* But on a regular GetFeature (without count specified), it will return
3120
       */
3121
      /* numberMatched="15000" (and numberReturned="15000")) */
3122
      /* */
3123
      /* GeoServer 2.4.1 will also ignore its server limitation for a
3124
       * resultType=hits */
3125
      /* but when using a regular GetFeature (without count specified), il will
3126
       * return */
3127
      /* numberMatched=numberReturned=total_number_of_features_without_taking_into_account_server_limit
3128
       */
3129
      /* but the number of actually returned features will be truncated to the
3130
       * server */
3131
      /* limit, likely a bug */
3132
      /* */
3133
      /* If wfs_compute_number_matched is set to TRUE, we will issue a full */
3134
      /* count of potentially matching features, ignoring any client or server
3135
       */
3136
      /* side limits. */
3137
3138
0
      const char *pszComputeNumberMatched = msOWSLookupMetadata(
3139
0
          &(map->web.metadata), "F", "compute_number_matched");
3140
0
      if (pszComputeNumberMatched != NULL &&
3141
0
          strcasecmp(pszComputeNumberMatched, "true") == 0) {
3142
0
        int j;
3143
0
        mapObj *mapTmp = (mapObj *)msSmallCalloc(1, sizeof(mapObj));
3144
0
        if (initMap(mapTmp) == -1) {
3145
0
          free(mapTmp);
3146
0
          return 0;
3147
0
        }
3148
0
        msCopyMap(mapTmp, map);
3149
3150
        /* Re-run the query but with no limit */
3151
0
        mapTmp->query.maxfeatures = -1;
3152
0
        mapTmp->query.startindex = -1;
3153
0
        mapTmp->query.only_cache_result_count = MS_TRUE;
3154
0
        for (j = 0; j < mapTmp->numlayers; j++) {
3155
0
          layerObj *lp = GET_LAYER(mapTmp, j);
3156
          /* Reset layer paging */
3157
0
          lp->maxfeatures = -1;
3158
0
          lp->startindex = -1;
3159
0
        }
3160
3161
0
        nMatchingFeatures = 0;
3162
0
        msWFSRetrieveFeatures(
3163
0
            mapTmp, ows_request, paramsObj, pgmlinfo, paramsObj->pszFilter,
3164
0
            paramsObj->pszBbox != NULL, sBBoxSrs, bbox, paramsObj->pszFeatureId,
3165
0
            layers, numlayers, -1, nWFSVersion, &nMatchingFeatures, NULL);
3166
3167
0
        msFreeMap(mapTmp);
3168
0
      }
3169
0
    }
3170
0
  }
3171
3172
0
  return nMatchingFeatures;
3173
0
}
3174
3175
static int msWFSResolveStoredQuery(mapObj *map, wfsParamsObj *paramsObj,
3176
0
                                   cgiRequestObj *req) {
3177
0
  if (paramsObj->pszStoredQueryId != NULL) {
3178
0
    int i;
3179
0
    int status;
3180
0
    hashTableObj *hashTable = msCreateHashTable();
3181
0
    char *pszResolvedQuery;
3182
3183
0
    if (req->NumParams > 0 && req->postrequest == NULL) {
3184
0
      for (i = 0; i < req->NumParams; i++) {
3185
0
        if (req->ParamNames[i] && req->ParamValues[i]) {
3186
0
          char *pszXMLValue = CPLEscapeString(
3187
0
              req->ParamValues[i], strlen(req->ParamValues[i]), CPLES_XML);
3188
0
          msInsertHashTable(hashTable, req->ParamNames[i], pszXMLValue);
3189
0
          CPLFree(pszXMLValue);
3190
0
        }
3191
0
      }
3192
0
    }
3193
3194
0
    pszResolvedQuery = msWFSGetResolvedStoredQuery20(
3195
0
        map, paramsObj, paramsObj->pszStoredQueryId, hashTable);
3196
0
    msFreeHashTable(hashTable);
3197
3198
0
    if (pszResolvedQuery == NULL)
3199
0
      return MS_FAILURE;
3200
3201
0
    status = msWFSAnalyzeStoredQuery(
3202
0
        map, paramsObj, paramsObj->pszStoredQueryId, pszResolvedQuery);
3203
0
    msFree(pszResolvedQuery);
3204
3205
0
    if (status != MS_SUCCESS)
3206
0
      return status;
3207
3208
0
    msWFSSimplifyPropertyNameAndFilter(paramsObj);
3209
0
  }
3210
0
  return MS_SUCCESS;
3211
0
}
3212
3213
/*
3214
** msWFSUnaliasItem()
3215
*/
3216
0
static const char *msWFSUnaliasItem(layerObj *lp, const char *name) {
3217
0
  const char *pszFullName;
3218
0
  char szTmp[256];
3219
0
  int z;
3220
0
  for (z = 0; z < lp->numitems; z++) {
3221
0
    if (!lp->items[z] || strlen(lp->items[z]) <= 0)
3222
0
      continue;
3223
0
    snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[z]);
3224
0
    pszFullName = msOWSLookupMetadata(&(lp->metadata), "G", szTmp);
3225
0
    if (pszFullName && strcmp(name, pszFullName) == 0)
3226
0
      return lp->items[z];
3227
0
  }
3228
0
  return name;
3229
0
}
3230
3231
/*
3232
** msWFSIsAllowedItem()
3233
*/
3234
0
static int msWFSIsAllowedItem(gmlItemListObj *itemList, const char *name) {
3235
0
  int z;
3236
0
  for (z = 0; z < itemList->numitems; z++) {
3237
0
    if (itemList->items[z].visible &&
3238
0
        strcasecmp(name, itemList->items[z].name) == 0) {
3239
0
      return MS_TRUE;
3240
0
    }
3241
0
  }
3242
0
  return MS_FALSE;
3243
0
}
3244
3245
/*
3246
** msWFSUngroupItem()
3247
*/
3248
static const char *msWFSUngroupItem(layerObj *lp, gmlGroupListObj *groupList,
3249
0
                                    const char *name, int *pnGroupIndex) {
3250
0
  int z;
3251
3252
0
  *pnGroupIndex = -1;
3253
0
  for (z = 0; z < groupList->numgroups; z++) {
3254
0
    const char *pszGroupName = groupList->groups[z].name;
3255
0
    size_t nGroupNameLen = strlen(pszGroupName);
3256
3257
    /* Is it a group name ? */
3258
0
    if (strcasecmp(name, pszGroupName) == 0) {
3259
0
      *pnGroupIndex = z;
3260
0
      return NULL;
3261
0
    }
3262
3263
    /* Is it an item of a group ? */
3264
0
    if (strncasecmp(name, pszGroupName, nGroupNameLen) == 0 &&
3265
0
        name[nGroupNameLen] == '/') {
3266
3267
0
      int w;
3268
0
      const char *pszSubName = msWFSStripNS(name + nGroupNameLen + 1);
3269
0
      pszSubName = msWFSUnaliasItem(lp, pszSubName);
3270
0
      for (w = 0; w < groupList->groups[z].numitems; w++) {
3271
0
        if (strcasecmp(pszSubName, groupList->groups[z].items[w]) == 0) {
3272
0
          return pszSubName;
3273
0
        }
3274
0
      }
3275
0
    }
3276
0
  }
3277
3278
0
  return name;
3279
0
}
3280
3281
/*
3282
** msWFSApplySortBy()
3283
*/
3284
static int msWFSApplySortBy(mapObj *map, wfsParamsObj *paramsObj, layerObj *lp,
3285
                            const char *pszSortBy, gmlItemListObj *itemList,
3286
0
                            gmlGroupListObj *groupList) {
3287
0
  int y, n;
3288
0
  char **tokens;
3289
0
  int invalidSortBy = MS_FALSE;
3290
0
  sortByClause sortBy;
3291
3292
0
  if (!msLayerSupportsSorting(lp)) {
3293
0
    msSetError(MS_WFSERR, "Layer %s does not support sorting",
3294
0
               "msWFSGetFeature()", lp->name);
3295
0
    return msWFSException(map, "mapserv",
3296
0
                          MS_WFS_ERROR_OPERATION_PROCESSING_FAILED,
3297
0
                          paramsObj->pszVersion);
3298
0
  }
3299
3300
0
  tokens = msStringSplit(pszSortBy, ',', &n);
3301
0
  sortBy.nProperties = n;
3302
0
  sortBy.properties =
3303
0
      (sortByProperties *)msSmallMalloc(n * sizeof(sortByProperties));
3304
0
  for (y = 0; y < n; y++) {
3305
0
    char *pszSpace;
3306
0
    int nGroupIndex = -1;
3307
0
    const char *pszSortProperty;
3308
0
    char *pszTmp;
3309
3310
0
    sortBy.properties[y].item = msStrdup(tokens[y]);
3311
0
    sortBy.properties[y].sortOrder = SORT_ASC;
3312
0
    pszSpace = strchr(sortBy.properties[y].item, ' ');
3313
0
    if (pszSpace) {
3314
0
      *pszSpace = '\0';
3315
0
      if (strcasecmp(pszSpace + 1, "A") == 0 ||
3316
0
          strcasecmp(pszSpace + 1, "ASC") == 0)
3317
0
        ;
3318
0
      else if (strcasecmp(pszSpace + 1, "D") == 0 ||
3319
0
               strcasecmp(pszSpace + 1, "DESC") == 0)
3320
0
        sortBy.properties[y].sortOrder = SORT_DESC;
3321
0
      else
3322
0
        invalidSortBy = MS_TRUE;
3323
0
    }
3324
3325
0
    pszSortProperty = msWFSStripNS(sortBy.properties[y].item);
3326
0
    pszSortProperty = msWFSUnaliasItem(lp, pszSortProperty);
3327
0
    pszSortProperty =
3328
0
        msWFSUngroupItem(lp, groupList, pszSortProperty, &nGroupIndex);
3329
0
    if (nGroupIndex >= 0)
3330
0
      invalidSortBy = MS_TRUE;
3331
3332
0
    if (!msWFSIsAllowedItem(itemList, pszSortProperty))
3333
0
      invalidSortBy = MS_TRUE;
3334
3335
0
    pszTmp = msStrdup(pszSortProperty);
3336
0
    msFree(sortBy.properties[y].item);
3337
0
    sortBy.properties[y].item = pszTmp;
3338
0
  }
3339
0
  msFreeCharArray(tokens, n);
3340
3341
0
  if (!invalidSortBy)
3342
0
    msLayerSetSort(lp, &sortBy);
3343
3344
0
  for (y = 0; y < n; y++) {
3345
0
    msFree(sortBy.properties[y].item);
3346
0
  }
3347
0
  msFree(sortBy.properties);
3348
3349
0
  if (invalidSortBy) {
3350
0
    msSetError(MS_WFSERR, "Invalid SORTBY clause", "msWFSGetFeature()");
3351
0
    return msWFSException(map, "sortby", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3352
0
                          paramsObj->pszVersion);
3353
0
  }
3354
3355
0
  return MS_SUCCESS;
3356
0
}
3357
3358
0
static void msWFSSetShapeCache(mapObj *map) {
3359
0
  const char *pszFeaturesCacheCount =
3360
0
      msOWSLookupMetadata(&(map->web.metadata), "F", "features_cache_count");
3361
0
  const char *pszFeaturesCacheSize =
3362
0
      msOWSLookupMetadata(&(map->web.metadata), "F", "features_cache_size");
3363
0
  if (pszFeaturesCacheCount) {
3364
0
    map->query.cache_shapes = MS_TRUE;
3365
0
    map->query.max_cached_shape_count = atoi(pszFeaturesCacheCount);
3366
0
    if (map->debug >= MS_DEBUGLEVEL_V) {
3367
0
      msDebug("Caching up to %d shapes\n", map->query.max_cached_shape_count);
3368
0
    }
3369
0
  }
3370
3371
0
  if (pszFeaturesCacheSize) {
3372
0
    map->query.cache_shapes = MS_TRUE;
3373
0
    map->query.max_cached_shape_ram_amount = atoi(pszFeaturesCacheSize);
3374
0
    if (strstr(pszFeaturesCacheSize, "mb") ||
3375
0
        strstr(pszFeaturesCacheSize, "MB"))
3376
0
      map->query.max_cached_shape_ram_amount *= 1024 * 1024;
3377
0
    if (map->debug >= MS_DEBUGLEVEL_V) {
3378
0
      msDebug("Caching up to %d bytes of shapes\n",
3379
0
              map->query.max_cached_shape_ram_amount);
3380
0
    }
3381
0
  }
3382
0
}
3383
3384
/*
3385
** msWFSGetFeature()
3386
*/
3387
static int msWFSGetFeature(mapObj *map, wfsParamsObj *paramsObj,
3388
                           cgiRequestObj *req, owsRequestObj *ows_request,
3389
0
                           int nWFSVersion) {
3390
0
  int status;
3391
0
  int maxfeatures = -1, startindex = -1;
3392
0
  rectObj bbox;
3393
3394
0
  char **layers = NULL;
3395
0
  int numlayers = 0;
3396
3397
0
  char *sBBoxSrs = NULL;
3398
3399
0
  WFSGMLInfo gmlinfo;
3400
3401
0
  OWSGMLVersion outputformat = OWS_GML2; /* default output is GML 2.1 */
3402
0
  outputFormatObj *psFormat = NULL;
3403
3404
0
  int iNumberOfFeatures = 0;
3405
0
  int iResultTypeHits = 0;
3406
0
  int nMatchingFeatures = -1;
3407
0
  int bHasNextFeatures = MS_FALSE;
3408
3409
0
  char **papszGMLGroups = NULL;
3410
0
  char **papszGMLIncludeItems = NULL;
3411
0
  char **papszGMLGeometries = NULL;
3412
3413
0
  msIOContext *old_context = NULL;
3414
3415
  /* Initialize gml options */
3416
0
  msWFSInitGMLInfo(&gmlinfo);
3417
3418
0
  if (paramsObj->pszResultType != NULL) {
3419
0
    if (strcasecmp(paramsObj->pszResultType, "hits") == 0)
3420
0
      iResultTypeHits = 1;
3421
0
  }
3422
3423
0
  status = msWFSResolveStoredQuery(map, paramsObj, req);
3424
0
  if (status != MS_SUCCESS)
3425
0
    return status;
3426
3427
  /* typename is mandatory unless featureid is specified. We do not
3428
     support featureid */
3429
0
  if (paramsObj->pszTypeName == NULL && paramsObj->pszFeatureId == NULL) {
3430
0
    msSetError(MS_WFSERR, "Incomplete WFS request: %s parameter missing",
3431
0
               "msWFSGetFeature()",
3432
0
               (nWFSVersion >= OWS_2_0_0) ? "TYPENAMES" : "TYPENAME");
3433
0
    return msWFSException(
3434
0
        map, (nWFSVersion >= OWS_2_0_0) ? "typenames" : "typename",
3435
0
        MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
3436
0
  }
3437
3438
  /* Is it a WFS 2.0 GetFeatureById stored query with an unknown id ? */
3439
  /* If so, CITE interprets the spec as requesting to issue a
3440
   * OperationProcessingFailed */
3441
  /* exception. But it only checks that the HTTP error code is 403 or 404. So
3442
   * emit 404. */
3443
  /* A bit strange when a similar request but with RESOURCEID= should */
3444
  /* produce an empty FeatureCollection */
3445
0
  if (nWFSVersion >= OWS_2_0_0 && paramsObj->pszTypeName != NULL &&
3446
0
      strcmp(paramsObj->pszTypeName, "?") == 0 &&
3447
0
      paramsObj->pszFilter != NULL && paramsObj->pszFeatureId == NULL) {
3448
0
    CPLXMLNode *psDoc;
3449
3450
0
    psDoc = CPLParseXMLString(paramsObj->pszFilter);
3451
0
    if (psDoc != NULL) {
3452
0
      const char *rid;
3453
3454
0
      CPLStripXMLNamespace(psDoc, NULL, 1);
3455
0
      rid = CPLGetXMLValue(psDoc, "=Filter.ResourceId.rid", NULL);
3456
0
      if (rid != NULL) {
3457
0
        msSetError(MS_WFSERR, "Invalid rid '%s'", "msWFSGetFeature()", rid);
3458
0
        CPLDestroyXMLNode(psDoc);
3459
0
        return msWFSException(map, NULL, MS_OWS_ERROR_NOT_FOUND,
3460
0
                              paramsObj->pszVersion);
3461
0
      }
3462
0
      CPLDestroyXMLNode(psDoc);
3463
0
    }
3464
0
  }
3465
3466
0
  papszGMLGroups = (char **)calloc(map->numlayers, sizeof(char *));
3467
0
  papszGMLIncludeItems = (char **)calloc(map->numlayers, sizeof(char *));
3468
0
  papszGMLGeometries = (char **)calloc(map->numlayers, sizeof(char *));
3469
3470
0
  if (paramsObj->pszTypeName) {
3471
0
    int k, y, z;
3472
3473
0
    char **tokens;
3474
0
    int n = 0, i = 0;
3475
0
    char **papszPropertyName = NULL;
3476
0
    char **papszSortBy = NULL;
3477
3478
    /* keep a ref for layer use. */
3479
0
    gmlinfo._typename = paramsObj->pszTypeName;
3480
3481
    /* Parse comma-delimited list of type names (layers) */
3482
    /*  */
3483
    /* __TODO__ Need to handle type grouping, e.g. "(l1,l2),l3,l4" */
3484
    /*  */
3485
0
    layers = msStringSplit(gmlinfo._typename, ',', &numlayers);
3486
3487
    /* ==================================================================== */
3488
    /*      TODO: check if the typename contains namespaces (ex cdf:Other), */
3489
    /*      If that is the case extract only the layer name.                */
3490
    /* ==================================================================== */
3491
3492
0
    if (layers == NULL || numlayers < 1) {
3493
0
      msSetError(MS_WFSERR, "At least one type name required in %s parameter.",
3494
0
                 "msWFSGetFeature()",
3495
0
                 (nWFSVersion >= OWS_2_0_0) ? "TYPENAMES" : "TYPENAME");
3496
0
      return msWFSException(
3497
0
          map, (nWFSVersion >= OWS_2_0_0) ? "typenames" : "typename",
3498
0
          MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
3499
0
    }
3500
3501
    /* strip namespace if there is one :ex TYPENAME=cdf:Other */
3502
0
    for (i = 0; i < numlayers; i++) {
3503
0
      char *pszTmp = msStrdup(msWFSStripNS(layers[i]));
3504
0
      free(layers[i]);
3505
0
      layers[i] = pszTmp;
3506
0
    }
3507
3508
    /* Keep only selected layers, set to OFF by default. */
3509
0
    msWFSTurnOffAllLayer(map);
3510
3511
0
    papszPropertyName =
3512
0
        msWFSParsePropertyNameOrSortBy(paramsObj->pszPropertyName, numlayers);
3513
0
    if (papszPropertyName == NULL) {
3514
0
      msFreeCharArray(layers, numlayers);
3515
0
      msFreeCharArray(papszGMLGroups, map->numlayers);
3516
0
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3517
0
      msFreeCharArray(papszGMLGeometries, map->numlayers);
3518
0
      msSetError(MS_WFSERR,
3519
0
                 "Optional PROPERTYNAME parameter. A list of properties may be "
3520
0
                 "specified for each type name. Example "
3521
0
                 "TYPENAME=name1,name2&PROPERTYNAME=(prop1,prop2)(prop1)",
3522
0
                 "msWFSGetFeature()");
3523
0
      return msWFSException(map, "PROPERTYNAME",
3524
0
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3525
0
                            paramsObj->pszVersion);
3526
0
    }
3527
3528
0
    papszSortBy =
3529
0
        msWFSParsePropertyNameOrSortBy(paramsObj->pszSortBy, numlayers);
3530
0
    if (papszSortBy == NULL) {
3531
0
      msFreeCharArray(layers, numlayers);
3532
0
      msFreeCharArray(papszPropertyName, numlayers);
3533
0
      msFreeCharArray(papszGMLGroups, map->numlayers);
3534
0
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3535
0
      msFreeCharArray(papszGMLGeometries, map->numlayers);
3536
0
      msSetError(
3537
0
          MS_WFSERR,
3538
0
          "Optional SORTBY parameter. A list may be specified for each type "
3539
0
          "name. Example TYPENAME=name1,name2&SORTBY=(prop1,prop2)(prop1)",
3540
0
          "msWFSGetFeature()");
3541
0
      return msWFSException(map, "SORTBY", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3542
0
                            paramsObj->pszVersion);
3543
0
    }
3544
3545
0
    for (k = 0; k < numlayers; k++) {
3546
0
      layerObj *lp;
3547
0
      lp = msWFSGetLayerByName(map, ows_request, layers[k]);
3548
0
      if (lp != NULL) {
3549
0
        gmlItemListObj *itemList = NULL;
3550
0
        gmlGeometryListObj *geometryList = NULL;
3551
0
        gmlGroupListObj *groupList = NULL;
3552
3553
0
        lp->status = MS_ON;
3554
0
        if (lp->_template == NULL) {
3555
          /* Force setting a template to enable query. */
3556
0
          lp->_template = msStrdup("ttt.html");
3557
0
        }
3558
3559
        /*do an alias replace for the current layer*/
3560
0
        if (msLayerOpen(lp) == MS_SUCCESS &&
3561
0
            msLayerGetItems(lp) == MS_SUCCESS) {
3562
3563
0
          itemList = msGMLGetItems(lp, "G");
3564
0
          groupList = msGMLGetGroups(lp, "G");
3565
3566
0
          if (strcmp(papszSortBy[k], "!") != 0) {
3567
0
            status = msWFSApplySortBy(map, paramsObj, lp, papszSortBy[k],
3568
0
                                      itemList, groupList);
3569
0
            if (status != MS_SUCCESS) {
3570
0
              msFreeCharArray(papszPropertyName, numlayers);
3571
0
              msFreeCharArray(papszSortBy, numlayers);
3572
0
              msFreeCharArray(layers, numlayers);
3573
0
              msFreeCharArray(papszGMLGroups, map->numlayers);
3574
0
              msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3575
0
              msFreeCharArray(papszGMLGeometries, map->numlayers);
3576
0
              msGMLFreeItems(itemList);
3577
0
              msGMLFreeGroups(groupList);
3578
0
              return status;
3579
0
            }
3580
0
          }
3581
3582
0
          geometryList = msGMLGetGeometries(lp, "GFO", MS_TRUE);
3583
3584
          /* unalias, ungroup and validate that the property names passed are
3585
           * part of the items list*/
3586
0
          tokens = msStringSplit(papszPropertyName[k], ',', &n);
3587
0
          for (y = 0; y < n; y++) {
3588
0
            const char *pszPropertyNameItem = tokens[y];
3589
0
            int nGroupIndex = -1;
3590
3591
0
            if (strcasecmp(pszPropertyNameItem, "*") == 0 ||
3592
0
                strcasecmp(pszPropertyNameItem, "!") == 0)
3593
0
              continue;
3594
3595
0
            pszPropertyNameItem = msWFSStripNS(pszPropertyNameItem);
3596
0
            pszPropertyNameItem = msWFSUnaliasItem(lp, pszPropertyNameItem);
3597
0
            pszPropertyNameItem = msWFSUngroupItem(
3598
0
                lp, groupList, pszPropertyNameItem, &nGroupIndex);
3599
0
            if (nGroupIndex >= 0)
3600
0
              continue;
3601
3602
            /* Check that the property name is allowed (#3563) */
3603
            /*we need to check of the property name is the geometry name; In
3604
              that case it is a valid property name*/
3605
0
            if (!msWFSIsAllowedItem(itemList, pszPropertyNameItem)) {
3606
0
              for (z = 0; z < geometryList->numgeometries; z++) {
3607
0
                if (strcasecmp(pszPropertyNameItem,
3608
0
                               geometryList->geometries[z].name) == 0)
3609
0
                  break;
3610
0
              }
3611
3612
0
              if (z == geometryList->numgeometries) {
3613
0
                msSetError(MS_WFSERR, "Invalid PROPERTYNAME %s",
3614
0
                           "msWFSGetFeature()", tokens[y]);
3615
0
                msFreeCharArray(tokens, n);
3616
0
                msGMLFreeItems(itemList);
3617
0
                msGMLFreeGroups(groupList);
3618
0
                msGMLFreeGeometries(geometryList);
3619
0
                msFreeCharArray(papszPropertyName, numlayers);
3620
0
                msFreeCharArray(papszSortBy, numlayers);
3621
0
                msFreeCharArray(layers, numlayers);
3622
0
                msFreeCharArray(papszGMLGroups, map->numlayers);
3623
0
                msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3624
0
                msFreeCharArray(papszGMLGeometries, map->numlayers);
3625
0
                return msWFSException(map, "PROPERTYNAME",
3626
0
                                      MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3627
0
                                      paramsObj->pszVersion);
3628
0
              }
3629
0
            } else {
3630
0
              char *pszTmp = msStrdup(pszPropertyNameItem);
3631
0
              msFree(tokens[y]);
3632
0
              tokens[y] = pszTmp;
3633
0
            }
3634
0
          }
3635
3636
          /* Add mandatory items that are not explicitly listed. */
3637
          /* When the user specifies PROPERTYNAME, we might have to augment */
3638
          /* the list of returned parameters so as to validate against the
3639
           * schema. */
3640
          /* This is made clear in the WFS 1.0.0, 1.1.0 and 2.0.0 specs (#3319)
3641
           */
3642
0
          for (z = 0; z < itemList->numitems; z++) {
3643
0
            if (itemList->items[z].visible &&
3644
0
                itemList->items[z].minOccurs == 1) {
3645
0
              int bFound = MS_FALSE;
3646
0
              for (y = 0; y < n; y++) {
3647
0
                if (strcasecmp(tokens[y], "*") == 0 ||
3648
0
                    strcasecmp(tokens[y], "!") == 0 ||
3649
0
                    strcasecmp(tokens[y], itemList->items[z].name) == 0) {
3650
0
                  bFound = MS_TRUE;
3651
0
                  break;
3652
0
                }
3653
0
              }
3654
0
              if (!bFound) {
3655
0
                tokens = (char **)realloc(tokens, sizeof(char *) * (n + 1));
3656
0
                tokens[n] = msStrdup(itemList->items[z].name);
3657
0
                n++;
3658
0
              }
3659
0
            }
3660
0
          }
3661
3662
          /* Rebuild papszPropertyName[k] */
3663
0
          msFree(papszPropertyName[k]);
3664
0
          papszPropertyName[k] = NULL;
3665
0
          for (y = 0; y < n; y++) {
3666
0
            if (y == 0)
3667
0
              papszPropertyName[k] = msStrdup(tokens[y]);
3668
0
            else {
3669
0
              papszPropertyName[k] =
3670
0
                  msStringConcatenate(papszPropertyName[k], ",");
3671
0
              papszPropertyName[k] =
3672
0
                  msStringConcatenate(papszPropertyName[k], tokens[y]);
3673
0
            }
3674
0
          }
3675
3676
0
          msFreeCharArray(tokens, n);
3677
0
          msLayerClose(lp);
3678
3679
0
          if (strlen(papszPropertyName[k]) > 0) {
3680
3681
0
            if (strcasecmp(papszPropertyName[k], "*") == 0) {
3682
              /* Add all non-excluded items, including optional ones */
3683
0
              msFree(papszPropertyName[k]);
3684
0
              papszPropertyName[k] = NULL;
3685
0
              for (z = 0; z < itemList->numitems; z++) {
3686
0
                if (itemList->items[z].visible) {
3687
0
                  if (papszPropertyName[k] != NULL)
3688
0
                    papszPropertyName[k] =
3689
0
                        msStringConcatenate(papszPropertyName[k], ",");
3690
0
                  papszPropertyName[k] = msStringConcatenate(
3691
0
                      papszPropertyName[k], itemList->items[z].name);
3692
0
                }
3693
0
              }
3694
0
              if (papszPropertyName[k])
3695
0
                papszGMLIncludeItems[lp->index] =
3696
0
                    msStrdup(papszPropertyName[k]);
3697
0
              else
3698
0
                papszGMLIncludeItems[lp->index] = msStrdup("");
3699
3700
              /* The user didn't specify explicitly PROPERTYNAME for this layer
3701
               */
3702
0
            } else if (strcasecmp(papszPropertyName[k], "!") == 0) {
3703
              /* Add all non-excluded items, but only default and mandatory ones
3704
               * (and geometry) */
3705
0
              msFree(papszPropertyName[k]);
3706
0
              papszPropertyName[k] = NULL;
3707
0
              for (z = 0; z < itemList->numitems; z++) {
3708
0
                if (itemList->items[z].visible &&
3709
0
                    (itemList->items[z].outputByDefault == 1 ||
3710
0
                     itemList->items[z].minOccurs == 1)) {
3711
0
                  if (papszPropertyName[k] != NULL)
3712
0
                    papszPropertyName[k] =
3713
0
                        msStringConcatenate(papszPropertyName[k], ",");
3714
0
                  papszPropertyName[k] = msStringConcatenate(
3715
0
                      papszPropertyName[k], itemList->items[z].name);
3716
0
                }
3717
0
              }
3718
0
              if (papszPropertyName[k])
3719
0
                papszGMLIncludeItems[lp->index] =
3720
0
                    msStrdup(papszPropertyName[k]);
3721
0
              else
3722
0
                papszGMLIncludeItems[lp->index] = msStrdup("");
3723
3724
0
            } else {
3725
0
              char *pszGeometryList = NULL;
3726
3727
0
              for (z = 0; z < groupList->numgroups; z++) {
3728
0
                const char *pszNeedle =
3729
0
                    strstr(papszPropertyName[k], groupList->groups[z].name);
3730
0
                char chAfterNeedle = 0;
3731
0
                if (pszNeedle != NULL)
3732
0
                  chAfterNeedle =
3733
0
                      papszPropertyName[k][strlen(groupList->groups[z].name)];
3734
0
                if (pszNeedle != NULL &&
3735
0
                    (chAfterNeedle == '\0' || chAfterNeedle == ',')) {
3736
0
                  int w;
3737
0
                  for (w = 0; w < groupList->groups[z].numitems; w++) {
3738
0
                    if (strstr(papszPropertyName[k],
3739
0
                               groupList->groups[z].items[w]) == NULL) {
3740
0
                      papszPropertyName[k] =
3741
0
                          msStringConcatenate(papszPropertyName[k], ",");
3742
0
                      papszPropertyName[k] = msStringConcatenate(
3743
0
                          papszPropertyName[k], groupList->groups[z].items[w]);
3744
0
                    }
3745
0
                  }
3746
0
                }
3747
0
              }
3748
3749
              // Set the "gml_select_items" metadata items that is used by
3750
              // msQueryByRect() to restrict the number of properties to
3751
              // select for faster results.
3752
0
              if (msOWSLookupMetadata(&(lp->metadata), "OFG", "groups") ==
3753
0
                  NULL) {
3754
0
                auto properties = msStringSplit(papszPropertyName[k], ',');
3755
0
                std::string selectItems;
3756
0
                const char *featureid =
3757
0
                    msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
3758
0
                bool foundFeatureId = false;
3759
0
                for (const auto &prop : properties) {
3760
0
                  bool isGeom = false;
3761
0
                  const char *propNoNS = prop.c_str();
3762
0
                  const char *pszColon = strchr(propNoNS, ':');
3763
0
                  if (pszColon)
3764
0
                    propNoNS = pszColon + 1;
3765
0
                  for (z = 0; z < geometryList->numgeometries; z++) {
3766
0
                    if (strcasecmp(propNoNS,
3767
0
                                   geometryList->geometries[z].name) == 0) {
3768
0
                      isGeom = true;
3769
0
                      break;
3770
0
                    }
3771
0
                  }
3772
0
                  if (!isGeom) {
3773
0
                    if (!selectItems.empty())
3774
0
                      selectItems += ',';
3775
0
                    selectItems += prop;
3776
0
                  }
3777
0
                  if (featureid && featureid == prop) {
3778
0
                    foundFeatureId = true;
3779
0
                  }
3780
0
                }
3781
0
                if (featureid && !foundFeatureId) {
3782
0
                  if (!selectItems.empty())
3783
0
                    selectItems += ',';
3784
0
                  selectItems += featureid;
3785
0
                }
3786
0
                if (!selectItems.empty()) {
3787
0
                  msInsertHashTable(&(lp->metadata), "gml_select_items",
3788
0
                                    selectItems.c_str());
3789
0
                }
3790
0
              }
3791
3792
0
              papszGMLIncludeItems[lp->index] = msStrdup(papszPropertyName[k]);
3793
3794
0
              for (z = 0; z < geometryList->numgeometries; z++) {
3795
0
                if (strstr(papszPropertyName[k],
3796
0
                           geometryList->geometries[z].name) != NULL) {
3797
0
                  if (pszGeometryList != NULL)
3798
0
                    pszGeometryList = msStringConcatenate(pszGeometryList, ",");
3799
0
                  pszGeometryList = msStringConcatenate(
3800
0
                      pszGeometryList, geometryList->geometries[z].name);
3801
0
                }
3802
0
              }
3803
3804
0
              if (pszGeometryList != NULL) {
3805
0
                papszGMLGeometries[lp->index] = msStrdup(pszGeometryList);
3806
0
                msFree(pszGeometryList);
3807
0
              } else {
3808
0
                papszGMLGeometries[lp->index] = msStrdup("none");
3809
0
              }
3810
0
            }
3811
0
          } else { /*empty string*/
3812
0
            papszGMLGeometries[lp->index] = msStrdup("none");
3813
0
          }
3814
0
        } else {
3815
0
          msFreeCharArray(papszPropertyName, numlayers);
3816
0
          msFreeCharArray(papszSortBy, numlayers);
3817
0
          msFreeCharArray(layers, numlayers);
3818
0
          msFreeCharArray(papszGMLGroups, map->numlayers);
3819
0
          msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3820
0
          msFreeCharArray(papszGMLGeometries, map->numlayers);
3821
0
          msGMLFreeItems(itemList);
3822
0
          msGMLFreeGroups(groupList);
3823
0
          msGMLFreeGeometries(geometryList);
3824
0
          return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
3825
0
                                paramsObj->pszVersion);
3826
0
        }
3827
3828
0
        msGMLFreeItems(itemList);
3829
0
        msGMLFreeGroups(groupList);
3830
0
        msGMLFreeGeometries(geometryList);
3831
0
      } else {
3832
        /* Requested layer name was not in capabilities:
3833
         * either it just doesn't exist
3834
         */
3835
0
        msSetError(MS_WFSERR,
3836
0
                   "TYPENAME '%s' doesn't exist in this server.  Please check "
3837
0
                   "the capabilities and reformulate your request.",
3838
0
                   "msWFSGetFeature()", layers[k]);
3839
0
        msFreeCharArray(papszPropertyName, numlayers);
3840
0
        msFreeCharArray(papszSortBy, numlayers);
3841
0
        msFreeCharArray(layers, numlayers);
3842
0
        msFreeCharArray(papszGMLGroups, map->numlayers);
3843
0
        msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3844
0
        msFreeCharArray(papszGMLGeometries, map->numlayers);
3845
0
        return msWFSException(map, "typename",
3846
0
                              MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3847
0
                              paramsObj->pszVersion);
3848
0
      }
3849
0
    }
3850
3851
0
    msFreeCharArray(papszPropertyName, numlayers);
3852
0
    msFreeCharArray(papszSortBy, numlayers);
3853
0
  }
3854
3855
  /* Validate outputformat */
3856
0
  status = msWFSGetGMLOutputFormat(paramsObj, &gmlinfo, nWFSVersion);
3857
0
  if (status < 0) {
3858
0
    psFormat = msWFSGetOtherOutputFormat(map, paramsObj);
3859
0
    if (psFormat == NULL) {
3860
0
      msFreeCharArray(layers, numlayers);
3861
0
      msFreeCharArray(papszGMLGroups, map->numlayers);
3862
0
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3863
0
      msFreeCharArray(papszGMLGeometries, map->numlayers);
3864
0
      return msWFSException(map, "outputformat",
3865
0
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3866
0
                            paramsObj->pszVersion);
3867
0
    }
3868
    // only GML is supported for resultType=hits, so ignore any other
3869
    // outputformat
3870
0
    if (iResultTypeHits == 1) {
3871
0
      psFormat = NULL;
3872
0
    }
3873
0
  } else {
3874
0
    outputformat = (OWSGMLVersion)status;
3875
0
  }
3876
3877
0
  msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, iResultTypeHits,
3878
0
                                        &maxfeatures, &startindex);
3879
3880
0
  status = msWFSAnalyzeBBOX(map, paramsObj, &bbox, &sBBoxSrs);
3881
0
  if (status != 0) {
3882
0
    msFreeCharArray(layers, numlayers);
3883
0
    msFreeCharArray(papszGMLGroups, map->numlayers);
3884
0
    msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3885
0
    msFreeCharArray(papszGMLGeometries, map->numlayers);
3886
0
    return status;
3887
0
  }
3888
3889
0
  if (iResultTypeHits == 1) {
3890
0
    map->query.only_cache_result_count = MS_TRUE;
3891
0
  } else {
3892
0
    msWFSSetShapeCache(map);
3893
0
  }
3894
3895
0
  status = msWFSRetrieveFeatures(
3896
0
      map, ows_request, paramsObj, &gmlinfo, paramsObj->pszFilter,
3897
0
      paramsObj->pszBbox != NULL, sBBoxSrs, bbox, paramsObj->pszFeatureId,
3898
0
      layers, numlayers, maxfeatures, nWFSVersion, &iNumberOfFeatures,
3899
0
      &bHasNextFeatures);
3900
0
  if (status != MS_SUCCESS) {
3901
0
    msFreeCharArray(layers, numlayers);
3902
0
    msFree(sBBoxSrs);
3903
0
    msFreeCharArray(papszGMLGroups, map->numlayers);
3904
0
    msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3905
0
    msFreeCharArray(papszGMLGeometries, map->numlayers);
3906
0
    return status;
3907
0
  }
3908
3909
  /* ----------------------------------------- */
3910
  /* Now compute nMatchingFeatures for WFS 2.0 */
3911
  /* ----------------------------------------- */
3912
3913
0
  nMatchingFeatures = msWFSComputeMatchingFeatures(
3914
0
      map, ows_request, paramsObj, iNumberOfFeatures, maxfeatures, &gmlinfo,
3915
0
      bbox, sBBoxSrs, layers, numlayers, nWFSVersion);
3916
3917
0
  msFreeCharArray(layers, numlayers);
3918
3919
0
  msFree(sBBoxSrs);
3920
0
  sBBoxSrs = NULL;
3921
3922
  /*
3923
  ** GML Header generation.
3924
  */
3925
3926
0
  status = MS_SUCCESS;
3927
3928
0
  if (psFormat == NULL) {
3929
0
    if (paramsObj->countGetFeatureById == 1 && maxfeatures != 0 &&
3930
0
        iResultTypeHits == 0) {
3931
      /* When there's a single  urn:ogc:def:query:OGC-WFS::GetFeatureById
3932
       * GetFeature request */
3933
      /* we must not output a FeatureCollection but the object itself */
3934
      /* We do that as a post-processing step, so let print into a buffer and
3935
       * modify the */
3936
      /* XML afterwards */
3937
0
      old_context = msIO_pushStdoutToBufferAndGetOldContext();
3938
0
    } else {
3939
0
      msIO_setHeader("Content-Type", "%s; charset=UTF-8",
3940
0
                     gmlinfo.output_mime_type);
3941
0
      msIO_sendHeaders();
3942
0
    }
3943
3944
0
    status = msWFSGetFeature_GMLPreamble(
3945
0
        map, req, &gmlinfo, paramsObj, outputformat, iResultTypeHits,
3946
0
        iNumberOfFeatures, nMatchingFeatures, maxfeatures, bHasNextFeatures,
3947
0
        nWFSVersion);
3948
0
    if (status != MS_SUCCESS) {
3949
0
      if (old_context != NULL)
3950
0
        msIO_restoreOldStdoutContext(old_context);
3951
0
      msWFSCleanupGMLInfo(&gmlinfo);
3952
0
      msFreeCharArray(papszGMLGroups, map->numlayers);
3953
0
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3954
0
      msFreeCharArray(papszGMLGeometries, map->numlayers);
3955
0
      return MS_FAILURE;
3956
0
    }
3957
0
  }
3958
3959
0
  {
3960
0
    int i;
3961
0
    for (i = 0; i < map->numlayers; i++) {
3962
0
      layerObj *lp = GET_LAYER(map, i);
3963
0
      if (papszGMLGroups[i])
3964
0
        msInsertHashTable(&(lp->metadata), "GML_GROUPS", papszGMLGroups[i]);
3965
0
      if (papszGMLIncludeItems[i])
3966
0
        msInsertHashTable(&(lp->metadata), "GML_INCLUDE_ITEMS",
3967
0
                          papszGMLIncludeItems[i]);
3968
0
      if (papszGMLGeometries[i])
3969
0
        msInsertHashTable(&(lp->metadata), "GML_GEOMETRIES",
3970
0
                          papszGMLGeometries[i]);
3971
0
    }
3972
0
  }
3973
3974
  /* handle case of maxfeatures = 0 */
3975
  /*internally use a start index that start with 0 as the first index*/
3976
0
  if (psFormat == NULL) {
3977
0
    if (maxfeatures != 0 && iResultTypeHits == 0) {
3978
0
      int bWFS2MultipleFeatureCollection = MS_FALSE;
3979
3980
      /* Would make sense for WFS 1.1.0 too ! See #3576 */
3981
0
      int bUseURN = (nWFSVersion == OWS_2_0_0);
3982
0
      const char *useurn =
3983
0
          msOWSLookupMetadata(&(map->web.metadata), "F", "return_srs_as_urn");
3984
0
      if (useurn && strcasecmp(useurn, "true") == 0)
3985
0
        bUseURN = 1;
3986
0
      else if (useurn && strcasecmp(useurn, "false") == 0)
3987
0
        bUseURN = 0;
3988
3989
      /* For WFS 2.0, when we request several types, we must present each type
3990
       */
3991
      /* in its own FeatureCollection (§ 11.3.3.5 ) */
3992
0
      if (nWFSVersion >= OWS_2_0_0 && iResultTypeHits != 1) {
3993
0
        int i;
3994
0
        int nLayersWithFeatures = 0;
3995
0
        for (i = 0; i < map->numlayers; i++) {
3996
0
          layerObj *lp = GET_LAYER(map, i);
3997
0
          if (lp->resultcache && lp->resultcache->numresults > 0)
3998
0
            nLayersWithFeatures++;
3999
0
        }
4000
0
        if (nLayersWithFeatures > 1) {
4001
0
          char timestring[100];
4002
0
          resultCacheObj **saveResultCache = (resultCacheObj **)msSmallMalloc(
4003
0
              map->numlayers * sizeof(resultCacheObj *));
4004
0
          int iLastNonEmptyLayer = -1;
4005
4006
0
          msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
4007
4008
          /* Emit global bounds */
4009
0
          msGMLWriteWFSBounds(map, stdout, "  ", outputformat, nWFSVersion,
4010
0
                              bUseURN);
4011
4012
          /* Save the result cache that contains the features that we want to */
4013
          /* emit in the response */
4014
0
          for (i = 0; i < map->numlayers; i++) {
4015
0
            layerObj *lp = GET_LAYER(map, i);
4016
0
            saveResultCache[i] = lp->resultcache;
4017
0
            if (lp->resultcache && lp->resultcache->numresults > 0) {
4018
0
              iLastNonEmptyLayer = i;
4019
0
            }
4020
0
            lp->resultcache = NULL;
4021
0
          }
4022
4023
          /* Just dump one layer at a time */
4024
0
          for (i = 0; i < map->numlayers; i++) {
4025
0
            layerObj *lp = GET_LAYER(map, i);
4026
0
            lp->resultcache = saveResultCache[i];
4027
0
            if (lp->resultcache && lp->resultcache->numresults > 0) {
4028
0
              msIO_fprintf(stdout, "  <wfs:member>\n");
4029
0
              msIO_fprintf(
4030
0
                  stdout,
4031
0
                  "   <wfs:FeatureCollection timeStamp=\"%s\" numberMatched=\"",
4032
0
                  timestring);
4033
0
              if (i < iLastNonEmptyLayer || nMatchingFeatures >= 0)
4034
0
                msIO_fprintf(stdout, "%d", lp->resultcache->numresults);
4035
0
              else
4036
0
                msIO_fprintf(stdout, "unknown");
4037
0
              msIO_fprintf(stdout, "\" numberReturned=\"%d\">\n",
4038
0
                           lp->resultcache->numresults);
4039
4040
0
              msGMLWriteWFSQuery(map, stdout, gmlinfo.user_namespace_prefix,
4041
0
                                 outputformat, nWFSVersion, bUseURN, MS_FALSE);
4042
0
              msIO_fprintf(stdout, "   </wfs:FeatureCollection>\n");
4043
0
              msIO_fprintf(stdout, "  </wfs:member>\n");
4044
0
            }
4045
0
            lp->resultcache = NULL;
4046
0
          }
4047
4048
          /* Restore for later cleanup */
4049
0
          for (i = 0; i < map->numlayers; i++) {
4050
0
            layerObj *lp = GET_LAYER(map, i);
4051
0
            lp->resultcache = saveResultCache[i];
4052
0
          }
4053
0
          msFree(saveResultCache);
4054
4055
0
          bWFS2MultipleFeatureCollection = MS_TRUE;
4056
0
        }
4057
0
      }
4058
4059
0
      if (!bWFS2MultipleFeatureCollection) {
4060
0
        msGMLWriteWFSQuery(map, stdout, gmlinfo.user_namespace_prefix,
4061
0
                           outputformat, nWFSVersion, bUseURN, MS_FALSE);
4062
0
      }
4063
4064
0
      status = MS_SUCCESS;
4065
0
    }
4066
0
  } else {
4067
0
    mapservObj *mapserv = msAllocMapServObj();
4068
4069
    /* Setup dummy mapserv object */
4070
0
    mapserv->sendheaders = MS_TRUE;
4071
0
    mapserv->map = map;
4072
0
    msFreeCgiObj(mapserv->request);
4073
0
    mapserv->request = req;
4074
0
    map->querymap.status = MS_FALSE;
4075
4076
0
    if (nMatchingFeatures >= 0) {
4077
0
      char szMatchingFeatures[12];
4078
0
      snprintf(szMatchingFeatures, sizeof(szMatchingFeatures), "%d",
4079
0
               nMatchingFeatures);
4080
0
      msSetOutputFormatOption(psFormat, "_matching_features_",
4081
0
                              szMatchingFeatures);
4082
0
    } else {
4083
0
      msSetOutputFormatOption(psFormat, "_matching_features_", "");
4084
0
    }
4085
0
    status = msReturnTemplateQuery(mapserv, psFormat->name, NULL);
4086
4087
0
    mapserv->request = NULL;
4088
0
    mapserv->map = NULL;
4089
4090
0
    msFreeMapServObj(mapserv);
4091
4092
0
    if (status != MS_SUCCESS) {
4093
0
      msFreeCharArray(papszGMLGroups, map->numlayers);
4094
0
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
4095
0
      msFreeCharArray(papszGMLGeometries, map->numlayers);
4096
0
      return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
4097
0
                            paramsObj->pszVersion);
4098
0
    }
4099
0
  }
4100
4101
0
  msFreeCharArray(papszGMLGroups, map->numlayers);
4102
0
  msFreeCharArray(papszGMLIncludeItems, map->numlayers);
4103
0
  msFreeCharArray(papszGMLGeometries, map->numlayers);
4104
4105
0
  if (psFormat == NULL && status == MS_SUCCESS) {
4106
0
    msWFSGetFeature_GMLPostfix(&gmlinfo, outputformat, maxfeatures,
4107
0
                               iResultTypeHits, iNumberOfFeatures, nWFSVersion);
4108
0
  }
4109
4110
  /*Special case for a single urn:ogc:def:query:OGC-WFS::GetFeatureById
4111
   * GetFeature request */
4112
0
  if (old_context != NULL) {
4113
0
    msIOContext *new_context;
4114
0
    msIOBuffer *buffer;
4115
0
    CPLXMLNode *psRoot;
4116
4117
0
    new_context = msIO_getHandler(stdout);
4118
0
    buffer = (msIOBuffer *)new_context->cbData;
4119
0
    psRoot = CPLParseXMLString((const char *)buffer->data);
4120
0
    msIO_restoreOldStdoutContext(old_context);
4121
0
    if (psRoot != NULL) {
4122
0
      CPLXMLNode *psFeatureCollection =
4123
0
          CPLGetXMLNode(psRoot, "=wfs:FeatureCollection");
4124
0
      if (psFeatureCollection != NULL) {
4125
0
        CPLXMLNode *psMember = CPLGetXMLNode(psFeatureCollection, "wfs:member");
4126
0
        if (psMember != NULL && psMember->psChild != NULL) {
4127
0
          CPLXMLNode *psIter;
4128
0
          CPLXMLNode *psFeatureNode;
4129
0
          char *pszTmpXML;
4130
4131
          /* Transfer all namespaces of FeatureCollection to the Feature */
4132
0
          psFeatureNode = psMember->psChild;
4133
0
          psIter = psFeatureCollection->psChild;
4134
0
          while (psIter != NULL) {
4135
0
            if (psIter->eType == CXT_Attribute &&
4136
0
                (strncmp(psIter->pszValue, "xmlns:", strlen("xmlns:")) == 0 ||
4137
0
                 strncmp(psIter->pszValue, "xsi:", strlen("xsi:")) == 0)) {
4138
0
              CPLXMLNode *psIterNext = psIter->psNext;
4139
0
              psIter->psNext = NULL;
4140
0
              CPLAddXMLChild(psFeatureNode, CPLCloneXMLTree(psIter));
4141
0
              psIter->psNext = psIterNext;
4142
0
            }
4143
0
            psIter = psIter->psNext;
4144
0
          }
4145
4146
0
          pszTmpXML = CPLSerializeXMLTree(psFeatureNode);
4147
0
          msIO_setHeader("Content-Type", "%s; charset=UTF-8",
4148
0
                         gmlinfo.output_mime_type);
4149
0
          msIO_sendHeaders();
4150
0
          msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
4151
0
          msIO_printf("%s", pszTmpXML);
4152
0
          CPLFree(pszTmpXML);
4153
0
        } else {
4154
0
          status = msWFSException(map, NULL, MS_OWS_ERROR_NOT_FOUND,
4155
0
                                  paramsObj->pszVersion);
4156
0
        }
4157
0
      }
4158
0
      CPLDestroyXMLNode(psRoot);
4159
0
    }
4160
0
  }
4161
4162
0
  msWFSCleanupGMLInfo(&gmlinfo);
4163
4164
0
  return status;
4165
0
}
4166
4167
/*
4168
** msWFSGetPropertyValue()
4169
*/
4170
static int msWFSGetPropertyValue(mapObj *map, wfsParamsObj *paramsObj,
4171
                                 cgiRequestObj *req, owsRequestObj *ows_request,
4172
0
                                 int nWFSVersion) {
4173
0
  int status;
4174
0
  int maxfeatures = -1, startindex = -1;
4175
0
  rectObj bbox;
4176
4177
0
  char *sBBoxSrs = NULL;
4178
4179
0
  WFSGMLInfo gmlinfo;
4180
4181
0
  OWSGMLVersion outputformat = OWS_GML2; /* default output is GML 2.1 */
4182
4183
0
  int iNumberOfFeatures = 0;
4184
0
  int iResultTypeHits = 0;
4185
0
  int nMatchingFeatures = -1;
4186
0
  int bHasNextFeatures = MS_FALSE;
4187
4188
0
  const char *pszTypeName;
4189
0
  layerObj *lp;
4190
4191
0
  char *pszGMLGroups = NULL;
4192
0
  char *pszGMLIncludeItems = NULL;
4193
0
  char *pszGMLGeometries = NULL;
4194
4195
0
  status = msWFSResolveStoredQuery(map, paramsObj, req);
4196
0
  if (status != MS_SUCCESS)
4197
0
    return status;
4198
4199
0
  if (paramsObj->pszTypeName == NULL) {
4200
0
    msSetError(MS_WFSERR, "Incomplete WFS request: TYPENAMES parameter missing",
4201
0
               "msWFSGetPropertyValue()");
4202
0
    return msWFSException(map, "typenames",
4203
0
                          MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4204
0
                          paramsObj->pszVersion);
4205
0
  }
4206
4207
0
  if (paramsObj->pszValueReference == NULL) {
4208
0
    msSetError(MS_WFSERR,
4209
0
               "Incomplete WFS request: VALUEREFERENCE parameter missing",
4210
0
               "msWFSGetPropertyValue()");
4211
0
    return msWFSException(map, "valuereference",
4212
0
                          MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4213
0
                          paramsObj->pszVersion);
4214
0
  }
4215
4216
  /* Keep only selected layers, set to OFF by default. */
4217
0
  msWFSTurnOffAllLayer(map);
4218
4219
  /* Remove namespace prefix */
4220
0
  pszTypeName = msWFSStripNS(paramsObj->pszTypeName);
4221
4222
0
  lp = msWFSGetLayerByName(map, ows_request, pszTypeName);
4223
0
  if (lp != NULL) {
4224
0
    gmlItemListObj *itemList = NULL;
4225
0
    gmlGeometryListObj *geometryList = NULL;
4226
0
    gmlGroupListObj *groupList = NULL;
4227
4228
0
    lp->status = MS_ON;
4229
0
    if (lp->_template == NULL) {
4230
      /* Force setting a template to enable query. */
4231
0
      lp->_template = msStrdup("ttt.html");
4232
0
    }
4233
4234
    /*do an alias replace for the current layer*/
4235
0
    if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
4236
0
      const char *pszValueReference;
4237
0
      int z;
4238
0
      int nGroupIndex = -1;
4239
4240
0
      itemList = msGMLGetItems(lp, "G");
4241
0
      groupList = msGMLGetGroups(lp, "G");
4242
4243
0
      if (paramsObj->pszSortBy != NULL &&
4244
0
          strcmp(paramsObj->pszSortBy, "!") != 0) {
4245
0
        status = msWFSApplySortBy(map, paramsObj, lp, paramsObj->pszSortBy,
4246
0
                                  itemList, groupList);
4247
0
        if (status != MS_SUCCESS) {
4248
0
          msGMLFreeItems(itemList);
4249
0
          msGMLFreeGroups(groupList);
4250
0
          return status;
4251
0
        }
4252
0
      }
4253
4254
0
      geometryList = msGMLGetGeometries(lp, "GFO", MS_TRUE);
4255
4256
0
      pszValueReference = msWFSStripNS(paramsObj->pszValueReference);
4257
0
      pszValueReference = msWFSUnaliasItem(lp, pszValueReference);
4258
0
      pszValueReference =
4259
0
          msWFSUngroupItem(lp, groupList, pszValueReference, &nGroupIndex);
4260
4261
0
      if (strcmp(paramsObj->pszValueReference, "@gml:id") == 0) {
4262
0
        pszGMLGroups = msStrdup("");
4263
0
        pszGMLIncludeItems = msStrdup(paramsObj->pszValueReference);
4264
0
        pszGMLGeometries = msStrdup("none");
4265
0
      } else if (nGroupIndex >= 0) {
4266
0
        int w;
4267
0
        char *pszItems = NULL;
4268
0
        for (w = 0; w < groupList->groups[nGroupIndex].numitems; w++) {
4269
0
          if (w > 0)
4270
0
            pszItems = msStringConcatenate(pszItems, ",");
4271
0
          pszItems = msStringConcatenate(
4272
0
              pszItems, groupList->groups[nGroupIndex].items[w]);
4273
0
        }
4274
0
        pszGMLGroups = msStrdup(groupList->groups[nGroupIndex].name);
4275
0
        pszGMLIncludeItems = msStrdup(pszItems);
4276
0
        pszGMLGeometries = msStrdup("none");
4277
0
        msFree(pszItems);
4278
0
      } else {
4279
0
        pszGMLGroups = msStrdup("");
4280
4281
        /* Check that the property name is allowed (#3563) */
4282
        /*we need to check of the property name is the geometry name; In that
4283
           case it is a valid property name*/
4284
0
        if (!msWFSIsAllowedItem(itemList, pszValueReference)) {
4285
0
          for (z = 0; z < geometryList->numgeometries; z++) {
4286
0
            if (strcasecmp(pszValueReference,
4287
0
                           geometryList->geometries[z].name) == 0)
4288
0
              break;
4289
0
          }
4290
4291
0
          if (z == geometryList->numgeometries) {
4292
0
            msSetError(MS_WFSERR, "Invalid VALUEREFERENCE %s",
4293
0
                       "msWFSGetPropertyValue()", paramsObj->pszValueReference);
4294
0
            msFree(pszGMLGroups);
4295
0
            msGMLFreeItems(itemList);
4296
0
            msGMLFreeGroups(groupList);
4297
0
            msGMLFreeGeometries(geometryList);
4298
0
            return msWFSException(map, "valuereference",
4299
0
                                  MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4300
0
                                  paramsObj->pszVersion);
4301
0
          } else {
4302
0
            pszGMLIncludeItems = msStrdup("");
4303
0
            pszGMLGeometries = msStrdup(pszValueReference);
4304
0
          }
4305
0
        } else {
4306
0
          pszGMLIncludeItems = msStrdup(pszValueReference);
4307
0
          pszGMLGeometries = msStrdup("none");
4308
0
        }
4309
0
      }
4310
4311
0
      msLayerClose(lp);
4312
4313
0
      msGMLFreeItems(itemList);
4314
0
      msGMLFreeGroups(groupList);
4315
0
      msGMLFreeGeometries(geometryList);
4316
0
    }
4317
0
  } else {
4318
    /* Requested layer name was not in capabilities:
4319
     * either it just doesn't exist
4320
     */
4321
0
    msSetError(MS_WFSERR,
4322
0
               "TYPENAME '%s' doesn't exist in this server.  Please check the "
4323
0
               "capabilities and reformulate your request.",
4324
0
               "msWFSGetFeature()", paramsObj->pszTypeName);
4325
0
    msFree(pszGMLGroups);
4326
0
    msFree(pszGMLIncludeItems);
4327
0
    msFree(pszGMLGeometries);
4328
0
    return msWFSException(map, "typename", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4329
0
                          paramsObj->pszVersion);
4330
0
  }
4331
4332
  /* Initialize gml options */
4333
0
  msWFSInitGMLInfo(&gmlinfo);
4334
4335
0
  if (paramsObj->pszResultType != NULL) {
4336
0
    if (strcasecmp(paramsObj->pszResultType, "hits") == 0)
4337
0
      iResultTypeHits = 1;
4338
0
  }
4339
4340
0
  gmlinfo._typename = paramsObj->pszTypeName;
4341
4342
  /* Validate outputformat */
4343
0
  status = msWFSGetGMLOutputFormat(paramsObj, &gmlinfo, nWFSVersion);
4344
0
  if (status < 0) {
4345
0
    msSetError(
4346
0
        MS_WFSERR,
4347
0
        "'%s' is not a permitted output format for GetPropertyValue request.",
4348
0
        "msWFSGetPropertyValue()", paramsObj->pszOutputFormat);
4349
0
    msFree(pszGMLGroups);
4350
0
    msFree(pszGMLIncludeItems);
4351
0
    msFree(pszGMLGeometries);
4352
0
    return msWFSException(map, "outputformat",
4353
0
                          MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4354
0
                          paramsObj->pszVersion);
4355
0
  } else {
4356
0
    outputformat = (OWSGMLVersion)status;
4357
0
  }
4358
4359
0
  msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, iResultTypeHits,
4360
0
                                        &maxfeatures, &startindex);
4361
4362
0
  status = msWFSAnalyzeBBOX(map, paramsObj, &bbox, &sBBoxSrs);
4363
0
  if (status != 0) {
4364
0
    msFree(pszGMLGroups);
4365
0
    msFree(pszGMLIncludeItems);
4366
0
    msFree(pszGMLGeometries);
4367
0
    return status;
4368
0
  }
4369
4370
0
  if (iResultTypeHits == 1) {
4371
0
    map->query.only_cache_result_count = MS_TRUE;
4372
0
  } else {
4373
0
    msWFSSetShapeCache(map);
4374
0
  }
4375
4376
0
  status = msWFSRetrieveFeatures(
4377
0
      map, ows_request, paramsObj, &gmlinfo, paramsObj->pszFilter,
4378
0
      paramsObj->pszBbox != NULL, sBBoxSrs, bbox, paramsObj->pszFeatureId,
4379
0
      (char **)&pszTypeName, 1, maxfeatures, nWFSVersion, &iNumberOfFeatures,
4380
0
      &bHasNextFeatures);
4381
0
  if (status != MS_SUCCESS) {
4382
0
    msFree(sBBoxSrs);
4383
0
    msFree(pszGMLGroups);
4384
0
    msFree(pszGMLIncludeItems);
4385
0
    msFree(pszGMLGeometries);
4386
0
    return status;
4387
0
  }
4388
4389
  /* ----------------------------------------- */
4390
  /* Now compute nMatchingFeatures for WFS 2.0 */
4391
  /* ----------------------------------------- */
4392
4393
0
  nMatchingFeatures = msWFSComputeMatchingFeatures(
4394
0
      map, ows_request, paramsObj, iNumberOfFeatures, maxfeatures, &gmlinfo,
4395
0
      bbox, sBBoxSrs, (char **)&pszTypeName, 1, nWFSVersion);
4396
4397
0
  msFree(sBBoxSrs);
4398
0
  sBBoxSrs = NULL;
4399
4400
  /*
4401
  ** GML Header generation.
4402
  */
4403
0
  msIO_setHeader("Content-Type", "%s; charset=UTF-8", gmlinfo.output_mime_type);
4404
0
  msIO_sendHeaders();
4405
4406
0
  status = msWFSGetFeature_GMLPreamble(
4407
0
      map, req, &gmlinfo, paramsObj, outputformat, iResultTypeHits,
4408
0
      iNumberOfFeatures, nMatchingFeatures, maxfeatures, bHasNextFeatures,
4409
0
      nWFSVersion);
4410
4411
0
  if (status == MS_SUCCESS && maxfeatures != 0 && iResultTypeHits == 0) {
4412
0
    if (pszGMLGroups)
4413
0
      msInsertHashTable(&(lp->metadata), "GML_GROUPS", pszGMLGroups);
4414
0
    if (pszGMLIncludeItems)
4415
0
      msInsertHashTable(&(lp->metadata), "GML_INCLUDE_ITEMS",
4416
0
                        pszGMLIncludeItems);
4417
0
    if (pszGMLGeometries)
4418
0
      msInsertHashTable(&(lp->metadata), "GML_GEOMETRIES", pszGMLGeometries);
4419
4420
0
    status = msGMLWriteWFSQuery(map, stdout, gmlinfo.user_namespace_prefix,
4421
0
                                outputformat, nWFSVersion, MS_TRUE, MS_TRUE);
4422
0
  }
4423
4424
0
  msIO_printf("</wfs:ValueCollection>\n");
4425
4426
0
  msFree(pszGMLGroups);
4427
0
  msFree(pszGMLIncludeItems);
4428
0
  msFree(pszGMLGeometries);
4429
4430
0
  free(gmlinfo.script_url);
4431
0
  free(gmlinfo.script_url_encoded);
4432
0
  msFree(gmlinfo.user_namespace_uri_encoded);
4433
4434
0
  return status;
4435
0
}
4436
4437
#endif /* USE_WFS_SVR */
4438
4439
/*
4440
** msWFSDispatch() is the entry point for WFS requests.
4441
** - If this is a valid request then it is processed and MS_SUCCESS is returned
4442
**   on success, or MS_FAILURE on failure.
4443
** - If this does not appear to be a valid WFS request then MS_DONE
4444
**   is returned and MapServer is expected to process this as a regular
4445
**   MapServer request.
4446
*/
4447
int msWFSDispatch(mapObj *map, cgiRequestObj *requestobj,
4448
0
                  owsRequestObj *ows_request, int force_wfs_mode) {
4449
0
#ifdef USE_WFS_SVR
4450
0
  int status;
4451
0
  int returnvalue = MS_DONE;
4452
0
  int nWFSVersion;
4453
0
  char *validated_language;
4454
4455
  /* static char *wmtver = NULL, *request=NULL, *service=NULL; */
4456
0
  wfsParamsObj *paramsObj;
4457
  /*
4458
  ** Populate the Params object based on the request
4459
  */
4460
0
  paramsObj = msWFSCreateParamsObj();
4461
  /* TODO : store also parameters that are inside the map object */
4462
  /* into the paramsObj.  */
4463
0
  status = msWFSParseRequest(map, requestobj, paramsObj, force_wfs_mode);
4464
0
  if (status != MS_SUCCESS) {
4465
0
    msWFSFreeParamsObj(paramsObj);
4466
0
    return status;
4467
0
  }
4468
4469
0
  validated_language =
4470
0
      msOWSGetLanguageFromList(map, "FO", paramsObj->pszLanguage);
4471
0
  if (validated_language != NULL) {
4472
0
    msMapSetLanguageSpecificConnection(map, validated_language);
4473
0
  }
4474
0
  msFree(validated_language);
4475
4476
0
  if (force_wfs_mode) {
4477
    /*request is always required*/
4478
0
    if (paramsObj->pszRequest == NULL || strlen(paramsObj->pszRequest) <= 0) {
4479
0
      msSetError(MS_WFSERR, "Incomplete WFS request: REQUEST parameter missing",
4480
0
                 "msWFSDispatch()");
4481
0
      returnvalue =
4482
0
          msWFSException(map, "request", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4483
0
                         paramsObj->pszVersion);
4484
0
      msWFSFreeParamsObj(paramsObj);
4485
0
      return returnvalue;
4486
0
    }
4487
4488
    /*version:
4489
      wfs 1.0 and 1.1.0 POST request: optional
4490
      wfs 1.0 and 1.1.0 GET request: optional for getcapabilities and required
4491
      for describefeature and getfeature
4492
     */
4493
0
    if (paramsObj->pszVersion == NULL && requestobj &&
4494
0
        requestobj->postrequest == NULL &&
4495
0
        strcasecmp(paramsObj->pszRequest, "GetCapabilities") != 0) {
4496
0
      msSetError(MS_WFSERR, "Invalid WFS request: VERSION parameter missing",
4497
0
                 "msWFSDispatch()");
4498
0
      returnvalue =
4499
0
          msWFSException(map, "version", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4500
0
                         paramsObj->pszVersion);
4501
0
      msWFSFreeParamsObj(paramsObj);
4502
0
      return returnvalue;
4503
0
    }
4504
4505
0
    if (paramsObj->pszVersion == NULL || strlen(paramsObj->pszVersion) <= 0)
4506
0
      paramsObj->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
4507
4508
    /*service
4509
      wfs 1.0 and 1.1.0 GET: required and should be set to WFS
4510
      wfs 1.0 POST: required
4511
      wfs 1.1.1 POST: optional
4512
    */
4513
0
    if ((paramsObj->pszService == NULL || strlen(paramsObj->pszService) == 0) &&
4514
0
        ((requestobj && requestobj->postrequest == NULL) ||
4515
0
         strcasecmp(paramsObj->pszVersion, "1.0") == 0)) {
4516
0
      msSetError(MS_WFSERR, "Invalid WFS request: Missing SERVICE parameter",
4517
0
                 "msWFSDispatch()");
4518
0
      returnvalue =
4519
0
          msWFSException(map, "service", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4520
0
                         paramsObj->pszVersion);
4521
0
      msWFSFreeParamsObj(paramsObj);
4522
0
      return returnvalue;
4523
0
    }
4524
4525
0
    if (paramsObj->pszService == NULL || strlen(paramsObj->pszService) == 0)
4526
0
      paramsObj->pszService = msStrdup("WFS");
4527
4528
0
    if (paramsObj->pszService != NULL &&
4529
0
        strcasecmp(paramsObj->pszService, "WFS") != 0) {
4530
0
      msSetError(MS_WFSERR,
4531
0
                 "Invalid WFS request: SERVICE parameter must be set to WFS",
4532
0
                 "msWFSDispatch()");
4533
0
      returnvalue =
4534
0
          msWFSException(map, "service", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4535
0
                         paramsObj->pszVersion);
4536
0
      msWFSFreeParamsObj(paramsObj);
4537
0
      return returnvalue;
4538
0
    }
4539
0
  }
4540
  /* If SERVICE is specified then it MUST be "WFS" */
4541
0
  if (paramsObj->pszService != NULL &&
4542
0
      strcasecmp(paramsObj->pszService, "WFS") != 0) {
4543
0
    msWFSFreeParamsObj(paramsObj);
4544
0
    return MS_DONE; /* Not a WFS request */
4545
0
  }
4546
4547
  /* If SERVICE, VERSION and REQUEST not included than this isn't a WFS req*/
4548
0
  if (paramsObj->pszService == NULL && paramsObj->pszVersion == NULL &&
4549
0
      paramsObj->pszRequest == NULL) {
4550
0
    msWFSFreeParamsObj(paramsObj);
4551
0
    return MS_DONE; /* Not a WFS request */
4552
0
  }
4553
4554
  /* VERSION *and* REQUEST *and* SERVICE required by all WFS requests including
4555
   * GetCapabilities.
4556
   */
4557
0
  if (paramsObj->pszVersion == NULL || strlen(paramsObj->pszVersion) <= 0) {
4558
0
    msSetError(MS_WFSERR, "Incomplete WFS request: VERSION parameter missing",
4559
0
               "msWFSDispatch()");
4560
4561
0
    returnvalue =
4562
0
        msWFSException11(map, "version", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4563
0
                         msWFSGetDefaultVersion(map));
4564
0
    msWFSFreeParamsObj(paramsObj);
4565
0
    return returnvalue;
4566
0
  }
4567
4568
0
  if (paramsObj->pszRequest == NULL || strlen(paramsObj->pszRequest) <= 0) {
4569
0
    msSetError(MS_WFSERR, "Incomplete WFS request: REQUEST parameter missing",
4570
0
               "msWFSDispatch()");
4571
0
    returnvalue =
4572
0
        msWFSException(map, "request", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4573
0
                       paramsObj->pszVersion);
4574
0
    msWFSFreeParamsObj(paramsObj);
4575
0
    return returnvalue;
4576
0
  }
4577
4578
0
  if (paramsObj->pszService == NULL || strlen(paramsObj->pszService) <= 0) {
4579
0
    msSetError(MS_WFSERR, "Incomplete WFS request: SERVICE parameter missing",
4580
0
               "msWFSDispatch()");
4581
4582
0
    returnvalue =
4583
0
        msWFSException(map, "service", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4584
0
                       paramsObj->pszVersion);
4585
0
    msWFSFreeParamsObj(paramsObj);
4586
0
    return returnvalue;
4587
0
  }
4588
4589
0
  if ((status = msOWSMakeAllLayersUnique(map)) != MS_SUCCESS) {
4590
0
    msSetError(MS_WFSERR, "msOWSMakeAllLayersUnique() failed",
4591
0
               "msWFSDispatch()");
4592
0
    returnvalue = msWFSException(
4593
0
        map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
4594
0
    msWFSFreeParamsObj(paramsObj);
4595
0
    return returnvalue;
4596
0
  }
4597
  /*
4598
  ** Start dispatching requests
4599
  */
4600
0
  if (strcasecmp(paramsObj->pszRequest, "GetCapabilities") == 0) {
4601
0
    msOWSRequestLayersEnabled(map, "F", paramsObj->pszRequest, ows_request);
4602
0
    if (ows_request->numlayers == 0) {
4603
0
      msSetError(
4604
0
          MS_WFSERR,
4605
0
          "WFS request not enabled. Check wfs/ows_enable_request settings.",
4606
0
          "msWFSDispatch()");
4607
0
      returnvalue =
4608
0
          msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4609
0
                         paramsObj->pszVersion);
4610
0
      msWFSFreeParamsObj(paramsObj);
4611
0
      return returnvalue;
4612
0
    }
4613
4614
0
    returnvalue = msWFSGetCapabilities(map, paramsObj, requestobj, ows_request);
4615
0
    msWFSFreeParamsObj(paramsObj);
4616
0
    return returnvalue;
4617
0
  }
4618
4619
  /*
4620
  ** Validate VERSION against the versions that we support... we don't do this
4621
  ** for GetCapabilities in order to allow version negotiation.
4622
  */
4623
0
  if (strcmp(paramsObj->pszVersion, "1.0.0") != 0 &&
4624
0
      strcmp(paramsObj->pszVersion, "1.1.0") != 0 &&
4625
0
      strcmp(paramsObj->pszVersion, "2.0.0") != 0) {
4626
0
    msSetError(MS_WFSERR, "WFS Server does not support VERSION %s.",
4627
0
               "msWFSDispatch()", paramsObj->pszVersion);
4628
0
    returnvalue =
4629
0
        msWFSException(map, "version", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4630
0
                       msWFSGetDefaultVersion(map));
4631
0
    msWFSFreeParamsObj(paramsObj);
4632
0
    return returnvalue;
4633
0
  }
4634
4635
0
  nWFSVersion = msOWSParseVersionString(paramsObj->pszVersion);
4636
4637
  /* Since the function can still return MS_DONE, we add an extra service check
4638
     to not call msOWSRequestLayersEnabled twice */
4639
0
  if (strcasecmp(paramsObj->pszService, "WFS") == 0) {
4640
0
    msOWSRequestLayersEnabled(map, "F", paramsObj->pszRequest, ows_request);
4641
0
    if (ows_request->numlayers == 0) {
4642
0
      msSetError(
4643
0
          MS_WFSERR,
4644
0
          "WFS request not enabled. Check wfs/ows_enable_request settings.",
4645
0
          "msWFSDispatch()");
4646
0
      returnvalue =
4647
0
          msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4648
0
                         paramsObj->pszVersion);
4649
0
      msWFSFreeParamsObj(paramsObj);
4650
0
      return returnvalue;
4651
0
    }
4652
0
  }
4653
4654
0
  returnvalue = MS_DONE;
4655
  /* Continue dispatching...
4656
   */
4657
0
  if (strcasecmp(paramsObj->pszRequest, "DescribeFeatureType") == 0)
4658
0
    returnvalue =
4659
0
        msWFSDescribeFeatureType(map, paramsObj, ows_request, nWFSVersion);
4660
4661
0
  else if (strcasecmp(paramsObj->pszRequest, "GetFeature") == 0)
4662
0
    returnvalue =
4663
0
        msWFSGetFeature(map, paramsObj, requestobj, ows_request, nWFSVersion);
4664
4665
0
  else if (nWFSVersion >= OWS_2_0_0 &&
4666
0
           strcasecmp(paramsObj->pszRequest, "GetPropertyValue") == 0)
4667
0
    returnvalue = msWFSGetPropertyValue(map, paramsObj, requestobj, ows_request,
4668
0
                                        nWFSVersion);
4669
4670
0
  else if (nWFSVersion >= OWS_2_0_0 &&
4671
0
           strcasecmp(paramsObj->pszRequest, "ListStoredQueries") == 0)
4672
0
    returnvalue = msWFSListStoredQueries20(map, ows_request);
4673
4674
0
  else if (nWFSVersion >= OWS_2_0_0 &&
4675
0
           strcasecmp(paramsObj->pszRequest, "DescribeStoredQueries") == 0)
4676
0
    returnvalue = msWFSDescribeStoredQueries20(map, paramsObj, ows_request);
4677
4678
0
  else if (msWFSGetIndexUnsupportedOperation(paramsObj->pszRequest) >= 0) {
4679
    /* Unsupported WFS request */
4680
0
    msSetError(MS_WFSERR, "Unsupported WFS request: %s", "msWFSDispatch()",
4681
0
               paramsObj->pszRequest);
4682
0
    returnvalue = msWFSException(map, paramsObj->pszRequest,
4683
0
                                 MS_OWS_ERROR_OPERATION_NOT_SUPPORTED,
4684
0
                                 paramsObj->pszVersion);
4685
0
  } else if (strcasecmp(paramsObj->pszService, "WFS") == 0) {
4686
    /* Invalid WFS request */
4687
0
    msSetError(MS_WFSERR, "Invalid WFS request: %s", "msWFSDispatch()",
4688
0
               paramsObj->pszRequest);
4689
0
    returnvalue =
4690
0
        msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4691
0
                       paramsObj->pszVersion);
4692
0
  }
4693
4694
  /* This was not detected as a WFS request... let MapServer handle it */
4695
0
  msWFSFreeParamsObj(paramsObj);
4696
0
  return returnvalue;
4697
4698
#else
4699
  (void)map;
4700
  (void)requestobj;
4701
  (void)ows_request;
4702
  (void)force_wfs_mode;
4703
  msSetError(MS_WFSERR, "WFS server support is not available.",
4704
             "msWFSDispatch()");
4705
  return (MS_FAILURE);
4706
#endif
4707
0
}
4708
4709
/************************************************************************/
4710
/*                           msWFSCreateParamsObj                       */
4711
/*                                                                      */
4712
/*      Create a parameter object, initialize it.                       */
4713
/*      The caller should free the object using msWFSFreeParamsObj.     */
4714
/************************************************************************/
4715
0
wfsParamsObj *msWFSCreateParamsObj() {
4716
0
  wfsParamsObj *paramsObj = (wfsParamsObj *)calloc(1, sizeof(wfsParamsObj));
4717
0
  MS_CHECK_ALLOC(paramsObj, sizeof(wfsParamsObj), NULL);
4718
4719
0
  paramsObj->nMaxFeatures = -1;
4720
0
  paramsObj->nStartIndex = -1;
4721
4722
0
  return paramsObj;
4723
0
}
4724
4725
/************************************************************************/
4726
/*                            msWFSFreeParmsObj                         */
4727
/*                                                                      */
4728
/*      Free params object.                                             */
4729
/************************************************************************/
4730
0
void msWFSFreeParamsObj(wfsParamsObj *wfsparams) {
4731
0
  if (wfsparams) {
4732
0
    free(wfsparams->pszVersion);
4733
0
    free(wfsparams->pszUpdateSequence);
4734
0
    free(wfsparams->pszRequest);
4735
0
    free(wfsparams->pszService);
4736
0
    free(wfsparams->pszTypeName);
4737
0
    free(wfsparams->pszFilter);
4738
0
    free(wfsparams->pszFilterLanguage);
4739
0
    free(wfsparams->pszBbox);
4740
0
    free(wfsparams->pszGeometryName);
4741
0
    free(wfsparams->pszOutputFormat);
4742
0
    free(wfsparams->pszFeatureId);
4743
0
    free(wfsparams->pszSrs);
4744
0
    free(wfsparams->pszResultType);
4745
0
    free(wfsparams->pszPropertyName);
4746
0
    free(wfsparams->pszAcceptVersions);
4747
0
    free(wfsparams->pszSections);
4748
0
    free(wfsparams->pszSortBy);
4749
0
    free(wfsparams->pszLanguage);
4750
0
    free(wfsparams->pszValueReference);
4751
0
    free(wfsparams->pszStoredQueryId);
4752
4753
0
    free(wfsparams);
4754
0
  }
4755
0
}
4756
4757
#ifdef USE_WFS_SVR
4758
/************************************************************************/
4759
/*                       msWFSGetDefaultVersion                         */
4760
/************************************************************************/
4761
0
static const char *msWFSGetDefaultVersion(mapObj *map) {
4762
0
  const char *pszVersion =
4763
0
      msOWSLookupMetadata(&(map->web.metadata), "F", "getcapabilities_version");
4764
0
  if (pszVersion != NULL) {
4765
    /* Check that the metadata value is one of the recognized versions */
4766
0
    int iVersion = msOWSParseVersionString(pszVersion);
4767
0
    int i;
4768
0
    for (i = 0; i < wfsNumSupportedVersions; i++) {
4769
0
      if (iVersion == wfsSupportedVersions[i])
4770
0
        return wfsSupportedVersionsStr[i];
4771
0
    }
4772
    /* If not, use the default (= latest) version */
4773
0
    msDebug("msWFSGetDefaultVersion(): invalid value for "
4774
0
            "wfs_getcapabilities_version: %s\n",
4775
0
            pszVersion);
4776
0
  }
4777
0
  return WFS_LATEST_VERSION;
4778
0
}
4779
4780
/************************************************************************/
4781
/*                             msWFSSetParam                            */
4782
/************************************************************************/
4783
static int msWFSSetParam(char **ppszOut, cgiRequestObj *request, int i,
4784
0
                         const char *pszExpectedParamName) {
4785
0
  if (strcasecmp(request->ParamNames[i], pszExpectedParamName) == 0) {
4786
0
    if (*ppszOut == NULL)
4787
0
      *ppszOut = msStrdup(request->ParamValues[i]);
4788
0
    return 1;
4789
0
  }
4790
0
  return 0;
4791
0
}
4792
4793
/************************************************************************/
4794
/*                         msWFSParseXMLQueryNode                       */
4795
/************************************************************************/
4796
4797
static void msWFSParseXMLQueryNode(CPLXMLNode *psQuery,
4798
0
                                   wfsParamsObj *wfsparams) {
4799
0
  const char *pszTypename;
4800
0
  const char *pszValue;
4801
0
  char *pszSerializedFilter, *pszTmp, *pszTmp2;
4802
0
  CPLXMLNode *psPropertyName;
4803
0
  CPLXMLNode *psFilter;
4804
0
  CPLXMLNode *psSortBy;
4805
0
  char *pszSortBy = NULL;
4806
4807
0
  pszValue = CPLGetXMLValue(psQuery, "srsName", NULL);
4808
0
  if (pszValue) {
4809
0
    msFree(wfsparams->pszSrs);
4810
0
    wfsparams->pszSrs = msStrdup(pszValue);
4811
0
  }
4812
4813
  /* parse typenames */
4814
0
  pszTypename = CPLGetXMLValue(psQuery, "typename", NULL);
4815
0
  if (pszTypename == NULL) /* WFS 2.0 */
4816
0
    pszTypename = CPLGetXMLValue(psQuery, "typeNames", NULL);
4817
4818
  /*parse property name*/
4819
0
  psPropertyName = CPLGetXMLNode(psQuery, "PropertyName");
4820
0
  pszTmp2 = NULL;
4821
4822
  /*when there is no PropertyName, we output (!) so that it is parsed properly
4823
   * in GetFeature*/
4824
0
  if (psPropertyName == NULL)
4825
0
    pszTmp2 = msStrdup("!");
4826
4827
0
  while (psPropertyName) {
4828
0
    if (!psPropertyName->pszValue ||
4829
0
        strcasecmp(psPropertyName->pszValue, "PropertyName") != 0) {
4830
0
      psPropertyName = psPropertyName->psNext;
4831
0
      continue;
4832
0
    }
4833
0
    pszValue = CPLGetXMLValue(psPropertyName, NULL, NULL);
4834
0
    if (pszTmp2 == NULL) {
4835
0
      pszTmp2 = msStrdup(pszValue);
4836
0
    } else {
4837
0
      pszTmp = msStrdup(pszTmp2);
4838
0
      const size_t nSize = strlen(pszTmp) + strlen(pszValue) + 2;
4839
0
      pszTmp2 = (char *)msSmallRealloc(pszTmp2, nSize);
4840
0
      snprintf(pszTmp2, nSize, "%s,%s", pszTmp, pszValue);
4841
0
      msFree(pszTmp);
4842
0
    }
4843
0
    psPropertyName = psPropertyName->psNext;
4844
0
  }
4845
0
  if (pszTmp2) {
4846
0
    pszTmp = msStrdup(pszTmp2);
4847
0
    const size_t nSize = strlen(pszTmp) + 3;
4848
0
    pszTmp2 = (char *)msSmallRealloc(pszTmp2, nSize);
4849
0
    snprintf(pszTmp2, nSize, "(%s)", pszTmp);
4850
0
    msFree(pszTmp);
4851
4852
0
    msWFSBuildParamList(&(wfsparams->pszPropertyName), pszTmp2, "");
4853
0
    msFree(pszTmp2);
4854
0
    pszTmp2 = NULL;
4855
0
  }
4856
4857
  /* parse filter */
4858
0
  psFilter = CPLGetXMLNode(psQuery, "Filter");
4859
4860
0
  if (psFilter == NULL) {
4861
    /*when there is no Filter, we output (!) so that it is parsed properly in
4862
     * GetFeature*/
4863
0
    pszSerializedFilter = msStrdup("(!)");
4864
0
  } else {
4865
0
    char *pszCPLTmp = CPLSerializeXMLTree(psFilter);
4866
0
    const size_t nSize = strlen(pszCPLTmp) + 3;
4867
0
    pszSerializedFilter = (char *)msSmallMalloc(nSize);
4868
0
    snprintf(pszSerializedFilter, nSize, "(%s)", pszCPLTmp);
4869
0
    CPLFree(pszCPLTmp);
4870
0
  }
4871
4872
0
  msWFSBuildParamList(&(wfsparams->pszFilter), pszSerializedFilter, "");
4873
0
  free(pszSerializedFilter);
4874
4875
  /* parse SortBy */
4876
0
  psSortBy = CPLGetXMLNode(psQuery, "SortBy");
4877
0
  if (psSortBy != NULL) {
4878
0
    int bFirstProperty = MS_TRUE;
4879
0
    CPLXMLNode *psIter = psSortBy->psChild;
4880
4881
0
    pszSortBy = msStringConcatenate(pszSortBy, "(");
4882
4883
0
    while (psIter != NULL) {
4884
0
      if (psIter->eType == CXT_Element &&
4885
0
          strcmp(psIter->pszValue, "SortProperty") == 0) {
4886
0
        const char *pszPropertyName =
4887
0
            CPLGetXMLValue(psIter, "PropertyName", NULL);
4888
0
        if (pszPropertyName == NULL)
4889
0
          pszPropertyName = CPLGetXMLValue(psIter, "ValueReference", NULL);
4890
0
        if (pszPropertyName != NULL) {
4891
0
          const char *pszSortOrder = CPLGetXMLValue(psIter, "SortOrder", NULL);
4892
0
          if (!bFirstProperty)
4893
0
            pszSortBy = msStringConcatenate(pszSortBy, ",");
4894
0
          bFirstProperty = MS_FALSE;
4895
4896
0
          pszSortBy = msStringConcatenate(pszSortBy, pszPropertyName);
4897
0
          if (pszSortOrder != NULL) {
4898
0
            pszSortBy = msStringConcatenate(pszSortBy, " ");
4899
0
            pszSortBy = msStringConcatenate(pszSortBy, pszSortOrder);
4900
0
          }
4901
0
        }
4902
0
      }
4903
0
      psIter = psIter->psNext;
4904
0
    }
4905
4906
0
    pszSortBy = msStringConcatenate(pszSortBy, ")");
4907
0
  } else {
4908
0
    pszSortBy = msStrdup("(!)");
4909
0
  }
4910
4911
0
  msWFSBuildParamList(&(wfsparams->pszSortBy), pszSortBy, "");
4912
0
  free(pszSortBy);
4913
4914
  /* Special case for "urn:ogc:def:query:OGC-WFS::GetFeatureById" */
4915
  /* Resolve the property typename */
4916
0
  if (pszTypename != NULL && strcmp(pszTypename, "?") == 0 &&
4917
0
      psFilter != NULL) {
4918
0
    const char *rid;
4919
0
    rid = CPLGetXMLValue(psFilter, "ResourceId.rid", NULL);
4920
0
    if (rid != NULL) {
4921
0
      char *pszTmpTypename = msStrdup(rid);
4922
0
      char *pszDot = strchr(pszTmpTypename, '.');
4923
0
      if (pszDot) {
4924
0
        *pszDot = '\0';
4925
0
        msWFSBuildParamList(&(wfsparams->pszTypeName), pszTmpTypename, ",");
4926
0
        pszTypename = NULL;
4927
4928
0
        if (wfsparams->countGetFeatureById >= 0)
4929
0
          wfsparams->countGetFeatureById++;
4930
0
      } else
4931
0
        wfsparams->countGetFeatureById = -1;
4932
0
      msFree(pszTmpTypename);
4933
0
    } else
4934
0
      wfsparams->countGetFeatureById = -1;
4935
0
  } else
4936
0
    wfsparams->countGetFeatureById = -1;
4937
4938
0
  if (pszTypename) {
4939
0
    msWFSBuildParamList(&(wfsparams->pszTypeName), pszTypename, ",");
4940
0
  }
4941
0
}
4942
4943
/************************************************************************/
4944
/*                     msWFSAnalyzeStoredQuery                          */
4945
/************************************************************************/
4946
4947
static int msWFSAnalyzeStoredQuery(mapObj *map, wfsParamsObj *wfsparams,
4948
                                   const char *id,
4949
0
                                   const char *pszResolvedQuery) {
4950
0
  CPLXMLNode *psRoot;
4951
0
  CPLXMLNode *psQuery;
4952
0
  CPLXMLNode *psIter;
4953
4954
0
  psRoot = CPLParseXMLString(pszResolvedQuery);
4955
4956
0
  if (psRoot == NULL) {
4957
0
    msSetError(MS_WFSERR,
4958
0
               "Resolved definition for stored query '%s' is invalid",
4959
0
               "msWFSParseRequest()", id);
4960
0
    msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
4961
0
                   wfsparams->pszVersion);
4962
0
    return MS_FAILURE;
4963
0
  }
4964
4965
0
  CPLStripXMLNamespace(psRoot, NULL, 1);
4966
0
  psQuery = CPLGetXMLNode(psRoot,
4967
0
                          "=StoredQueryDescription.QueryExpressionText.Query");
4968
0
  if (psQuery == NULL) {
4969
0
    msSetError(MS_WFSERR,
4970
0
               "Resolved definition for stored query '%s' is invalid",
4971
0
               "msWFSParseRequest()", id);
4972
0
    msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
4973
0
                   wfsparams->pszVersion);
4974
0
    CPLDestroyXMLNode(psRoot);
4975
0
    return MS_FAILURE;
4976
0
  }
4977
4978
0
  psIter = psQuery;
4979
0
  while (psIter != NULL) {
4980
0
    if (psIter->eType == CXT_Element &&
4981
0
        strcmp(psIter->pszValue, "Query") == 0) {
4982
0
      msWFSParseXMLQueryNode(psIter, wfsparams);
4983
0
    }
4984
0
    psIter = psIter->psNext;
4985
0
  }
4986
4987
0
  CPLDestroyXMLNode(psRoot);
4988
4989
0
  return MS_SUCCESS;
4990
0
}
4991
4992
/************************************************************************/
4993
/*                     msWFSParseXMLStoredQueryNode                     */
4994
/************************************************************************/
4995
4996
static int msWFSParseXMLStoredQueryNode(mapObj *map, wfsParamsObj *wfsparams,
4997
0
                                        CPLXMLNode *psQuery) {
4998
0
  const char *id;
4999
0
  CPLXMLNode *psIter;
5000
0
  hashTableObj *hashTable;
5001
0
  char *pszResolvedQuery;
5002
0
  int status;
5003
5004
0
  id = CPLGetXMLValue(psQuery, "id", NULL);
5005
0
  if (id == NULL) {
5006
0
    msSetError(MS_WFSERR, "Missing 'id' attribute in StoredQuery",
5007
0
               "msWFSParseRequest()");
5008
0
    return msWFSException(map, "id", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
5009
0
                          wfsparams->pszVersion);
5010
0
  }
5011
5012
0
  hashTable = msCreateHashTable();
5013
0
  psIter = psQuery->psChild;
5014
0
  while (psIter != NULL) {
5015
0
    if (psIter->eType == CXT_Element &&
5016
0
        strcmp(psIter->pszValue, "Parameter") == 0) {
5017
0
      const char *name;
5018
0
      CPLXMLNode *psIter2;
5019
0
      char *pszValue;
5020
5021
0
      name = CPLGetXMLValue(psIter, "name", NULL);
5022
0
      if (name == NULL) {
5023
0
        msSetError(MS_WFSERR,
5024
0
                   "Missing 'name' attribute in Parameter of StoredQuery",
5025
0
                   "msWFSParseRequest()");
5026
0
        msFreeHashTable(hashTable);
5027
0
        return msWFSException(map, NULL, MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
5028
0
                              wfsparams->pszVersion);
5029
0
      }
5030
5031
0
      psIter2 = psIter->psChild;
5032
0
      while (psIter2 != NULL) {
5033
0
        if (psIter2->eType != CXT_Attribute)
5034
0
          break;
5035
0
        psIter2 = psIter2->psNext;
5036
0
      }
5037
5038
0
      pszValue = CPLSerializeXMLTree(psIter2);
5039
0
      msInsertHashTable(hashTable, name, pszValue);
5040
0
      CPLFree(pszValue);
5041
0
    }
5042
0
    psIter = psIter->psNext;
5043
0
  }
5044
5045
0
  pszResolvedQuery =
5046
0
      msWFSGetResolvedStoredQuery20(map, wfsparams, id, hashTable);
5047
0
  msFreeHashTable(hashTable);
5048
0
  if (pszResolvedQuery == NULL)
5049
0
    return MS_FAILURE;
5050
5051
0
  status = msWFSAnalyzeStoredQuery(map, wfsparams, id, pszResolvedQuery);
5052
5053
0
  msFree(pszResolvedQuery);
5054
5055
0
  return status;
5056
0
}
5057
5058
/************************************************************************/
5059
/*                    msWFSSimplifyPropertyNameAndFilter                */
5060
/************************************************************************/
5061
5062
0
static void msWFSSimplifyPropertyNameAndFilter(wfsParamsObj *wfsparams) {
5063
0
  int i;
5064
5065
  /* If no PropertyName at all was specified, then clear the field */
5066
  /* that will be easier to generate valid 'next' and 'previous' uri */
5067
  /* for WFS 2.0 GetFeature */
5068
0
  if (wfsparams->pszPropertyName != NULL) {
5069
0
    i = 0;
5070
0
    while (strncmp(wfsparams->pszPropertyName + i, "(!)", 3) == 0)
5071
0
      i += 3;
5072
0
    if (wfsparams->pszPropertyName[i] == '\0') {
5073
0
      msFree(wfsparams->pszPropertyName);
5074
0
      wfsparams->pszPropertyName = NULL;
5075
0
    }
5076
0
  }
5077
5078
  /* Same with filter */
5079
0
  if (wfsparams->pszFilter != NULL) {
5080
0
    i = 0;
5081
0
    while (strncmp(wfsparams->pszFilter + i, "(!)", 3) == 0)
5082
0
      i += 3;
5083
0
    if (wfsparams->pszFilter[i] == '\0') {
5084
0
      msFree(wfsparams->pszFilter);
5085
0
      wfsparams->pszFilter = NULL;
5086
0
    }
5087
0
  }
5088
5089
  /* Same with SortBy */
5090
0
  if (wfsparams->pszSortBy != NULL) {
5091
0
    i = 0;
5092
0
    while (strncmp(wfsparams->pszSortBy + i, "(!)", 3) == 0)
5093
0
      i += 3;
5094
0
    if (wfsparams->pszSortBy[i] == '\0') {
5095
0
      msFree(wfsparams->pszSortBy);
5096
0
      wfsparams->pszSortBy = NULL;
5097
0
    }
5098
0
  }
5099
0
}
5100
5101
#endif /* USE_WFS_SVR */
5102
5103
/************************************************************************/
5104
/*                            msWFSParseRequest                         */
5105
/*                                                                      */
5106
/*      Parse request into the params object.                           */
5107
/************************************************************************/
5108
int msWFSParseRequest(mapObj *map, cgiRequestObj *request,
5109
0
                      wfsParamsObj *wfsparams, int force_wfs_mode) {
5110
0
#ifdef USE_WFS_SVR
5111
0
  int i = 0;
5112
5113
0
  if (!request || !wfsparams)
5114
0
    return msWFSException(map, "request", "InvalidRequest", NULL);
5115
5116
0
  if (request->NumParams > 0 && request->postrequest == NULL) {
5117
0
    for (i = 0; i < request->NumParams; i++) {
5118
0
      if (request->ParamNames[i] && request->ParamValues[i]) {
5119
0
        if (msWFSSetParam(&(wfsparams->pszVersion), request, i, "VERSION")) {
5120
0
        }
5121
5122
0
        else if (msWFSSetParam(&(wfsparams->pszUpdateSequence), request, i,
5123
0
                               "UPDATESEQUENCE")) {
5124
0
        }
5125
5126
0
        else if (msWFSSetParam(&(wfsparams->pszRequest), request, i,
5127
0
                               "REQUEST")) {
5128
0
        }
5129
5130
0
        else if (msWFSSetParam(&(wfsparams->pszService), request, i,
5131
0
                               "SERVICE")) {
5132
0
        }
5133
5134
0
        else if (strcasecmp(request->ParamNames[i], "MAXFEATURES") == 0)
5135
0
          wfsparams->nMaxFeatures = atoi(request->ParamValues[i]);
5136
5137
        /* WFS 2.0 */
5138
0
        else if (strcasecmp(request->ParamNames[i], "COUNT") == 0)
5139
0
          wfsparams->nMaxFeatures = atoi(request->ParamValues[i]);
5140
5141
0
        else if (strcasecmp(request->ParamNames[i], "STARTINDEX") == 0)
5142
0
          wfsparams->nStartIndex = atoi(request->ParamValues[i]);
5143
5144
0
        else if (msWFSSetParam(&(wfsparams->pszBbox), request, i, "BBOX")) {
5145
0
        }
5146
5147
0
        else if (msWFSSetParam(&(wfsparams->pszSrs), request, i, "SRSNAME")) {
5148
0
        }
5149
5150
0
        else if (msWFSSetParam(&(wfsparams->pszResultType), request, i,
5151
0
                               "RESULTTYPE")) {
5152
0
        }
5153
5154
0
        else if (msWFSSetParam(&(wfsparams->pszTypeName), request, i,
5155
0
                               "TYPENAME")) {
5156
0
        }
5157
5158
        /* WFS 2.0 */
5159
0
        else if (msWFSSetParam(&(wfsparams->pszTypeName), request, i,
5160
0
                               "TYPENAMES")) {
5161
0
        }
5162
5163
0
        else if (msWFSSetParam(&(wfsparams->pszFilter), request, i, "FILTER")) {
5164
0
        }
5165
5166
        /* WFS 2.0 */
5167
0
        else if (msWFSSetParam(&(wfsparams->pszFilterLanguage), request, i,
5168
0
                               "FILTER_LANGUAGE")) {
5169
0
        }
5170
5171
0
        else if (msWFSSetParam(&(wfsparams->pszOutputFormat), request, i,
5172
0
                               "OUTPUTFORMAT")) {
5173
0
        }
5174
5175
0
        else if (msWFSSetParam(&(wfsparams->pszFeatureId), request, i,
5176
0
                               "FEATUREID")) {
5177
0
        }
5178
5179
        /* WFS 2.0 */
5180
0
        else if (msWFSSetParam(&(wfsparams->pszFeatureId), request, i,
5181
0
                               "RESOURCEID")) {
5182
0
        }
5183
5184
0
        else if (msWFSSetParam(&(wfsparams->pszPropertyName), request, i,
5185
0
                               "PROPERTYNAME")) {
5186
0
        }
5187
5188
0
        else if (msWFSSetParam(&(wfsparams->pszAcceptVersions), request, i,
5189
0
                               "ACCEPTVERSIONS")) {
5190
0
        }
5191
5192
0
        else if (msWFSSetParam(&(wfsparams->pszSections), request, i,
5193
0
                               "SECTIONS")) {
5194
0
        }
5195
5196
0
        else if (msWFSSetParam(&(wfsparams->pszSortBy), request, i, "SORTBY")) {
5197
0
        }
5198
5199
0
        else if (msWFSSetParam(&(wfsparams->pszLanguage), request, i,
5200
0
                               "LANGUAGE")) {
5201
0
        }
5202
5203
0
        else if (msWFSSetParam(&(wfsparams->pszValueReference), request, i,
5204
0
                               "VALUEREFERENCE")) {
5205
0
        }
5206
5207
0
        else if (msWFSSetParam(&(wfsparams->pszStoredQueryId), request, i,
5208
0
                               "STOREDQUERY_ID")) {
5209
0
        }
5210
0
      }
5211
0
    }
5212
    /* version is optional is the GetCapabilities. If not */
5213
    /* provided, set it. Default it to latest implemented version */
5214
    /* or to the specified one in wfs_getcapabilities_version */
5215
0
    if (wfsparams->pszVersion == NULL && wfsparams->pszRequest &&
5216
0
        strcasecmp(wfsparams->pszRequest, "GetCapabilities") == 0) {
5217
0
      wfsparams->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
5218
0
    }
5219
0
  }
5220
5221
  /* -------------------------------------------------------------------- */
5222
  /*      Parse the post request. It is assumed to be an XML document.    */
5223
  /* -------------------------------------------------------------------- */
5224
0
  if (request->postrequest != NULL) {
5225
5226
0
    CPLXMLNode *psRoot;
5227
0
    CPLXMLNode *psGetFeature = NULL;
5228
0
    CPLXMLNode *psGetCapabilities = NULL;
5229
0
    CPLXMLNode *psDescribeFeature = NULL;
5230
0
    CPLXMLNode *psGetPropertyValue = NULL;
5231
0
    CPLXMLNode *psListStoredQueries = NULL;
5232
0
    CPLXMLNode *psDescribeStoredQueries = NULL;
5233
0
    CPLXMLNode *psOperation = NULL;
5234
0
    const char *pszValue;
5235
0
    char *pszSchemaLocation = NULL;
5236
5237
0
    psRoot = CPLParseXMLString(request->postrequest);
5238
0
    if (map->debug == MS_DEBUGLEVEL_VVV) {
5239
0
      msDebug("msWFSParseRequest(): WFS post request: %s\n",
5240
0
              request->postrequest);
5241
0
    }
5242
0
    if (psRoot) {
5243
      /* need to strip namespaces */
5244
0
      CPLStripXMLNamespace(psRoot, NULL, 1);
5245
5246
0
      for (psOperation = psRoot; psOperation != NULL;
5247
0
           psOperation = psOperation->psNext) {
5248
0
        if (psOperation->eType == CXT_Element) {
5249
5250
          /* keep schemaLocation so as to be able to validate against
5251
           * appropriate */
5252
          /* wfs.xsd schema afterwards */
5253
0
          if (pszSchemaLocation == NULL &&
5254
0
              CPLGetXMLValue(psOperation, "schemaLocation", NULL) != NULL)
5255
0
            pszSchemaLocation =
5256
0
                msStrdup(CPLGetXMLValue(psOperation, "schemaLocation", NULL));
5257
5258
0
          if (strcasecmp(psOperation->pszValue, "GetFeature") == 0) {
5259
0
            psGetFeature = psOperation;
5260
0
            break;
5261
0
          } else if (strcasecmp(psOperation->pszValue, "GetCapabilities") ==
5262
0
                     0) {
5263
0
            psGetCapabilities = psOperation;
5264
0
            break;
5265
0
          } else if (strcasecmp(psOperation->pszValue, "DescribeFeatureType") ==
5266
0
                     0) {
5267
0
            psDescribeFeature = psOperation;
5268
0
            break;
5269
0
          } else if (strcasecmp(psOperation->pszValue, "GetPropertyValue") ==
5270
0
                     0) {
5271
0
            psGetPropertyValue = psOperation;
5272
0
            break;
5273
0
          } else if (strcasecmp(psOperation->pszValue, "ListStoredQueries") ==
5274
0
                     0) {
5275
0
            psListStoredQueries = psOperation;
5276
0
            break;
5277
0
          } else if (strcasecmp(psOperation->pszValue,
5278
0
                                "DescribeStoredQueries") == 0) {
5279
0
            psDescribeStoredQueries = psOperation;
5280
0
            break;
5281
0
          }
5282
          /* these are unsupported requests. Just set the  */
5283
          /* request value and return; */
5284
0
          else {
5285
0
            int idx = msWFSGetIndexUnsupportedOperation(psOperation->pszValue);
5286
0
            if (idx >= 0) {
5287
0
              wfsparams->pszRequest = msStrdup(wfsUnsupportedOperations[idx]);
5288
0
              break;
5289
0
            }
5290
0
          }
5291
0
        }
5292
0
      }
5293
5294
0
      if (psOperation != NULL) {
5295
0
        pszValue = CPLGetXMLValue(psOperation, "version", NULL);
5296
0
        if (pszValue)
5297
0
          wfsparams->pszVersion = msStrdup(pszValue);
5298
5299
0
        pszValue = CPLGetXMLValue(psOperation, "service", NULL);
5300
0
        if (pszValue)
5301
0
          wfsparams->pszService = msStrdup(pszValue);
5302
0
      }
5303
5304
      /* -------------------------------------------------------------------- */
5305
      /*      Parse the GetFeature                                            */
5306
      /* -------------------------------------------------------------------- */
5307
0
      if (psGetFeature) {
5308
0
        CPLXMLNode *psIter;
5309
5310
0
        wfsparams->pszRequest = msStrdup("GetFeature");
5311
5312
0
        pszValue = CPLGetXMLValue(psGetFeature, "resultType", NULL);
5313
0
        if (pszValue)
5314
0
          wfsparams->pszResultType = msStrdup(pszValue);
5315
5316
0
        pszValue = CPLGetXMLValue(psGetFeature, "maxFeatures", NULL);
5317
0
        if (pszValue)
5318
0
          wfsparams->nMaxFeatures = atoi(pszValue);
5319
0
        else {
5320
          /* WFS 2.0 */
5321
0
          pszValue = CPLGetXMLValue(psGetFeature, "count", NULL);
5322
0
          if (pszValue)
5323
0
            wfsparams->nMaxFeatures = atoi(pszValue);
5324
0
        }
5325
5326
0
        pszValue = CPLGetXMLValue(psGetFeature, "startIndex", NULL);
5327
0
        if (pszValue)
5328
0
          wfsparams->nStartIndex = atoi(pszValue);
5329
5330
0
        pszValue = CPLGetXMLValue(psGetFeature, "outputFormat", NULL);
5331
0
        if (pszValue)
5332
0
          wfsparams->pszOutputFormat = msStrdup(pszValue);
5333
5334
        /* --------------------------------------------------------------------
5335
         */
5336
        /*      Parse typenames and filters. If there are multiple queries, */
5337
        /*      typenames will be build with comma between them */
5338
        /*      (typename1,typename2,...) and filters will be build with */
5339
        /*      bracets enclosinf the filters :(filter1)(filter2)... */
5340
        /*      propertynames are stored like (property1,property2)(property1)
5341
         */
5342
        /* --------------------------------------------------------------------
5343
         */
5344
0
        psIter = psGetFeature->psChild;
5345
0
        while (psIter != NULL) {
5346
0
          if (psIter->eType == CXT_Element &&
5347
0
              strcmp(psIter->pszValue, "Query") == 0) {
5348
0
            msWFSParseXMLQueryNode(psIter, wfsparams);
5349
0
          } else if (psIter->eType == CXT_Element &&
5350
0
                     strcmp(psIter->pszValue, "StoredQuery") == 0) {
5351
0
            int status = msWFSParseXMLStoredQueryNode(map, wfsparams, psIter);
5352
0
            if (status != MS_SUCCESS) {
5353
0
              CPLDestroyXMLNode(psRoot);
5354
0
              msFree(pszSchemaLocation);
5355
0
              return status;
5356
0
            }
5357
0
            wfsparams->bHasPostStoredQuery = MS_TRUE;
5358
0
          }
5359
0
          psIter = psIter->psNext;
5360
0
        }
5361
5362
0
        msWFSSimplifyPropertyNameAndFilter(wfsparams);
5363
0
      } /* end of GetFeature */
5364
5365
      /* -------------------------------------------------------------------- */
5366
      /*      Parse the GetPropertyValue                                      */
5367
      /* -------------------------------------------------------------------- */
5368
0
      if (psGetPropertyValue) {
5369
0
        CPLXMLNode *psIter;
5370
5371
0
        wfsparams->pszRequest = msStrdup("GetPropertyValue");
5372
5373
0
        pszValue = CPLGetXMLValue(psGetPropertyValue, "valueReference", NULL);
5374
0
        if (pszValue)
5375
0
          wfsparams->pszValueReference = msStrdup(pszValue);
5376
5377
0
        pszValue = CPLGetXMLValue(psGetPropertyValue, "resultType", NULL);
5378
0
        if (pszValue)
5379
0
          wfsparams->pszResultType = msStrdup(pszValue);
5380
5381
0
        pszValue = CPLGetXMLValue(psGetPropertyValue, "count", NULL);
5382
0
        if (pszValue)
5383
0
          wfsparams->nMaxFeatures = atoi(pszValue);
5384
5385
0
        pszValue = CPLGetXMLValue(psGetPropertyValue, "startIndex", NULL);
5386
0
        if (pszValue)
5387
0
          wfsparams->nStartIndex = atoi(pszValue);
5388
5389
0
        pszValue = CPLGetXMLValue(psGetPropertyValue, "outputFormat", NULL);
5390
0
        if (pszValue)
5391
0
          wfsparams->pszOutputFormat = msStrdup(pszValue);
5392
5393
0
        psIter = psGetPropertyValue->psChild;
5394
0
        while (psIter != NULL) {
5395
0
          if (psIter->eType == CXT_Element &&
5396
0
              strcmp(psIter->pszValue, "Query") == 0) {
5397
0
            msWFSParseXMLQueryNode(psIter, wfsparams);
5398
            /* Just one is allowed for GetPropertyValue */
5399
0
            break;
5400
0
          } else if (psIter->eType == CXT_Element &&
5401
0
                     strcmp(psIter->pszValue, "StoredQuery") == 0) {
5402
0
            int status = msWFSParseXMLStoredQueryNode(map, wfsparams, psIter);
5403
0
            if (status != MS_SUCCESS) {
5404
0
              CPLDestroyXMLNode(psRoot);
5405
0
              msFree(pszSchemaLocation);
5406
0
              return status;
5407
0
            }
5408
0
            wfsparams->bHasPostStoredQuery = MS_TRUE;
5409
            /* Just one is allowed for GetPropertyValue */
5410
0
            break;
5411
0
          }
5412
0
          psIter = psIter->psNext;
5413
0
        }
5414
5415
0
        msWFSSimplifyPropertyNameAndFilter(wfsparams);
5416
0
      } /* end of GetPropertyValue */
5417
5418
      /* -------------------------------------------------------------------- */
5419
      /*      Parse GetCapabilities.                                          */
5420
      /* -------------------------------------------------------------------- */
5421
0
      if (psGetCapabilities) {
5422
0
        CPLXMLNode *psAcceptVersions;
5423
0
        CPLXMLNode *psSections;
5424
5425
0
        wfsparams->pszRequest = msStrdup("GetCapabilities");
5426
5427
0
        pszValue = CPLGetXMLValue(psGetCapabilities, "updateSequence", NULL);
5428
0
        if (pszValue)
5429
0
          wfsparams->pszUpdateSequence = msStrdup(pszValue);
5430
5431
        /* version is optional for the GetCapabilities. If not */
5432
        /* provided, set it. */
5433
0
        if (wfsparams->pszVersion == NULL)
5434
0
          wfsparams->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
5435
5436
0
        psAcceptVersions = CPLGetXMLNode(psGetCapabilities, "AcceptVersions");
5437
0
        if (psAcceptVersions != NULL) {
5438
0
          CPLXMLNode *psIter = psAcceptVersions->psChild;
5439
0
          while (psIter != NULL) {
5440
0
            if (psIter->eType == CXT_Element &&
5441
0
                strcmp(psIter->pszValue, "Version") == 0) {
5442
0
              pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5443
0
              if (pszValue) {
5444
0
                msWFSBuildParamList(&(wfsparams->pszAcceptVersions), pszValue,
5445
0
                                    ",");
5446
0
              }
5447
0
            }
5448
0
            psIter = psIter->psNext;
5449
0
          }
5450
0
        }
5451
5452
0
        psSections = CPLGetXMLNode(psGetCapabilities, "Sections");
5453
0
        if (psSections != NULL) {
5454
0
          CPLXMLNode *psIter = psSections->psChild;
5455
0
          while (psIter != NULL) {
5456
0
            if (psIter->eType == CXT_Element &&
5457
0
                strcmp(psIter->pszValue, "Section") == 0) {
5458
0
              pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5459
0
              if (pszValue) {
5460
0
                msWFSBuildParamList(&(wfsparams->pszSections), pszValue, ",");
5461
0
              }
5462
0
            }
5463
0
            psIter = psIter->psNext;
5464
0
          }
5465
0
        }
5466
0
      } /* end of GetCapabilities */
5467
      /* -------------------------------------------------------------------- */
5468
      /*      Parse DescribeFeatureType                                       */
5469
      /* -------------------------------------------------------------------- */
5470
0
      if (psDescribeFeature) {
5471
0
        CPLXMLNode *psIter;
5472
0
        wfsparams->pszRequest = msStrdup("DescribeFeatureType");
5473
5474
0
        pszValue = CPLGetXMLValue(psDescribeFeature, "outputFormat", NULL);
5475
0
        if (pszValue)
5476
0
          wfsparams->pszOutputFormat = msStrdup(pszValue);
5477
5478
0
        psIter = CPLGetXMLNode(psDescribeFeature, "TypeName");
5479
0
        if (psIter) {
5480
          /* free typname and filter. There may have been */
5481
          /* values if they were passed in the URL */
5482
0
          if (wfsparams->pszTypeName)
5483
0
            free(wfsparams->pszTypeName);
5484
0
          wfsparams->pszTypeName = NULL;
5485
5486
0
          while (psIter) {
5487
0
            if (psIter->eType == CXT_Element &&
5488
0
                strcasecmp(psIter->pszValue, "TypeName") == 0) {
5489
0
              pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5490
0
              if (pszValue) {
5491
0
                msWFSBuildParamList(&(wfsparams->pszTypeName), pszValue, ",");
5492
0
              }
5493
0
            }
5494
0
            psIter = psIter->psNext;
5495
0
          }
5496
0
        }
5497
5498
0
      } /* end of DescribeFeatureType */
5499
5500
      /* -------------------------------------------------------------------- */
5501
      /*      Parse the ListStoredQueries                                     */
5502
      /* -------------------------------------------------------------------- */
5503
0
      if (psListStoredQueries) {
5504
0
        wfsparams->pszRequest = msStrdup("ListStoredQueries");
5505
0
      } /* end of ListStoredQueries */
5506
5507
      /* -------------------------------------------------------------------- */
5508
      /*      Parse the DescribeStoredQueries                                 */
5509
      /* -------------------------------------------------------------------- */
5510
0
      if (psDescribeStoredQueries) {
5511
0
        CPLXMLNode *psIter;
5512
5513
0
        wfsparams->pszRequest = msStrdup("DescribeStoredQueries");
5514
5515
0
        psIter = CPLGetXMLNode(psDescribeStoredQueries, "StoredQueryId");
5516
0
        while (psIter) {
5517
0
          if (psIter->eType == CXT_Element &&
5518
0
              strcasecmp(psIter->pszValue, "StoredQueryId") == 0) {
5519
0
            pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5520
0
            if (pszValue) {
5521
0
              msWFSBuildParamList(&(wfsparams->pszStoredQueryId), pszValue,
5522
0
                                  ",");
5523
0
            }
5524
0
          }
5525
0
          psIter = psIter->psNext;
5526
0
        }
5527
0
      } /* end of DescribeStoredQueries */
5528
5529
0
      CPLDestroyXMLNode(psRoot);
5530
0
    }
5531
5532
0
#if defined(USE_LIBXML2)
5533
0
    {
5534
0
      const char *schema_location = NULL, *validate = NULL;
5535
5536
      /*do we validate the xml ?*/
5537
0
      validate =
5538
0
          msOWSLookupMetadata(&(map->web.metadata), "FO", "validate_xml");
5539
0
      if (validate && strcasecmp(validate, "true") == 0 &&
5540
0
          (schema_location = msOWSLookupMetadata(&(map->web.metadata), "FO",
5541
0
                                                 "schemas_dir"))) {
5542
0
        if ((wfsparams->pszService &&
5543
0
             strcmp(wfsparams->pszService, "WFS") == 0) ||
5544
0
            force_wfs_mode) {
5545
0
          char *schema_file = NULL;
5546
0
          if (pszSchemaLocation != NULL &&
5547
0
              strstr(pszSchemaLocation, "wfs/1.0") != NULL) {
5548
0
            schema_file = msStringConcatenate(schema_file, schema_location);
5549
0
            schema_file = msStringConcatenate(
5550
0
                schema_file, MS_OWSCOMMON_WFS_10_SCHEMA_LOCATION);
5551
0
          } else if (pszSchemaLocation != NULL &&
5552
0
                     strstr(pszSchemaLocation, "wfs/1.1") != NULL) {
5553
0
            schema_file = msStringConcatenate(schema_file, schema_location);
5554
0
            schema_file = msStringConcatenate(
5555
0
                schema_file, MS_OWSCOMMON_WFS_11_SCHEMA_LOCATION);
5556
0
          } else if (pszSchemaLocation != NULL &&
5557
0
                     strstr(pszSchemaLocation, "wfs/2.0") != NULL) {
5558
0
            schema_file = msStringConcatenate(schema_file, schema_location);
5559
0
            schema_file = msStringConcatenate(
5560
0
                schema_file, MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION);
5561
0
          }
5562
0
          if (schema_file != NULL) {
5563
0
            if (msOWSSchemaValidation(schema_file, request->postrequest) != 0) {
5564
0
              msSetError(MS_WFSERR, "Invalid POST request.  XML is not valid",
5565
0
                         "msWFSParseRequest()");
5566
0
              msFree(schema_file);
5567
0
              return msWFSException(map, "request", "InvalidRequest", NULL);
5568
0
            }
5569
0
            msFree(schema_file);
5570
0
          }
5571
0
        }
5572
0
      }
5573
0
    }
5574
0
#endif
5575
5576
0
    msFree(pszSchemaLocation);
5577
0
  }
5578
5579
#else
5580
  (void)map;
5581
  (void)request;
5582
  (void)wfsparams;
5583
  (void)force_wfs_mode;
5584
#endif
5585
0
  return MS_SUCCESS;
5586
0
}
5587
5588
/* msIsLayerSupportedForWFSOrOAPIF()
5589
**
5590
** Returns true (1) is this layer meets the requirements to be served as
5591
** a WFS feature type or OGC API Features.
5592
*/
5593
0
int msIsLayerSupportedForWFSOrOAPIF(layerObj *lp) {
5594
  /* In order to be supported, lp->type must be specified, even for
5595
  ** layers that are OGR, SDE, SDO, etc connections.
5596
  */
5597
0
  if ((lp->type == MS_LAYER_POINT || lp->type == MS_LAYER_LINE ||
5598
0
       lp->type == MS_LAYER_POLYGON) &&
5599
0
      lp->connectiontype != MS_WMS && lp->connectiontype != MS_GRATICULE) {
5600
0
    return 1; /* true */
5601
0
  }
5602
5603
0
  return 0; /* false */
5604
0
}