Coverage Report

Created: 2026-04-10 07:04

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