• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geographika / mapserver / 23004945429

12 Mar 2026 01:39PM UTC coverage: 42.423% (+0.003%) from 42.42%
23004945429

push

github

geographika
Use std::string and avoid strlcpy

8 of 9 new or added lines in 1 file covered. (88.89%)

955 existing lines in 4 files now uncovered.

64589 of 152250 relevant lines covered (42.42%)

27323.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.72
/src/mapwfs.cpp
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
#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
static int msWFSGetIndexUnsupportedOperation(const char *pszOp) {
49✔
97
  int i;
98
  for (i = 0; i < wfsNumUnsupportedOperations; i++) {
279✔
99
    if (strcasecmp(wfsUnsupportedOperations[i], pszOp) == 0)
233✔
100
      return i;
3✔
101
  }
102
  return -1;
103
}
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,
102✔
114
                                  const char *code, const char *version,
115
                                  int locatorShouldBeNull) {
116
  char *schemalocation = NULL;
117
  /* In WFS, exceptions are always XML.
118
   */
119

120
  if (version == NULL)
102✔
121
    version = msWFSGetDefaultVersion(map);
7✔
122

123
  if (msOWSParseVersionString(version) >= OWS_2_0_0)
102✔
124
    return msWFSException20(map, (locatorShouldBeNull) ? NULL : locator, code);
60✔
125
  if (msOWSParseVersionString(version) >= OWS_1_1_0)
44✔
126
    return msWFSException11(map, (locatorShouldBeNull) ? NULL : locator, code,
14✔
127
                            version);
12✔
128

129
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
32✔
130
  msIO_sendHeaders();
32✔
131

132
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
32✔
133

134
  msIO_printf("<ServiceExceptionReport ");
32✔
135
  msIO_printf("version=\"1.2.0\" ");
32✔
136
  msIO_printf("xmlns=\"http://www.opengis.net/ogc\" ");
32✔
137
  msIO_printf("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
32✔
138
  schemalocation = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
32✔
139
  msIO_printf("xsi:schemaLocation=\"http://www.opengis.net/ogc "
32✔
140
              "%s/wfs/1.0.0/OGC-exception.xsd\">\n",
141
              schemalocation);
142
  free(schemalocation);
32✔
143
  msIO_printf("  <ServiceException code=\"%s\" locator=\"%s\">\n", code,
32✔
144
              locator);
145
  /* Optional <Locator> element currently unused. */
146
  /* msIO_printf("    <Message>\n"); */
147
  msWriteErrorXML(stdout);
32✔
148
  /* msIO_printf("    </Message>\n"); */
149
  msIO_printf("  </ServiceException>\n");
32✔
150
  msIO_printf("</ServiceExceptionReport>\n");
32✔
151

152
  return MS_FAILURE; /* so we can call 'return msWFSException();' anywhere */
32✔
153
}
154

155
int msWFSException(mapObj *map, const char *locator, const char *code,
96✔
156
                   const char *version) {
157
  return msWFSExceptionInternal(map, locator, code, version, FALSE);
96✔
158
}
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
                                   const char *code, const char *version) {
170
  return msWFSExceptionInternal(map, locator, code, version, TRUE);
1✔
171
}
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
char *msWFSGetOutputFormatList(mapObj *map, layerObj *layer, int nWFSVersion) {
128✔
182
  int i, got_map_list = 0;
183
  static const int out_list_size = 20000;
184
  char *out_list = (char *)msSmallCalloc(1, out_list_size);
128✔
185

186
  if (nWFSVersion == OWS_1_0_0)
128✔
187
    strcpy(out_list, "GML2");
188
  else if (nWFSVersion == OWS_1_1_0)
110✔
189
    strcpy(out_list, "text/xml; subtype=gml/3.1.1");
190
  else
191
    strcpy(out_list, "application/gml+xml; version=3.2,"
192
                     "text/xml; subtype=gml/3.2.1,"
193
                     "text/xml; subtype=gml/3.1.1,"
194
                     "text/xml; subtype=gml/2.1.2");
195

196
  for (i = 0; i < map->numlayers; i++) {
376✔
197
    const char *format_list;
198
    layerObj *lp;
199
    int j, n;
200
    char **tokens;
201

202
    lp = GET_LAYER(map, i);
248✔
203
    if (layer != NULL && layer != lp)
248✔
204
      continue;
225✔
205

206
    format_list =
207
        msOWSLookupMetadata(&(lp->metadata), "F", "getfeature_formatlist");
178✔
208

209
    if (format_list == NULL && !got_map_list) {
178✔
210
      format_list = msOWSLookupMetadata(&(map->web.metadata), "F",
120✔
211
                                        "getfeature_formatlist");
212
      got_map_list = 1;
213
    }
214

215
    if (format_list == NULL)
178✔
216
      continue;
155✔
217

218
    n = 0;
23✔
219
    tokens = msStringSplit(format_list, ',', &n);
23✔
220

221
    for (j = 0; j < n; j++) {
157✔
222
      int iformat;
223
      const char *fname, *hit;
224
      outputFormatObj *format_obj;
225

226
      msStringTrim(tokens[j]);
134✔
227
      iformat = msGetOutputFormatIndex(map, tokens[j]);
134✔
228
      if (iformat < 0)
134✔
229
        continue;
9✔
230

231
      format_obj = map->outputformatlist[iformat];
125✔
232

233
      fname = format_obj->name;
125✔
234
      if (nWFSVersion >= OWS_1_1_0 && format_obj->mimetype != NULL)
125✔
235
        fname = format_obj->mimetype;
236

237
      hit = strstr(out_list, fname);
238
      if (hit != NULL &&
125✔
239
          (hit[strlen(fname)] == '\0' || hit[strlen(fname)] == ','))
62✔
240
        continue;
62✔
241

242
      if (strlen(out_list) + strlen(fname) + 3 < out_list_size) {
63✔
243
        strcat(out_list, ",");
244
        strcat(out_list, fname);
245
      } else
246
        break;
247
    }
248

249
    msFreeCharArray(tokens, n);
23✔
250
  }
251

252
  return out_list;
128✔
253
}
254

255
/*
256
**
257
*/
258
static void msWFSPrintRequestCap(const char *request, const char *script_url,
54✔
259
                                 const char *format_tag,
260
                                 const char *formats_list) {
261
  msIO_printf("    <%s>\n", request);
54✔
262

263
  /* We expect to receive a NULL-terminated args list of formats */
264
  if (format_tag != NULL) {
54✔
265
    int i, n;
266
    char **tokens;
267

268
    n = 0;
36✔
269
    tokens = msStringSplit(formats_list, ',', &n);
36✔
270

271
    msIO_printf("      <%s>\n", format_tag);
36✔
272

273
    for (i = 0; i < n; i++) {
83✔
274
      msIO_printf("        <%s/>\n", tokens[i]);
47✔
275
    }
276

277
    msFreeCharArray(tokens, n);
36✔
278

279
    msIO_printf("      </%s>\n", format_tag);
36✔
280
  }
281

282
  msIO_printf("      <DCPType>\n");
54✔
283
  msIO_printf("        <HTTP>\n");
54✔
284
  msIO_printf("          <Get onlineResource=\"%s\" />\n", script_url);
54✔
285
  msIO_printf("        </HTTP>\n");
54✔
286
  msIO_printf("      </DCPType>\n");
54✔
287
  msIO_printf("      <DCPType>\n");
54✔
288
  msIO_printf("        <HTTP>\n");
54✔
289
  msIO_printf("          <Post onlineResource=\"%s\" />\n", script_url);
54✔
290
  msIO_printf("        </HTTP>\n");
54✔
291
  msIO_printf("      </DCPType>\n");
54✔
292

293
  msIO_printf("    </%s>\n", request);
54✔
294
}
54✔
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
static int msWFSLocateSRSInList(const char *pszList, const char *srs) {
20✔
307
  int nTokens = 0;
20✔
308
  char **tokens = NULL;
309
  int bFound = MS_FALSE;
310
  std::string authority, code;
311

312
  if (!pszList || !srs)
20✔
313
    return MS_FALSE;
314

315
  /* OGC URN: urn:ogc:def:crs:AUTHORITY::CODE or
316
   * urn:ogc:def:crs:AUTHORITY:version:CODE */
317
  if (strncasecmp(srs, "urn:ogc:def:crs:", 16) == 0) {
20✔
318
    const char *p = srs + 16;
3✔
319
    const char *sep = strchr(p, ':');
320
    if (sep == NULL)
3✔
321
      return MS_FALSE;
322
    authority = std::string(p, sep - p);
3✔
323
    /* skip version field (may be empty) */
324
    p = sep + 1;
3✔
325
    sep = strchr(p, ':');
326
    if (sep == NULL)
3✔
327
      return MS_FALSE;
328
    code = sep + 1;
3✔
329
  }
330
  /* OGC URI: http://www.opengis.net/def/crs/AUTHORITY/version/CODE */
331
  else if (strncasecmp(srs, "http://www.opengis.net/def/crs/", 31) == 0) {
17✔
UNCOV
332
    const char *p = srs + 31;
×
333
    const char *sep = strchr(p, '/');
UNCOV
334
    if (sep == NULL)
×
335
      return MS_FALSE;
NEW
336
    authority = std::string(p, sep - p);
×
337
    /* skip version field */
UNCOV
338
    p = sep + 1;
×
339
    sep = strchr(p, '/');
UNCOV
340
    if (sep == NULL)
×
341
      return MS_FALSE;
UNCOV
342
    code = sep + 1;
×
343
  }
344
  /* Legacy URN format: urn:EPSG:geographicCRS:CODE */
345
  else if (strncasecmp(srs, "urn:EPSG:geographicCRS:", 23) == 0) {
17✔
346
    authority = "EPSG";
347
    code = srs + 23;
4✔
348
  }
349
  /* Simple AUTHORITY:CODE e.g. EPSG:4326, ESRI:54030, IAU_2015:30100 */
350
  else {
351
    const char *sep = strchr(srs, ':');
352
    if (sep == NULL)
13✔
353
      return MS_FALSE;
354
    authority = std::string(srs, sep - srs);
13✔
355
    code = sep + 1;
13✔
356
  }
357

358
  if (code.empty())
20✔
359
    return MS_FALSE;
360

361
  tokens = msStringSplit(pszList, ' ', &nTokens);
20✔
362
  if (tokens) {
20✔
363
    for (int i = 0; i < nTokens; i++) {
50✔
364
      const char *token_sep = strchr(tokens[i], ':');
46✔
365
      if (token_sep == NULL)
46✔
UNCOV
366
        continue;
×
367
      std::string token_auth(tokens[i], token_sep - tokens[i]);
46✔
368
      /* Compare authority case-insensitively and code exactly */
369
      if (strcasecmp(token_auth.c_str(), authority.c_str()) == 0 &&
46✔
370
          code == (token_sep + 1)) {
34✔
371
        bFound = MS_TRUE;
372
        break;
373
      }
374
    }
375
  }
376

377
  msFreeCharArray(tokens, nTokens);
20✔
378
  return bFound;
379
}
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,
600✔
388
                                   int nWFSVersion) {
389
  char *pszMapSRS = NULL;
600✔
390
  char *pszOutputSRS = NULL;
391
  layerObj *lp;
392
  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
  msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE,
600✔
405
                   &pszMapSRS);
406
  if (pszMapSRS && nWFSVersion > OWS_1_0_0) {
600✔
407
    projectionObj proj;
408
    msInitProjection(&proj);
258✔
409
    msProjectionInheritContextFrom(&proj, &(map->projection));
258✔
410
    if (map->projection.numargs > 0 &&
400✔
411
        msLoadProjectionStringEPSG(&proj, pszMapSRS) == 0) {
142✔
412
      msProjectRect(&(map->projection), &proj, &map->extent);
142✔
413
    }
414
    msLoadProjectionStringEPSG(&(map->projection), pszMapSRS);
258✔
415
    msFreeProjection(&proj);
258✔
416
  }
417

418
  if (srs == NULL || nWFSVersion == OWS_1_0_0) {
600✔
419
    for (i = 0; i < map->numlayers; i++) {
2,756✔
420
      char *pszLayerSRS;
421
      lp = GET_LAYER(map, i);
2,177✔
422
      if (lp->status != MS_ON)
2,177✔
423
        continue;
1,499✔
424

425
      if (pszMapSRS)
678✔
426
        pszLayerSRS = pszMapSRS;
613✔
427
      else
428
        msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE,
65✔
429
                         &pszLayerSRS);
430

431
      if (pszLayerSRS == NULL) {
678✔
UNCOV
432
        msSetError(MS_WFSERR,
×
433
                   "Server config error: SRS must be set at least at the map "
434
                   "or at the layer level.",
435
                   "msWFSGetFeature()");
UNCOV
436
        if (pszOutputSRS)
×
UNCOV
437
          msFree(pszOutputSRS);
×
438
        /*pszMapSrs would also be NULL, no use freeing*/
439
        return MS_FAILURE;
1✔
440
      }
441
      if (pszOutputSRS == NULL)
678✔
442
        pszOutputSRS = msStrdup(pszLayerSRS);
578✔
443
      else if (strcasecmp(pszLayerSRS, pszOutputSRS) != 0) {
100✔
444
        msSetError(
1✔
445
            MS_WFSERR,
446
            "Invalid GetFeature Request: All TYPENAMES in a single GetFeature "
447
            "request must have been advertized in the same SRS.  Please check "
448
            "the capabilities and reformulate your request.",
449
            "msWFSGetFeature()");
450
        if (pszOutputSRS)
451
          msFree(pszOutputSRS);
1✔
452
        if (pszLayerSRS != pszMapSRS)
1✔
453
          msFree(pszLayerSRS);
1✔
454
        msFree(pszMapSRS);
1✔
455
        return MS_FAILURE;
1✔
456
      }
457
      if (pszLayerSRS != pszMapSRS)
677✔
458
        msFree(pszLayerSRS);
64✔
459
    }
460
  } 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
    msFree(pszMapSRS);
20✔
464
    msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_FALSE,
20✔
465
                     &pszMapSRS);
466
    if (pszMapSRS) {
20✔
467
      if (!msWFSLocateSRSInList(pszMapSRS, srs)) {
18✔
468
        msSetError(MS_WFSERR,
3✔
469
                   "Invalid GetFeature Request:Invalid SRS.  Please check the "
470
                   "capabilities and reformulate your request.",
471
                   "msWFSGetFeature()");
472
        msFree(pszMapSRS);
3✔
473
        return MS_FAILURE;
3✔
474
      }
475
      pszOutputSRS = msStrdup(srs);
15✔
476
    } else {
477
      for (i = 0; i < map->numlayers; i++) {
9✔
478
        char *pszLayerSRS = NULL;
8✔
479
        lp = GET_LAYER(map, i);
8✔
480
        if (lp->status != MS_ON)
8✔
481
          continue;
6✔
482

483
        msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_FALSE,
2✔
484
                         &pszLayerSRS);
485
        if (!pszLayerSRS) {
2✔
UNCOV
486
          msSetError(MS_WFSERR,
×
487
                     "Server config error: SRS must be set at least at the map "
488
                     "or at the layer level.",
489
                     "msWFSGetFeature()");
UNCOV
490
          msFree(pszMapSRS);
×
491
          return MS_FAILURE;
1✔
492
        }
493
        if (!msWFSLocateSRSInList(pszLayerSRS, srs)) {
2✔
494
          msSetError(MS_WFSERR,
1✔
495
                     "Invalid GetFeature Request:Invalid SRS.  Please check "
496
                     "the capabilities and reformulate your request.",
497
                     "msWFSGetFeature()");
498
          msFree(pszMapSRS);
1✔
499
          msFree(pszLayerSRS);
1✔
500
          return MS_FAILURE;
1✔
501
        }
502
        msFree(pszLayerSRS);
1✔
503
      }
504
      pszOutputSRS = msStrdup(srs);
1✔
505
    }
506
  }
507

508
  if (pszOutputSRS) {
595✔
509
    projectionObj sProjTmp;
510
    int nTmp = 0;
511

512
    msInitProjection(&sProjTmp);
593✔
513
    msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
593✔
514
    if (nWFSVersion >= OWS_1_1_0) {
593✔
515
      nTmp = msLoadProjectionStringEPSG(&(sProjTmp), pszOutputSRS);
269✔
516
    } else {
517
      nTmp = msLoadProjectionString(&(sProjTmp), pszOutputSRS);
324✔
518
    }
519
    if (nTmp == 0) {
593✔
520
      msProjectRect(&(map->projection), &(sProjTmp), &map->extent);
593✔
521
    }
522
    msFreeProjection(&(sProjTmp));
593✔
523

524
    if (nTmp != 0) {
593✔
UNCOV
525
      msSetError(MS_WFSERR, "msLoadProjectionString() failed",
×
526
                 "msWFSGetFeature()");
UNCOV
527
      return MS_FAILURE;
×
528
    }
529

530
    /*we load the projection sting in the map and possibly
531
    set the axis order*/
532
    if (nWFSVersion >= OWS_1_1_0) {
593✔
533
      msLoadProjectionStringEPSG(&(map->projection), pszOutputSRS);
269✔
534
    } else {
535
      msLoadProjectionString(&(map->projection), pszOutputSRS);
324✔
536
    }
537

538
    nTmp = GetMapserverUnitUsingProj(&(map->projection));
593✔
539
    if (nTmp != -1) {
593✔
540
      map->units = static_cast<MS_UNITS>(nTmp);
593✔
541
    }
542
  }
543

544
  msFree(pszOutputSRS);
595✔
545
  msFree(pszMapSRS);
595✔
546
  return MS_SUCCESS;
595✔
547
}
548

549
static int msWFSIsLayerAllowed(layerObj *lp, owsRequestObj *ows_request) {
2,225✔
550
  return msIsLayerSupportedForWFSOrOAPIF(lp) &&
4,436✔
551
         (msIntegerInArray(lp->index, ows_request->enabled_layers,
2,211✔
552
                           ows_request->numlayers));
2,225✔
553
}
554

555
static layerObj *msWFSGetLayerByName(mapObj *map, owsRequestObj *ows_request,
1,132✔
556
                                     const char *name) {
557
  int j;
558
  for (j = 0; j < map->numlayers; j++) {
2,140✔
559
    layerObj *lp;
560

561
    lp = GET_LAYER(map, j);
2,133✔
562

563
    if (msWFSIsLayerAllowed(lp, ows_request) && lp->name &&
2,133✔
564
        (strcasecmp(lp->name, name) == 0)) {
2,122✔
565
      return lp;
1,125✔
566
    }
567
  }
568
  return NULL;
569
}
570

571
/*
572
** msWFSDumpLayer()
573
*/
574
int msWFSDumpLayer(mapObj *map, layerObj *lp, const char *script_url_encoded) {
26✔
575
  rectObj ext;
576
  char *pszWfsSrs = NULL;
26✔
577
  projectionObj poWfs;
578

579
  msIO_printf("    <FeatureType>\n");
26✔
580

581
  if (lp->name && strlen(lp->name) > 0 &&
52✔
582
      (msIsXMLTagValid(lp->name) == MS_FALSE || isdigit(lp->name[0])))
52✔
UNCOV
583
    msIO_fprintf(stdout,
×
584
                 "<!-- WARNING: The layer name '%s' might contain spaces or "
585
                 "invalid characters or may start with a number. This could "
586
                 "lead to potential problems. -->\n",
587
                 lp->name);
588

589
  msOWSPrintEncodeParam(stdout, "LAYER.NAME", lp->name, OWS_WARN,
26✔
590
                        "        <Name>%s</Name>\n", NULL);
591

592
  msOWSPrintEncodeMetadata(stdout, &(lp->metadata), "FO", "title", OWS_WARN,
26✔
593
                           "        <Title>%s</Title>\n", lp->name);
26✔
594

595
  msOWSPrintEncodeMetadata(stdout, &(lp->metadata), "FO", "abstract", OWS_NOERR,
26✔
596
                           "        <Abstract>%s</Abstract>\n", NULL);
597

598
  msOWSPrintEncodeMetadataList(stdout, &(lp->metadata), "FO", "keywordlist",
26✔
599
                               "        <Keywords>\n", "        </Keywords>\n",
600
                               "          %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
  msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE,
26✔
616
                   &pszWfsSrs);
617
  if (!pszWfsSrs) {
26✔
618
    /* Map has no SRS.  Use layer SRS or produce a warning. */
619
    msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE,
6✔
620
                     &pszWfsSrs);
621
  }
622

623
  msOWSPrintEncodeParam(
26✔
624
      stdout,
625
      "(at least one of) MAP.PROJECTION, LAYER.PROJECTION or wfs_srs metadata",
626
      pszWfsSrs, OWS_WARN, "        <SRS>%s</SRS>\n", NULL);
627

628
  /* If layer has no proj set then use map->proj for bounding box. */
629
  if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS) {
26✔
630
    msInitProjection(&poWfs);
26✔
631
    msProjectionInheritContextFrom(&poWfs, &(map->projection));
26✔
632
    if (pszWfsSrs != NULL)
26✔
633
      msLoadProjectionString(&(poWfs), pszWfsSrs);
26✔
634

635
    if (lp->projection.numargs > 0) {
26✔
636
      msOWSPrintLatLonBoundingBox(stdout, "        ", &(ext), &(lp->projection),
26✔
637
                                  &(poWfs), OWS_WFS);
638
    } else {
UNCOV
639
      msOWSPrintLatLonBoundingBox(stdout, "        ", &(ext),
×
640
                                  &(map->projection), &(poWfs), OWS_WFS);
641
    }
642
    msFreeProjection(&poWfs);
26✔
643
  } else {
UNCOV
644
    msIO_printf("<!-- WARNING: Optional LatLongBoundingBox could not be "
×
645
                "established for this layer.  Consider setting the EXTENT in "
646
                "the LAYER object, or wfs_extent metadata. Also check that "
647
                "your data exists in the DATA statement -->\n");
648
  }
649

650
  const char *metadataurl_list =
651
      msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_list");
26✔
652
  if (metadataurl_list) {
26✔
653
    int ntokens = 0;
1✔
654
    char **tokens = msStringSplit(metadataurl_list, ' ', &ntokens);
1✔
655
    for (int i = 0; i < ntokens; i++) {
3✔
656
      std::string key("metadataurl_");
2✔
657
      key += tokens[i];
2✔
658
      msOWSPrintURLType(stdout, &(lp->metadata), "FO", key.c_str(), OWS_WARN,
2✔
659
                        NULL, "MetadataURL", " type=\"%s\"", NULL, NULL,
660
                        " format=\"%s\"", "%s", MS_TRUE, MS_FALSE, MS_FALSE,
661
                        MS_TRUE, MS_TRUE, NULL, NULL, NULL, NULL, NULL,
662
                        "        ");
663
    }
664
    msFreeCharArray(tokens, ntokens);
1✔
665
  } else {
666
    if (!msOWSLookupMetadata(&(lp->metadata), "FO", "metadataurl_href"))
25✔
667
      msMetadataSetGetMetadataURL(lp, script_url_encoded);
19✔
668

669
    msOWSPrintURLType(stdout, &(lp->metadata), "FO", "metadataurl", OWS_WARN,
25✔
670
                      NULL, "MetadataURL", " type=\"%s\"", NULL, NULL,
671
                      " format=\"%s\"", "%s", MS_TRUE, MS_FALSE, MS_FALSE,
672
                      MS_TRUE, MS_TRUE, NULL, NULL, NULL, NULL, NULL,
673
                      "        ");
674
  }
675

676
  if (msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid") == NULL) {
26✔
677
    msIO_fprintf(
1✔
678
        stdout,
679
        "<!-- WARNING: Required Feature Id attribute (fid) not specified for "
680
        "this feature type. Make sure you set one of wfs_featureid, "
681
        "ows_featureid or gml_featureid metadata. -->\n");
682
  }
683

684
  msIO_printf("    </FeatureType>\n");
26✔
685

686
  msFree(pszWfsSrs);
26✔
687
  return MS_SUCCESS;
26✔
688
}
689

690
/*
691
** msWFSHandleUpdateSequence()
692
*/
693
int msWFSHandleUpdateSequence(mapObj *map, wfsParamsObj *params,
84✔
694
                              const char *pszFunction) {
695
  /* -------------------------------------------------------------------- */
696
  /*      Handle updatesequence                                           */
697
  /* -------------------------------------------------------------------- */
698

699
  const char *updatesequence =
700
      msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence");
84✔
701

702
  if (params->pszUpdateSequence != NULL) {
84✔
703
    int i =
704
        msOWSNegotiateUpdateSequence(params->pszUpdateSequence, updatesequence);
17✔
705
    if (i == 0) { /* current */
17✔
706
      msSetError(MS_WFSERR,
6✔
707
                 "UPDATESEQUENCE parameter (%s) is equal to server (%s)",
708
                 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
      return msWFSException(map, "updatesequence", "CurrentUpdateSequence",
6✔
713
                            params->pszVersion);
6✔
714
    }
715
    if (i > 0) { /* invalid */
11✔
716
      msSetError(MS_WFSERR,
5✔
717
                 "UPDATESEQUENCE parameter (%s) is higher than server (%s)",
718
                 pszFunction, params->pszUpdateSequence, updatesequence);
719
      /* locator must be NULL. See Table 25 of OWS 1.1 */
720
      return msWFSExceptionNoLocator(map, "updatesequence",
721
                                     MS_OWS_ERROR_INVALID_UPDATE_SEQUENCE,
722
                                     params->pszVersion);
5✔
723
    }
724
  }
725

726
  return MS_SUCCESS;
727
}
728

729
/*
730
** msWFSGetCapabilitiesNegotiateVersion()
731
*/
732
static int msWFSGetCapabilitiesNegotiateVersion(mapObj *map,
89✔
733
                                                wfsParamsObj *wfsparams) {
734
  int iVersion = -1;
735
  char tmpString[OWS_VERSION_MAXLEN];
736

737
  /* acceptversions: do OWS Common style of version negotiation */
738
  if (wfsparams->pszAcceptVersions &&
89✔
739
      strlen(wfsparams->pszAcceptVersions) > 0) {
10✔
740
    char **tokens;
741
    int i, j;
742

743
    tokens = msStringSplit(wfsparams->pszAcceptVersions, ',', &j);
10✔
744
    for (i = 0; i < j; i++) {
11✔
745
      iVersion = msOWSParseVersionString(tokens[i]);
10✔
746

747
      if (iVersion < 0) {
10✔
748
        msSetError(MS_WFSERR, "Invalid version format : %s.",
1✔
749
                   "msWFSGetCapabilities()", tokens[i]);
750
        msFreeCharArray(tokens, j);
1✔
751
        return msWFSException(map, "acceptversions",
1✔
752
                              MS_OWS_ERROR_INVALID_PARAMETER_VALUE, NULL);
2✔
753
      }
754

755
      /* negotiate version */
756
      iVersion = msOWSCommonNegotiateVersion(iVersion, wfsSupportedVersions,
9✔
757
                                             wfsNumSupportedVersions);
758
      if (iVersion != -1)
9✔
759
        break;
760
    }
761
    msFreeCharArray(tokens, j);
9✔
762
    if (iVersion == -1) {
9✔
763
      msSetError(MS_WFSERR,
1✔
764
                 "ACCEPTVERSIONS list (%s) does not match supported versions",
765
                 "msWFSGetCapabilities()", wfsparams->pszAcceptVersions);
766
      /* locator must be NULL. See Table 25 of OWS 1.1 */
767
      return msWFSExceptionNoLocator(
768
          map, "acceptversions", MS_OWS_ERROR_VERSION_NEGOTIATION_FAILED, NULL);
769
    }
770
  } else {
8✔
771
    /* negotiate version */
772
    int tmpInt;
773
    iVersion = msOWSParseVersionString(wfsparams->pszVersion);
79✔
774
    if (iVersion < 0) {
79✔
775
      return msWFSException(map, "version",
3✔
776
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE, NULL);
777
    }
778
    tmpInt = msOWSCommonNegotiateVersion(iVersion, wfsSupportedVersions,
76✔
779
                                         wfsNumSupportedVersions);
780
    /* Old style negotiation : paragraph D.11 of OWS 1.1.0 spec */
781
    if (tmpInt < 0) {
76✔
782
      int i;
783
      for (i = 0; i < wfsNumSupportedVersions; i++) {
20✔
784
        if (iVersion >= wfsSupportedVersions[i]) {
18✔
785
          iVersion = wfsSupportedVersions[i];
786
          break;
787
        }
788
      }
789
      if (i == wfsNumSupportedVersions)
8✔
790
        iVersion = wfsSupportedVersions[wfsNumSupportedVersions - 1];
791
    }
792
  }
793

794
  /* set result as string and carry on */
795
  if (wfsparams->pszVersion)
84✔
796
    msFree(wfsparams->pszVersion);
84✔
797
  wfsparams->pszVersion = msStrdup(msOWSGetVersionString(iVersion, tmpString));
84✔
798

799
  return MS_SUCCESS;
84✔
800
}
801

802
/*
803
** msWFSGetCapabilities()
804
*/
805
int msWFSGetCapabilities(mapObj *map, wfsParamsObj *wfsparams,
89✔
806
                         cgiRequestObj *req, owsRequestObj *ows_request) {
807
  char *script_url = NULL, *script_url_encoded;
808
  const char *updatesequence = NULL;
809
  const char *wmtver = NULL;
810
  char *formats_list;
811
  int ret;
812
  int iVersion;
813
  int i = 0;
814

815
  ret = msWFSGetCapabilitiesNegotiateVersion(map, wfsparams);
89✔
816
  if (ret != MS_SUCCESS)
89✔
817
    return ret;
818

819
  iVersion = msOWSParseVersionString(wfsparams->pszVersion);
84✔
820
  if (iVersion == OWS_2_0_0)
84✔
821
    return msWFSGetCapabilities20(map, wfsparams, req, ows_request);
38✔
822
  if (iVersion == OWS_1_1_0)
46✔
823
    return msWFSGetCapabilities11(map, wfsparams, req, ows_request);
24✔
824

825
  /* Decide which version we're going to return... only 1.0.0 for now */
826
  wmtver = "1.0.0";
827

828
  /* We need this server's onlineresource. */
829
  if ((script_url = msOWSGetOnlineResource(map, "FO", "onlineresource", req)) ==
22✔
830
          NULL ||
44✔
831
      (script_url_encoded = msEncodeHTMLEntities(script_url)) == NULL) {
22✔
UNCOV
832
    msSetError(MS_WFSERR, "Server URL not found", "msWFSGetCapabilities()");
×
UNCOV
833
    return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
834
                          wmtver);
×
835
  }
836
  free(script_url);
22✔
837
  script_url = NULL;
838

839
  ret = msWFSHandleUpdateSequence(map, wfsparams, "msWFSGetCapabilities()");
22✔
840
  if (ret != MS_SUCCESS) {
22✔
841
    free(script_url_encoded);
4✔
842
    return ret;
4✔
843
  }
844

845
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
18✔
846
  msIO_sendHeaders();
18✔
847

848
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
18✔
849

850
  updatesequence =
851
      msOWSLookupMetadata(&(map->web.metadata), "FO", "updatesequence");
18✔
852
  msIO_printf("<WFS_Capabilities \n"
20✔
853
              "   version=\"%s\" \n"
854
              "   updateSequence=\"%s\" \n"
855
              "   xmlns=\"http://www.opengis.net/wfs\" \n"
856
              "   xmlns:ogc=\"http://www.opengis.net/ogc\" \n"
857
              "   xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
858
              "   xsi:schemaLocation=\"http://www.opengis.net/wfs "
859
              "%s/wfs/%s/WFS-capabilities.xsd\">\n",
860
              wmtver, updatesequence ? updatesequence : "0",
861
              msOWSGetSchemasLocation(map), wmtver);
862

863
  /*
864
  ** SERVICE definition
865
  */
866
  msIO_printf("<Service>\n");
18✔
867
  msIO_printf("  <Name>MapServer WFS</Name>\n");
18✔
868

869
  /* the majority of this section is dependent on appropriately named metadata
870
   * in the WEB object */
871
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "title",
18✔
872
                           OWS_WARN, "  <Title>%s</Title>\n", map->name);
18✔
873
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "abstract",
18✔
874
                           OWS_NOERR, "  <Abstract>%s</Abstract>\n", NULL);
875

876
  msOWSPrintEncodeMetadataList(stdout, &(map->web.metadata), "FO",
18✔
877
                               "keywordlist", "  <Keywords>\n",
878
                               "  </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
  msOWSPrintEncodeMetadata(
18✔
884
      stdout, &(map->web.metadata), "FO", "service_onlineresource", OWS_NOERR,
885
      "  <OnlineResource>%s</OnlineResource>\n", script_url_encoded);
886

887
  msOWSPrintEncodeMetadata(stdout, &(map->web.metadata), "FO", "fees",
18✔
888
                           OWS_NOERR, "  <Fees>%s</Fees>\n", NULL);
889

890
  msOWSPrintEncodeMetadata(
18✔
891
      stdout, &(map->web.metadata), "FO", "accessconstraints", OWS_NOERR,
892
      "  <AccessConstraints>%s</AccessConstraints>\n", NULL);
893

894
  msIO_printf("</Service>\n\n");
18✔
895

896
  /*
897
  ** CAPABILITY definitions: list of supported requests
898
  */
899

900
  msIO_printf("<Capability>\n");
18✔
901

902
  msIO_printf("  <Request>\n");
18✔
903
  msWFSPrintRequestCap("GetCapabilities", script_url_encoded, NULL, NULL);
18✔
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
  if (msOWSRequestIsEnabled(map, NULL, "F", "DescribeFeatureType", MS_TRUE))
18✔
911
    msWFSPrintRequestCap("DescribeFeatureType", script_url_encoded,
18✔
912
                         "SchemaDescriptionLanguage", "XMLSCHEMA");
913

914
  if (msOWSRequestIsEnabled(map, NULL, "F", "GetFeature", MS_TRUE)) {
18✔
915
    formats_list = msWFSGetOutputFormatList(map, NULL, OWS_1_0_0);
18✔
916
    msWFSPrintRequestCap("GetFeature", script_url_encoded, "ResultFormat",
18✔
917
                         formats_list);
918
    msFree(formats_list);
18✔
919
  }
920

921
  msIO_printf("  </Request>\n");
18✔
922
  msIO_printf("</Capability>\n\n");
18✔
923

924
  /*
925
  ** FeatureTypeList: layers
926
  */
927

928
  msIO_printf("<FeatureTypeList>\n");
18✔
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
  msIO_printf("  <Operations>\n");
18✔
933
  msIO_printf("    <Query/>\n");
18✔
934
  msIO_printf("  </Operations>\n");
18✔
935

936
  for (i = 0; i < map->numlayers; i++) {
51✔
937
    layerObj *lp;
938
    lp = GET_LAYER(map, i);
33✔
939

940
    if (lp->status == MS_DELETE)
33✔
UNCOV
941
      continue;
×
942

943
    if (msWFSIsLayerAllowed(lp, ows_request)) {
33✔
944
      msWFSDumpLayer(map, lp, script_url_encoded);
26✔
945
    }
946
  }
947

948
  msIO_printf("</FeatureTypeList>\n\n");
18✔
949

950
  /*
951
  ** OGC Filter Capabilities ... for now we support only BBOX
952
  */
953

954
  msIO_printf("<ogc:Filter_Capabilities>\n");
18✔
955
  msIO_printf("  <ogc:Spatial_Capabilities>\n");
18✔
956
  msIO_printf("    <ogc:Spatial_Operators>\n");
18✔
957
#ifdef USE_GEOS
958
  msIO_printf("      <ogc:Equals/>\n");
18✔
959
  msIO_printf("      <ogc:Disjoint/>\n");
18✔
960
  msIO_printf("      <ogc:Touches/>\n");
18✔
961
  msIO_printf("      <ogc:Within/>\n");
18✔
962
  msIO_printf("      <ogc:Overlaps/>\n");
18✔
963
  msIO_printf("      <ogc:Crosses/>\n");
18✔
964
  msIO_printf("      <ogc:Intersect/>\n");
18✔
965
  msIO_printf("      <ogc:Contains/>\n");
18✔
966
  msIO_printf("      <ogc:DWithin/>\n");
18✔
967
#endif
968
  msIO_printf("      <ogc:BBOX/>\n");
18✔
969
  msIO_printf("    </ogc:Spatial_Operators>\n");
18✔
970
  msIO_printf("  </ogc:Spatial_Capabilities>\n");
18✔
971

972
  msIO_printf("  <ogc:Scalar_Capabilities>\n");
18✔
973
  msIO_printf("    <ogc:Logical_Operators />\n");
18✔
974
  msIO_printf("    <ogc:Comparison_Operators>\n");
18✔
975
  msIO_printf("      <ogc:Simple_Comparisons />\n");
18✔
976
  msIO_printf("      <ogc:Like />\n");
18✔
977
  msIO_printf("      <ogc:Between />\n");
18✔
978
  msIO_printf("    </ogc:Comparison_Operators>\n");
18✔
979
  msIO_printf("  </ogc:Scalar_Capabilities>\n");
18✔
980

981
  msIO_printf("</ogc:Filter_Capabilities>\n\n");
18✔
982

983
  /*
984
  ** Done!
985
  */
986
  msIO_printf("</WFS_Capabilities>\n");
18✔
987

988
  free(script_url_encoded);
18✔
989

990
  return MS_SUCCESS;
18✔
991
}
992

993
/*
994
** Helper functions for producing XML schema.
995
*/
996

997
static const char *msWFSGetGeometryType(const char *type,
49✔
998
                                        OWSGMLVersion outputformat) {
999
  if (!type)
49✔
1000
    return "GeometryPropertyType";
1001

1002
  if (strcasecmp(type, "point") == 0) {
30✔
1003
    switch (outputformat) {
4✔
1004
    case OWS_GML2:
4✔
1005
    case OWS_GML3:
1006
    case OWS_GML32:
1007
      return "PointPropertyType";
4✔
1008
    }
1009
  } else if (strcasecmp(type, "multipoint") == 0) {
26✔
1010
    switch (outputformat) {
4✔
1011
    case OWS_GML2:
4✔
1012
    case OWS_GML3:
1013
    case OWS_GML32:
1014
      return "MultiPointPropertyType";
4✔
1015
    }
1016
  } else if (strcasecmp(type, "line") == 0) {
22✔
1017
    switch (outputformat) {
2✔
1018
    case OWS_GML2:
1✔
1019
      return "LineStringPropertyType";
1✔
1020
    case OWS_GML3:
1✔
1021
    case OWS_GML32:
1022
      return "CurvePropertyType";
1✔
1023
    }
1024
  } else if (strcasecmp(type, "multiline") == 0) {
20✔
1025
    switch (outputformat) {
2✔
1026
    case OWS_GML2:
1✔
1027
      return "MultiLineStringPropertyType";
1✔
1028
    case OWS_GML3:
1✔
1029
    case OWS_GML32:
1030
      return "MultiCurvePropertyType";
1✔
1031
    }
1032
  } else if (strcasecmp(type, "polygon") == 0) {
18✔
1033
    switch (outputformat) {
15✔
1034
    case OWS_GML2:
3✔
1035
      return "PolygonPropertyType";
3✔
1036
    case OWS_GML3:
12✔
1037
    case OWS_GML32:
1038
      return "SurfacePropertyType";
12✔
1039
    }
1040
  } else if (strcasecmp(type, "multipolygon") == 0) {
3✔
1041
    switch (outputformat) {
3✔
1042
    case OWS_GML2:
2✔
1043
      return "MultiPolygonPropertyType";
2✔
1044
    case OWS_GML3:
1✔
1045
    case OWS_GML32:
1046
      return "MultiSurfacePropertyType";
1✔
1047
    }
1048
  }
1049

1050
  return "???unknown???";
1051
}
1052

1053
static void msWFSWriteGeometryElement(FILE *stream,
48✔
1054
                                      gmlGeometryListObj *geometryList,
1055
                                      OWSGMLVersion outputformat,
1056
                                      const char *tab) {
1057
  int i;
1058
  gmlGeometryObj *geometry = NULL;
1059

1060
  if (!stream || !tab)
48✔
1061
    return;
1062
  if (geometryList->numgeometries == 1 &&
48✔
1063
      strcasecmp(geometryList->geometries[0].name, "none") == 0)
47✔
1064
    return;
1065

1066
  if (geometryList->numgeometries == 1) {
48✔
1067
    geometry = &(geometryList->geometries[0]);
47✔
1068
    msIO_fprintf(
47✔
1069
        stream, "%s<element name=\"%s\" type=\"gml:%s\" minOccurs=\"%d\"", tab,
1070
        geometry->name, msWFSGetGeometryType(geometry->type, outputformat),
47✔
1071
        geometry->occurmin);
1072
    if (geometry->occurmax == OWS_GML_OCCUR_UNBOUNDED)
47✔
UNCOV
1073
      msIO_fprintf(stream, " maxOccurs=\"unbounded\"/>\n");
×
1074
    else
1075
      msIO_fprintf(stream, " maxOccurs=\"%d\"/>\n", geometry->occurmax);
47✔
1076
  } else {
1077
    msIO_fprintf(stream, "%s<choice>\n", tab);
1✔
1078
    for (i = 0; i < geometryList->numgeometries; i++) {
3✔
1079
      geometry = &(geometryList->geometries[i]);
2✔
1080

1081
      msIO_fprintf(stream,
2✔
1082
                   "  %s<element name=\"%s\" type=\"gml:%s\" minOccurs=\"%d\"",
1083
                   tab, geometry->name,
1084
                   msWFSGetGeometryType(geometry->type, outputformat),
2✔
1085
                   geometry->occurmin);
1086
      if (geometry->occurmax == OWS_GML_OCCUR_UNBOUNDED)
2✔
UNCOV
1087
        msIO_fprintf(stream, " maxOccurs=\"unbounded\"/>\n");
×
1088
      else
1089
        msIO_fprintf(stream, " maxOccurs=\"%d\"/>\n", geometry->occurmax);
2✔
1090
    }
1091
    msIO_fprintf(stream, "%s</choice>\n", tab);
1✔
1092
  }
1093

1094
  return;
1095
}
1096

1097
static OWSGMLVersion
1098
msWFSGetGMLVersionFromSchemaVersion(WFSSchemaVersion outputformat) {
1099
  switch (outputformat) {
35✔
1100
  case OWS_DEFAULT_SCHEMA:
1101
    return OWS_GML2;
1102
  case OWS_SFE_SCHEMA:
8✔
1103
    return OWS_GML3;
8✔
1104
  case OWS_GML32_SFE_SCHEMA:
25✔
1105
    return OWS_GML32;
25✔
1106
  }
1107
  return OWS_GML2;
1108
}
1109

1110
static void msWFSSchemaWriteGeometryElement(FILE *stream,
1111
                                            gmlGeometryListObj *geometryList,
1112
                                            WFSSchemaVersion outputformat,
1113
                                            const char *tab) {
1114
  OWSGMLVersion gmlversion = msWFSGetGMLVersionFromSchemaVersion(outputformat);
1115
  msWFSWriteGeometryElement(stream, geometryList, gmlversion, tab);
48✔
1116
}
48✔
1117

1118
static const char *msWFSMapServTypeToXMLType(const char *type) {
103✔
1119
  const char *element_type = "string";
1120
  /* Map from MapServer types to XSD types */
1121
  if (strcasecmp(type, "Integer") == 0)
103✔
1122
    element_type = "integer";
1123
  /* Note : xs:int and xs:integer differ */
1124
  else if (EQUAL(type, "int"))
84✔
1125
    element_type = "int";
1126
  if (strcasecmp(type, "Long") == 0) /* 64bit integer */
103✔
1127
    element_type = "long";
1128
  else if (
95✔
1129
      EQUAL(type, "Real") ||
95✔
1130
      EQUAL(type,
74✔
1131
            "double") /* just in case someone provided the xsd type directly */)
1132
    element_type = "double";
1133
  else if (EQUAL(type, "Character"))
74✔
1134
    element_type = "string";
1135
  else if (EQUAL(type, "Date"))
44✔
1136
    element_type = "date";
1137
  else if (EQUAL(type, "Time"))
39✔
1138
    element_type = "time";
1139
  else if (EQUAL(type, "DateTime"))
34✔
1140
    element_type = "dateTime";
1141
  else if (EQUAL(type, "Boolean"))
25✔
1142
    element_type = "boolean";
1143
  return element_type;
103✔
1144
}
1145

1146
static void msWFSWriteItemElement(FILE *stream, gmlItemObj *item,
356✔
1147
                                  const char *tab,
1148
                                  WFSSchemaVersion outputformat,
1149
                                  int is_nillable) {
1150
  const char *element_name;
1151
  const char *element_type = "string";
1152
  const char *pszMinOccurs = "";
1153
  const char *pszNillable = "";
1154

1155
  if (!stream || !item || !tab)
356✔
1156
    return;
1157
  if (!item->visible)
356✔
1158
    return; /* not exposing this attribute */
1159
  if (item->_template)
167✔
1160
    return; /* can't adequately deal with templated items yet */
1161

1162
  if (item->alias) /* TODO: what about name spaces embedded in the alias? */
167✔
1163
    element_name = item->alias;
1164
  else
1165
    element_name = item->name;
163✔
1166

1167
  if (item->type) {
167✔
1168
    if (outputformat == OWS_GML32_SFE_SCHEMA &&
114✔
1169
        (EQUAL(item->type, "Date") || EQUAL(item->type, "Time") ||
37✔
1170
         EQUAL(item->type, "DateTime")))
31✔
1171
      element_type = "gml:TimeInstantType";
1172
    else
1173
      element_type = msWFSMapServTypeToXMLType(item->type);
103✔
1174
  }
1175

1176
  if (item->minOccurs == 0)
167✔
1177
    pszMinOccurs = " minOccurs=\"0\"";
1178

1179
  if (is_nillable)
167✔
1180
    pszNillable = " nillable=\"true\"";
1181

1182
  msIO_fprintf(stream, "%s<element name=\"%s\"%s%s type=\"%s\"/>\n", tab,
167✔
1183
               element_name, pszMinOccurs, pszNillable, element_type);
1184

1185
  return;
167✔
1186
}
1187

UNCOV
1188
static void msWFSWriteConstantElement(FILE *stream, gmlConstantObj *constant,
×
1189
                                      const char *tab) {
1190
  const char *element_type = "string";
1191

UNCOV
1192
  if (!stream || !constant || !tab)
×
1193
    return;
1194

UNCOV
1195
  if (constant->type)
×
UNCOV
1196
    element_type = msWFSMapServTypeToXMLType(constant->type);
×
1197

UNCOV
1198
  msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s\"/>\n", tab,
×
1199
               constant->name, element_type);
1200

UNCOV
1201
  return;
×
1202
}
1203

1204
static void msWFSWriteGroupElement(FILE *stream, gmlGroupObj *group,
2✔
1205
                                   const char *tab, const char *_namespace) {
1206
  if (group->type)
2✔
UNCOV
1207
    msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s:%s\"/>\n", tab,
×
1208
                 group->name, _namespace, group->type);
1209
  else
1210
    msIO_fprintf(stream, "%s<element name=\"%s\" type=\"%s:%sType\"/>\n", tab,
2✔
1211
                 group->name, _namespace, group->name);
1212

1213
  return;
2✔
1214
}
1215

1216
static void msWFSWriteGroupElementType(FILE *stream, gmlGroupObj *group,
2✔
1217
                                       gmlItemListObj *itemList,
1218
                                       gmlConstantListObj *constantList,
1219
                                       const char *tab,
1220
                                       WFSSchemaVersion outputformat) {
1221
  int i, j;
1222

1223
  gmlItemObj *item = NULL;
1224
  gmlConstantObj *constant = NULL;
1225

1226
  /* setup the element tab */
1227
  std::string element_tab(tab);
2✔
1228
  element_tab += "    ";
1229

1230
  if (group->type)
2✔
UNCOV
1231
    msIO_fprintf(stream, "%s<complexType name=\"%s\">\n", tab, group->type);
×
1232
  else
1233
    msIO_fprintf(stream, "%s<complexType name=\"%sType\">\n", tab, group->name);
2✔
1234

1235
  msIO_fprintf(stream, "%s  <sequence>\n", tab);
2✔
1236

1237
  /* now the items/constants (e.g. elements) in the group */
1238
  for (i = 0; i < group->numitems; i++) {
8✔
1239
    for (j = 0; j < constantList->numconstants;
6✔
1240
         j++) { /* find the right gmlConstantObj */
UNCOV
1241
      constant = &(constantList->constants[j]);
×
UNCOV
1242
      if (strcasecmp(constant->name, group->items[i]) == 0) {
×
UNCOV
1243
        msWFSWriteConstantElement(stream, constant, element_tab.c_str());
×
1244
        break;
1245
      }
1246
    }
1247
    if (j != constantList->numconstants)
6✔
UNCOV
1248
      continue;                                /* found this item */
×
1249
    for (j = 0; j < itemList->numitems; j++) { /* find the right gmlItemObj */
57✔
1250
      item = &(itemList->items[j]);
57✔
1251
      if (strcasecmp(item->name, group->items[i]) == 0) {
57✔
1252
        msWFSWriteItemElement(stream, item, element_tab.c_str(), outputformat,
6✔
1253
                              MS_FALSE);
1254
        break;
1255
      }
1256
    }
1257
  }
1258

1259
  msIO_fprintf(stream, "%s  </sequence>\n", tab);
2✔
1260
  msIO_fprintf(stream, "%s</complexType>\n", tab);
2✔
1261

1262
  return;
2✔
1263
}
1264

1265
static const char *msWFSGetGMLSchemaLocation(OWSGMLVersion outputformat) {
1266
  switch (outputformat) {
186✔
1267
  case OWS_GML2:
1268
    return MS_OWSCOMMON_GML_212_SCHEMA_LOCATION;
1269
  case OWS_GML3:
1270
    return MS_OWSCOMMON_GML_311_SCHEMA_LOCATION;
1271
  case OWS_GML32:
1272
    return MS_OWSCOMMON_GML_321_SCHEMA_LOCATION;
1273
  }
1274
  return "/unknown.xsd";
1275
}
1276

1277
static const char *msWFSGetGMLNamespaceURI(WFSSchemaVersion outputformat) {
1278
  switch (outputformat) {
70✔
1279
  case OWS_DEFAULT_SCHEMA:
1280
    return MS_OWSCOMMON_GML_NAMESPACE_URI;
1281
  case OWS_SFE_SCHEMA:
1282
    return MS_OWSCOMMON_GML_NAMESPACE_URI;
1283
  case OWS_GML32_SFE_SCHEMA:
36✔
1284
    return MS_OWSCOMMON_GML_32_NAMESPACE_URI;
36✔
1285
  }
1286
  return "http://unknown";
1287
}
1288

1289
static const char *
1290
msWFSGetGMLNamespaceURIFromGMLVersion(OWSGMLVersion outputformat) {
1291
  switch (outputformat) {
186✔
1292
  case OWS_GML2:
1293
    return MS_OWSCOMMON_GML_NAMESPACE_URI;
1294
  case OWS_GML3:
1295
    return MS_OWSCOMMON_GML_NAMESPACE_URI;
1296
  case OWS_GML32:
175✔
1297
    return MS_OWSCOMMON_GML_32_NAMESPACE_URI;
175✔
1298
  }
UNCOV
1299
  return "http://unknown";
×
1300
}
1301

1302
static void msWFS_NS_printf(const char *prefix, const char *uri) {
509✔
1303
  if (prefix == NULL)
509✔
1304
    msIO_printf("   xmlns=\"%s\"\n", uri);
35✔
1305
  else
1306
    msIO_printf("   xmlns:%s=\"%s\"\n", prefix, uri);
1,088✔
1307
}
509✔
1308

1309
static void
1310
msWFSPrintAdditionalNamespaces(const gmlNamespaceListObj *namespaceList) {
544✔
1311
  int i;
1312
  /* any additional namespaces */
1313
  for (i = 0; i < namespaceList->numnamespaces; i++) {
544✔
UNCOV
1314
    if (namespaceList->namespaces[i].uri) {
×
1315
      char *uri_encoded = NULL;
1316

UNCOV
1317
      uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
×
UNCOV
1318
      msWFS_NS_printf(namespaceList->namespaces[i].prefix, uri_encoded);
×
UNCOV
1319
      msFree(uri_encoded);
×
1320
    }
1321
  }
1322
}
544✔
1323

1324
static const char *msWFSStripNS(const char *name) {
769✔
1325
  const char *pszColon = strchr(name, ':');
1326
  const char *pszSlash = strchr(name, '/');
1327
  if (pszColon && (pszSlash == NULL || pszColon < pszSlash))
769✔
1328
    return pszColon + 1;
24✔
1329
  return name;
1330
}
1331

1332
/*
1333
** msWFSDescribeFeatureType()
1334
*/
1335
static int msWFSDescribeFeatureType(mapObj *map, wfsParamsObj *paramsObj,
37✔
1336
                                    owsRequestObj *ows_request,
1337
                                    int nWFSVersion) {
1338
  int i, numlayers = 0;
37✔
1339
  char **layers = NULL;
1340

1341
  const char *value;
1342
  const char *user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
1343
  const char *user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
1344
  char *user_namespace_uri_encoded = NULL;
1345
  const char *collection_name = OWS_WFS_FEATURE_COLLECTION_NAME;
1346
  char *encoded;
1347

1348
  WFSSchemaVersion outputformat =
1349
      OWS_DEFAULT_SCHEMA; /* default output is GML 2.1 compliant schema*/
1350

1351
  gmlNamespaceListObj *namespaceList =
1352
      NULL; /* for external application schema support */
1353
  char *mimetype = NULL;
1354

1355
  if (paramsObj->pszTypeName && numlayers == 0) {
37✔
1356
    /* Parse comma-delimited list of type names (layers) */
1357
    layers = msStringSplit(paramsObj->pszTypeName, ',', &numlayers);
14✔
1358
    if (numlayers > 0) {
14✔
1359
      /* strip namespace if there is one :ex TYPENAME=cdf:Other */
1360
      for (i = 0; i < numlayers; i++) {
30✔
1361
        char *pszTmp = msStrdup(msWFSStripNS(layers[i]));
16✔
1362
        free(layers[i]);
16✔
1363
        layers[i] = pszTmp;
16✔
1364
      }
1365
    }
1366
  }
1367

1368
  if (paramsObj->pszOutputFormat) {
37✔
1369
    if (strcasecmp(paramsObj->pszOutputFormat, "XMLSCHEMA") == 0 ||
8✔
1370
        strstr(paramsObj->pszOutputFormat, "gml/2") != NULL) {
1371
      mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/2.1.2");
2✔
1372
      outputformat = OWS_DEFAULT_SCHEMA;
1373
    } else if (strcasecmp(paramsObj->pszOutputFormat, "SFE_XMLSCHEMA") == 0 ||
6✔
1374
               strstr(paramsObj->pszOutputFormat, "gml/3.1") != NULL) {
1375
      mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/3.1.1");
3✔
1376
      outputformat = OWS_SFE_SCHEMA;
1377
    } else if (strstr(paramsObj->pszOutputFormat, "gml/3.2") != NULL ||
3✔
1378
               strstr(paramsObj->pszOutputFormat,
1379
                      "application/gml+xml; version=3.2") != NULL) {
1380
      mimetype = msEncodeHTMLEntities("application/gml+xml; version=3.2");
2✔
1381
      outputformat = OWS_GML32_SFE_SCHEMA;
1382
    } else {
1383
      msSetError(MS_WFSERR,
1✔
1384
                 "Unsupported DescribeFeatureType outputFormat (%s).",
1385
                 "msWFSDescribeFeatureType()", paramsObj->pszOutputFormat);
1386
      if (layers)
1✔
UNCOV
1387
        msFreeCharArray(layers, numlayers);
×
1388
      msFree(mimetype);
1389
      return msWFSException(map, "outputformat",
1✔
1390
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1391
                            paramsObj->pszVersion);
1✔
1392
    }
1393
  }
1394
  /* If no outputFormat explicitly asked, use a sensible default for the WFS
1395
     version */
1396
  else {
1397
    switch (nWFSVersion) {
29✔
1398
    case OWS_1_0_0:
7✔
1399
    default:
1400
      mimetype = msEncodeHTMLEntities(
7✔
1401
          "text/xml"); /* ERO: why not "text/xml; subtype=gml/2.1.2" ? */
1402
      break;
1403

1404
    case OWS_1_1_0:
5✔
1405
      mimetype = msEncodeHTMLEntities("text/xml; subtype=gml/3.1.1");
5✔
1406
      outputformat = OWS_SFE_SCHEMA;
1407
      break;
1408

1409
    case OWS_2_0_0:
17✔
1410
      mimetype = msEncodeHTMLEntities("application/gml+xml; version=3.2");
17✔
1411
      outputformat = OWS_GML32_SFE_SCHEMA;
1412
      break;
1413
    }
1414
  }
1415

1416
  /* Validate layers */
1417
  if (numlayers > 0) {
36✔
1418
    for (i = 0; i < numlayers; i++) {
29✔
1419
      layerObj *lp = msWFSGetLayerByName(map, ows_request, layers[i]);
16✔
1420
      if (lp == NULL) {
16✔
1421
        msSetError(MS_WFSERR,
1✔
1422
                   "Invalid typename (%s). A layer might be disabled for \
1423
this request. Check wfs/ows_enable_request settings.",
1424
                   "msWFSDescribeFeatureType()",
1425
                   layers[i]); /* paramsObj->pszTypeName); */
1426
        msFreeCharArray(layers, numlayers);
1✔
1427
        msFree(mimetype);
1✔
1428
        return msWFSException(map, "typename",
1✔
1429
                              MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1430
                              paramsObj->pszVersion);
1✔
1431
      }
1432
    }
1433
  }
1434

1435
  /*
1436
  ** retrieve any necessary external namespace/schema configuration information
1437
  */
1438
  namespaceList = msGMLGetNamespaces(&(map->web), "G");
35✔
1439
  if (namespaceList == NULL) {
35✔
UNCOV
1440
    msSetError(MS_MISCERR, "Unable to populate namespace list",
×
1441
               "msWFSDescribeFeatureType()");
UNCOV
1442
    return MS_FAILURE;
×
1443
  }
1444

1445
  /*
1446
  ** DescribeFeatureType response
1447
  */
1448

1449
  msIO_setHeader("Content-Type", "%s; charset=UTF-8", mimetype);
35✔
1450
  msIO_sendHeaders();
35✔
1451

1452
  if (mimetype)
35✔
1453
    msFree(mimetype);
35✔
1454

1455
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
35✔
1456

1457
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
35✔
1458
  if (value)
35✔
1459
    user_namespace_uri = value;
1460
  user_namespace_uri_encoded = msEncodeHTMLEntities(user_namespace_uri);
35✔
1461

1462
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
35✔
1463
  if (value)
35✔
1464
    user_namespace_prefix = value;
1465
  if (user_namespace_prefix != NULL &&
35✔
1466
      msIsXMLTagValid(user_namespace_prefix) == MS_FALSE)
35✔
UNCOV
1467
    msIO_printf(
×
1468
        "<!-- WARNING: The value '%s' is not valid XML namespace. -->\n",
1469
        user_namespace_prefix);
1470

1471
  msIO_printf("<schema\n"
35✔
1472
              "   targetNamespace=\"%s\" \n"
1473
              "   xmlns:%s=\"%s\" \n",
1474
              user_namespace_uri_encoded, user_namespace_prefix,
1475
              user_namespace_uri_encoded);
1476
  if (nWFSVersion < OWS_2_0_0)
35✔
1477
    msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
1478
                    MS_OWSCOMMON_OGC_NAMESPACE_URI);
1479
  msWFS_NS_printf("xsd", MS_OWSCOMMON_W3C_XS_NAMESPACE_URI);
1480
  msWFS_NS_printf(NULL, MS_OWSCOMMON_W3C_XS_NAMESPACE_URI);
1481
  msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1482
                  msWFSGetGMLNamespaceURI(outputformat));
1483

1484
  msWFSPrintAdditionalNamespaces(namespaceList);
35✔
1485

1486
  msIO_printf("   elementFormDefault=\"qualified\" version=\"0.1\" >\n");
35✔
1487

1488
  encoded = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
35✔
1489

1490
  msIO_printf("\n  <import namespace=\"%s\"\n"
35✔
1491
              "          schemaLocation=\"%s%s\" />\n",
1492
              msWFSGetGMLNamespaceURI(outputformat), encoded,
1493
              msWFSGetGMLSchemaLocation(
1494
                  msWFSGetGMLVersionFromSchemaVersion(outputformat)));
1495

1496
  msFree(encoded);
35✔
1497

1498
  /* any additional namespace includes */
1499
  for (i = 0; i < namespaceList->numnamespaces; i++) {
35✔
UNCOV
1500
    if (namespaceList->namespaces[i].uri &&
×
UNCOV
1501
        namespaceList->namespaces[i].schemalocation) {
×
1502
      char *schema_location_encoded = NULL, *uri_encoded = NULL;
1503

UNCOV
1504
      uri_encoded = msEncodeHTMLEntities(namespaceList->namespaces[i].uri);
×
1505
      schema_location_encoded =
UNCOV
1506
          msEncodeHTMLEntities(namespaceList->namespaces[i].schemalocation);
×
UNCOV
1507
      msIO_printf("\n  <import namespace=\"%s\"\n schemaLocation=\"%s\" />\n",
×
1508
                  uri_encoded, schema_location_encoded);
UNCOV
1509
      msFree(uri_encoded);
×
UNCOV
1510
      msFree(schema_location_encoded);
×
1511
    }
1512
  }
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
  if (outputformat == OWS_SFE_SCHEMA && nWFSVersion == OWS_1_0_0) {
35✔
1519
    value =
1520
        msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection");
1✔
1521
    if (value)
1✔
1522
      collection_name = value;
1523

1524
    msIO_printf("  <element name=\"%s\" type=\"%s:%sType\" "
1✔
1525
                "substitutionGroup=\"gml:_FeatureCollection\"/>\n",
1526
                collection_name, user_namespace_prefix, collection_name);
1527
    msIO_printf("  <complexType name=\"%sType\">\n", collection_name);
1✔
1528
    msIO_printf("    <complexContent>\n");
1✔
1529
    msIO_printf(
1✔
1530
        "      <extension base=\"gml:AbstractFeatureCollectionType\">\n");
1531
    msIO_printf("        <attribute name=\"version\" type=\"string\" "
1✔
1532
                "use=\"required\" fixed=\"1.0.0\"/>\n");
1533
    msIO_printf("      </extension>\n");
1✔
1534
    msIO_printf("    </complexContent>\n");
1✔
1535
    msIO_printf("  </complexType>\n");
1✔
1536
  }
1537

1538
  /*
1539
  ** loop through layers
1540
  */
1541
  for (i = 0; i < map->numlayers; i++) {
109✔
1542
    layerObj *lp;
1543
    int j, bFound = 0;
1544

1545
    lp = GET_LAYER(map, i);
74✔
1546

1547
    for (j = 0; j < numlayers && !bFound; j++) {
114✔
1548
      if (lp->name && strcasecmp(lp->name, layers[j]) == 0)
40✔
1549
        bFound = 1;
1550
    }
1551

1552
    if ((numlayers == 0 || bFound) && msWFSIsLayerAllowed(lp, ows_request)) {
74✔
1553

1554
      /*
1555
      ** OK, describe this layer IF you can open it and retrieve items
1556
      */
1557
      if (msLayerOpen(lp) == MS_SUCCESS) {
48✔
1558
        if (msLayerGetItems(lp) == MS_SUCCESS) {
48✔
1559
          int k;
1560
          gmlGroupListObj *groupList = NULL;
1561
          gmlItemListObj *itemList = NULL;
1562
          gmlConstantListObj *constantList = NULL;
1563
          gmlGeometryListObj *geometryList = NULL;
1564
          gmlItemObj *item = NULL;
1565
          gmlConstantObj *constant = NULL;
1566
          char *encoded_name = NULL;
1567

1568
          const char *layer_namespace_prefix;
1569
          const char *substitution_group;
1570
          char *encoded_type = NULL;
1571

1572
          itemList = msGMLGetItems(lp, "G"); /* GML-related metadata */
48✔
1573
          constantList = msGMLGetConstants(lp, "G");
48✔
1574
          groupList = msGMLGetGroups(lp, "G");
48✔
1575
          geometryList = msGMLGetGeometries(lp, "GFO", MS_TRUE);
48✔
1576
          if (itemList == NULL || constantList == NULL || groupList == NULL ||
48✔
1577
              geometryList == NULL) {
48✔
UNCOV
1578
            msSetError(MS_MISCERR,
×
1579
                       "Unable to populate item and group metadata structures",
1580
                       "msWFSDescribeFeatureType()");
UNCOV
1581
            return MS_FAILURE;
×
1582
          }
1583

1584
          value =
1585
              msOWSLookupMetadata(&(lp->metadata), "OFG", "namespace_prefix");
48✔
1586
          if (value)
48✔
1587
            layer_namespace_prefix = value;
1588
          else
1589
            layer_namespace_prefix = user_namespace_prefix;
1590

1591
          encoded_name = msEncodeHTMLEntities(lp->name);
48✔
1592
          value = msOWSLookupMetadata(&(lp->metadata), "F", "layer_type");
48✔
1593
          if (value) {
48✔
UNCOV
1594
            encoded_type = msEncodeHTMLEntities(value);
×
1595
          } else {
1596
            size_t sz = strlen(encoded_name) + strlen("Type") + 1;
48✔
1597
            encoded_type = (char *)msSmallMalloc(sz);
48✔
1598
            strlcpy(encoded_type, encoded_name, sz);
1599
            strlcat(encoded_type, "Type", sz);
1600
          }
1601

1602
          switch (outputformat) {
48✔
1603
          case OWS_DEFAULT_SCHEMA: /* default GML 2.1.x schema */
1604
          case OWS_SFE_SCHEMA:     /* reference GML 3.1.1 schema */
1605
          default:
1606
            substitution_group = "gml:_Feature";
1607
            break;
1608

1609
          case OWS_GML32_SFE_SCHEMA: /* reference GML 3.2.1 schema */
25✔
1610
            substitution_group = "gml:AbstractFeature";
1611
            break;
25✔
1612
          }
1613

1614
          msIO_printf("\n"
48✔
1615
                      "  <element name=\"%s\" \n"
1616
                      "           type=\"%s:%s\" \n"
1617
                      "           substitutionGroup=\"%s\" />\n\n",
1618
                      encoded_name, layer_namespace_prefix, encoded_type,
1619
                      substitution_group);
1620
          msFree(encoded_type);
48✔
1621

1622
          if (strcmp(layer_namespace_prefix, user_namespace_prefix) != 0) {
48✔
UNCOV
1623
            msFree(encoded_name);
×
UNCOV
1624
            msGMLFreeItems(itemList);
×
UNCOV
1625
            msGMLFreeConstants(constantList);
×
UNCOV
1626
            msGMLFreeGroups(groupList);
×
UNCOV
1627
            msGMLFreeGeometries(geometryList);
×
UNCOV
1628
            continue; /* the rest is defined in an external schema */
×
1629
          }
1630

1631
          msIO_printf("  <complexType name=\"%sType\">\n", encoded_name);
48✔
1632
          msIO_printf("    <complexContent>\n");
48✔
1633
          msIO_printf("      <extension base=\"gml:AbstractFeatureType\">\n");
48✔
1634
          msIO_printf("        <sequence>\n");
48✔
1635

1636
          /* write the geometry schema element(s) */
1637
          msWFSSchemaWriteGeometryElement(stdout, geometryList, outputformat,
48✔
1638
                                          "          ");
1639

1640
          /* write the constant-based schema elements */
1641
          for (k = 0; k < constantList->numconstants; k++) {
48✔
UNCOV
1642
            constant = &(constantList->constants[k]);
×
UNCOV
1643
            if (msItemInGroups(constant->name, groupList) == MS_FALSE)
×
UNCOV
1644
              msWFSWriteConstantElement(stdout, constant, "          ");
×
1645
          }
1646

1647
          /* write the item-based schema elements */
1648
          for (k = 0; k < itemList->numitems; k++) {
404✔
1649
            item = &(itemList->items[k]);
356✔
1650
            if (msItemInGroups(item->name, groupList) == MS_FALSE) {
356✔
1651
              int nillable = MS_FALSE;
1652
              char mdname[256];
1653
              const char *pszNillable;
1654
              snprintf(mdname, sizeof(mdname), "%s_nillable", item->name);
350✔
1655
              pszNillable = msOWSLookupMetadata(&(lp->metadata), "G", mdname);
350✔
1656
              if (pszNillable && strcasecmp(pszNillable, "true") == 0)
350✔
1657
                nillable = MS_TRUE;
1658
              msWFSWriteItemElement(stdout, item, "          ", outputformat,
350✔
1659
                                    nillable);
1660
            }
1661
          }
1662

1663
          for (k = 0; k < groupList->numgroups; k++)
50✔
1664
            msWFSWriteGroupElement(stdout, &(groupList->groups[k]),
2✔
1665
                                   "          ", user_namespace_prefix);
1666

1667
          msIO_printf("        </sequence>\n");
48✔
1668
          msIO_printf("      </extension>\n");
48✔
1669
          msIO_printf("    </complexContent>\n");
48✔
1670
          msIO_printf("  </complexType>\n");
48✔
1671

1672
          /* any group types */
1673
          for (k = 0; k < groupList->numgroups; k++)
50✔
1674
            msWFSWriteGroupElementType(stdout, &(groupList->groups[k]),
2✔
1675
                                       itemList, constantList, "  ",
1676
                                       outputformat);
1677

1678
          msGMLFreeItems(itemList);
48✔
1679
          msGMLFreeConstants(constantList);
48✔
1680
          msGMLFreeGroups(groupList);
48✔
1681
          msGMLFreeGeometries(geometryList);
48✔
1682

1683
          msFree(encoded_name);
48✔
1684
        }
1685

1686
        msLayerClose(lp);
48✔
1687
      } else {
UNCOV
1688
        msIO_printf("\n\n<!-- ERROR: Failed opening layer %s -->\n\n",
×
1689
                    lp->name);
1690
      }
1691
    }
1692
  }
1693

1694
  /*
1695
  ** Done!
1696
  */
1697
  msIO_printf("\n</schema>\n");
35✔
1698

1699
  msFree(user_namespace_uri_encoded);
35✔
1700

1701
  if (layers)
35✔
1702
    msFreeCharArray(layers, numlayers);
13✔
1703

1704
  msGMLFreeNamespaces(namespaceList);
35✔
1705

1706
  return MS_SUCCESS;
1707
}
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
static void msWFSPrintURLAndXMLEncoded(const char *str) {
272✔
1728
  char *url_encoded = msEncodeUrl(str);
272✔
1729
  char *xml_encoded = msEncodeHTMLEntities(url_encoded);
272✔
1730
  msIO_printf("%s", xml_encoded);
272✔
1731
  msFree(xml_encoded);
272✔
1732
  msFree(url_encoded);
272✔
1733
}
272✔
1734

1735
static void msWFSGetFeature_PrintBasePrevNextURI(
58✔
1736
    cgiRequestObj *req, WFSGMLInfo *gmlinfo, wfsParamsObj *paramsObj,
1737
    const char *encoded_version, const char *encoded_typename,
1738
    const char *encoded_mime_type) {
1739
  int i;
1740
  int bFirstArg = MS_TRUE;
1741
  msIO_printf("%s", gmlinfo->script_url_encoded);
58✔
1742

1743
  if (req->postrequest != NULL) {
58✔
1744
    msIO_printf("SERVICE=WFS&amp;VERSION=");
7✔
1745
    msIO_printf("%s", encoded_version);
7✔
1746
    msIO_printf("&amp;REQUEST=");
7✔
1747
    msIO_printf("%s", paramsObj->pszRequest);
7✔
1748
    msIO_printf("&amp;TYPENAMES=");
7✔
1749
    msIO_printf("%s", encoded_typename);
7✔
1750
    msIO_printf("&amp;OUTPUTFORMAT=");
7✔
1751
    msIO_printf("%s", encoded_mime_type);
7✔
1752
    if (paramsObj->pszBbox != NULL) {
7✔
UNCOV
1753
      msIO_printf("&amp;BBOX=");
×
UNCOV
1754
      msWFSPrintURLAndXMLEncoded(paramsObj->pszBbox);
×
1755
    }
1756
    if (paramsObj->pszSrs != NULL) {
7✔
1757
      msIO_printf("&amp;SRSNAME=");
2✔
1758
      msWFSPrintURLAndXMLEncoded(paramsObj->pszSrs);
2✔
1759
    }
1760
    if (paramsObj->pszFilter != NULL) {
7✔
1761
      msIO_printf("&amp;FILTER=");
2✔
1762
      msWFSPrintURLAndXMLEncoded(paramsObj->pszFilter);
2✔
1763
    }
1764
    if (paramsObj->pszPropertyName != NULL) {
7✔
1765
      msIO_printf("&amp;PROPERTYNAME=");
2✔
1766
      msWFSPrintURLAndXMLEncoded(paramsObj->pszPropertyName);
2✔
1767
    }
1768
    if (paramsObj->pszValueReference != NULL) {
7✔
1769
      msIO_printf("&amp;VALUEREFERENCE=");
2✔
1770
      msWFSPrintURLAndXMLEncoded(paramsObj->pszValueReference);
2✔
1771
    }
1772
    if (paramsObj->pszSortBy != NULL) {
7✔
UNCOV
1773
      msIO_printf("&amp;SORTBY=");
×
UNCOV
1774
      msWFSPrintURLAndXMLEncoded(paramsObj->pszSortBy);
×
1775
    }
1776
    if (paramsObj->nMaxFeatures >= 0)
7✔
1777
      msIO_printf("&amp;COUNT=%d", paramsObj->nMaxFeatures);
6✔
1778
  } else {
1779
    for (i = 0; i < req->NumParams; i++) {
408✔
1780
      if (req->ParamNames[i] && req->ParamValues[i] &&
357✔
1781
          strcasecmp(req->ParamNames[i], "MAP") != 0 &&
357✔
1782
          strcasecmp(req->ParamNames[i], "STARTINDEX") != 0 &&
306✔
1783
          strcasecmp(req->ParamNames[i], "RESULTTYPE") != 0) {
288✔
1784
        if (!bFirstArg)
264✔
1785
          msIO_printf("&amp;");
213✔
1786
        bFirstArg = MS_FALSE;
1787
        msIO_printf("%s=", req->ParamNames[i]);
264✔
1788
        msWFSPrintURLAndXMLEncoded(req->ParamValues[i]);
264✔
1789
      }
1790
    }
1791
  }
1792
}
58✔
1793

1794
static void msWFSGetFeature_GetTimeStamp(char *timestring,
211✔
1795
                                         size_t timestringlen) {
1796
  struct tm *now;
1797
  time_t tim = time(NULL);
211✔
1798

1799
  now = localtime(&tim);
211✔
1800

1801
  snprintf(timestring, timestringlen, "%d-%02d-%02dT%02d:%02d:%02d",
211✔
1802
           now->tm_year + 1900, now->tm_mon + 1, now->tm_mday, now->tm_hour,
211✔
1803
           now->tm_min, now->tm_sec);
1804
}
211✔
1805

1806
static int msWFSGetFeature_GMLPreamble(
509✔
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
{
1813
  const char *value;
1814
  char *encoded_version, *encoded_typename, *encoded_schema;
1815
  gmlNamespaceListObj *namespaceList =
1816
      NULL; /* for external application schema support */
1817
  char timestring[100];
1818
  timestring[0] = '\0';
509✔
1819

1820
  namespaceList = msGMLGetNamespaces(&(map->web), "G");
509✔
1821
  if (namespaceList == NULL) {
509✔
UNCOV
1822
    msSetError(MS_MISCERR, "Unable to populate namespace list",
×
1823
               "msWFSGetFeature_GMLPreamble()");
UNCOV
1824
    return MS_FAILURE;
×
1825
  }
1826

1827
  /*
1828
  ** Establish script_url.
1829
  */
1830

1831
  if ((gmlinfo->script_url =
509✔
1832
           msOWSGetOnlineResource(map, "FO", "onlineresource", req)) == NULL ||
509✔
1833
      (gmlinfo->script_url_encoded =
509✔
1834
           msEncodeHTMLEntities(gmlinfo->script_url)) == NULL) {
509✔
1835
    msSetError(MS_WFSERR, "Server URL not found", "msWFSGetFeature()");
×
UNCOV
1836
    msGMLFreeNamespaces(namespaceList);
×
UNCOV
1837
    return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
1838
                          paramsObj->pszVersion);
×
1839
  }
1840

1841
  /*
1842
  ** Write encoding.
1843
  */
1844
  msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
509✔
1845

1846
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_uri");
509✔
1847
  if (value)
509✔
1848
    gmlinfo->user_namespace_uri = value;
2✔
1849
  gmlinfo->user_namespace_uri_encoded =
509✔
1850
      msEncodeHTMLEntities(gmlinfo->user_namespace_uri);
509✔
1851

1852
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "namespace_prefix");
509✔
1853
  if (value)
509✔
1854
    gmlinfo->user_namespace_prefix = value;
2✔
1855

1856
  if (gmlinfo->user_namespace_prefix != NULL &&
1,018✔
1857
      msIsXMLTagValid(gmlinfo->user_namespace_prefix) == MS_FALSE)
509✔
UNCOV
1858
    msIO_printf(
×
1859
        "<!-- WARNING: The value '%s' is not valid XML namespace. -->\n",
1860
        gmlinfo->user_namespace_prefix);
1861

1862
  value = msOWSLookupMetadata(&(map->web.metadata), "FO", "feature_collection");
509✔
1863
  if (value)
509✔
UNCOV
1864
    gmlinfo->collection_name = value;
×
1865

1866
  encoded_version = msEncodeHTMLEntities(paramsObj->pszVersion);
509✔
1867
  encoded_typename = msEncodeHTMLEntities(gmlinfo->_typename);
509✔
1868
  encoded_schema = msEncodeHTMLEntities(msOWSGetSchemasLocation(map));
509✔
1869

1870
  if (nWFSVersion >= OWS_2_0_0) {
509✔
1871
    int nNextStartIndex;
1872
    char *tmp;
1873
    char *encoded_mime_type;
1874
    int bAbleToPrintPreviousOrNext = MS_TRUE;
1875

1876
    tmp = msEncodeUrl(gmlinfo->output_mime_type);
186✔
1877
    encoded_mime_type = msEncodeHTMLEntities(tmp);
186✔
1878
    msFree(tmp);
186✔
1879

1880
    msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
186✔
1881

1882
    if (strcasecmp(paramsObj->pszRequest, "GetFeature") == 0)
186✔
1883
      msIO_printf("<wfs:FeatureCollection\n");
171✔
1884
    else
1885
      msIO_printf("<wfs:ValueCollection\n");
15✔
1886

1887
    msWFS_NS_printf(gmlinfo->user_namespace_prefix,
186✔
1888
                    gmlinfo->user_namespace_uri_encoded);
186✔
1889
    msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
1890
                    msWFSGetGMLNamespaceURIFromGMLVersion(outputformat));
1891
    msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
1892
                    MS_OWSCOMMON_WFS_20_NAMESPACE_URI);
1893
    msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
1894
                    MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
1895
    msWFSPrintAdditionalNamespaces(namespaceList);
186✔
1896

1897
    msIO_printf("   xsi:schemaLocation=\"%s "
186✔
1898
                "%sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&"
1899
                "amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s "
1900
                "%s %s%s "
1901
                "%s %s%s\"\n",
1902
                gmlinfo->user_namespace_uri_encoded,
1903
                gmlinfo->script_url_encoded, encoded_version, encoded_typename,
1904
                gmlinfo->output_schema_format,
1905

1906
                MS_OWSCOMMON_WFS_20_NAMESPACE_URI, encoded_schema,
1907
                MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION,
1908

1909
                msWFSGetGMLNamespaceURIFromGMLVersion(outputformat),
1910
                encoded_schema, msWFSGetGMLSchemaLocation(outputformat));
1911

1912
    msIO_printf("   timeStamp=\"%s\" numberMatched=\"", timestring);
186✔
1913
    if (nMatchingFeatures < 0) {
186✔
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
      if (iResultTypeHits == MS_TRUE)
27✔
1918
        msIO_printf("%d", iNumberOfFeatures);
8✔
1919
      else
1920
        msIO_printf("unknown");
19✔
1921
    } else
1922
      msIO_printf("%d", nMatchingFeatures);
159✔
1923
    msIO_printf("\" numberReturned=\"%d\"",
236✔
1924
                (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
    if (req->postrequest != NULL &&
186✔
1932
        (paramsObj->bHasPostStoredQuery ||
37✔
1933
         (strchr(encoded_typename, ',') != NULL &&
7✔
1934
          (paramsObj->pszFilter != NULL || paramsObj->pszPropertyName != NULL ||
7✔
1935
           paramsObj->pszSortBy != NULL)))) {
2✔
1936
      bAbleToPrintPreviousOrNext = MS_FALSE;
1937
    }
1938

1939
    if (bAbleToPrintPreviousOrNext) {
1940
      if (maxfeatures > 0 && iResultTypeHits != 1 &&
178✔
1941
          paramsObj->nStartIndex > 0 &&
105✔
1942
          ((nMatchingFeatures < 0 && iNumberOfFeatures > 0) ||
11✔
1943
           (nMatchingFeatures >= 0 &&
9✔
1944
            paramsObj->nStartIndex < nMatchingFeatures))) {
1945
        int nPrevStartIndex;
1946

1947
        msIO_printf("\n");
10✔
1948
        msIO_printf("   previous=\"");
10✔
1949
        msWFSGetFeature_PrintBasePrevNextURI(req, gmlinfo, paramsObj,
10✔
1950
                                             encoded_version, encoded_typename,
1951
                                             encoded_mime_type);
1952

1953
        nPrevStartIndex = paramsObj->nStartIndex - maxfeatures;
10✔
1954
        if (nPrevStartIndex > 0)
10✔
1955
          msIO_printf("&amp;STARTINDEX=%d", nPrevStartIndex);
4✔
1956
        msIO_printf("\"");
10✔
1957
      }
1958

1959
      if (iResultTypeHits != 1 && paramsObj->nStartIndex >= 0)
178✔
1960
        nNextStartIndex = paramsObj->nStartIndex;
1961
      else
1962
        nNextStartIndex = 0;
1963
      if (maxfeatures > 0 &&
178✔
1964
          (bHasNextFeatures ||
111✔
1965
           (nMatchingFeatures > 0 &&
89✔
1966
            (iResultTypeHits == 1 ||
67✔
1967
             iNumberOfFeatures + nNextStartIndex < nMatchingFeatures)))) {
67✔
1968
        msIO_printf("\n");
48✔
1969
        msIO_printf("   next=\"");
48✔
1970
        msWFSGetFeature_PrintBasePrevNextURI(req, gmlinfo, paramsObj,
48✔
1971
                                             encoded_version, encoded_typename,
1972
                                             encoded_mime_type);
1973

1974
        if (iResultTypeHits != 1) {
48✔
1975
          if (bHasNextFeatures && iNumberOfFeatures < maxfeatures)
23✔
1976
            /**
1977
             * There are potentially more features to be found here, even
1978
             * though result contain less than asked for.
1979
             */
1980
            nNextStartIndex += maxfeatures;
1✔
1981
          else
1982
            nNextStartIndex += iNumberOfFeatures;
22✔
1983
        }
1984

1985
        if (nNextStartIndex > 0)
48✔
1986
          msIO_printf("&amp;STARTINDEX=%d", nNextStartIndex);
23✔
1987
        msIO_printf("\"");
48✔
1988
      }
1989
    }
1990

1991
    msIO_printf(">\n");
186✔
1992

1993
    msFree(encoded_mime_type);
186✔
1994
  }
1995
  /*
1996
  ** GML 2.x
1997
  */
1998
  else if (outputformat == OWS_GML2) { /* use a wfs:FeatureCollection */
323✔
1999
    msIO_printf("<wfs:FeatureCollection\n");
287✔
2000
    msWFS_NS_printf(gmlinfo->user_namespace_prefix,
287✔
2001
                    gmlinfo->user_namespace_uri_encoded);
287✔
2002
    msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
2003
                    MS_OWSCOMMON_WFS_NAMESPACE_URI);
2004
    msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
2005
                    MS_OWSCOMMON_GML_NAMESPACE_URI);
2006
    msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
2007
                    MS_OWSCOMMON_OGC_NAMESPACE_URI);
2008
    msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
2009
                    MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
2010
    msWFSPrintAdditionalNamespaces(namespaceList);
287✔
2011

2012
    /* FIXME ? : the schemaLocation will be only valid for WFS 1.0.0 */
2013
    msIO_printf("   xsi:schemaLocation=\"http://www.opengis.net/wfs "
287✔
2014
                "%s/wfs/%s/WFS-basic.xsd \n"
2015
                "                       %s "
2016
                "%sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&"
2017
                "amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s\">\n",
2018
                encoded_schema, encoded_version,
2019
                gmlinfo->user_namespace_uri_encoded,
2020
                gmlinfo->script_url_encoded, encoded_version, encoded_typename,
2021
                gmlinfo->output_schema_format);
2022
  }
2023

2024
  /*
2025
  ** GML 3
2026
  */
2027
  else if (outputformat == OWS_GML3) {
36✔
2028
    if (nWFSVersion == OWS_1_1_0) {
36✔
2029
      msIO_printf("<wfs:FeatureCollection\n");
35✔
2030
      msWFS_NS_printf(gmlinfo->user_namespace_prefix,
35✔
2031
                      gmlinfo->user_namespace_uri_encoded);
35✔
2032
      msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
2033
                      MS_OWSCOMMON_GML_NAMESPACE_URI);
2034
      msWFS_NS_printf(MS_OWSCOMMON_WFS_NAMESPACE_PREFIX,
2035
                      MS_OWSCOMMON_WFS_NAMESPACE_URI);
2036
      msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
2037
                      MS_OWSCOMMON_OGC_NAMESPACE_URI);
2038
      msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
2039
                      MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
2040
    } else {
2041
      msIO_printf("<%s:%s\n"
1✔
2042
                  "   version=\"1.0.0\"\n",
2043
                  gmlinfo->user_namespace_prefix, gmlinfo->collection_name);
2044
      msWFS_NS_printf(gmlinfo->user_namespace_prefix,
1✔
2045
                      gmlinfo->user_namespace_uri_encoded);
1✔
2046
      msWFS_NS_printf(MS_OWSCOMMON_GML_NAMESPACE_PREFIX,
2047
                      MS_OWSCOMMON_GML_NAMESPACE_URI);
2048
      msWFS_NS_printf(MS_OWSCOMMON_OGC_NAMESPACE_PREFIX,
2049
                      MS_OWSCOMMON_OGC_NAMESPACE_URI);
2050
      msWFS_NS_printf(MS_OWSCOMMON_W3C_XSI_NAMESPACE_PREFIX,
2051
                      MS_OWSCOMMON_W3C_XSI_NAMESPACE_URI);
2052
    }
2053

2054
    msWFSPrintAdditionalNamespaces(namespaceList);
36✔
2055

2056
    msIO_printf("   xsi:schemaLocation=\"%s "
36✔
2057
                "%sSERVICE=WFS&amp;VERSION=%s&amp;REQUEST=DescribeFeatureType&"
2058
                "amp;TYPENAME=%s&amp;OUTPUTFORMAT=%s",
2059
                gmlinfo->user_namespace_uri_encoded,
2060
                gmlinfo->script_url_encoded, encoded_version, encoded_typename,
2061
                gmlinfo->output_schema_format);
2062

2063
    if (nWFSVersion == OWS_1_1_0) {
36✔
2064

2065
      msIO_printf("  %s %s%s", MS_OWSCOMMON_WFS_NAMESPACE_URI, encoded_schema,
35✔
2066
                  MS_OWSCOMMON_WFS_11_SCHEMA_LOCATION);
2067

2068
      if (iResultTypeHits == 1) {
35✔
2069
        msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
11✔
2070

2071
        msIO_printf("\" timeStamp=\"%s\" numberOfFeatures=\"%d", timestring,
11✔
2072
                    iNumberOfFeatures);
2073
      }
2074
    }
2075
    msIO_printf("\">\n");
36✔
2076
  }
2077

2078
  msFree(encoded_version);
509✔
2079
  msFree(encoded_schema);
509✔
2080
  msFree(encoded_typename);
509✔
2081

2082
  msGMLFreeNamespaces(namespaceList);
509✔
2083

2084
  return MS_SUCCESS;
2085
}
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,
494✔
2094
                                      OWSGMLVersion outputformat,
2095
                                      int maxfeatures, int iResultTypeHits,
2096
                                      int iNumberOfFeatures, int nWFSVersion)
2097

2098
{
2099
  if (((iNumberOfFeatures == 0) || (maxfeatures == 0)) &&
494✔
2100
      iResultTypeHits == 0) {
2101

2102
    if (nWFSVersion < OWS_2_0_0) {
56✔
2103
      msIO_printf("   <gml:boundedBy>\n");
42✔
2104
      if (outputformat == OWS_GML3 || outputformat == OWS_GML32)
42✔
2105
        msIO_printf("      <gml:Null>missing</gml:Null>\n");
1✔
2106
      else
2107
        msIO_printf("      <gml:null>missing</gml:null>\n");
41✔
2108
      msIO_printf("   </gml:boundedBy>\n");
42✔
2109
    }
2110
  }
2111

2112
  if (outputformat == OWS_GML2)
494✔
2113
    msIO_printf("</wfs:FeatureCollection>\n\n");
293✔
2114
  else {
2115
    if (nWFSVersion >= OWS_1_1_0)
201✔
2116
      msIO_printf("</wfs:FeatureCollection>\n\n");
200✔
2117
    else
2118
      msIO_printf("</%s:%s>\n\n", gmlinfo->user_namespace_prefix,
1✔
2119
                  gmlinfo->collection_name);
2120
  }
2121

2122
  return MS_SUCCESS;
494✔
2123
}
2124

2125
/*
2126
** msWFSBuildParamList()
2127
*/
2128
static void msWFSBuildParamList(char **ppszStrList, const char *pszValue,
249✔
2129
                                const char *pszSep) {
2130
  if (*ppszStrList == NULL)
249✔
2131
    *ppszStrList = msStrdup(pszValue);
209✔
2132
  else {
2133
    char *pszTmp = msStrdup(*ppszStrList);
40✔
2134
    size_t nSize = strlen(pszTmp) + strlen(pszSep) + strlen(pszValue) + 1;
40✔
2135
    *ppszStrList = (char *)msSmallRealloc(*ppszStrList, nSize);
40✔
2136

2137
    snprintf(*ppszStrList, nSize, "%s%s%s", pszTmp, pszSep, pszValue);
2138
    free(pszTmp);
40✔
2139
  }
2140
}
249✔
2141

2142
/*
2143
** msWFSTurnOffAllLayer()
2144
*/
2145
static void msWFSTurnOffAllLayer(mapObj *map) {
2146
  int j;
2147
  for (j = 0; j < map->numlayers; j++) {
2,905✔
2148
    layerObj *lp;
2149
    lp = GET_LAYER(map, j);
2,287✔
2150
    lp->status = MS_OFF;
2,287✔
2151
  }
2152
}
2153

2154
/*
2155
** msWFSRunFilter()
2156
*/
2157
static int msWFSRunFilter(mapObj *map, layerObj *lp,
378✔
2158
                          const wfsParamsObj *paramsObj, const char *pszFilter,
2159
                          int nWFSVersion) {
2160
  int layerWasOpened;
2161
  FilterEncodingNode *psNode = NULL;
2162

2163
  psNode = FLTParseFilterEncoding(pszFilter);
378✔
2164

2165
  if (!psNode) {
378✔
2166
    msSetError(MS_WFSERR, "Invalid or Unsupported FILTER in GetFeature : %s",
19✔
2167
               "msWFSGetFeature()", pszFilter);
2168
    return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
19✔
2169
                          paramsObj->pszVersion);
19✔
2170
  }
2171

2172
  /* Starting with WFS 1.1, we need to swap coordinates of BBOX or geometry */
2173
  /* parameters in some circumstances */
2174
  if (nWFSVersion >= OWS_1_1_0) {
359✔
2175
    int bDefaultSRSNeedsAxisSwapping = MS_FALSE;
2176
    char *srs;
2177
    msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE,
109✔
2178
                     &srs);
2179
    if (!srs) {
109✔
UNCOV
2180
      msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE, &srs);
×
2181
    }
2182
    if (srs && strncasecmp(srs, "EPSG:", 5) == 0) {
109✔
2183
      bDefaultSRSNeedsAxisSwapping = msIsAxisInverted(atoi(srs + 5));
109✔
2184
    }
2185
    msFree(srs);
109✔
2186
    FLTDoAxisSwappingIfNecessary(map, psNode, bDefaultSRSNeedsAxisSwapping);
109✔
2187
  }
2188

2189
  layerWasOpened = msLayerIsOpen(lp);
359✔
2190
  if (!layerWasOpened && msLayerOpen(lp) != MS_SUCCESS) {
359✔
UNCOV
2191
    FLTFreeFilterEncodingNode(psNode);
×
UNCOV
2192
    return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
2193
                          paramsObj->pszVersion);
×
2194
  }
2195

2196
  FLTProcessPropertyIsNull(psNode, map, lp->index);
359✔
2197

2198
  /*preparse the filter for gml aliases*/
2199
  FLTPreParseFilterForAliasAndGroup(psNode, map, lp->index, "G");
359✔
2200

2201
  /* Check that FeatureId filters are consistent with the active layer */
2202
  if (FLTCheckFeatureIdFilters(psNode, map, lp->index) == MS_FAILURE) {
359✔
2203
    FLTFreeFilterEncodingNode(psNode);
1✔
2204
    return msWFSException(map, "resourceid",
1✔
2205
                          MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2206
                          paramsObj->pszVersion);
1✔
2207
  }
2208

2209
  /* FIXME?: could probably apply to WFS 1.1 too */
2210
  if (nWFSVersion >= OWS_2_0_0) {
358✔
2211
    int nEvaluation;
2212

2213
    if (FLTCheckInvalidOperand(psNode) == MS_FAILURE) {
97✔
2214
      FLTFreeFilterEncodingNode(psNode);
1✔
2215
      return msWFSException(map, "filter",
1✔
2216
                            MS_WFS_ERROR_OPERATION_PROCESSING_FAILED,
2217
                            paramsObj->pszVersion);
15✔
2218
    }
2219

2220
    if (FLTCheckInvalidProperty(psNode, map, lp->index) == MS_FAILURE) {
96✔
2221
      FLTFreeFilterEncodingNode(psNode);
1✔
2222
      return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1✔
2223
                            paramsObj->pszVersion);
1✔
2224
    }
2225

2226
    psNode = FLTSimplify(psNode, &nEvaluation);
95✔
2227
    if (psNode == NULL) {
95✔
2228
      FLTFreeFilterEncodingNode(psNode);
13✔
2229
      if (nEvaluation == 1) {
13✔
2230
        /* return full layer */
2231
        return msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
7✔
2232
      } else {
2233
        /* return empty result set */
2234
        return MS_SUCCESS;
2235
      }
2236
    }
2237
  }
2238

2239
  // ignore any classes used by the WFS layer to avoid selecting fields we don't
2240
  // need
2241
  int numclasses = lp->numclasses;
343✔
2242
  lp->numclasses = 0;
343✔
2243

2244
  /* run filter.  If no results are found, do not throw exception */
2245
  /* this is a null result */
2246
  int status = FLTApplyFilterToLayer(psNode, map, lp->index);
343✔
2247

2248
  // set the class count to the original value
2249
  lp->numclasses = numclasses;
343✔
2250

2251
  if (status != MS_SUCCESS) {
343✔
2252
    errorObj *ms_error = msGetErrorObj();
1✔
2253

2254
    if (ms_error->code != MS_NOTFOUND) {
1✔
2255
      msSetError(MS_WFSERR, "FLTApplyFilterToLayer() failed",
1✔
2256
                 "msWFSGetFeature()");
2257
      FLTFreeFilterEncodingNode(psNode);
1✔
2258
      return msWFSException(map, "mapserv",
1✔
2259
                            (nWFSVersion >= OWS_2_0_0)
2260
                                ? MS_WFS_ERROR_OPERATION_PROCESSING_FAILED
2261
                                : MS_OWS_ERROR_NO_APPLICABLE_CODE,
2262
                            paramsObj->pszVersion);
2✔
2263
    }
2264
  }
2265

2266
  FLTFreeFilterEncodingNode(psNode);
342✔
2267

2268
  return MS_SUCCESS;
342✔
2269
}
2270

2271
/*
2272
** msWFSRunBasicGetFeature()
2273
*/
2274
static int msWFSRunBasicGetFeature(mapObj *map, layerObj *lp,
255✔
2275
                                   const wfsParamsObj *paramsObj,
2276
                                   int nWFSVersion) {
2277
  rectObj ext;
2278
  int status;
2279

2280
  map->query.type = MS_QUERY_BY_RECT; /* setup the query */
255✔
2281
  map->query.mode = MS_QUERY_MULTIPLE;
255✔
2282
  map->query.rect = map->extent;
255✔
2283
  map->query.layer = lp->index;
255✔
2284

2285
  if (FLTLayerSetInvalidRectIfSupported(map, lp, &(map->query.rect), "FO")) {
255✔
2286
    /* do nothing */
2287
  } else if (msOWSGetLayerExtent(map, lp, "FO", &ext) == MS_SUCCESS) {
252✔
2288
    char *pszMapSRS = NULL;
252✔
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
    if (!paramsObj->pszSrs)
252✔
2293
      msOWSGetEPSGProj(&(map->projection), &(map->web.metadata), "FO", MS_TRUE,
236✔
2294
                       &pszMapSRS);
2295

2296
    /* For a single point layer, to avoid numerical precision issues */
2297
    /* when reprojection is involved */
2298
    ext.minx -= 1e-5;
252✔
2299
    ext.miny -= 1e-5;
252✔
2300
    ext.maxx += 1e-5;
252✔
2301
    ext.maxy += 1e-5;
252✔
2302

2303
    if (pszMapSRS != NULL && strncmp(pszMapSRS, "EPSG:", 5) == 0) {
252✔
2304

2305
      if (nWFSVersion >= OWS_1_1_0)
236✔
2306
        status = msLoadProjectionStringEPSG(&(map->projection), pszMapSRS);
186✔
2307
      else
2308
        status = msLoadProjectionString(&(map->projection), pszMapSRS);
50✔
2309

2310
      if (status != 0) {
236✔
UNCOV
2311
        msSetError(MS_WFSERR, "msLoadProjectionString() failed: %s",
×
2312
                   "msWFSGetFeature()", pszMapSRS);
UNCOV
2313
        msFree(pszMapSRS);
×
UNCOV
2314
        return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
2315
                              paramsObj->pszVersion);
×
2316
      }
2317
    }
2318
    msFree(pszMapSRS);
252✔
2319

2320
    /*make sure that the layer projection is loaded.
2321
        It could come from a ows/wfs_srs metadata*/
2322
    if (lp->projection.numargs == 0) {
252✔
2323
      char *pszLayerSRS;
2324
      msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", MS_TRUE,
1✔
2325
                       &pszLayerSRS);
2326
      if (pszLayerSRS) {
1✔
2327
        if (strncmp(pszLayerSRS, "EPSG:", 5) == 0) {
×
UNCOV
2328
          if (nWFSVersion >= OWS_1_1_0)
×
UNCOV
2329
            msLoadProjectionStringEPSG(&(lp->projection), pszLayerSRS);
×
2330
          else
UNCOV
2331
            msLoadProjectionString(&(lp->projection), pszLayerSRS);
×
2332
        }
2333
      }
2334
      msFree(pszLayerSRS);
1✔
2335
    }
2336

2337
    if (msProjectionsDiffer(&map->projection, &lp->projection) == MS_TRUE) {
252✔
2338
      msProjectRect(&lp->projection, &map->projection, &(ext));
193✔
2339
    }
2340
    map->query.rect = ext;
252✔
2341
  }
2342

2343
  if (msQueryByRect(map) != MS_SUCCESS) {
255✔
2344
    errorObj *ms_error;
UNCOV
2345
    ms_error = msGetErrorObj();
×
2346

UNCOV
2347
    if (ms_error->code != MS_NOTFOUND) {
×
UNCOV
2348
      msSetError(MS_WFSERR, "ms_error->code not found", "msWFSGetFeature()");
×
UNCOV
2349
      return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
2350
                            paramsObj->pszVersion);
×
2351
    }
2352
  }
2353

2354
  return MS_SUCCESS;
2355
}
2356

2357
/*
2358
** msWFSRetrieveFeatures()
2359
*/
2360
static int msWFSRetrieveFeatures(
605✔
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
    int *pnOutTotalFeatures, int *pnHasNext) {
2366
  int i, j;
2367
  int iNumberOfFeatures = 0;
2368
  int anyLyrHasNext = MS_FALSE;
2369

2370
  if (pszFilter && strlen(pszFilter) > 0) {
605✔
2371
    int nFilters;
2372
    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
    if (nWFSVersion >= OWS_2_0_0 && paramsObj->pszFilterLanguage != NULL &&
376✔
2384
        strcasecmp(paramsObj->pszFilterLanguage,
3✔
2385
                   "urn:ogc:def:query Language:OGC-FES:Filter") != 0) {
2386
      msSetError(MS_WFSERR,
1✔
2387
                 "Unhandled value for FILTER_LANGUAGE parameter. Only "
2388
                 "\"urn:ogc:def:query Language:OGC-FES:Filter\" accepted.",
2389
                 "msWFSGetFeature()");
2390
      return msWFSException(map, "filter_language",
1✔
2391
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2392
                            paramsObj->pszVersion);
27✔
2393
    }
2394

2395
    if (gmlinfo->_typename == NULL || strlen(gmlinfo->_typename) <= 0 ||
375✔
2396
        layers == NULL || numlayers <= 0) {
375✔
UNCOV
2397
      msSetError(MS_WFSERR,
×
2398
                 "Required %s parameter missing for GetFeature with a FILTER "
2399
                 "parameter.",
2400
                 "msWFSGetFeature()",
2401
                 (nWFSVersion >= OWS_2_0_0) ? "TYPENAMES" : "TYPENAME");
UNCOV
2402
      return msWFSException(
×
2403
          map, (nWFSVersion >= OWS_2_0_0) ? "typenames" : "typename",
UNCOV
2404
          MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
×
2405
    }
2406

2407
    if (bBBOXSet) {
375✔
2408
      msSetError(MS_WFSERR,
1✔
2409
                 "BBOX parameter and FILTER parameter are mutually exclusive "
2410
                 "in GetFeature.",
2411
                 "msWFSGetFeature()");
2412
      return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
1✔
2413
                            paramsObj->pszVersion);
1✔
2414
    }
2415

2416
    if (pszFeatureId != NULL) {
374✔
2417
      msSetError(MS_WFSERR,
2✔
2418
                 "%s parameter and FILTER parameter are mutually exclusive in "
2419
                 "GetFeature.",
2420
                 "msWFSGetFeature()",
2421
                 (nWFSVersion < OWS_2_0_0) ? "FEATUREID" : "RESOURCEID");
2422
      return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
1✔
2423
                            paramsObj->pszVersion);
1✔
2424
    }
2425

2426
    if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) ==
373✔
2427
        MS_FAILURE)
UNCOV
2428
      return msWFSException(map, "srsname",
×
2429
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
UNCOV
2430
                            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
    nFilters = 0;
373✔
2444
    if (strlen(pszFilter) > 0 && pszFilter[0] == '(') {
373✔
2445
      paszFilter = FLTSplitFilters(pszFilter, &nFilters);
51✔
2446
    } else if (numlayers == 1) {
322✔
2447
      nFilters = 1;
322✔
2448
      paszFilter = (char **)msSmallMalloc(sizeof(char *) * nFilters);
322✔
2449
      paszFilter[0] = msStrdup(pszFilter);
322✔
2450
    }
2451

2452
    if (numlayers != nFilters) {
373✔
2453
      msSetError(MS_WFSERR,
1✔
2454
                 "Wrong number of filter elements, one filter must be "
2455
                 "specified for each feature type listed in the %s parameter.",
2456
                 "msWFSGetFeature()",
2457
                 (nWFSVersion >= OWS_2_0_0) ? "TYPENAMES" : "TYPENAME");
2458
      msFreeCharArray(paszFilter, nFilters);
1✔
2459
      return msWFSException(map, "filter", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1✔
2460
                            paramsObj->pszVersion);
1✔
2461
    }
2462

2463
    /* -------------------------------------------------------------------- */
2464
    /*      run through the filters and build the class expressions.        */
2465
    /* -------------------------------------------------------------------- */
2466
    for (i = 0; i < nFilters; i++) {
727✔
2467
      int status;
2468
      layerObj *lp;
2469

2470
      lp = msWFSGetLayerByName(map, ows_request, layers[i]);
384✔
2471

2472
      /* Special value set when parsing XML Post when there is a mix of */
2473
      /* Query with and without Filter */
2474
      if (strcmp(paszFilter[i], "!") == 0)
384✔
2475
        status = msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
6✔
2476
      else
2477
        status = msWFSRunFilter(map, lp, paramsObj, paszFilter[i], nWFSVersion);
378✔
2478

2479
      if (status != MS_SUCCESS) {
384✔
2480
        msFreeCharArray(paszFilter, nFilters);
23✔
2481
        return status;
23✔
2482
      }
2483

2484
      /* Decrement the total maxfeatures */
2485
      if (map->query.maxfeatures >= 0) {
361✔
2486
        if (lp->resultcache && lp->resultcache->numresults > 0) {
64✔
2487
          map->query.maxfeatures -= lp->resultcache->numresults;
52✔
2488
          if (map->query.maxfeatures <= 0)
52✔
2489
            break;
2490
        }
2491
      }
2492
    }
2493

2494
    msFreeCharArray(paszFilter, nFilters);
349✔
2495
  } /* end if filter set */
2496

2497
  if (pszFeatureId != NULL) {
578✔
2498
    char **tokens = NULL;
2499
    int nTokens = 0, j = 0, k = 0;
31✔
2500
    char **aFIDLayers = NULL;
2501
    char **aFIDValues = NULL;
2502
    int iFIDLayers = 0;
2503

2504
    /* Keep only selected layers, set to OFF by default. */
2505
    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
    tokens = msStringSplit(pszFeatureId, ',', &nTokens);
31✔
2512
    iFIDLayers = 0;
2513
    if (tokens && nTokens >= 1) {
31✔
2514
      aFIDLayers = (char **)msSmallMalloc(sizeof(char *) * nTokens);
31✔
2515
      aFIDValues = (char **)msSmallMalloc(sizeof(char *) * nTokens);
31✔
2516
      for (j = 0; j < nTokens; j++) {
66✔
2517
        aFIDLayers[j] = NULL;
35✔
2518
        aFIDValues[j] = NULL;
35✔
2519
      }
2520
      for (j = 0; j < nTokens; j++) {
66✔
2521
        const char *pszLastDot = strrchr(tokens[j], '.');
35✔
2522
        if (pszLastDot != NULL) {
35✔
2523
          char *pszLayerInFID = msStrdup(tokens[j]);
33✔
2524
          pszLayerInFID[pszLastDot - tokens[j]] = '\0';
33✔
2525
          /* Find if the layer is already requested */
2526
          for (k = 0; k < iFIDLayers; k++) {
36✔
2527
            if (strcasecmp(aFIDLayers[k], pszLayerInFID) == 0)
4✔
2528
              break;
2529
          }
2530
          /* If not, add it to the list of requested layers */
2531
          if (k == iFIDLayers) {
33✔
2532
            aFIDLayers[iFIDLayers] = msStrdup(pszLayerInFID);
32✔
2533
            iFIDLayers++;
32✔
2534
          }
2535
          /* Add the id to the list of search identifiers of the layer */
2536
          if (aFIDValues[k] != NULL)
33✔
2537
            aFIDValues[k] = msStringConcatenate(aFIDValues[k], ",");
1✔
2538
          aFIDValues[k] = msStringConcatenate(aFIDValues[k], pszLastDot + 1);
33✔
2539
          msFree(pszLayerInFID);
33✔
2540
        } else {
2541
          /* In WFS 20, an unknown/invalid feature id shouldn't trigger an */
2542
          /* exception. Tested by CITE */
2543
          if (nWFSVersion < OWS_2_0_0) {
2✔
UNCOV
2544
            msSetError(MS_WFSERR,
×
2545
                       "Invalid FeatureId in GetFeature. Expecting "
2546
                       "layername.value : %s",
2547
                       "msWFSGetFeature()", tokens[j]);
2548
            if (tokens)
UNCOV
2549
              msFreeCharArray(tokens, nTokens);
×
UNCOV
2550
            msFreeCharArray(aFIDLayers, iFIDLayers);
×
UNCOV
2551
            msFreeCharArray(aFIDValues, iFIDLayers);
×
UNCOV
2552
            return msWFSException(map, "featureid",
×
2553
                                  MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2554
                                  paramsObj->pszVersion);
2✔
2555
          }
2556
        }
2557
      }
2558
    }
2559
    if (tokens)
31✔
2560
      msFreeCharArray(tokens, nTokens);
31✔
2561

2562
    /*turn on the layers and make sure projections are set properly*/
2563
    for (j = 0; j < iFIDLayers; j++) {
61✔
2564
      layerObj *lp;
2565
      lp = msWFSGetLayerByName(map, ows_request, aFIDLayers[j]);
32✔
2566
      if (lp == NULL) {
32✔
2567
        msSetError(
2✔
2568
            MS_WFSERR,
2569
            "Invalid typename given with FeatureId in GetFeature : %s. A layer might be disabled for \
2570
this request. Check wfs/ows_enable_request settings.",
2571
            "msWFSGetFeature()", aFIDLayers[j]);
2572

2573
        msFreeCharArray(aFIDLayers, iFIDLayers);
2✔
2574
        msFreeCharArray(aFIDValues, iFIDLayers);
2✔
2575
        return msWFSException(map, "featureid",
2✔
2576
                              MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2577
                              paramsObj->pszVersion);
2✔
2578
      }
2579

2580
      lp->status = MS_ON;
30✔
2581
    }
2582

2583
    if (map->query.only_cache_result_count == MS_FALSE)
29✔
2584
      msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, FALSE, NULL, NULL);
26✔
2585

2586
    if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) ==
29✔
2587
        MS_FAILURE)
UNCOV
2588
      return msWFSException(map, "srsname",
×
2589
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
UNCOV
2590
                            paramsObj->pszVersion);
×
2591

2592
    for (j = 0; j < iFIDLayers; j++) {
58✔
2593
      layerObj *lp;
2594
      lp = msWFSGetLayerByName(map, ows_request, aFIDLayers[j]);
30✔
2595
      if (lp->_template == NULL) {
30✔
2596
        /* Force setting a template to enable query. */
2597
        lp->_template = msStrdup("ttt.html");
4✔
2598
      }
2599
      FilterEncodingNode *psNode =
2600
          FLTCreateFeatureIdFilterEncoding(aFIDValues[j]);
30✔
2601

2602
      if (FLTApplyFilterToLayer(psNode, map, lp->index) != MS_SUCCESS) {
30✔
UNCOV
2603
        msSetError(MS_WFSERR, "FLTApplyFilterToLayer() failed",
×
2604
                   "msWFSGetFeature");
UNCOV
2605
        FLTFreeFilterEncodingNode(psNode);
×
UNCOV
2606
        msFreeCharArray(aFIDLayers, iFIDLayers);
×
UNCOV
2607
        msFreeCharArray(aFIDValues, iFIDLayers);
×
UNCOV
2608
        return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
2609
                              paramsObj->pszVersion);
×
2610
      }
2611

2612
      FLTFreeFilterEncodingNode(psNode);
30✔
2613

2614
      /* Decrement the total maxfeatures */
2615
      if (map->query.maxfeatures >= 0) {
30✔
2616
        if (lp->resultcache && lp->resultcache->numresults > 0) {
6✔
2617
          map->query.maxfeatures -= lp->resultcache->numresults;
5✔
2618
          if (map->query.maxfeatures <= 0)
5✔
2619
            break;
2620
        }
2621
      }
2622
    }
2623

2624
    msFreeCharArray(aFIDLayers, iFIDLayers);
29✔
2625
    msFreeCharArray(aFIDValues, iFIDLayers);
29✔
2626
  }
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
  if (pszFilter == NULL && pszFeatureId == NULL) {
576✔
2634

2635
    /* Apply the requested SRS */
2636
    if (msWFSGetFeatureApplySRS(map, paramsObj->pszSrs, nWFSVersion) ==
198✔
2637
        MS_FAILURE)
2638
      return msWFSException(map, "srsname",
5✔
2639
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2640
                            paramsObj->pszVersion);
5✔
2641

2642
    if (!bBBOXSet) {
193✔
2643
      for (j = 0; j < map->numlayers; j++) {
635✔
2644
        layerObj *lp;
2645
        lp = GET_LAYER(map, j);
460✔
2646
        if (lp->status == MS_ON) {
460✔
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
          int numclasses = lp->numclasses;
242✔
2651
          lp->numclasses = 0;
242✔
2652
          int status = msWFSRunBasicGetFeature(map, lp, paramsObj, nWFSVersion);
242✔
2653
          // set the class count back to its original value once the query is
2654
          // run
2655
          lp->numclasses = numclasses;
242✔
2656
          if (status != MS_SUCCESS)
242✔
UNCOV
2657
            return status;
×
2658
        }
2659
      }
2660
    } else {
2661

2662
      char *sBBoxSrs = NULL;
18✔
2663

2664
      if (pszBBOXSRS != NULL)
18✔
2665
        sBBoxSrs = msStrdup(pszBBOXSRS);
8✔
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
      if (nWFSVersion >= OWS_2_0_0 && sBBoxSrs == NULL) {
18✔
2671
        projectionObj sProjTmp;
2672

2673
        msInitProjection(&sProjTmp);
9✔
2674
        msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
9✔
2675
        msOWSGetEPSGProj(&sProjTmp, &(map->web.metadata), "FO", MS_TRUE,
9✔
2676
                         &sBBoxSrs);
2677
        msFreeProjection(&sProjTmp);
9✔
2678
      }
2679

2680
      if (sBBoxSrs) {
18✔
2681
        int status;
2682
        projectionObj sProjTmp;
2683

2684
        msInitProjection(&sProjTmp);
17✔
2685
        msProjectionInheritContextFrom(&sProjTmp, &(map->projection));
17✔
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
        if (nWFSVersion >= OWS_1_1_0) {
17✔
2690
          if ((status = msLoadProjectionStringEPSG(&sProjTmp, sBBoxSrs)) == 0) {
17✔
2691
            msAxisNormalizePoints(&sProjTmp, 1, &bbox.minx, &bbox.miny);
17✔
2692
            msAxisNormalizePoints(&sProjTmp, 1, &bbox.maxx, &bbox.maxy);
17✔
2693
          }
2694
        } else
UNCOV
2695
          status = msLoadProjectionString(&sProjTmp, sBBoxSrs);
×
2696

2697
        if (status == 0 && map->projection.numargs > 0)
17✔
2698
          msProjectRect(&sProjTmp, &map->projection, &bbox);
17✔
2699
        msFreeProjection(&sProjTmp);
17✔
2700

2701
        msFree(sBBoxSrs);
17✔
2702
        sBBoxSrs = NULL;
17✔
2703
      }
2704
      map->query.type = MS_QUERY_BY_RECT; /* setup the query */
18✔
2705
      map->query.mode = MS_QUERY_MULTIPLE;
18✔
2706
      map->query.rect = bbox;
18✔
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
      if (msQueryByRect(map) != MS_SUCCESS) {
18✔
2727
        errorObj *ms_error;
UNCOV
2728
        ms_error = msGetErrorObj();
×
2729

UNCOV
2730
        if (ms_error->code != MS_NOTFOUND) {
×
UNCOV
2731
          msSetError(MS_WFSERR, "ms_error->code not found",
×
2732
                     "msWFSGetFeature()");
UNCOV
2733
          return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
2734
                                paramsObj->pszVersion);
×
2735
        }
2736
      }
2737
    }
2738
  }
2739

2740
  /* Count total number of features retrieved */
2741
  for (j = 0; j < map->numlayers; j++) {
2,681✔
2742
    if (GET_LAYER(map, j)->resultcache &&
2,110✔
2743
        GET_LAYER(map, j)->resultcache->numresults > 0) {
649✔
2744
      iNumberOfFeatures += GET_LAYER(map, j)->resultcache->numresults;
587✔
2745
      if (GET_LAYER(map, j)->resultcache->hasnext == MS_TRUE)
587✔
2746
        anyLyrHasNext = MS_TRUE;
2747
    }
2748
  }
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
  if (maxfeatures >= 0 && iNumberOfFeatures == maxfeatures + 1) {
571✔
2755
    int lastJ = -1;
2756
    for (j = 0; j < map->numlayers; j++) {
265✔
2757
      if (GET_LAYER(map, j)->resultcache &&
203✔
2758
          GET_LAYER(map, j)->resultcache->numresults > 0) {
68✔
2759
        lastJ = j;
2760
      }
2761
    }
2762
    GET_LAYER(map, lastJ)->resultcache->numresults--;
62✔
2763
    GET_LAYER(map, lastJ)->resultcache->bounds =
62✔
2764
        GET_LAYER(map, lastJ)->resultcache->previousBounds;
2765
    iNumberOfFeatures--;
62✔
2766
    if (pnHasNext)
62✔
2767
      *pnHasNext = MS_TRUE;
62✔
2768
  } else if (pnHasNext) {
509✔
2769
    *pnHasNext = anyLyrHasNext;
479✔
2770
  }
2771

2772
  *pnOutTotalFeatures = iNumberOfFeatures;
571✔
2773

2774
  return MS_SUCCESS;
571✔
2775
}
2776

2777
/*
2778
** msWFSParsePropertyNameOrSortBy()
2779
*/
2780
static char **msWFSParsePropertyNameOrSortBy(const char *pszPropertyName,
1,133✔
2781
                                             int numlayers) {
2782
  char **tokens = NULL;
2783
  int nPropertyNames = 0;
1,133✔
2784
  char **papszPropertyName = NULL;
2785
  int i;
2786

2787
  if (pszPropertyName == NULL) {
1,133✔
2788
    papszPropertyName = (char **)msSmallMalloc(sizeof(char *) * numlayers);
1,088✔
2789
    for (i = 0; i < numlayers; i++) {
2,345✔
2790
      papszPropertyName[i] = msStrdup("!");
1,257✔
2791
    }
2792
    return papszPropertyName;
2793
  }
2794

2795
  if (!(pszPropertyName[0] == '(' || numlayers == 1)) {
45✔
2796
    return NULL;
2797
  }
2798

2799
  if (numlayers == 1 && pszPropertyName[0] != '(') {
44✔
2800
    /* Accept PROPERTYNAME without () when there is a single TYPENAME */
2801
    std::string osTmpPropertyName;
2802
    osTmpPropertyName = '(';
2803
    osTmpPropertyName += pszPropertyName;
2804
    osTmpPropertyName += ')';
2805
    tokens = msStringSplit(osTmpPropertyName.c_str() + 1, '(', &nPropertyNames);
23✔
2806
  } else
23✔
2807
    tokens = msStringSplit(pszPropertyName + 1, '(', &nPropertyNames);
21✔
2808

2809
  /*expecting to always have a list of property names equal to
2810
      the number of layers(typename)*/
2811
  if (nPropertyNames != numlayers) {
44✔
2812
    msFreeCharArray(tokens, nPropertyNames);
1✔
2813
    return NULL;
1✔
2814
  }
2815

2816
  papszPropertyName = (char **)msSmallMalloc(sizeof(char *) * nPropertyNames);
43✔
2817
  for (i = 0; i < nPropertyNames; i++) {
88✔
2818
    if (strlen(tokens[i]) > 0) {
45✔
2819
      papszPropertyName[i] = msStrdup(tokens[i]);
45✔
2820
      /* remove trailing ) */
2821
      papszPropertyName[i][strlen(papszPropertyName[i]) - 1] = '\0';
45✔
2822
    } else {
UNCOV
2823
      for (i--; i >= 0; i--)
×
UNCOV
2824
        msFree(papszPropertyName[i]);
×
UNCOV
2825
      msFree(papszPropertyName);
×
UNCOV
2826
      msFreeCharArray(tokens, nPropertyNames);
×
UNCOV
2827
      return NULL;
×
2828
    }
2829
  }
2830

2831
  msFreeCharArray(tokens, nPropertyNames);
43✔
2832

2833
  return papszPropertyName;
2834
}
2835

2836
static void msWFSInitGMLInfo(WFSGMLInfo *pgmlinfo) {
593✔
2837
  memset(pgmlinfo, 0, sizeof(WFSGMLInfo));
2838
  pgmlinfo->user_namespace_prefix = MS_DEFAULT_NAMESPACE_PREFIX;
593✔
2839
  pgmlinfo->user_namespace_uri = MS_DEFAULT_NAMESPACE_URI;
593✔
2840
  pgmlinfo->collection_name = OWS_WFS_FEATURE_COLLECTION_NAME;
593✔
2841
  pgmlinfo->_typename = "";
593✔
2842
  pgmlinfo->output_mime_type = "text/xml";
593✔
2843
  pgmlinfo->output_schema_format = "XMLSCHEMA";
593✔
2844
}
593✔
2845

2846
static void msWFSCleanupGMLInfo(WFSGMLInfo *pgmlinfo) {
525✔
2847
  free(pgmlinfo->script_url);
525✔
2848
  free(pgmlinfo->script_url_encoded);
525✔
2849
  msFree(pgmlinfo->user_namespace_uri_encoded);
525✔
2850
}
525✔
2851

2852
static int msWFSGetGMLOutputFormat(wfsParamsObj *paramsObj,
578✔
2853
                                   WFSGMLInfo *pgmlinfo, int nWFSVersion) {
2854
  OWSGMLVersion outputformat = OWS_GML2;
2855

2856
  /* Validate outputformat */
2857
  if (paramsObj->pszOutputFormat) {
578✔
2858
    /* We support GML2, GML3, GML3.2 for now. */
2859
    if (strcasecmp(paramsObj->pszOutputFormat, "GML2") == 0 ||
135✔
2860
        strcasecmp(paramsObj->pszOutputFormat, "text/xml; subtype=gml/2.1.2") ==
49✔
2861
            0) {
2862
      outputformat = OWS_GML2;
2863
      pgmlinfo->output_schema_format = "XMLSCHEMA";
89✔
2864
      /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE
2865
       * WFS 2.0 */
2866
      pgmlinfo->output_mime_type = "text/xml; subtype=gml/2.1.2";
89✔
2867
    } else if (strcasecmp(paramsObj->pszOutputFormat, "GML3") == 0 ||
46✔
2868
               strcasecmp(paramsObj->pszOutputFormat,
44✔
2869
                          "text/xml; subtype=gml/3.1.1") == 0) {
2870
      outputformat = OWS_GML3;
2871
      pgmlinfo->output_schema_format = "SFE_XMLSCHEMA";
6✔
2872
      /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE
2873
       * WFS 2.0 */
2874
      pgmlinfo->output_mime_type = "text/xml; subtype=gml/3.1.1";
6✔
2875
    } else if (strcasecmp(paramsObj->pszOutputFormat, "GML32") == 0 ||
40✔
2876
               strcasecmp(paramsObj->pszOutputFormat,
40✔
2877
                          "text/xml; subtype=gml/3.2.1") == 0 ||
39✔
2878
               strcasecmp(paramsObj->pszOutputFormat,
39✔
2879
                          "text/xml; subtype=\"gml/3.2.1\"") == 0 ||
39✔
2880
               strcasecmp(paramsObj->pszOutputFormat,
39✔
2881
                          "application/gml+xml; version=3.2") == 0) {
2882
      outputformat = OWS_GML32;
2883
      pgmlinfo->output_schema_format =
4✔
2884
          "application%2Fgml%2Bxml%3B%20version%3D3.2";
2885
      pgmlinfo->output_mime_type = "text/xml; subtype=\"gml/3.2.1\"";
4✔
2886
    } else {
2887
      return -1;
2888
    }
2889

2890
    /* If OUTPUTFORMAT not set, default to gml */
2891
  } else {
2892
    /*set the output format using the version if available*/
2893
    if (nWFSVersion == OWS_1_1_0) {
443✔
2894
      outputformat = OWS_GML3;
2895
      pgmlinfo->output_schema_format = "text/xml;%20subtype=gml/3.1.1";
37✔
2896
      /* FIXME? : subtype=gml/XXXX is invalid without quoting. Seen with CITE
2897
       * WFS 2.0 */
2898
      pgmlinfo->output_mime_type = "text/xml; subtype=gml/3.1.1";
37✔
2899
    } else if (nWFSVersion >= OWS_2_0_0) {
406✔
2900
      outputformat = OWS_GML32;
2901
      pgmlinfo->output_schema_format =
183✔
2902
          "application%2Fgml%2Bxml%3B%20version%3D3.2";
2903
      pgmlinfo->output_mime_type = "text/xml; subtype=\"gml/3.2.1\"";
183✔
2904
    }
2905
  }
2906

2907
  if (nWFSVersion == OWS_1_0_0) {
542✔
2908
    pgmlinfo->output_mime_type = "text/xml";
306✔
2909
  }
2910

2911
  return outputformat;
542✔
2912
}
2913

2914
static outputFormatObj *msWFSGetOtherOutputFormat(mapObj *map,
35✔
2915
                                                  wfsParamsObj *paramsObj) {
2916
  const char *format_list;
2917
  hashTableObj *md;
2918
  outputFormatObj *psFormat = NULL;
2919
  int j;
2920

2921
  /* validate selected format against all selected layers. */
2922
  for (j = 0; j < map->numlayers; j++) {
168✔
2923
    layerObj *lp;
2924

2925
    lp = GET_LAYER(map, j);
134✔
2926

2927
    if (lp->status != MS_ON)
134✔
2928
      continue;
92✔
2929

2930
    md = &(lp->metadata);
42✔
2931
    format_list = msOWSLookupMetadata(md, "F", "getfeature_formatlist");
42✔
2932
    if (format_list == NULL) {
42✔
2933
      md = &(map->web.metadata);
11✔
2934
      format_list = msOWSLookupMetadata(md, "F", "getfeature_formatlist");
11✔
2935
    }
2936
    if (format_list) {
11✔
2937
      psFormat = msOwsIsOutputFormatValid(map, paramsObj->pszOutputFormat, md,
41✔
2938
                                          "F", "getfeature_formatlist");
2939
    }
2940
    if (psFormat == NULL) {
42✔
2941
      msSetError(MS_WFSERR,
1✔
2942
                 "'%s' is not a permitted output format for layer '%s', review "
2943
                 "wfs_getfeature_formatlist setting.",
2944
                 "msWFSGetFeature()", paramsObj->pszOutputFormat, lp->name);
2945
      return NULL;
1✔
2946
    }
2947

2948
    if (psFormat->imagemode != MS_IMAGEMODE_FEATURE) {
41✔
UNCOV
2949
      msSetError(MS_WFSERR,
×
2950
                 "OUTPUTFORMAT '%s' does not have IMAGEMODE FEATURE, and is "
2951
                 "not permitted for WFS output.",
2952
                 "msWFSGetFeature()", paramsObj->pszOutputFormat);
UNCOV
2953
      return NULL;
×
2954
    }
2955
  }
2956

2957
  return psFormat;
2958
}
2959

2960
static void msWFSAnalyzeStartIndexAndFeatureCount(mapObj *map,
602✔
2961
                                                  const wfsParamsObj *paramsObj,
2962
                                                  int bIsHits,
2963
                                                  int *pmaxfeatures,
2964
                                                  int *pstartindex) {
2965
  const char *tmpmaxfeatures = NULL;
2966
  int maxfeatures = -1, startindex = -1;
2967
  int j;
2968

2969
  int nQueriedLayers = 0;
2970
  layerObj *lpQueried = NULL;
2971

2972
  tmpmaxfeatures =
2973
      msOWSLookupMetadata(&(map->web.metadata), "FO", "maxfeatures");
602✔
2974
  if (tmpmaxfeatures)
602✔
2975
    maxfeatures = atoi(tmpmaxfeatures);
2976

2977
  if (bIsHits) {
602✔
2978
    const char *ignoreMaxFeaturesForHits = msOWSLookupMetadata(
63✔
2979
        &(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
    if (ignoreMaxFeaturesForHits == NULL) {
63✔
2984
      int bHasEfficientHits = MS_TRUE;
2985
      for (j = 0; j < map->numlayers; j++) {
99✔
2986
        layerObj *lp;
2987
        lp = GET_LAYER(map, j);
86✔
2988
        if (lp->status == MS_ON) {
86✔
2989
          if (lp->connectiontype != MS_POSTGIS) {
57✔
2990
            bHasEfficientHits = MS_FALSE;
2991
            break;
2992
          }
2993
        }
2994
      }
2995

2996
      if (bHasEfficientHits)
57✔
2997
        ignoreMaxFeaturesForHits = "true";
2998
    }
2999

3000
    if (ignoreMaxFeaturesForHits != NULL &&
19✔
3001
        strcasecmp(ignoreMaxFeaturesForHits, "true") == 0)
19✔
3002
      maxfeatures = -1;
3003
  }
3004

3005
  if (paramsObj->nMaxFeatures >= 0) {
602✔
3006
    if (maxfeatures < 0 ||
75✔
3007
        (maxfeatures > 0 && paramsObj->nMaxFeatures < maxfeatures))
27✔
3008
      maxfeatures = paramsObj->nMaxFeatures;
3009
  }
3010

3011
  nQueriedLayers = 0;
3012
  for (j = 0; j < map->numlayers; j++) {
2,851✔
3013
    layerObj *lp;
3014
    lp = GET_LAYER(map, j);
2,249✔
3015
    if (lp->status == MS_ON) {
2,249✔
3016
      /* No reason to handle tolerances for WFS GetFeature */
3017
      lp->tolerance = 0;
694✔
3018
      lpQueried = GET_LAYER(map, j);
3019
      nQueriedLayers++;
694✔
3020
    }
3021
  }
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
  if (paramsObj->nStartIndex >= 0) {
602✔
3029
    startindex = 1 + paramsObj->nStartIndex;
28✔
3030
    map->query.startindex = startindex;
28✔
3031
  }
3032

3033
  /* maxfeatures set */
3034
  if (maxfeatures >= 0) {
602✔
3035
    int extrafeature = 1;
3036
    for (j = 0; j < map->numlayers; j++) {
737✔
3037
      layerObj *lp;
3038
      lp = GET_LAYER(map, j);
534✔
3039
      if (lp->status == MS_ON) {
534✔
3040
        /*over-ride the value only if it is unset or wfs maxfeatures is
3041
          lower that what is currently set*/
3042
        if (lp->maxfeatures <= 0 ||
261✔
3043
            (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
          if (lp->sortBy.nProperties > 0) {
261✔
3048
            int k;
3049
            int countDesc = 1;
3050
            for (k = 0; k < lp->sortBy.nProperties; k++) {
19✔
3051
              if (lp->sortBy.properties[k].sortOrder == SORT_DESC)
12✔
3052
                countDesc++;
7✔
3053
            }
3054
            if (countDesc > 0)
7✔
3055
              extrafeature = 0;
3056
          }
3057

3058
          /* + 1 to be able to detect if there are more features for a next
3059
           * request */
3060
          lp->maxfeatures = maxfeatures + extrafeature;
261✔
3061
        }
3062
      }
3063
    }
3064
    /* + 1 to be able to detect if there are more features for a next request */
3065
    map->query.maxfeatures = maxfeatures + extrafeature;
203✔
3066
  }
3067

3068
  /* startindex set */
3069
  if (startindex > 0 && nQueriedLayers > 1) {
602✔
3070
    for (j = 0; j < map->numlayers; j++) {
7✔
3071
      layerObj *lp;
3072
      lp = GET_LAYER(map, j);
6✔
3073
      if (lp->status == MS_ON) {
6✔
3074
        msLayerEnablePaging(lp, MS_FALSE);
6✔
3075
      }
3076
    }
3077
  } else if (startindex > 0 && lpQueried) {
601✔
3078
    lpQueried->startindex = startindex;
27✔
3079
  }
3080

3081
  if (pmaxfeatures)
602✔
3082
    *pmaxfeatures = maxfeatures;
576✔
3083
  if (pstartindex)
602✔
3084
    *pstartindex = startindex;
576✔
3085
}
602✔
3086

3087
static int msWFSAnalyzeBBOX(mapObj *map, wfsParamsObj *paramsObj,
576✔
3088
                            rectObj *pbbox, char **pszBBoxSRS) {
3089
  /* Default filter is map extents */
3090
  *pbbox = map->extent;
576✔
3091

3092
  if (paramsObj->pszBbox) {
576✔
3093
    char **tokens;
3094
    int n;
3095

3096
    tokens = msStringSplit(paramsObj->pszBbox, ',', &n);
18✔
3097
    if (tokens == NULL || (n != 4 && n != 5)) {
18✔
3098
      msSetError(MS_WFSERR, "Wrong number of arguments for BBOX.",
1✔
3099
                 "msWFSGetFeature()");
3100
      msFreeCharArray(tokens, n);
1✔
3101
      return msWFSException(map, "bbox", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1✔
3102
                            paramsObj->pszVersion);
1✔
3103
    }
3104
    pbbox->minx = atof(tokens[0]);
17✔
3105
    pbbox->miny = atof(tokens[1]);
17✔
3106
    pbbox->maxx = atof(tokens[2]);
17✔
3107
    pbbox->maxy = atof(tokens[3]);
17✔
3108
    /*5th aregument is assumed to be projection*/
3109
    if (n == 5)
17✔
3110
      *pszBBoxSRS = msStrdup(tokens[4]);
7✔
3111

3112
    msFreeCharArray(tokens, n);
17✔
3113
  }
3114
  return 0;
3115
}
3116

3117
static int msWFSComputeMatchingFeatures(mapObj *map, owsRequestObj *ows_request,
541✔
3118
                                        wfsParamsObj *paramsObj,
3119
                                        int iNumberOfFeatures, int maxfeatures,
3120
                                        WFSGMLInfo *pgmlinfo, rectObj bbox,
3121
                                        const char *sBBoxSrs, char **layers,
3122
                                        int numlayers, int nWFSVersion) {
3123
  int nMatchingFeatures = -1;
541✔
3124

3125
  if (nWFSVersion >= OWS_2_0_0) {
541✔
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
    if (iNumberOfFeatures == 0 && maxfeatures < 0) {
194✔
3132
      nMatchingFeatures = 0;
4✔
3133
    }
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
    else if (iNumberOfFeatures > 0 &&
190✔
3144
             (maxfeatures < 0 || iNumberOfFeatures < maxfeatures)) {
170✔
3145
      if (paramsObj->nStartIndex >= 0)
129✔
3146
        nMatchingFeatures = iNumberOfFeatures + paramsObj->nStartIndex;
5✔
3147
      else
3148
        nMatchingFeatures = iNumberOfFeatures;
124✔
3149
    }
3150

3151
    /* Otherwise big hammer ! */
3152
    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
      const char *pszComputeNumberMatched = msOWSLookupMetadata(
61✔
3183
          &(map->web.metadata), "F", "compute_number_matched");
61✔
3184
      if (pszComputeNumberMatched != NULL &&
61✔
3185
          strcasecmp(pszComputeNumberMatched, "true") == 0) {
30✔
3186
        int j;
3187
        mapObj *mapTmp = (mapObj *)msSmallCalloc(1, sizeof(mapObj));
30✔
3188
        if (initMap(mapTmp) == -1) {
30✔
UNCOV
3189
          free(mapTmp);
×
UNCOV
3190
          return 0;
×
3191
        }
3192
        msCopyMap(mapTmp, map);
30✔
3193

3194
        /* Re-run the query but with no limit */
3195
        mapTmp->query.maxfeatures = -1;
30✔
3196
        mapTmp->query.startindex = -1;
30✔
3197
        mapTmp->query.only_cache_result_count = MS_TRUE;
30✔
3198
        for (j = 0; j < mapTmp->numlayers; j++) {
122✔
3199
          layerObj *lp = GET_LAYER(mapTmp, j);
92✔
3200
          /* Reset layer paging */
3201
          lp->maxfeatures = -1;
92✔
3202
          lp->startindex = -1;
92✔
3203
        }
3204

3205
        nMatchingFeatures = 0;
30✔
3206
        msWFSRetrieveFeatures(
30✔
3207
            mapTmp, ows_request, paramsObj, pgmlinfo, paramsObj->pszFilter,
30✔
3208
            paramsObj->pszBbox != NULL, sBBoxSrs, bbox, paramsObj->pszFeatureId,
30✔
3209
            layers, numlayers, -1, nWFSVersion, &nMatchingFeatures, NULL);
3210

3211
        msFreeMap(mapTmp);
30✔
3212
      }
3213
    }
3214
  }
3215

3216
  return nMatchingFeatures;
541✔
3217
}
3218

3219
static int msWFSResolveStoredQuery(mapObj *map, wfsParamsObj *paramsObj,
599✔
3220
                                   cgiRequestObj *req) {
3221
  if (paramsObj->pszStoredQueryId != NULL) {
599✔
3222
    int i;
3223
    int status;
3224
    hashTableObj *hashTable = msCreateHashTable();
13✔
3225
    char *pszResolvedQuery;
3226

3227
    if (req->NumParams > 0 && req->postrequest == NULL) {
13✔
3228
      for (i = 0; i < req->NumParams; i++) {
93✔
3229
        if (req->ParamNames[i] && req->ParamValues[i]) {
80✔
3230
          char *pszXMLValue = CPLEscapeString(
80✔
3231
              req->ParamValues[i], strlen(req->ParamValues[i]), CPLES_XML);
80✔
3232
          msInsertHashTable(hashTable, req->ParamNames[i], pszXMLValue);
80✔
3233
          CPLFree(pszXMLValue);
80✔
3234
        }
3235
      }
3236
    }
3237

3238
    pszResolvedQuery = msWFSGetResolvedStoredQuery20(
13✔
3239
        map, paramsObj, paramsObj->pszStoredQueryId, hashTable);
13✔
3240
    msFreeHashTable(hashTable);
13✔
3241

3242
    if (pszResolvedQuery == NULL)
13✔
3243
      return MS_FAILURE;
3244

3245
    status = msWFSAnalyzeStoredQuery(
9✔
3246
        map, paramsObj, paramsObj->pszStoredQueryId, pszResolvedQuery);
9✔
3247
    msFree(pszResolvedQuery);
9✔
3248

3249
    if (status != MS_SUCCESS)
9✔
3250
      return status;
3251

3252
    msWFSSimplifyPropertyNameAndFilter(paramsObj);
9✔
3253
  }
3254
  return MS_SUCCESS;
3255
}
3256

3257
/*
3258
** msWFSUnaliasItem()
3259
*/
3260
static const char *msWFSUnaliasItem(layerObj *lp, const char *name) {
80✔
3261
  const char *pszFullName;
3262
  char szTmp[256];
3263
  int z;
3264
  for (z = 0; z < lp->numitems; z++) {
1,116✔
3265
    if (!lp->items[z] || strlen(lp->items[z]) <= 0)
1,050✔
UNCOV
3266
      continue;
×
3267
    snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[z]);
3268
    pszFullName = msOWSLookupMetadata(&(lp->metadata), "G", szTmp);
1,050✔
3269
    if (pszFullName && strcmp(name, pszFullName) == 0)
1,050✔
3270
      return lp->items[z];
14✔
3271
  }
3272
  return name;
3273
}
3274

3275
/*
3276
** msWFSIsAllowedItem()
3277
*/
3278
static int msWFSIsAllowedItem(gmlItemListObj *itemList, const char *name) {
69✔
3279
  int z;
3280
  for (z = 0; z < itemList->numitems; z++) {
608✔
3281
    if (itemList->items[z].visible &&
590✔
3282
        strcasecmp(name, itemList->items[z].name) == 0) {
355✔
3283
      return MS_TRUE;
3284
    }
3285
  }
3286
  return MS_FALSE;
3287
}
3288

3289
/*
3290
** msWFSUngroupItem()
3291
*/
3292
static const char *msWFSUngroupItem(layerObj *lp, gmlGroupListObj *groupList,
73✔
3293
                                    const char *name, int *pnGroupIndex) {
3294
  int z;
3295

3296
  *pnGroupIndex = -1;
73✔
3297
  for (z = 0; z < groupList->numgroups; z++) {
77✔
3298
    const char *pszGroupName = groupList->groups[z].name;
13✔
3299
    size_t nGroupNameLen = strlen(pszGroupName);
13✔
3300

3301
    /* Is it a group name ? */
3302
    if (strcasecmp(name, pszGroupName) == 0) {
13✔
3303
      *pnGroupIndex = z;
2✔
3304
      return NULL;
2✔
3305
    }
3306

3307
    /* Is it an item of a group ? */
3308
    if (strncasecmp(name, pszGroupName, nGroupNameLen) == 0 &&
11✔
3309
        name[nGroupNameLen] == '/') {
7✔
3310

3311
      int w;
3312
      const char *pszSubName = msWFSStripNS(name + nGroupNameLen + 1);
7✔
3313
      pszSubName = msWFSUnaliasItem(lp, pszSubName);
7✔
3314
      for (w = 0; w < groupList->groups[z].numitems; w++) {
17✔
3315
        if (strcasecmp(pszSubName, groupList->groups[z].items[w]) == 0) {
17✔
3316
          return pszSubName;
7✔
3317
        }
3318
      }
3319
    }
3320
  }
3321

3322
  return name;
3323
}
3324

3325
/*
3326
** msWFSApplySortBy()
3327
*/
3328
static int msWFSApplySortBy(mapObj *map, wfsParamsObj *paramsObj, layerObj *lp,
12✔
3329
                            const char *pszSortBy, gmlItemListObj *itemList,
3330
                            gmlGroupListObj *groupList) {
3331
  int y, n;
3332
  char **tokens;
3333
  int invalidSortBy = MS_FALSE;
3334
  sortByClause sortBy;
3335

3336
  if (!msLayerSupportsSorting(lp)) {
12✔
3337
    msSetError(MS_WFSERR, "Layer %s does not support sorting",
2✔
3338
               "msWFSGetFeature()", lp->name);
3339
    return msWFSException(map, "mapserv",
2✔
3340
                          MS_WFS_ERROR_OPERATION_PROCESSING_FAILED,
3341
                          paramsObj->pszVersion);
2✔
3342
  }
3343

3344
  tokens = msStringSplit(pszSortBy, ',', &n);
10✔
3345
  sortBy.nProperties = n;
10✔
3346
  sortBy.properties =
10✔
3347
      (sortByProperties *)msSmallMalloc(n * sizeof(sortByProperties));
10✔
3348
  for (y = 0; y < n; y++) {
25✔
3349
    char *pszSpace;
3350
    int nGroupIndex = -1;
15✔
3351
    const char *pszSortProperty;
3352
    char *pszTmp;
3353

3354
    sortBy.properties[y].item = msStrdup(tokens[y]);
15✔
3355
    sortBy.properties[y].sortOrder = SORT_ASC;
15✔
3356
    pszSpace = strchr(sortBy.properties[y].item, ' ');
15✔
3357
    if (pszSpace) {
15✔
3358
      *pszSpace = '\0';
10✔
3359
      if (strcasecmp(pszSpace + 1, "A") == 0 ||
10✔
3360
          strcasecmp(pszSpace + 1, "ASC") == 0)
9✔
3361
        ;
3362
      else if (strcasecmp(pszSpace + 1, "D") == 0 ||
8✔
3363
               strcasecmp(pszSpace + 1, "DESC") == 0)
6✔
3364
        sortBy.properties[y].sortOrder = SORT_DESC;
7✔
3365
      else
3366
        invalidSortBy = MS_TRUE;
3367
    }
3368

3369
    pszSortProperty = msWFSStripNS(sortBy.properties[y].item);
15✔
3370
    pszSortProperty = msWFSUnaliasItem(lp, pszSortProperty);
15✔
3371
    pszSortProperty =
3372
        msWFSUngroupItem(lp, groupList, pszSortProperty, &nGroupIndex);
15✔
3373
    if (nGroupIndex >= 0)
15✔
3374
      invalidSortBy = MS_TRUE;
3375

3376
    if (!msWFSIsAllowedItem(itemList, pszSortProperty))
15✔
3377
      invalidSortBy = MS_TRUE;
3378

3379
    pszTmp = msStrdup(pszSortProperty);
15✔
3380
    msFree(sortBy.properties[y].item);
15✔
3381
    sortBy.properties[y].item = pszTmp;
15✔
3382
  }
3383
  msFreeCharArray(tokens, n);
10✔
3384

3385
  if (!invalidSortBy)
10✔
3386
    msLayerSetSort(lp, &sortBy);
8✔
3387

3388
  for (y = 0; y < n; y++) {
25✔
3389
    msFree(sortBy.properties[y].item);
15✔
3390
  }
3391
  msFree(sortBy.properties);
10✔
3392

3393
  if (invalidSortBy) {
10✔
3394
    msSetError(MS_WFSERR, "Invalid SORTBY clause", "msWFSGetFeature()");
2✔
3395
    return msWFSException(map, "sortby", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2✔
3396
                          paramsObj->pszVersion);
2✔
3397
  }
3398

3399
  return MS_SUCCESS;
3400
}
3401

3402
/*
3403
** msWFSGetFeature()
3404
*/
3405
static int msWFSGetFeature(mapObj *map, wfsParamsObj *paramsObj,
577✔
3406
                           cgiRequestObj *req, owsRequestObj *ows_request,
3407
                           int nWFSVersion) {
3408
  int status;
3409
  int maxfeatures = -1, startindex = -1;
577✔
3410
  rectObj bbox;
3411

3412
  char **layers = NULL;
3413
  int numlayers = 0;
577✔
3414

3415
  char *sBBoxSrs = NULL;
577✔
3416

3417
  WFSGMLInfo gmlinfo;
3418

3419
  OWSGMLVersion outputformat = OWS_GML2; /* default output is GML 2.1 */
3420
  outputFormatObj *psFormat = NULL;
3421

3422
  int iNumberOfFeatures = 0;
577✔
3423
  int iResultTypeHits = 0;
3424
  int nMatchingFeatures = -1;
3425
  int bHasNextFeatures = MS_FALSE;
577✔
3426

3427
  char **papszGMLGroups = NULL;
3428
  char **papszGMLIncludeItems = NULL;
3429
  char **papszGMLGeometries = NULL;
3430

3431
  msIOContext *old_context = NULL;
3432

3433
  /* Initialize gml options */
3434
  msWFSInitGMLInfo(&gmlinfo);
577✔
3435

3436
  if (paramsObj->pszResultType != NULL) {
577✔
3437
    if (strcasecmp(paramsObj->pszResultType, "hits") == 0)
62✔
3438
      iResultTypeHits = 1;
3439
  }
3440

3441
  status = msWFSResolveStoredQuery(map, paramsObj, req);
577✔
3442
  if (status != MS_SUCCESS)
577✔
3443
    return status;
3444

3445
  /* typename is mandatory unless featureid is specified. We do not
3446
     support featureid */
3447
  if (paramsObj->pszTypeName == NULL && paramsObj->pszFeatureId == NULL) {
573✔
3448
    msSetError(MS_WFSERR, "Incomplete WFS request: %s parameter missing",
1✔
3449
               "msWFSGetFeature()",
3450
               (nWFSVersion >= OWS_2_0_0) ? "TYPENAMES" : "TYPENAME");
3451
    return msWFSException(
1✔
3452
        map, (nWFSVersion >= OWS_2_0_0) ? "typenames" : "typename",
3453
        MS_OWS_ERROR_MISSING_PARAMETER_VALUE, paramsObj->pszVersion);
1✔
3454
  }
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
  if (nWFSVersion >= OWS_2_0_0 && paramsObj->pszTypeName != NULL &&
572✔
3464
      strcmp(paramsObj->pszTypeName, "?") == 0 &&
198✔
3465
      paramsObj->pszFilter != NULL && paramsObj->pszFeatureId == NULL) {
1✔
3466
    CPLXMLNode *psDoc;
3467

3468
    psDoc = CPLParseXMLString(paramsObj->pszFilter);
1✔
3469
    if (psDoc != NULL) {
1✔
3470
      const char *rid;
3471

3472
      CPLStripXMLNamespace(psDoc, NULL, 1);
1✔
3473
      rid = CPLGetXMLValue(psDoc, "=Filter.ResourceId.rid", NULL);
1✔
3474
      if (rid != NULL) {
1✔
3475
        msSetError(MS_WFSERR, "Invalid rid '%s'", "msWFSGetFeature()", rid);
1✔
3476
        CPLDestroyXMLNode(psDoc);
1✔
3477
        return msWFSException(map, NULL, MS_OWS_ERROR_NOT_FOUND,
1✔
3478
                              paramsObj->pszVersion);
1✔
3479
      }
UNCOV
3480
      CPLDestroyXMLNode(psDoc);
×
3481
    }
3482
  }
3483

3484
  papszGMLGroups = (char **)calloc(map->numlayers, sizeof(char *));
571✔
3485
  papszGMLIncludeItems = (char **)calloc(map->numlayers, sizeof(char *));
571✔
3486
  papszGMLGeometries = (char **)calloc(map->numlayers, sizeof(char *));
571✔
3487

3488
  if (paramsObj->pszTypeName) {
571✔
3489
    int k, y, z;
3490

3491
    char **tokens;
3492
    int n = 0, i = 0;
567✔
3493
    char **papszPropertyName = NULL;
3494
    char **papszSortBy = NULL;
3495

3496
    /* keep a ref for layer use. */
3497
    gmlinfo._typename = paramsObj->pszTypeName;
567✔
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
    layers = msStringSplit(gmlinfo._typename, ',', &numlayers);
567✔
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
    if (layers == NULL || numlayers < 1) {
567✔
UNCOV
3511
      msSetError(MS_WFSERR, "At least one type name required in %s parameter.",
×
3512
                 "msWFSGetFeature()",
3513
                 (nWFSVersion >= OWS_2_0_0) ? "TYPENAMES" : "TYPENAME");
UNCOV
3514
      return msWFSException(
×
3515
          map, (nWFSVersion >= OWS_2_0_0) ? "typenames" : "typename",
3516
          MS_OWS_ERROR_INVALID_PARAMETER_VALUE, paramsObj->pszVersion);
9✔
3517
    }
3518

3519
    /* strip namespace if there is one :ex TYPENAME=cdf:Other */
3520
    for (i = 0; i < numlayers; i++) {
1,220✔
3521
      char *pszTmp = msStrdup(msWFSStripNS(layers[i]));
653✔
3522
      free(layers[i]);
653✔
3523
      layers[i] = pszTmp;
653✔
3524
    }
3525

3526
    /* Keep only selected layers, set to OFF by default. */
3527
    msWFSTurnOffAllLayer(map);
3528

3529
    papszPropertyName =
3530
        msWFSParsePropertyNameOrSortBy(paramsObj->pszPropertyName, numlayers);
567✔
3531
    if (papszPropertyName == NULL) {
567✔
3532
      msFreeCharArray(layers, numlayers);
1✔
3533
      msFreeCharArray(papszGMLGroups, map->numlayers);
1✔
3534
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
1✔
3535
      msFreeCharArray(papszGMLGeometries, map->numlayers);
1✔
3536
      msSetError(MS_WFSERR,
1✔
3537
                 "Optional PROPERTYNAME parameter. A list of properties may be "
3538
                 "specified for each type name. Example "
3539
                 "TYPENAME=name1,name2&PROPERTYNAME=(prop1,prop2)(prop1)",
3540
                 "msWFSGetFeature()");
3541
      return msWFSException(map, "PROPERTYNAME",
1✔
3542
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3543
                            paramsObj->pszVersion);
1✔
3544
    }
3545

3546
    papszSortBy =
3547
        msWFSParsePropertyNameOrSortBy(paramsObj->pszSortBy, numlayers);
566✔
3548
    if (papszSortBy == NULL) {
566✔
3549
      msFreeCharArray(layers, numlayers);
1✔
3550
      msFreeCharArray(papszPropertyName, numlayers);
1✔
3551
      msFreeCharArray(papszGMLGroups, map->numlayers);
1✔
3552
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
1✔
3553
      msFreeCharArray(papszGMLGeometries, map->numlayers);
1✔
3554
      msSetError(
1✔
3555
          MS_WFSERR,
3556
          "Optional SORTBY parameter. A list may be specified for each type "
3557
          "name. Example TYPENAME=name1,name2&SORTBY=(prop1,prop2)(prop1)",
3558
          "msWFSGetFeature()");
3559
      return msWFSException(map, "SORTBY", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1✔
3560
                            paramsObj->pszVersion);
1✔
3561
    }
3562

3563
    for (k = 0; k < numlayers; k++) {
1,208✔
3564
      layerObj *lp;
3565
      lp = msWFSGetLayerByName(map, ows_request, layers[k]);
650✔
3566
      if (lp != NULL) {
650✔
3567
        gmlItemListObj *itemList = NULL;
3568
        gmlGeometryListObj *geometryList = NULL;
3569
        gmlGroupListObj *groupList = NULL;
3570

3571
        lp->status = MS_ON;
647✔
3572
        if (lp->_template == NULL) {
647✔
3573
          /* Force setting a template to enable query. */
3574
          lp->_template = msStrdup("ttt.html");
540✔
3575
        }
3576

3577
        /*do an alias replace for the current layer*/
3578
        if (msLayerOpen(lp) == MS_SUCCESS &&
1,294✔
3579
            msLayerGetItems(lp) == MS_SUCCESS) {
647✔
3580

3581
          itemList = msGMLGetItems(lp, "G");
647✔
3582
          groupList = msGMLGetGroups(lp, "G");
647✔
3583

3584
          if (strcmp(papszSortBy[k], "!") != 0) {
647✔
3585
            status = msWFSApplySortBy(map, paramsObj, lp, papszSortBy[k],
11✔
3586
                                      itemList, groupList);
3587
            if (status != MS_SUCCESS) {
11✔
3588
              msFreeCharArray(papszPropertyName, numlayers);
3✔
3589
              msFreeCharArray(papszSortBy, numlayers);
3✔
3590
              msFreeCharArray(layers, numlayers);
3✔
3591
              msFreeCharArray(papszGMLGroups, map->numlayers);
3✔
3592
              msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3✔
3593
              msFreeCharArray(papszGMLGeometries, map->numlayers);
3✔
3594
              msGMLFreeItems(itemList);
3✔
3595
              msGMLFreeGroups(groupList);
3✔
3596
              return status;
3✔
3597
            }
3598
          }
3599

3600
          geometryList = msGMLGetGeometries(lp, "GFO", MS_TRUE);
644✔
3601

3602
          /* unalias, ungroup and validate that the property names passed are
3603
           * part of the items list*/
3604
          tokens = msStringSplit(papszPropertyName[k], ',', &n);
644✔
3605
          for (y = 0; y < n; y++) {
1,294✔
3606
            const char *pszPropertyNameItem = tokens[y];
651✔
3607
            int nGroupIndex = -1;
651✔
3608

3609
            if (strcasecmp(pszPropertyNameItem, "*") == 0 ||
651✔
3610
                strcasecmp(pszPropertyNameItem, "!") == 0)
650✔
3611
              continue;
612✔
3612

3613
            pszPropertyNameItem = msWFSStripNS(pszPropertyNameItem);
40✔
3614
            pszPropertyNameItem = msWFSUnaliasItem(lp, pszPropertyNameItem);
40✔
3615
            pszPropertyNameItem = msWFSUngroupItem(
40✔
3616
                lp, groupList, pszPropertyNameItem, &nGroupIndex);
3617
            if (nGroupIndex >= 0)
40✔
3618
              continue;
1✔
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
            if (!msWFSIsAllowedItem(itemList, pszPropertyNameItem)) {
39✔
3624
              for (z = 0; z < geometryList->numgeometries; z++) {
11✔
3625
                if (strcasecmp(pszPropertyNameItem,
10✔
3626
                               geometryList->geometries[z].name) == 0)
10✔
3627
                  break;
3628
              }
3629

3630
              if (z == geometryList->numgeometries) {
10✔
3631
                msSetError(MS_WFSERR, "Invalid PROPERTYNAME %s",
1✔
3632
                           "msWFSGetFeature()", tokens[y]);
3633
                msFreeCharArray(tokens, n);
1✔
3634
                msGMLFreeItems(itemList);
1✔
3635
                msGMLFreeGroups(groupList);
1✔
3636
                msGMLFreeGeometries(geometryList);
1✔
3637
                msFreeCharArray(papszPropertyName, numlayers);
1✔
3638
                msFreeCharArray(papszSortBy, numlayers);
1✔
3639
                msFreeCharArray(layers, numlayers);
1✔
3640
                msFreeCharArray(papszGMLGroups, map->numlayers);
1✔
3641
                msFreeCharArray(papszGMLIncludeItems, map->numlayers);
1✔
3642
                msFreeCharArray(papszGMLGeometries, map->numlayers);
1✔
3643
                return msWFSException(map, "PROPERTYNAME",
1✔
3644
                                      MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3645
                                      paramsObj->pszVersion);
1✔
3646
              }
3647
            } else {
3648
              char *pszTmp = msStrdup(pszPropertyNameItem);
29✔
3649
              msFree(tokens[y]);
29✔
3650
              tokens[y] = pszTmp;
29✔
3651
            }
3652
          }
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
          for (z = 0; z < itemList->numitems; z++) {
7,293✔
3661
            if (itemList->items[z].visible &&
6,650✔
3662
                itemList->items[z].minOccurs == 1) {
5,015✔
3663
              int bFound = MS_FALSE;
3664
              for (y = 0; y < n; y++) {
26✔
3665
                if (strcasecmp(tokens[y], "*") == 0 ||
18✔
3666
                    strcasecmp(tokens[y], "!") == 0 ||
17✔
3667
                    strcasecmp(tokens[y], itemList->items[z].name) == 0) {
13✔
3668
                  bFound = MS_TRUE;
3669
                  break;
3670
                }
3671
              }
3672
              if (!bFound) {
16✔
3673
                tokens = (char **)realloc(tokens, sizeof(char *) * (n + 1));
8✔
3674
                tokens[n] = msStrdup(itemList->items[z].name);
8✔
3675
                n++;
8✔
3676
              }
3677
            }
3678
          }
3679

3680
          /* Rebuild papszPropertyName[k] */
3681
          msFree(papszPropertyName[k]);
643✔
3682
          papszPropertyName[k] = NULL;
643✔
3683
          for (y = 0; y < n; y++) {
1,301✔
3684
            if (y == 0)
658✔
3685
              papszPropertyName[k] = msStrdup(tokens[y]);
643✔
3686
            else {
3687
              papszPropertyName[k] =
15✔
3688
                  msStringConcatenate(papszPropertyName[k], ",");
15✔
3689
              papszPropertyName[k] =
15✔
3690
                  msStringConcatenate(papszPropertyName[k], tokens[y]);
15✔
3691
            }
3692
          }
3693

3694
          msFreeCharArray(tokens, n);
643✔
3695
          msLayerClose(lp);
643✔
3696

3697
          if (strlen(papszPropertyName[k]) > 0) {
643✔
3698

3699
            if (strcasecmp(papszPropertyName[k], "*") == 0) {
643✔
3700
              /* Add all non-excluded items, including optional ones */
3701
              msFree(papszPropertyName[k]);
1✔
3702
              papszPropertyName[k] = NULL;
1✔
3703
              for (z = 0; z < itemList->numitems; z++) {
14✔
3704
                if (itemList->items[z].visible) {
13✔
3705
                  if (papszPropertyName[k] != NULL)
12✔
3706
                    papszPropertyName[k] =
11✔
3707
                        msStringConcatenate(papszPropertyName[k], ",");
11✔
3708
                  papszPropertyName[k] = msStringConcatenate(
12✔
3709
                      papszPropertyName[k], itemList->items[z].name);
12✔
3710
                }
3711
              }
3712
              if (papszPropertyName[k])
1✔
3713
                papszGMLIncludeItems[lp->index] =
1✔
3714
                    msStrdup(papszPropertyName[k]);
1✔
3715
              else
UNCOV
3716
                papszGMLIncludeItems[lp->index] = msStrdup("");
×
3717

3718
              /* The user didn't specify explicitly PROPERTYNAME for this layer
3719
               */
3720
            } else if (strcasecmp(papszPropertyName[k], "!") == 0) {
642✔
3721
              /* Add all non-excluded items, but only default and mandatory ones
3722
               * (and geometry) */
3723
              msFree(papszPropertyName[k]);
610✔
3724
              papszPropertyName[k] = NULL;
610✔
3725
              for (z = 0; z < itemList->numitems; z++) {
6,810✔
3726
                if (itemList->items[z].visible &&
6,200✔
3727
                    (itemList->items[z].outputByDefault == 1 ||
4,654✔
3728
                     itemList->items[z].minOccurs == 1)) {
223✔
3729
                  if (papszPropertyName[k] != NULL)
4,432✔
3730
                    papszPropertyName[k] =
4,014✔
3731
                        msStringConcatenate(papszPropertyName[k], ",");
4,014✔
3732
                  papszPropertyName[k] = msStringConcatenate(
4,432✔
3733
                      papszPropertyName[k], itemList->items[z].name);
4,432✔
3734
                }
3735
              }
3736
              if (papszPropertyName[k])
610✔
3737
                papszGMLIncludeItems[lp->index] =
418✔
3738
                    msStrdup(papszPropertyName[k]);
418✔
3739
              else
3740
                papszGMLIncludeItems[lp->index] = msStrdup("");
192✔
3741

3742
            } else {
3743
              char *pszGeometryList = NULL;
3744

3745
              for (z = 0; z < groupList->numgroups; z++) {
42✔
3746
                const char *pszNeedle =
3747
                    strstr(papszPropertyName[k], groupList->groups[z].name);
10✔
3748
                char chAfterNeedle = 0;
3749
                if (pszNeedle != NULL)
10✔
3750
                  chAfterNeedle =
1✔
3751
                      papszPropertyName[k][strlen(groupList->groups[z].name)];
1✔
3752
                if (pszNeedle != NULL &&
1✔
3753
                    (chAfterNeedle == '\0' || chAfterNeedle == ',')) {
1✔
3754
                  int w;
3755
                  for (w = 0; w < groupList->groups[z].numitems; w++) {
4✔
3756
                    if (strstr(papszPropertyName[k],
3✔
3757
                               groupList->groups[z].items[w]) == NULL) {
3✔
3758
                      papszPropertyName[k] =
2✔
3759
                          msStringConcatenate(papszPropertyName[k], ",");
2✔
3760
                      papszPropertyName[k] = msStringConcatenate(
2✔
3761
                          papszPropertyName[k], groupList->groups[z].items[w]);
2✔
3762
                    }
3763
                  }
3764
                }
3765
              }
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
              if (msOWSLookupMetadata(&(lp->metadata), "OFG", "groups") ==
32✔
3771
                  NULL) {
3772
                auto properties = msStringSplit(papszPropertyName[k], ',');
27✔
3773
                std::string selectItems;
3774
                const char *featureid =
3775
                    msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
27✔
3776
                bool foundFeatureId = false;
3777
                for (const auto &prop : properties) {
64✔
3778
                  bool isGeom = false;
3779
                  const char *propNoNS = prop.c_str();
3780
                  const char *pszColon = strchr(propNoNS, ':');
3781
                  if (pszColon)
37✔
3782
                    propNoNS = pszColon + 1;
4✔
3783
                  for (z = 0; z < geometryList->numgeometries; z++) {
65✔
3784
                    if (strcasecmp(propNoNS,
37✔
3785
                                   geometryList->geometries[z].name) == 0) {
37✔
3786
                      isGeom = true;
3787
                      break;
3788
                    }
3789
                  }
3790
                  if (!isGeom) {
37✔
3791
                    if (!selectItems.empty())
28✔
3792
                      selectItems += ',';
3793
                    selectItems += prop;
3794
                  }
3795
                  if (featureid && featureid == prop) {
37✔
3796
                    foundFeatureId = true;
3797
                  }
3798
                }
3799
                if (featureid && !foundFeatureId) {
27✔
3800
                  if (!selectItems.empty())
17✔
3801
                    selectItems += ',';
3802
                  selectItems += featureid;
3803
                }
3804
                if (!selectItems.empty()) {
27✔
3805
                  msInsertHashTable(&(lp->metadata), "gml_select_items",
27✔
3806
                                    selectItems.c_str());
3807
                }
3808
              }
27✔
3809

3810
              papszGMLIncludeItems[lp->index] = msStrdup(papszPropertyName[k]);
32✔
3811

3812
              for (z = 0; z < geometryList->numgeometries; z++) {
64✔
3813
                if (strstr(papszPropertyName[k],
32✔
3814
                           geometryList->geometries[z].name) != NULL) {
32✔
3815
                  if (pszGeometryList != NULL)
9✔
3816
                    pszGeometryList = msStringConcatenate(pszGeometryList, ",");
×
3817
                  pszGeometryList = msStringConcatenate(
9✔
3818
                      pszGeometryList, geometryList->geometries[z].name);
9✔
3819
                }
3820
              }
3821

3822
              if (pszGeometryList != NULL) {
32✔
3823
                papszGMLGeometries[lp->index] = msStrdup(pszGeometryList);
9✔
3824
                msFree(pszGeometryList);
9✔
3825
              } else {
3826
                papszGMLGeometries[lp->index] = msStrdup("none");
23✔
3827
              }
3828
            }
3829
          } else { /*empty string*/
UNCOV
3830
            papszGMLGeometries[lp->index] = msStrdup("none");
×
3831
          }
3832
        } else {
UNCOV
3833
          msFreeCharArray(papszPropertyName, numlayers);
×
UNCOV
3834
          msFreeCharArray(papszSortBy, numlayers);
×
UNCOV
3835
          msFreeCharArray(layers, numlayers);
×
UNCOV
3836
          msFreeCharArray(papszGMLGroups, map->numlayers);
×
UNCOV
3837
          msFreeCharArray(papszGMLIncludeItems, map->numlayers);
×
UNCOV
3838
          msFreeCharArray(papszGMLGeometries, map->numlayers);
×
UNCOV
3839
          msGMLFreeItems(itemList);
×
UNCOV
3840
          msGMLFreeGroups(groupList);
×
UNCOV
3841
          msGMLFreeGeometries(geometryList);
×
UNCOV
3842
          return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
3843
                                paramsObj->pszVersion);
×
3844
        }
3845

3846
        msGMLFreeItems(itemList);
643✔
3847
        msGMLFreeGroups(groupList);
643✔
3848
        msGMLFreeGeometries(geometryList);
643✔
3849
      } else {
3850
        /* Requested layer name was not in capabilities:
3851
         * either it just doesn't exist
3852
         */
3853
        msSetError(MS_WFSERR,
3✔
3854
                   "TYPENAME '%s' doesn't exist in this server.  Please check "
3855
                   "the capabilities and reformulate your request.",
3856
                   "msWFSGetFeature()", layers[k]);
3857
        msFreeCharArray(papszPropertyName, numlayers);
3✔
3858
        msFreeCharArray(papszSortBy, numlayers);
3✔
3859
        msFreeCharArray(layers, numlayers);
3✔
3860
        msFreeCharArray(papszGMLGroups, map->numlayers);
3✔
3861
        msFreeCharArray(papszGMLIncludeItems, map->numlayers);
3✔
3862
        msFreeCharArray(papszGMLGeometries, map->numlayers);
3✔
3863
        return msWFSException(map, "typename",
3✔
3864
                              MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3865
                              paramsObj->pszVersion);
3✔
3866
      }
3867
    }
3868

3869
    msFreeCharArray(papszPropertyName, numlayers);
558✔
3870
    msFreeCharArray(papszSortBy, numlayers);
558✔
3871
  }
3872

3873
  /* Validate outputformat */
3874
  status = msWFSGetGMLOutputFormat(paramsObj, &gmlinfo, nWFSVersion);
562✔
3875
  if (status < 0) {
562✔
3876
    psFormat = msWFSGetOtherOutputFormat(map, paramsObj);
35✔
3877
    if (psFormat == NULL) {
35✔
3878
      msFreeCharArray(layers, numlayers);
1✔
3879
      msFreeCharArray(papszGMLGroups, map->numlayers);
1✔
3880
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
1✔
3881
      msFreeCharArray(papszGMLGeometries, map->numlayers);
1✔
3882
      return msWFSException(map, "outputformat",
1✔
3883
                            MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
3884
                            paramsObj->pszVersion);
1✔
3885
    }
3886
    // only GML is supported for resultType=hits, so ignore any other
3887
    // outputformat
3888
    if (iResultTypeHits == 1) {
34✔
3889
      psFormat = NULL;
3890
    }
3891
  } else {
3892
    outputformat = (OWSGMLVersion)status;
527✔
3893
  }
3894

3895
  msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, iResultTypeHits,
561✔
3896
                                        &maxfeatures, &startindex);
3897

3898
  status = msWFSAnalyzeBBOX(map, paramsObj, &bbox, &sBBoxSrs);
561✔
3899
  if (status != 0) {
561✔
3900
    msFreeCharArray(layers, numlayers);
1✔
3901
    msFreeCharArray(papszGMLGroups, map->numlayers);
1✔
3902
    msFreeCharArray(papszGMLIncludeItems, map->numlayers);
1✔
3903
    msFreeCharArray(papszGMLGeometries, map->numlayers);
1✔
3904
    return status;
1✔
3905
  }
3906

3907
  if (iResultTypeHits == 1) {
560✔
3908
    map->query.only_cache_result_count = MS_TRUE;
62✔
3909
  } else {
3910
    msOWSSetShapeCache(map, "FO");
498✔
3911
  }
3912

3913
  status = msWFSRetrieveFeatures(
560✔
3914
      map, ows_request, paramsObj, &gmlinfo, paramsObj->pszFilter,
560✔
3915
      paramsObj->pszBbox != NULL, sBBoxSrs, bbox, paramsObj->pszFeatureId,
560✔
3916
      layers, numlayers, maxfeatures, nWFSVersion, &iNumberOfFeatures,
3917
      &bHasNextFeatures);
3918
  if (status != MS_SUCCESS) {
560✔
3919
    msFreeCharArray(layers, numlayers);
34✔
3920
    msFree(sBBoxSrs);
34✔
3921
    msFreeCharArray(papszGMLGroups, map->numlayers);
34✔
3922
    msFreeCharArray(papszGMLIncludeItems, map->numlayers);
34✔
3923
    msFreeCharArray(papszGMLGeometries, map->numlayers);
34✔
3924
    return status;
34✔
3925
  }
3926

3927
  /* ----------------------------------------- */
3928
  /* Now compute nMatchingFeatures for WFS 2.0 */
3929
  /* ----------------------------------------- */
3930

3931
  nMatchingFeatures = msWFSComputeMatchingFeatures(
526✔
3932
      map, ows_request, paramsObj, iNumberOfFeatures, maxfeatures, &gmlinfo,
3933
      bbox, sBBoxSrs, layers, numlayers, nWFSVersion);
3934

3935
  msFreeCharArray(layers, numlayers);
526✔
3936

3937
  msFree(sBBoxSrs);
526✔
3938
  sBBoxSrs = NULL;
526✔
3939

3940
  /*
3941
  ** GML Header generation.
3942
  */
3943

3944
  status = MS_SUCCESS;
3945

3946
  if (psFormat == NULL) {
526✔
3947
    if (paramsObj->countGetFeatureById == 1 && maxfeatures != 0 &&
494✔
3948
        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
      old_context = msIO_pushStdoutToBufferAndGetOldContext();
4✔
3956
    } else {
3957
      msIO_setHeader("Content-Type", "%s; charset=UTF-8",
490✔
3958
                     gmlinfo.output_mime_type);
3959
      msIO_sendHeaders();
490✔
3960
    }
3961

3962
    status = msWFSGetFeature_GMLPreamble(
494✔
3963
        map, req, &gmlinfo, paramsObj, outputformat, iResultTypeHits,
3964
        iNumberOfFeatures, nMatchingFeatures, maxfeatures, bHasNextFeatures,
3965
        nWFSVersion);
3966
    if (status != MS_SUCCESS) {
494✔
UNCOV
3967
      if (old_context != NULL)
×
UNCOV
3968
        msIO_restoreOldStdoutContext(old_context);
×
UNCOV
3969
      msWFSCleanupGMLInfo(&gmlinfo);
×
UNCOV
3970
      msFreeCharArray(papszGMLGroups, map->numlayers);
×
UNCOV
3971
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
×
UNCOV
3972
      msFreeCharArray(papszGMLGeometries, map->numlayers);
×
UNCOV
3973
      return MS_FAILURE;
×
3974
    }
3975
  }
3976

3977
  {
3978
    int i;
3979
    for (i = 0; i < map->numlayers; i++) {
2,527✔
3980
      layerObj *lp = GET_LAYER(map, i);
2,001✔
3981
      if (papszGMLGroups[i])
2,001✔
UNCOV
3982
        msInsertHashTable(&(lp->metadata), "GML_GROUPS", papszGMLGroups[i]);
×
3983
      if (papszGMLIncludeItems[i])
2,001✔
3984
        msInsertHashTable(&(lp->metadata), "GML_INCLUDE_ITEMS",
605✔
3985
                          papszGMLIncludeItems[i]);
3986
      if (papszGMLGeometries[i])
2,001✔
3987
        msInsertHashTable(&(lp->metadata), "GML_GEOMETRIES",
32✔
3988
                          papszGMLGeometries[i]);
3989
    }
3990
  }
3991

3992
  /* handle case of maxfeatures = 0 */
3993
  /*internally use a start index that start with 0 as the first index*/
3994
  if (psFormat == NULL) {
526✔
3995
    if (maxfeatures != 0 && iResultTypeHits == 0) {
494✔
3996
      int bWFS2MultipleFeatureCollection = MS_FALSE;
3997

3998
      /* Would make sense for WFS 1.1.0 too ! See #3576 */
3999
      int bUseURN = (nWFSVersion == OWS_2_0_0);
430✔
4000
      const char *useurn =
4001
          msOWSLookupMetadata(&(map->web.metadata), "F", "return_srs_as_urn");
430✔
4002
      if (useurn && strcasecmp(useurn, "true") == 0)
430✔
4003
        bUseURN = 1;
UNCOV
4004
      else if (useurn && strcasecmp(useurn, "false") == 0)
×
4005
        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
      if (nWFSVersion >= OWS_2_0_0 && iResultTypeHits != 1) {
430✔
4011
        int i;
4012
        int nLayersWithFeatures = 0;
4013
        for (i = 0; i < map->numlayers; i++) {
516✔
4014
          layerObj *lp = GET_LAYER(map, i);
398✔
4015
          if (lp->resultcache && lp->resultcache->numresults > 0)
398✔
4016
            nLayersWithFeatures++;
142✔
4017
        }
4018
        if (nLayersWithFeatures > 1) {
118✔
4019
          char timestring[100];
4020
          resultCacheObj **saveResultCache = (resultCacheObj **)msSmallMalloc(
14✔
4021
              map->numlayers * sizeof(resultCacheObj *));
14✔
4022
          int iLastNonEmptyLayer = -1;
4023

4024
          msWFSGetFeature_GetTimeStamp(timestring, sizeof(timestring));
14✔
4025

4026
          /* Emit global bounds */
4027
          msGMLWriteWFSBounds(map, stdout, "  ", outputformat, nWFSVersion,
14✔
4028
                              bUseURN);
4029

4030
          /* Save the result cache that contains the features that we want to */
4031
          /* emit in the response */
4032
          for (i = 0; i < map->numlayers; i++) {
98✔
4033
            layerObj *lp = GET_LAYER(map, i);
84✔
4034
            saveResultCache[i] = lp->resultcache;
84✔
4035
            if (lp->resultcache && lp->resultcache->numresults > 0) {
84✔
4036
              iLastNonEmptyLayer = i;
4037
            }
4038
            lp->resultcache = NULL;
84✔
4039
          }
4040

4041
          /* Just dump one layer at a time */
4042
          for (i = 0; i < map->numlayers; i++) {
98✔
4043
            layerObj *lp = GET_LAYER(map, i);
84✔
4044
            lp->resultcache = saveResultCache[i];
84✔
4045
            if (lp->resultcache && lp->resultcache->numresults > 0) {
84✔
4046
              msIO_fprintf(stdout, "  <wfs:member>\n");
48✔
4047
              msIO_fprintf(
48✔
4048
                  stdout,
4049
                  "   <wfs:FeatureCollection timeStamp=\"%s\" numberMatched=\"",
4050
                  timestring);
4051
              if (i < iLastNonEmptyLayer || nMatchingFeatures >= 0)
48✔
4052
                msIO_fprintf(stdout, "%d", lp->resultcache->numresults);
47✔
4053
              else
4054
                msIO_fprintf(stdout, "unknown");
1✔
4055
              msIO_fprintf(stdout, "\" numberReturned=\"%d\">\n",
48✔
4056
                           lp->resultcache->numresults);
48✔
4057

4058
              msGMLWriteWFSQuery(map, stdout, gmlinfo.user_namespace_prefix,
48✔
4059
                                 outputformat, nWFSVersion, bUseURN, MS_FALSE);
4060
              msIO_fprintf(stdout, "   </wfs:FeatureCollection>\n");
48✔
4061
              msIO_fprintf(stdout, "  </wfs:member>\n");
48✔
4062
            }
4063
            lp->resultcache = NULL;
84✔
4064
          }
4065

4066
          /* Restore for later cleanup */
4067
          for (i = 0; i < map->numlayers; i++) {
98✔
4068
            layerObj *lp = GET_LAYER(map, i);
84✔
4069
            lp->resultcache = saveResultCache[i];
84✔
4070
          }
4071
          msFree(saveResultCache);
14✔
4072

4073
          bWFS2MultipleFeatureCollection = MS_TRUE;
4074
        }
4075
      }
4076

4077
      if (!bWFS2MultipleFeatureCollection) {
4078
        msGMLWriteWFSQuery(map, stdout, gmlinfo.user_namespace_prefix,
416✔
4079
                           outputformat, nWFSVersion, bUseURN, MS_FALSE);
4080
      }
4081

4082
      status = MS_SUCCESS;
4083
    }
4084
  } else {
4085
    mapservObj *mapserv = msAllocMapServObj();
32✔
4086

4087
    /* Setup dummy mapserv object */
4088
    mapserv->sendheaders = MS_TRUE;
32✔
4089
    mapserv->map = map;
32✔
4090
    msFreeCgiObj(mapserv->request);
32✔
4091
    mapserv->request = req;
32✔
4092
    map->querymap.status = MS_FALSE;
32✔
4093

4094
    if (nMatchingFeatures >= 0) {
32✔
4095
      char szMatchingFeatures[12];
4096
      snprintf(szMatchingFeatures, sizeof(szMatchingFeatures), "%d",
4097
               nMatchingFeatures);
4098
      msSetOutputFormatOption(psFormat, "_matching_features_",
4✔
4099
                              szMatchingFeatures);
4100
    } else {
4101
      msSetOutputFormatOption(psFormat, "_matching_features_", "");
28✔
4102
    }
4103
    status = msReturnTemplateQuery(mapserv, psFormat->name, NULL);
32✔
4104

4105
    mapserv->request = NULL;
32✔
4106
    mapserv->map = NULL;
32✔
4107

4108
    msFreeMapServObj(mapserv);
32✔
4109

4110
    if (status != MS_SUCCESS) {
32✔
4111
      msFreeCharArray(papszGMLGroups, map->numlayers);
1✔
4112
      msFreeCharArray(papszGMLIncludeItems, map->numlayers);
1✔
4113
      msFreeCharArray(papszGMLGeometries, map->numlayers);
1✔
4114
      return msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
1✔
4115
                            paramsObj->pszVersion);
1✔
4116
    }
4117
  }
4118

4119
  msFreeCharArray(papszGMLGroups, map->numlayers);
525✔
4120
  msFreeCharArray(papszGMLIncludeItems, map->numlayers);
525✔
4121
  msFreeCharArray(papszGMLGeometries, map->numlayers);
525✔
4122

4123
  if (psFormat == NULL && status == MS_SUCCESS) {
525✔
4124
    msWFSGetFeature_GMLPostfix(&gmlinfo, outputformat, maxfeatures,
494✔
4125
                               iResultTypeHits, iNumberOfFeatures, nWFSVersion);
4126
  }
4127

4128
  /*Special case for a single urn:ogc:def:query:OGC-WFS::GetFeatureById
4129
   * GetFeature request */
4130
  if (old_context != NULL) {
525✔
4131
    msIOContext *new_context;
4132
    msIOBuffer *buffer;
4133
    CPLXMLNode *psRoot;
4134

4135
    new_context = msIO_getHandler(stdout);
4✔
4136
    buffer = (msIOBuffer *)new_context->cbData;
4✔
4137
    psRoot = CPLParseXMLString((const char *)buffer->data);
4✔
4138
    msIO_restoreOldStdoutContext(old_context);
4✔
4139
    if (psRoot != NULL) {
4✔
4140
      CPLXMLNode *psFeatureCollection =
4141
          CPLGetXMLNode(psRoot, "=wfs:FeatureCollection");
4✔
4142
      if (psFeatureCollection != NULL) {
4✔
4143
        CPLXMLNode *psMember = CPLGetXMLNode(psFeatureCollection, "wfs:member");
4✔
4144
        if (psMember != NULL && psMember->psChild != NULL) {
4✔
4145
          CPLXMLNode *psIter;
4146
          CPLXMLNode *psFeatureNode;
4147
          char *pszTmpXML;
4148

4149
          /* Transfer all namespaces of FeatureCollection to the Feature */
4150
          psFeatureNode = psMember->psChild;
4151
          psIter = psFeatureCollection->psChild;
3✔
4152
          while (psIter != NULL) {
33✔
4153
            if (psIter->eType == CXT_Attribute &&
30✔
4154
                (strncmp(psIter->pszValue, "xmlns:", strlen("xmlns:")) == 0 ||
24✔
4155
                 strncmp(psIter->pszValue, "xsi:", strlen("xsi:")) == 0)) {
12✔
4156
              CPLXMLNode *psIterNext = psIter->psNext;
15✔
4157
              psIter->psNext = NULL;
15✔
4158
              CPLAddXMLChild(psFeatureNode, CPLCloneXMLTree(psIter));
15✔
4159
              psIter->psNext = psIterNext;
15✔
4160
            }
4161
            psIter = psIter->psNext;
30✔
4162
          }
4163

4164
          pszTmpXML = CPLSerializeXMLTree(psFeatureNode);
3✔
4165
          msIO_setHeader("Content-Type", "%s; charset=UTF-8",
3✔
4166
                         gmlinfo.output_mime_type);
4167
          msIO_sendHeaders();
3✔
4168
          msIO_printf("<?xml version='1.0' encoding=\"UTF-8\" ?>\n");
3✔
4169
          msIO_printf("%s", pszTmpXML);
3✔
4170
          CPLFree(pszTmpXML);
3✔
4171
        } else {
4172
          status = msWFSException(map, NULL, MS_OWS_ERROR_NOT_FOUND,
1✔
4173
                                  paramsObj->pszVersion);
1✔
4174
        }
4175
      }
4176
      CPLDestroyXMLNode(psRoot);
4✔
4177
    }
4178
  }
4179

4180
  msWFSCleanupGMLInfo(&gmlinfo);
525✔
4181

4182
  return status;
525✔
4183
}
4184

4185
/*
4186
** msWFSGetPropertyValue()
4187
*/
4188
static int msWFSGetPropertyValue(mapObj *map, wfsParamsObj *paramsObj,
22✔
4189
                                 cgiRequestObj *req, owsRequestObj *ows_request,
4190
                                 int nWFSVersion) {
4191
  int status;
4192
  int maxfeatures = -1, startindex = -1;
22✔
4193
  rectObj bbox;
4194

4195
  char *sBBoxSrs = NULL;
22✔
4196

4197
  WFSGMLInfo gmlinfo;
4198

4199
  OWSGMLVersion outputformat = OWS_GML2; /* default output is GML 2.1 */
4200

4201
  int iNumberOfFeatures = 0;
22✔
4202
  int iResultTypeHits = 0;
4203
  int nMatchingFeatures = -1;
4204
  int bHasNextFeatures = MS_FALSE;
22✔
4205

4206
  const char *pszTypeName;
4207
  layerObj *lp;
4208

4209
  char *pszGMLGroups = NULL;
4210
  char *pszGMLIncludeItems = NULL;
4211
  char *pszGMLGeometries = NULL;
4212

4213
  status = msWFSResolveStoredQuery(map, paramsObj, req);
22✔
4214
  if (status != MS_SUCCESS)
22✔
4215
    return status;
4216

4217
  if (paramsObj->pszTypeName == NULL) {
22✔
4218
    msSetError(MS_WFSERR, "Incomplete WFS request: TYPENAMES parameter missing",
1✔
4219
               "msWFSGetPropertyValue()");
4220
    return msWFSException(map, "typenames",
1✔
4221
                          MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4222
                          paramsObj->pszVersion);
1✔
4223
  }
4224

4225
  if (paramsObj->pszValueReference == NULL) {
21✔
4226
    msSetError(MS_WFSERR,
1✔
4227
               "Incomplete WFS request: VALUEREFERENCE parameter missing",
4228
               "msWFSGetPropertyValue()");
4229
    return msWFSException(map, "valuereference",
1✔
4230
                          MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
4231
                          paramsObj->pszVersion);
1✔
4232
  }
4233

4234
  /* Keep only selected layers, set to OFF by default. */
4235
  msWFSTurnOffAllLayer(map);
4236

4237
  /* Remove namespace prefix */
4238
  pszTypeName = msWFSStripNS(paramsObj->pszTypeName);
20✔
4239

4240
  lp = msWFSGetLayerByName(map, ows_request, pszTypeName);
20✔
4241
  if (lp != NULL) {
20✔
4242
    gmlItemListObj *itemList = NULL;
4243
    gmlGeometryListObj *geometryList = NULL;
4244
    gmlGroupListObj *groupList = NULL;
4245

4246
    lp->status = MS_ON;
19✔
4247
    if (lp->_template == NULL) {
19✔
4248
      /* Force setting a template to enable query. */
4249
      lp->_template = msStrdup("ttt.html");
19✔
4250
    }
4251

4252
    /*do an alias replace for the current layer*/
4253
    if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
19✔
4254
      const char *pszValueReference;
4255
      int z;
4256
      int nGroupIndex = -1;
19✔
4257

4258
      itemList = msGMLGetItems(lp, "G");
19✔
4259
      groupList = msGMLGetGroups(lp, "G");
19✔
4260

4261
      if (paramsObj->pszSortBy != NULL &&
19✔
4262
          strcmp(paramsObj->pszSortBy, "!") != 0) {
1✔
4263
        status = msWFSApplySortBy(map, paramsObj, lp, paramsObj->pszSortBy,
1✔
4264
                                  itemList, groupList);
4265
        if (status != MS_SUCCESS) {
1✔
4266
          msGMLFreeItems(itemList);
1✔
4267
          msGMLFreeGroups(groupList);
1✔
4268
          return status;
3✔
4269
        }
4270
      }
4271

4272
      geometryList = msGMLGetGeometries(lp, "GFO", MS_TRUE);
18✔
4273

4274
      pszValueReference = msWFSStripNS(paramsObj->pszValueReference);
18✔
4275
      pszValueReference = msWFSUnaliasItem(lp, pszValueReference);
18✔
4276
      pszValueReference =
4277
          msWFSUngroupItem(lp, groupList, pszValueReference, &nGroupIndex);
18✔
4278

4279
      if (strcmp(paramsObj->pszValueReference, "@gml:id") == 0) {
18✔
4280
        pszGMLGroups = msStrdup("");
2✔
4281
        pszGMLIncludeItems = msStrdup(paramsObj->pszValueReference);
2✔
4282
        pszGMLGeometries = msStrdup("none");
2✔
4283
      } else if (nGroupIndex >= 0) {
16✔
4284
        int w;
4285
        char *pszItems = NULL;
4286
        for (w = 0; w < groupList->groups[nGroupIndex].numitems; w++) {
4✔
4287
          if (w > 0)
3✔
4288
            pszItems = msStringConcatenate(pszItems, ",");
2✔
4289
          pszItems = msStringConcatenate(
3✔
4290
              pszItems, groupList->groups[nGroupIndex].items[w]);
3✔
4291
        }
4292
        pszGMLGroups = msStrdup(groupList->groups[nGroupIndex].name);
1✔
4293
        pszGMLIncludeItems = msStrdup(pszItems);
1✔
4294
        pszGMLGeometries = msStrdup("none");
1✔
4295
        msFree(pszItems);
1✔
4296
      } else {
4297
        pszGMLGroups = msStrdup("");
15✔
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
        if (!msWFSIsAllowedItem(itemList, pszValueReference)) {
15✔
4303
          for (z = 0; z < geometryList->numgeometries; z++) {
9✔
4304
            if (strcasecmp(pszValueReference,
7✔
4305
                           geometryList->geometries[z].name) == 0)
7✔
4306
              break;
4307
          }
4308

4309
          if (z == geometryList->numgeometries) {
7✔
4310
            msSetError(MS_WFSERR, "Invalid VALUEREFERENCE %s",
2✔
4311
                       "msWFSGetPropertyValue()", paramsObj->pszValueReference);
4312
            msFree(pszGMLGroups);
2✔
4313
            msGMLFreeItems(itemList);
2✔
4314
            msGMLFreeGroups(groupList);
2✔
4315
            msGMLFreeGeometries(geometryList);
2✔
4316
            return msWFSException(map, "valuereference",
2✔
4317
                                  MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4318
                                  paramsObj->pszVersion);
2✔
4319
          } else {
4320
            pszGMLIncludeItems = msStrdup("");
5✔
4321
            pszGMLGeometries = msStrdup(pszValueReference);
5✔
4322
          }
4323
        } else {
4324
          pszGMLIncludeItems = msStrdup(pszValueReference);
8✔
4325
          pszGMLGeometries = msStrdup("none");
8✔
4326
        }
4327
      }
4328

4329
      msLayerClose(lp);
16✔
4330

4331
      msGMLFreeItems(itemList);
16✔
4332
      msGMLFreeGroups(groupList);
16✔
4333
      msGMLFreeGeometries(geometryList);
16✔
4334
    }
4335
  } else {
4336
    /* Requested layer name was not in capabilities:
4337
     * either it just doesn't exist
4338
     */
4339
    msSetError(MS_WFSERR,
1✔
4340
               "TYPENAME '%s' doesn't exist in this server.  Please check the "
4341
               "capabilities and reformulate your request.",
4342
               "msWFSGetFeature()", paramsObj->pszTypeName);
4343
    msFree(pszGMLGroups);
4344
    msFree(pszGMLIncludeItems);
4345
    msFree(pszGMLGeometries);
4346
    return msWFSException(map, "typename", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1✔
4347
                          paramsObj->pszVersion);
1✔
4348
  }
4349

4350
  /* Initialize gml options */
4351
  msWFSInitGMLInfo(&gmlinfo);
16✔
4352

4353
  if (paramsObj->pszResultType != NULL) {
16✔
4354
    if (strcasecmp(paramsObj->pszResultType, "hits") == 0)
1✔
4355
      iResultTypeHits = 1;
4356
  }
4357

4358
  gmlinfo._typename = paramsObj->pszTypeName;
16✔
4359

4360
  /* Validate outputformat */
4361
  status = msWFSGetGMLOutputFormat(paramsObj, &gmlinfo, nWFSVersion);
16✔
4362
  if (status < 0) {
16✔
4363
    msSetError(
1✔
4364
        MS_WFSERR,
4365
        "'%s' is not a permitted output format for GetPropertyValue request.",
4366
        "msWFSGetPropertyValue()", paramsObj->pszOutputFormat);
4367
    msFree(pszGMLGroups);
1✔
4368
    msFree(pszGMLIncludeItems);
1✔
4369
    msFree(pszGMLGeometries);
1✔
4370
    return msWFSException(map, "outputformat",
1✔
4371
                          MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4372
                          paramsObj->pszVersion);
1✔
4373
  } else {
4374
    outputformat = (OWSGMLVersion)status;
15✔
4375
  }
4376

4377
  msWFSAnalyzeStartIndexAndFeatureCount(map, paramsObj, iResultTypeHits,
15✔
4378
                                        &maxfeatures, &startindex);
4379

4380
  status = msWFSAnalyzeBBOX(map, paramsObj, &bbox, &sBBoxSrs);
15✔
4381
  if (status != 0) {
15✔
UNCOV
4382
    msFree(pszGMLGroups);
×
UNCOV
4383
    msFree(pszGMLIncludeItems);
×
UNCOV
4384
    msFree(pszGMLGeometries);
×
UNCOV
4385
    return status;
×
4386
  }
4387

4388
  if (iResultTypeHits == 1) {
15✔
4389
    map->query.only_cache_result_count = MS_TRUE;
1✔
4390
  } else {
4391
    msOWSSetShapeCache(map, "FO");
14✔
4392
  }
4393

4394
  status = msWFSRetrieveFeatures(
15✔
4395
      map, ows_request, paramsObj, &gmlinfo, paramsObj->pszFilter,
15✔
4396
      paramsObj->pszBbox != NULL, sBBoxSrs, bbox, paramsObj->pszFeatureId,
15✔
4397
      (char **)&pszTypeName, 1, maxfeatures, nWFSVersion, &iNumberOfFeatures,
4398
      &bHasNextFeatures);
4399
  if (status != MS_SUCCESS) {
15✔
UNCOV
4400
    msFree(sBBoxSrs);
×
UNCOV
4401
    msFree(pszGMLGroups);
×
UNCOV
4402
    msFree(pszGMLIncludeItems);
×
UNCOV
4403
    msFree(pszGMLGeometries);
×
UNCOV
4404
    return status;
×
4405
  }
4406

4407
  /* ----------------------------------------- */
4408
  /* Now compute nMatchingFeatures for WFS 2.0 */
4409
  /* ----------------------------------------- */
4410

4411
  nMatchingFeatures = msWFSComputeMatchingFeatures(
15✔
4412
      map, ows_request, paramsObj, iNumberOfFeatures, maxfeatures, &gmlinfo,
4413
      bbox, sBBoxSrs, (char **)&pszTypeName, 1, nWFSVersion);
4414

4415
  msFree(sBBoxSrs);
15✔
4416
  sBBoxSrs = NULL;
15✔
4417

4418
  /*
4419
  ** GML Header generation.
4420
  */
4421
  msIO_setHeader("Content-Type", "%s; charset=UTF-8", gmlinfo.output_mime_type);
15✔
4422
  msIO_sendHeaders();
15✔
4423

4424
  status = msWFSGetFeature_GMLPreamble(
15✔
4425
      map, req, &gmlinfo, paramsObj, outputformat, iResultTypeHits,
4426
      iNumberOfFeatures, nMatchingFeatures, maxfeatures, bHasNextFeatures,
4427
      nWFSVersion);
4428

4429
  if (status == MS_SUCCESS && maxfeatures != 0 && iResultTypeHits == 0) {
15✔
4430
    if (pszGMLGroups)
14✔
4431
      msInsertHashTable(&(lp->metadata), "GML_GROUPS", pszGMLGroups);
14✔
4432
    if (pszGMLIncludeItems)
14✔
4433
      msInsertHashTable(&(lp->metadata), "GML_INCLUDE_ITEMS",
14✔
4434
                        pszGMLIncludeItems);
4435
    if (pszGMLGeometries)
14✔
4436
      msInsertHashTable(&(lp->metadata), "GML_GEOMETRIES", pszGMLGeometries);
14✔
4437

4438
    status = msGMLWriteWFSQuery(map, stdout, gmlinfo.user_namespace_prefix,
14✔
4439
                                outputformat, nWFSVersion, MS_TRUE, MS_TRUE);
4440
  }
4441

4442
  msIO_printf("</wfs:ValueCollection>\n");
15✔
4443

4444
  msFree(pszGMLGroups);
15✔
4445
  msFree(pszGMLIncludeItems);
15✔
4446
  msFree(pszGMLGeometries);
15✔
4447

4448
  free(gmlinfo.script_url);
15✔
4449
  free(gmlinfo.script_url_encoded);
15✔
4450
  msFree(gmlinfo.user_namespace_uri_encoded);
15✔
4451

4452
  return status;
15✔
4453
}
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,
761✔
4466
                  owsRequestObj *ows_request, int force_wfs_mode) {
4467
#ifdef USE_WFS_SVR
4468
  int status;
4469
  int returnvalue = MS_DONE;
4470
  int nWFSVersion;
4471
  char *validated_language;
4472

4473
  /* static char *wmtver = NULL, *request=NULL, *service=NULL; */
4474
  wfsParamsObj *paramsObj;
4475
  /*
4476
  ** Populate the Params object based on the request
4477
  */
4478
  paramsObj = msWFSCreateParamsObj();
761✔
4479
  /* TODO : store also parameters that are inside the map object */
4480
  /* into the paramsObj.  */
4481
  status = msWFSParseRequest(map, requestobj, paramsObj, force_wfs_mode);
761✔
4482
  if (status != MS_SUCCESS) {
761✔
4483
    msWFSFreeParamsObj(paramsObj);
5✔
4484
    return status;
5✔
4485
  }
4486

4487
  validated_language =
4488
      msOWSGetLanguageFromList(map, "FO", paramsObj->pszLanguage);
756✔
4489
  if (validated_language != NULL) {
756✔
4490
    msMapSetLanguageSpecificConnection(map, validated_language);
5✔
4491
  }
4492
  msFree(validated_language);
756✔
4493

4494
  if (force_wfs_mode) {
756✔
4495
    /*request is always required*/
4496
    if (paramsObj->pszRequest == NULL || strlen(paramsObj->pszRequest) <= 0) {
×
4497
      msSetError(MS_WFSERR, "Incomplete WFS request: REQUEST parameter missing",
×
4498
                 "msWFSDispatch()");
4499
      returnvalue =
4500
          msWFSException(map, "request", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
×
4501
                         paramsObj->pszVersion);
×
UNCOV
4502
      msWFSFreeParamsObj(paramsObj);
×
UNCOV
4503
      return returnvalue;
×
4504
    }
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
    if (paramsObj->pszVersion == NULL && requestobj &&
×
UNCOV
4512
        requestobj->postrequest == NULL &&
×
UNCOV
4513
        strcasecmp(paramsObj->pszRequest, "GetCapabilities") != 0) {
×
4514
      msSetError(MS_WFSERR, "Invalid WFS request: VERSION parameter missing",
×
4515
                 "msWFSDispatch()");
4516
      returnvalue =
4517
          msWFSException(map, "version", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
×
UNCOV
4518
                         paramsObj->pszVersion);
×
UNCOV
4519
      msWFSFreeParamsObj(paramsObj);
×
4520
      return returnvalue;
×
4521
    }
4522

4523
    if (paramsObj->pszVersion == NULL || strlen(paramsObj->pszVersion) <= 0)
×
4524
      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
    if ((paramsObj->pszService == NULL || strlen(paramsObj->pszService) == 0) &&
×
4532
        ((requestobj && requestobj->postrequest == NULL) ||
×
UNCOV
4533
         strcasecmp(paramsObj->pszVersion, "1.0") == 0)) {
×
UNCOV
4534
      msSetError(MS_WFSERR, "Invalid WFS request: Missing SERVICE parameter",
×
4535
                 "msWFSDispatch()");
4536
      returnvalue =
UNCOV
4537
          msWFSException(map, "service", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
×
4538
                         paramsObj->pszVersion);
×
4539
      msWFSFreeParamsObj(paramsObj);
×
UNCOV
4540
      return returnvalue;
×
4541
    }
4542

UNCOV
4543
    if (paramsObj->pszService == NULL || strlen(paramsObj->pszService) == 0)
×
4544
      paramsObj->pszService = msStrdup("WFS");
×
4545

4546
    if (paramsObj->pszService != NULL &&
×
UNCOV
4547
        strcasecmp(paramsObj->pszService, "WFS") != 0) {
×
UNCOV
4548
      msSetError(MS_WFSERR,
×
4549
                 "Invalid WFS request: SERVICE parameter must be set to WFS",
4550
                 "msWFSDispatch()");
4551
      returnvalue =
UNCOV
4552
          msWFSException(map, "service", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
×
UNCOV
4553
                         paramsObj->pszVersion);
×
UNCOV
4554
      msWFSFreeParamsObj(paramsObj);
×
UNCOV
4555
      return returnvalue;
×
4556
    }
4557
  }
4558
  /* If SERVICE is specified then it MUST be "WFS" */
4559
  if (paramsObj->pszService != NULL &&
756✔
4560
      strcasecmp(paramsObj->pszService, "WFS") != 0) {
756✔
UNCOV
4561
    msWFSFreeParamsObj(paramsObj);
×
UNCOV
4562
    return MS_DONE; /* Not a WFS request */
×
4563
  }
4564

4565
  /* If SERVICE, VERSION and REQUEST not included than this isn't a WFS req*/
4566
  if (paramsObj->pszService == NULL && paramsObj->pszVersion == NULL &&
756✔
UNCOV
4567
      paramsObj->pszRequest == NULL) {
×
UNCOV
4568
    msWFSFreeParamsObj(paramsObj);
×
UNCOV
4569
    return MS_DONE; /* Not a WFS request */
×
4570
  }
4571

4572
  /* VERSION *and* REQUEST *and* SERVICE required by all WFS requests including
4573
   * GetCapabilities.
4574
   */
4575
  if (paramsObj->pszVersion == NULL || strlen(paramsObj->pszVersion) <= 0) {
756✔
4576
    msSetError(MS_WFSERR, "Incomplete WFS request: VERSION parameter missing",
1✔
4577
               "msWFSDispatch()");
4578

4579
    returnvalue =
4580
        msWFSException11(map, "version", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
1✔
4581
                         msWFSGetDefaultVersion(map));
4582
    msWFSFreeParamsObj(paramsObj);
1✔
4583
    return returnvalue;
1✔
4584
  }
4585

4586
  if (paramsObj->pszRequest == NULL || strlen(paramsObj->pszRequest) <= 0) {
755✔
4587
    msSetError(MS_WFSERR, "Incomplete WFS request: REQUEST parameter missing",
5✔
4588
               "msWFSDispatch()");
4589
    returnvalue =
4590
        msWFSException(map, "request", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
5✔
4591
                       paramsObj->pszVersion);
5✔
4592
    msWFSFreeParamsObj(paramsObj);
5✔
4593
    return returnvalue;
5✔
4594
  }
4595

4596
  if (paramsObj->pszService == NULL || strlen(paramsObj->pszService) <= 0) {
750✔
UNCOV
4597
    msSetError(MS_WFSERR, "Incomplete WFS request: SERVICE parameter missing",
×
4598
               "msWFSDispatch()");
4599

4600
    returnvalue =
UNCOV
4601
        msWFSException(map, "service", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
×
UNCOV
4602
                       paramsObj->pszVersion);
×
UNCOV
4603
    msWFSFreeParamsObj(paramsObj);
×
UNCOV
4604
    return returnvalue;
×
4605
  }
4606

4607
  if ((status = msOWSMakeAllLayersUnique(map)) != MS_SUCCESS) {
750✔
UNCOV
4608
    msSetError(MS_WFSERR, "msOWSMakeAllLayersUnique() failed",
×
4609
               "msWFSDispatch()");
UNCOV
4610
    returnvalue = msWFSException(
×
UNCOV
4611
        map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE, paramsObj->pszVersion);
×
UNCOV
4612
    msWFSFreeParamsObj(paramsObj);
×
UNCOV
4613
    return returnvalue;
×
4614
  }
4615
  /*
4616
  ** Start dispatching requests
4617
  */
4618
  if (strcasecmp(paramsObj->pszRequest, "GetCapabilities") == 0) {
750✔
4619
    msOWSRequestLayersEnabled(map, "F", paramsObj->pszRequest, ows_request);
93✔
4620
    if (ows_request->numlayers == 0) {
93✔
4621
      msSetError(
4✔
4622
          MS_WFSERR,
4623
          "WFS request not enabled. Check wfs/ows_enable_request settings.",
4624
          "msWFSDispatch()");
4625
      returnvalue =
4626
          msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
4✔
4627
                         paramsObj->pszVersion);
4✔
4628
      msWFSFreeParamsObj(paramsObj);
4✔
4629
      return returnvalue;
4✔
4630
    }
4631

4632
    returnvalue = msWFSGetCapabilities(map, paramsObj, requestobj, ows_request);
89✔
4633
    msWFSFreeParamsObj(paramsObj);
89✔
4634
    return returnvalue;
89✔
4635
  }
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
  if (strcmp(paramsObj->pszVersion, "1.0.0") != 0 &&
657✔
4642
      strcmp(paramsObj->pszVersion, "1.1.0") != 0 &&
322✔
4643
      strcmp(paramsObj->pszVersion, "2.0.0") != 0) {
271✔
4644
    msSetError(MS_WFSERR, "WFS Server does not support VERSION %s.",
1✔
4645
               "msWFSDispatch()", paramsObj->pszVersion);
4646
    returnvalue =
4647
        msWFSException(map, "version", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1✔
4648
                       msWFSGetDefaultVersion(map));
4649
    msWFSFreeParamsObj(paramsObj);
1✔
4650
    return returnvalue;
1✔
4651
  }
4652

4653
  nWFSVersion = msOWSParseVersionString(paramsObj->pszVersion);
656✔
4654

4655
  /* Since the function can still return MS_DONE, we add an extra service check
4656
     to not call msOWSRequestLayersEnabled twice */
4657
  if (strcasecmp(paramsObj->pszService, "WFS") == 0) {
656✔
4658
    msOWSRequestLayersEnabled(map, "F", paramsObj->pszRequest, ows_request);
656✔
4659
    if (ows_request->numlayers == 0) {
656✔
4660
      msSetError(
2✔
4661
          MS_WFSERR,
4662
          "WFS request not enabled. Check wfs/ows_enable_request settings.",
4663
          "msWFSDispatch()");
4664
      returnvalue =
4665
          msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
2✔
4666
                         paramsObj->pszVersion);
2✔
4667
      msWFSFreeParamsObj(paramsObj);
2✔
4668
      return returnvalue;
2✔
4669
    }
4670
  }
4671

4672
  returnvalue = MS_DONE;
4673
  /* Continue dispatching...
4674
   */
4675
  if (strcasecmp(paramsObj->pszRequest, "DescribeFeatureType") == 0)
654✔
4676
    returnvalue =
4677
        msWFSDescribeFeatureType(map, paramsObj, ows_request, nWFSVersion);
37✔
4678

4679
  else if (strcasecmp(paramsObj->pszRequest, "GetFeature") == 0)
617✔
4680
    returnvalue =
4681
        msWFSGetFeature(map, paramsObj, requestobj, ows_request, nWFSVersion);
577✔
4682

4683
  else if (nWFSVersion >= OWS_2_0_0 &&
40✔
4684
           strcasecmp(paramsObj->pszRequest, "GetPropertyValue") == 0)
40✔
4685
    returnvalue = msWFSGetPropertyValue(map, paramsObj, requestobj, ows_request,
22✔
4686
                                        nWFSVersion);
4687

4688
  else if (nWFSVersion >= OWS_2_0_0 &&
18✔
4689
           strcasecmp(paramsObj->pszRequest, "ListStoredQueries") == 0)
18✔
4690
    returnvalue = msWFSListStoredQueries20(map, ows_request);
4✔
4691

4692
  else if (nWFSVersion >= OWS_2_0_0 &&
14✔
4693
           strcasecmp(paramsObj->pszRequest, "DescribeStoredQueries") == 0)
14✔
4694
    returnvalue = msWFSDescribeStoredQueries20(map, paramsObj, ows_request);
11✔
4695

4696
  else if (msWFSGetIndexUnsupportedOperation(paramsObj->pszRequest) >= 0) {
3✔
4697
    /* Unsupported WFS request */
4698
    msSetError(MS_WFSERR, "Unsupported WFS request: %s", "msWFSDispatch()",
2✔
4699
               paramsObj->pszRequest);
4700
    returnvalue = msWFSException(map, paramsObj->pszRequest,
2✔
4701
                                 MS_OWS_ERROR_OPERATION_NOT_SUPPORTED,
4702
                                 paramsObj->pszVersion);
2✔
4703
  } else if (strcasecmp(paramsObj->pszService, "WFS") == 0) {
1✔
4704
    /* Invalid WFS request */
4705
    msSetError(MS_WFSERR, "Invalid WFS request: %s", "msWFSDispatch()",
1✔
4706
               paramsObj->pszRequest);
4707
    returnvalue =
4708
        msWFSException(map, "request", MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1✔
4709
                       paramsObj->pszVersion);
1✔
4710
  }
4711

4712
  /* This was not detected as a WFS request... let MapServer handle it */
4713
  msWFSFreeParamsObj(paramsObj);
654✔
4714
  return returnvalue;
654✔
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
}
4726

4727
/************************************************************************/
4728
/*                           msWFSCreateParamsObj                       */
4729
/*                                                                      */
4730
/*      Create a parameter object, initialize it.                       */
4731
/*      The caller should free the object using msWFSFreeParamsObj.     */
4732
/************************************************************************/
4733
wfsParamsObj *msWFSCreateParamsObj() {
764✔
4734
  wfsParamsObj *paramsObj = (wfsParamsObj *)calloc(1, sizeof(wfsParamsObj));
764✔
4735
  MS_CHECK_ALLOC(paramsObj, sizeof(wfsParamsObj), NULL);
764✔
4736

4737
  paramsObj->nMaxFeatures = -1;
764✔
4738
  paramsObj->nStartIndex = -1;
764✔
4739

4740
  return paramsObj;
764✔
4741
}
4742

4743
/************************************************************************/
4744
/*                            msWFSFreeParmsObj                         */
4745
/*                                                                      */
4746
/*      Free params object.                                             */
4747
/************************************************************************/
4748
void msWFSFreeParamsObj(wfsParamsObj *wfsparams) {
764✔
4749
  if (wfsparams) {
764✔
4750
    free(wfsparams->pszVersion);
764✔
4751
    free(wfsparams->pszUpdateSequence);
764✔
4752
    free(wfsparams->pszRequest);
764✔
4753
    free(wfsparams->pszService);
764✔
4754
    free(wfsparams->pszTypeName);
764✔
4755
    free(wfsparams->pszFilter);
764✔
4756
    free(wfsparams->pszFilterLanguage);
764✔
4757
    free(wfsparams->pszBbox);
764✔
4758
    free(wfsparams->pszGeometryName);
764✔
4759
    free(wfsparams->pszOutputFormat);
764✔
4760
    free(wfsparams->pszFeatureId);
764✔
4761
    free(wfsparams->pszSrs);
764✔
4762
    free(wfsparams->pszResultType);
764✔
4763
    free(wfsparams->pszPropertyName);
764✔
4764
    free(wfsparams->pszAcceptVersions);
764✔
4765
    free(wfsparams->pszSections);
764✔
4766
    free(wfsparams->pszSortBy);
764✔
4767
    free(wfsparams->pszLanguage);
764✔
4768
    free(wfsparams->pszValueReference);
764✔
4769
    free(wfsparams->pszStoredQueryId);
764✔
4770

4771
    free(wfsparams);
764✔
4772
  }
4773
}
764✔
4774

4775
#ifdef USE_WFS_SVR
4776
/************************************************************************/
4777
/*                       msWFSGetDefaultVersion                         */
4778
/************************************************************************/
4779
static const char *msWFSGetDefaultVersion(mapObj *map) {
32✔
4780
  const char *pszVersion =
4781
      msOWSLookupMetadata(&(map->web.metadata), "F", "getcapabilities_version");
32✔
4782
  if (pszVersion != NULL) {
32✔
4783
    /* Check that the metadata value is one of the recognized versions */
4784
    int iVersion = msOWSParseVersionString(pszVersion);
2✔
4785
    int i;
4786
    for (i = 0; i < wfsNumSupportedVersions; i++) {
4✔
4787
      if (iVersion == wfsSupportedVersions[i])
4✔
4788
        return wfsSupportedVersionsStr[i];
2✔
4789
    }
4790
    /* If not, use the default (= latest) version */
UNCOV
4791
    msDebug("msWFSGetDefaultVersion(): invalid value for "
×
4792
            "wfs_getcapabilities_version: %s\n",
4793
            pszVersion);
4794
  }
4795
  return WFS_LATEST_VERSION;
4796
}
4797

4798
/************************************************************************/
4799
/*                             msWFSSetParam                            */
4800
/************************************************************************/
4801
static int msWFSSetParam(char **ppszOut, cgiRequestObj *request, int i,
33,002✔
4802
                         const char *pszExpectedParamName) {
4803
  if (strcasecmp(request->ParamNames[i], pszExpectedParamName) == 0) {
33,002✔
4804
    if (*ppszOut == NULL)
3,315✔
4805
      *ppszOut = msStrdup(request->ParamValues[i]);
3,314✔
4806
    return 1;
3,315✔
4807
  }
4808
  return 0;
4809
}
4810

4811
/************************************************************************/
4812
/*                         msWFSParseXMLQueryNode                       */
4813
/************************************************************************/
4814

4815
static void msWFSParseXMLQueryNode(CPLXMLNode *psQuery,
59✔
4816
                                   wfsParamsObj *wfsparams) {
4817
  const char *pszTypename;
4818
  const char *pszValue;
4819
  char *pszSerializedFilter, *pszTmp, *pszTmp2;
4820
  CPLXMLNode *psPropertyName;
4821
  CPLXMLNode *psFilter;
4822
  CPLXMLNode *psSortBy;
4823
  char *pszSortBy = NULL;
4824

4825
  pszValue = CPLGetXMLValue(psQuery, "srsName", NULL);
59✔
4826
  if (pszValue) {
59✔
4827
    msFree(wfsparams->pszSrs);
2✔
4828
    wfsparams->pszSrs = msStrdup(pszValue);
2✔
4829
  }
4830

4831
  /* parse typenames */
4832
  pszTypename = CPLGetXMLValue(psQuery, "typename", NULL);
59✔
4833
  if (pszTypename == NULL) /* WFS 2.0 */
59✔
4834
    pszTypename = CPLGetXMLValue(psQuery, "typeNames", NULL);
56✔
4835

4836
  /*parse property name*/
4837
  psPropertyName = CPLGetXMLNode(psQuery, "PropertyName");
59✔
4838
  pszTmp2 = NULL;
4839

4840
  /*when there is no PropertyName, we output (!) so that it is parsed properly
4841
   * in GetFeature*/
4842
  if (psPropertyName == NULL)
59✔
4843
    pszTmp2 = msStrdup("!");
58✔
4844

4845
  while (psPropertyName) {
61✔
4846
    if (!psPropertyName->pszValue ||
2✔
4847
        strcasecmp(psPropertyName->pszValue, "PropertyName") != 0) {
2✔
4848
      psPropertyName = psPropertyName->psNext;
1✔
4849
      continue;
1✔
4850
    }
4851
    pszValue = CPLGetXMLValue(psPropertyName, NULL, NULL);
1✔
4852
    if (pszTmp2 == NULL) {
1✔
4853
      pszTmp2 = msStrdup(pszValue);
1✔
4854
    } else {
UNCOV
4855
      pszTmp = msStrdup(pszTmp2);
×
UNCOV
4856
      const size_t nSize = strlen(pszTmp) + strlen(pszValue) + 2;
×
UNCOV
4857
      pszTmp2 = (char *)msSmallRealloc(pszTmp2, nSize);
×
4858
      snprintf(pszTmp2, nSize, "%s,%s", pszTmp, pszValue);
UNCOV
4859
      msFree(pszTmp);
×
4860
    }
4861
    psPropertyName = psPropertyName->psNext;
1✔
4862
  }
4863
  if (pszTmp2) {
59✔
4864
    pszTmp = msStrdup(pszTmp2);
59✔
4865
    const size_t nSize = strlen(pszTmp) + 3;
59✔
4866
    pszTmp2 = (char *)msSmallRealloc(pszTmp2, nSize);
59✔
4867
    snprintf(pszTmp2, nSize, "(%s)", pszTmp);
4868
    msFree(pszTmp);
59✔
4869

4870
    msWFSBuildParamList(&(wfsparams->pszPropertyName), pszTmp2, "");
59✔
4871
    msFree(pszTmp2);
59✔
4872
    pszTmp2 = NULL;
4873
  }
4874

4875
  /* parse filter */
4876
  psFilter = CPLGetXMLNode(psQuery, "Filter");
59✔
4877

4878
  if (psFilter == NULL) {
59✔
4879
    /*when there is no Filter, we output (!) so that it is parsed properly in
4880
     * GetFeature*/
4881
    pszSerializedFilter = msStrdup("(!)");
17✔
4882
  } else {
4883
    char *pszCPLTmp = CPLSerializeXMLTree(psFilter);
42✔
4884
    const size_t nSize = strlen(pszCPLTmp) + 3;
42✔
4885
    pszSerializedFilter = (char *)msSmallMalloc(nSize);
42✔
4886
    snprintf(pszSerializedFilter, nSize, "(%s)", pszCPLTmp);
4887
    CPLFree(pszCPLTmp);
42✔
4888
  }
4889

4890
  msWFSBuildParamList(&(wfsparams->pszFilter), pszSerializedFilter, "");
59✔
4891
  free(pszSerializedFilter);
59✔
4892

4893
  /* parse SortBy */
4894
  psSortBy = CPLGetXMLNode(psQuery, "SortBy");
59✔
4895
  if (psSortBy != NULL) {
59✔
4896
    int bFirstProperty = MS_TRUE;
4897
    CPLXMLNode *psIter = psSortBy->psChild;
2✔
4898

4899
    pszSortBy = msStringConcatenate(pszSortBy, "(");
2✔
4900

4901
    while (psIter != NULL) {
6✔
4902
      if (psIter->eType == CXT_Element &&
4✔
4903
          strcmp(psIter->pszValue, "SortProperty") == 0) {
4✔
4904
        const char *pszPropertyName =
4905
            CPLGetXMLValue(psIter, "PropertyName", NULL);
4✔
4906
        if (pszPropertyName == NULL)
4✔
4907
          pszPropertyName = CPLGetXMLValue(psIter, "ValueReference", NULL);
2✔
4908
        if (pszPropertyName != NULL) {
2✔
4909
          const char *pszSortOrder = CPLGetXMLValue(psIter, "SortOrder", NULL);
4✔
4910
          if (!bFirstProperty)
4✔
4911
            pszSortBy = msStringConcatenate(pszSortBy, ",");
2✔
4912
          bFirstProperty = MS_FALSE;
4913

4914
          pszSortBy = msStringConcatenate(pszSortBy, pszPropertyName);
4✔
4915
          if (pszSortOrder != NULL) {
4✔
4916
            pszSortBy = msStringConcatenate(pszSortBy, " ");
2✔
4917
            pszSortBy = msStringConcatenate(pszSortBy, pszSortOrder);
2✔
4918
          }
4919
        }
4920
      }
4921
      psIter = psIter->psNext;
4✔
4922
    }
4923

4924
    pszSortBy = msStringConcatenate(pszSortBy, ")");
2✔
4925
  } else {
4926
    pszSortBy = msStrdup("(!)");
57✔
4927
  }
4928

4929
  msWFSBuildParamList(&(wfsparams->pszSortBy), pszSortBy, "");
59✔
4930
  free(pszSortBy);
59✔
4931

4932
  /* Special case for "urn:ogc:def:query:OGC-WFS::GetFeatureById" */
4933
  /* Resolve the property typename */
4934
  if (pszTypename != NULL && strcmp(pszTypename, "?") == 0 &&
59✔
4935
      psFilter != NULL) {
4936
    const char *rid;
4937
    rid = CPLGetXMLValue(psFilter, "ResourceId.rid", NULL);
12✔
4938
    if (rid != NULL) {
12✔
4939
      char *pszTmpTypename = msStrdup(rid);
12✔
4940
      char *pszDot = strchr(pszTmpTypename, '.');
4941
      if (pszDot) {
12✔
4942
        *pszDot = '\0';
11✔
4943
        msWFSBuildParamList(&(wfsparams->pszTypeName), pszTmpTypename, ",");
11✔
4944
        pszTypename = NULL;
4945

4946
        if (wfsparams->countGetFeatureById >= 0)
11✔
4947
          wfsparams->countGetFeatureById++;
11✔
4948
      } else
4949
        wfsparams->countGetFeatureById = -1;
1✔
4950
      msFree(pszTmpTypename);
12✔
4951
    } else
4952
      wfsparams->countGetFeatureById = -1;
×
4953
  } else
4954
    wfsparams->countGetFeatureById = -1;
47✔
4955

4956
  if (pszTypename) {
12✔
4957
    msWFSBuildParamList(&(wfsparams->pszTypeName), pszTypename, ",");
48✔
4958
  }
4959
}
59✔
4960

4961
/************************************************************************/
4962
/*                     msWFSAnalyzeStoredQuery                          */
4963
/************************************************************************/
4964

4965
static int msWFSAnalyzeStoredQuery(mapObj *map, wfsParamsObj *wfsparams,
13✔
4966
                                   const char *id,
4967
                                   const char *pszResolvedQuery) {
4968
  CPLXMLNode *psRoot;
4969
  CPLXMLNode *psQuery;
4970
  CPLXMLNode *psIter;
4971

4972
  psRoot = CPLParseXMLString(pszResolvedQuery);
13✔
4973

4974
  if (psRoot == NULL) {
13✔
UNCOV
4975
    msSetError(MS_WFSERR,
×
4976
               "Resolved definition for stored query '%s' is invalid",
4977
               "msWFSParseRequest()", id);
UNCOV
4978
    msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
4979
                   wfsparams->pszVersion);
×
UNCOV
4980
    return MS_FAILURE;
×
4981
  }
4982

4983
  CPLStripXMLNamespace(psRoot, NULL, 1);
13✔
4984
  psQuery = CPLGetXMLNode(psRoot,
13✔
4985
                          "=StoredQueryDescription.QueryExpressionText.Query");
4986
  if (psQuery == NULL) {
13✔
UNCOV
4987
    msSetError(MS_WFSERR,
×
4988
               "Resolved definition for stored query '%s' is invalid",
4989
               "msWFSParseRequest()", id);
UNCOV
4990
    msWFSException(map, "mapserv", MS_OWS_ERROR_NO_APPLICABLE_CODE,
×
UNCOV
4991
                   wfsparams->pszVersion);
×
UNCOV
4992
    CPLDestroyXMLNode(psRoot);
×
UNCOV
4993
    return MS_FAILURE;
×
4994
  }
4995

4996
  psIter = psQuery;
4997
  while (psIter != NULL) {
27✔
4998
    if (psIter->eType == CXT_Element &&
14✔
4999
        strcmp(psIter->pszValue, "Query") == 0) {
14✔
5000
      msWFSParseXMLQueryNode(psIter, wfsparams);
14✔
5001
    }
5002
    psIter = psIter->psNext;
14✔
5003
  }
5004

5005
  CPLDestroyXMLNode(psRoot);
13✔
5006

5007
  return MS_SUCCESS;
13✔
5008
}
5009

5010
/************************************************************************/
5011
/*                     msWFSParseXMLStoredQueryNode                     */
5012
/************************************************************************/
5013

5014
static int msWFSParseXMLStoredQueryNode(mapObj *map, wfsParamsObj *wfsparams,
9✔
5015
                                        CPLXMLNode *psQuery) {
5016
  const char *id;
5017
  CPLXMLNode *psIter;
5018
  hashTableObj *hashTable;
5019
  char *pszResolvedQuery;
5020
  int status;
5021

5022
  id = CPLGetXMLValue(psQuery, "id", NULL);
9✔
5023
  if (id == NULL) {
9✔
5024
    msSetError(MS_WFSERR, "Missing 'id' attribute in StoredQuery",
1✔
5025
               "msWFSParseRequest()");
5026
    return msWFSException(map, "id", MS_OWS_ERROR_MISSING_PARAMETER_VALUE,
1✔
5027
                          wfsparams->pszVersion);
1✔
5028
  }
5029

5030
  hashTable = msCreateHashTable();
8✔
5031
  psIter = psQuery->psChild;
8✔
5032
  while (psIter != NULL) {
20✔
5033
    if (psIter->eType == CXT_Element &&
13✔
5034
        strcmp(psIter->pszValue, "Parameter") == 0) {
5✔
5035
      const char *name;
5036
      CPLXMLNode *psIter2;
5037
      char *pszValue;
5038

5039
      name = CPLGetXMLValue(psIter, "name", NULL);
5✔
5040
      if (name == NULL) {
5✔
5041
        msSetError(MS_WFSERR,
1✔
5042
                   "Missing 'name' attribute in Parameter of StoredQuery",
5043
                   "msWFSParseRequest()");
5044
        msFreeHashTable(hashTable);
1✔
5045
        return msWFSException(map, NULL, MS_OWS_ERROR_INVALID_PARAMETER_VALUE,
1✔
5046
                              wfsparams->pszVersion);
1✔
5047
      }
5048

5049
      psIter2 = psIter->psChild;
4✔
5050
      while (psIter2 != NULL) {
8✔
5051
        if (psIter2->eType != CXT_Attribute)
8✔
5052
          break;
5053
        psIter2 = psIter2->psNext;
4✔
5054
      }
5055

5056
      pszValue = CPLSerializeXMLTree(psIter2);
4✔
5057
      msInsertHashTable(hashTable, name, pszValue);
4✔
5058
      CPLFree(pszValue);
4✔
5059
    }
5060
    psIter = psIter->psNext;
12✔
5061
  }
5062

5063
  pszResolvedQuery =
5064
      msWFSGetResolvedStoredQuery20(map, wfsparams, id, hashTable);
7✔
5065
  msFreeHashTable(hashTable);
7✔
5066
  if (pszResolvedQuery == NULL)
7✔
5067
    return MS_FAILURE;
5068

5069
  status = msWFSAnalyzeStoredQuery(map, wfsparams, id, pszResolvedQuery);
4✔
5070

5071
  msFree(pszResolvedQuery);
4✔
5072

5073
  return status;
4✔
5074
}
5075

5076
/************************************************************************/
5077
/*                    msWFSSimplifyPropertyNameAndFilter                */
5078
/************************************************************************/
5079

5080
static void msWFSSimplifyPropertyNameAndFilter(wfsParamsObj *wfsparams) {
50✔
5081
  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
  if (wfsparams->pszPropertyName != NULL) {
50✔
5087
    i = 0;
5088
    while (strncmp(wfsparams->pszPropertyName + i, "(!)", 3) == 0)
108✔
5089
      i += 3;
58✔
5090
    if (wfsparams->pszPropertyName[i] == '\0') {
50✔
5091
      msFree(wfsparams->pszPropertyName);
49✔
5092
      wfsparams->pszPropertyName = NULL;
49✔
5093
    }
5094
  }
5095

5096
  /* Same with filter */
5097
  if (wfsparams->pszFilter != NULL) {
50✔
5098
    i = 0;
5099
    while (strncmp(wfsparams->pszFilter + i, "(!)", 3) == 0)
65✔
5100
      i += 3;
15✔
5101
    if (wfsparams->pszFilter[i] == '\0') {
50✔
5102
      msFree(wfsparams->pszFilter);
11✔
5103
      wfsparams->pszFilter = NULL;
11✔
5104
    }
5105
  }
5106

5107
  /* Same with SortBy */
5108
  if (wfsparams->pszSortBy != NULL) {
50✔
5109
    i = 0;
5110
    while (strncmp(wfsparams->pszSortBy + i, "(!)", 3) == 0)
107✔
5111
      i += 3;
57✔
5112
    if (wfsparams->pszSortBy[i] == '\0') {
50✔
5113
      msFree(wfsparams->pszSortBy);
48✔
5114
      wfsparams->pszSortBy = NULL;
48✔
5115
    }
5116
  }
5117
}
50✔
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,
761✔
5127
                      wfsParamsObj *wfsparams, int force_wfs_mode) {
5128
#ifdef USE_WFS_SVR
5129
  int i = 0;
5130

5131
  if (!request || !wfsparams)
761✔
UNCOV
5132
    return msWFSException(map, "request", "InvalidRequest", NULL);
×
5133

5134
  if (request->NumParams > 0 && request->postrequest == NULL) {
761✔
5135
    for (i = 0; i < request->NumParams; i++) {
4,809✔
5136
      if (request->ParamNames[i] && request->ParamValues[i]) {
4,116✔
5137
        if (msWFSSetParam(&(wfsparams->pszVersion), request, i, "VERSION")) {
4,116✔
5138
        }
5139

5140
        else if (msWFSSetParam(&(wfsparams->pszUpdateSequence), request, i,
3,439✔
5141
                               "UPDATESEQUENCE")) {
5142
        }
5143

5144
        else if (msWFSSetParam(&(wfsparams->pszRequest), request, i,
3,424✔
5145
                               "REQUEST")) {
5146
        }
5147

5148
        else if (msWFSSetParam(&(wfsparams->pszService), request, i,
2,737✔
5149
                               "SERVICE")) {
5150
        }
5151

5152
        else if (strcasecmp(request->ParamNames[i], "MAXFEATURES") == 0)
2,044✔
5153
          wfsparams->nMaxFeatures = atoi(request->ParamValues[i]);
30✔
5154

5155
        /* WFS 2.0 */
5156
        else if (strcasecmp(request->ParamNames[i], "COUNT") == 0)
2,014✔
5157
          wfsparams->nMaxFeatures = atoi(request->ParamValues[i]);
38✔
5158

5159
        else if (strcasecmp(request->ParamNames[i], "STARTINDEX") == 0)
1,976✔
5160
          wfsparams->nStartIndex = atoi(request->ParamValues[i]);
27✔
5161

5162
        else if (msWFSSetParam(&(wfsparams->pszBbox), request, i, "BBOX")) {
1,949✔
5163
        }
5164

5165
        else if (msWFSSetParam(&(wfsparams->pszSrs), request, i, "SRSNAME")) {
1,931✔
5166
        }
5167

5168
        else if (msWFSSetParam(&(wfsparams->pszResultType), request, i,
1,914✔
5169
                               "RESULTTYPE")) {
5170
        }
5171

5172
        else if (msWFSSetParam(&(wfsparams->pszTypeName), request, i,
1,856✔
5173
                               "TYPENAME")) {
5174
        }
5175

5176
        /* WFS 2.0 */
5177
        else if (msWFSSetParam(&(wfsparams->pszTypeName), request, i,
1,445✔
5178
                               "TYPENAMES")) {
5179
        }
5180

5181
        else if (msWFSSetParam(&(wfsparams->pszFilter), request, i, "FILTER")) {
1,303✔
5182
        }
5183

5184
        /* WFS 2.0 */
5185
        else if (msWFSSetParam(&(wfsparams->pszFilterLanguage), request, i,
970✔
5186
                               "FILTER_LANGUAGE")) {
5187
        }
5188

5189
        else if (msWFSSetParam(&(wfsparams->pszOutputFormat), request, i,
968✔
5190
                               "OUTPUTFORMAT")) {
5191
        }
5192

5193
        else if (msWFSSetParam(&(wfsparams->pszFeatureId), request, i,
832✔
5194
                               "FEATUREID")) {
5195
        }
5196

5197
        /* WFS 2.0 */
5198
        else if (msWFSSetParam(&(wfsparams->pszFeatureId), request, i,
812✔
5199
                               "RESOURCEID")) {
5200
        }
5201

5202
        else if (msWFSSetParam(&(wfsparams->pszPropertyName), request, i,
803✔
5203
                               "PROPERTYNAME")) {
5204
        }
5205

5206
        else if (msWFSSetParam(&(wfsparams->pszAcceptVersions), request, i,
770✔
5207
                               "ACCEPTVERSIONS")) {
5208
        }
5209

5210
        else if (msWFSSetParam(&(wfsparams->pszSections), request, i,
763✔
5211
                               "SECTIONS")) {
5212
        }
5213

5214
        else if (msWFSSetParam(&(wfsparams->pszSortBy), request, i, "SORTBY")) {
757✔
5215
        }
5216

5217
        else if (msWFSSetParam(&(wfsparams->pszLanguage), request, i,
746✔
5218
                               "LANGUAGE")) {
5219
        }
5220

5221
        else if (msWFSSetParam(&(wfsparams->pszValueReference), request, i,
743✔
5222
                               "VALUEREFERENCE")) {
5223
        }
5224

5225
        else if (msWFSSetParam(&(wfsparams->pszStoredQueryId), request, i,
724✔
5226
                               "STOREDQUERY_ID")) {
5227
        }
5228
      }
5229
    }
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
    if (wfsparams->pszVersion == NULL && wfsparams->pszRequest &&
693✔
5234
        strcasecmp(wfsparams->pszRequest, "GetCapabilities") == 0) {
16✔
5235
      wfsparams->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
16✔
5236
    }
5237
  }
5238

5239
  /* -------------------------------------------------------------------- */
5240
  /*      Parse the post request. It is assumed to be an XML document.    */
5241
  /* -------------------------------------------------------------------- */
5242
  if (request->postrequest != NULL) {
761✔
5243

5244
    CPLXMLNode *psRoot;
5245
    CPLXMLNode *psGetFeature = NULL;
5246
    CPLXMLNode *psGetCapabilities = NULL;
5247
    CPLXMLNode *psDescribeFeature = NULL;
5248
    CPLXMLNode *psGetPropertyValue = NULL;
5249
    CPLXMLNode *psListStoredQueries = NULL;
5250
    CPLXMLNode *psDescribeStoredQueries = NULL;
5251
    CPLXMLNode *psOperation = NULL;
5252
    const char *pszValue;
5253
    char *pszSchemaLocation = NULL;
5254

5255
    psRoot = CPLParseXMLString(request->postrequest);
68✔
5256
    if (map->debug == MS_DEBUGLEVEL_VVV) {
68✔
UNCOV
5257
      msDebug("msWFSParseRequest(): WFS post request: %s\n",
×
5258
              request->postrequest);
5259
    }
5260
    if (psRoot) {
68✔
5261
      /* need to strip namespaces */
5262
      CPLStripXMLNamespace(psRoot, NULL, 1);
68✔
5263

5264
      for (psOperation = psRoot; psOperation != NULL;
113✔
5265
           psOperation = psOperation->psNext) {
45✔
5266
        if (psOperation->eType == CXT_Element) {
113✔
5267

5268
          /* keep schemaLocation so as to be able to validate against
5269
           * appropriate */
5270
          /* wfs.xsd schema afterwards */
5271
          if (pszSchemaLocation == NULL &&
226✔
5272
              CPLGetXMLValue(psOperation, "schemaLocation", NULL) != NULL)
113✔
5273
            pszSchemaLocation =
5274
                msStrdup(CPLGetXMLValue(psOperation, "schemaLocation", NULL));
67✔
5275

5276
          if (strcasecmp(psOperation->pszValue, "GetFeature") == 0) {
113✔
5277
            psGetFeature = psOperation;
5278
            break;
5279
          } else if (strcasecmp(psOperation->pszValue, "GetCapabilities") ==
70✔
5280
                     0) {
5281
            psGetCapabilities = psOperation;
5282
            break;
5283
          } else if (strcasecmp(psOperation->pszValue, "DescribeFeatureType") ==
60✔
5284
                     0) {
5285
            psDescribeFeature = psOperation;
5286
            break;
5287
          } else if (strcasecmp(psOperation->pszValue, "GetPropertyValue") ==
55✔
5288
                     0) {
5289
            psGetPropertyValue = psOperation;
5290
            break;
5291
          } else if (strcasecmp(psOperation->pszValue, "ListStoredQueries") ==
52✔
5292
                     0) {
5293
            psListStoredQueries = psOperation;
5294
            break;
5295
          } else if (strcasecmp(psOperation->pszValue,
50✔
5296
                                "DescribeStoredQueries") == 0) {
5297
            psDescribeStoredQueries = psOperation;
5298
            break;
5299
          }
5300
          /* these are unsupported requests. Just set the  */
5301
          /* request value and return; */
5302
          else {
5303
            int idx = msWFSGetIndexUnsupportedOperation(psOperation->pszValue);
46✔
5304
            if (idx >= 0) {
46✔
5305
              wfsparams->pszRequest = msStrdup(wfsUnsupportedOperations[idx]);
1✔
5306
              break;
1✔
5307
            }
5308
          }
5309
        }
5310
      }
5311

5312
      if (psOperation != NULL) {
68✔
5313
        pszValue = CPLGetXMLValue(psOperation, "version", NULL);
68✔
5314
        if (pszValue)
68✔
5315
          wfsparams->pszVersion = msStrdup(pszValue);
61✔
5316

5317
        pszValue = CPLGetXMLValue(psOperation, "service", NULL);
68✔
5318
        if (pszValue)
68✔
5319
          wfsparams->pszService = msStrdup(pszValue);
68✔
5320
      }
5321

5322
      /* -------------------------------------------------------------------- */
5323
      /*      Parse the GetFeature                                            */
5324
      /* -------------------------------------------------------------------- */
5325
      if (psGetFeature) {
68✔
5326
        CPLXMLNode *psIter;
5327

5328
        wfsparams->pszRequest = msStrdup("GetFeature");
43✔
5329

5330
        pszValue = CPLGetXMLValue(psGetFeature, "resultType", NULL);
43✔
5331
        if (pszValue)
43✔
5332
          wfsparams->pszResultType = msStrdup(pszValue);
5✔
5333

5334
        pszValue = CPLGetXMLValue(psGetFeature, "maxFeatures", NULL);
43✔
5335
        if (pszValue)
43✔
5336
          wfsparams->nMaxFeatures = atoi(pszValue);
1✔
5337
        else {
5338
          /* WFS 2.0 */
5339
          pszValue = CPLGetXMLValue(psGetFeature, "count", NULL);
42✔
5340
          if (pszValue)
42✔
5341
            wfsparams->nMaxFeatures = atoi(pszValue);
6✔
5342
        }
5343

5344
        pszValue = CPLGetXMLValue(psGetFeature, "startIndex", NULL);
43✔
5345
        if (pszValue)
43✔
5346
          wfsparams->nStartIndex = atoi(pszValue);
1✔
5347

5348
        pszValue = CPLGetXMLValue(psGetFeature, "outputFormat", NULL);
43✔
5349
        if (pszValue)
43✔
5350
          wfsparams->pszOutputFormat = msStrdup(pszValue);
4✔
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
        psIter = psGetFeature->psChild;
43✔
5363
        while (psIter != NULL) {
371✔
5364
          if (psIter->eType == CXT_Element &&
333✔
5365
              strcmp(psIter->pszValue, "Query") == 0) {
51✔
5366
            msWFSParseXMLQueryNode(psIter, wfsparams);
43✔
5367
          } else if (psIter->eType == CXT_Element &&
290✔
5368
                     strcmp(psIter->pszValue, "StoredQuery") == 0) {
8✔
5369
            int status = msWFSParseXMLStoredQueryNode(map, wfsparams, psIter);
8✔
5370
            if (status != MS_SUCCESS) {
8✔
5371
              CPLDestroyXMLNode(psRoot);
5✔
5372
              msFree(pszSchemaLocation);
5✔
5373
              return status;
5✔
5374
            }
5375
            wfsparams->bHasPostStoredQuery = MS_TRUE;
3✔
5376
          }
5377
          psIter = psIter->psNext;
328✔
5378
        }
5379

5380
        msWFSSimplifyPropertyNameAndFilter(wfsparams);
38✔
5381
      } /* end of GetFeature */
5382

5383
      /* -------------------------------------------------------------------- */
5384
      /*      Parse the GetPropertyValue                                      */
5385
      /* -------------------------------------------------------------------- */
5386
      if (psGetPropertyValue) {
63✔
5387
        CPLXMLNode *psIter;
5388

5389
        wfsparams->pszRequest = msStrdup("GetPropertyValue");
3✔
5390

5391
        pszValue = CPLGetXMLValue(psGetPropertyValue, "valueReference", NULL);
3✔
5392
        if (pszValue)
3✔
5393
          wfsparams->pszValueReference = msStrdup(pszValue);
3✔
5394

5395
        pszValue = CPLGetXMLValue(psGetPropertyValue, "resultType", NULL);
3✔
5396
        if (pszValue)
3✔
UNCOV
5397
          wfsparams->pszResultType = msStrdup(pszValue);
×
5398

5399
        pszValue = CPLGetXMLValue(psGetPropertyValue, "count", NULL);
3✔
5400
        if (pszValue)
3✔
5401
          wfsparams->nMaxFeatures = atoi(pszValue);
1✔
5402

5403
        pszValue = CPLGetXMLValue(psGetPropertyValue, "startIndex", NULL);
3✔
5404
        if (pszValue)
3✔
5405
          wfsparams->nStartIndex = atoi(pszValue);
1✔
5406

5407
        pszValue = CPLGetXMLValue(psGetPropertyValue, "outputFormat", NULL);
3✔
5408
        if (pszValue)
3✔
5409
          wfsparams->pszOutputFormat = msStrdup(pszValue);
1✔
5410

5411
        psIter = psGetPropertyValue->psChild;
3✔
5412
        while (psIter != NULL) {
25✔
5413
          if (psIter->eType == CXT_Element &&
25✔
5414
              strcmp(psIter->pszValue, "Query") == 0) {
3✔
5415
            msWFSParseXMLQueryNode(psIter, wfsparams);
2✔
5416
            /* Just one is allowed for GetPropertyValue */
5417
            break;
2✔
5418
          } else if (psIter->eType == CXT_Element &&
23✔
5419
                     strcmp(psIter->pszValue, "StoredQuery") == 0) {
1✔
5420
            int status = msWFSParseXMLStoredQueryNode(map, wfsparams, psIter);
1✔
5421
            if (status != MS_SUCCESS) {
1✔
UNCOV
5422
              CPLDestroyXMLNode(psRoot);
×
UNCOV
5423
              msFree(pszSchemaLocation);
×
UNCOV
5424
              return status;
×
5425
            }
5426
            wfsparams->bHasPostStoredQuery = MS_TRUE;
1✔
5427
            /* Just one is allowed for GetPropertyValue */
5428
            break;
1✔
5429
          }
5430
          psIter = psIter->psNext;
22✔
5431
        }
5432

5433
        msWFSSimplifyPropertyNameAndFilter(wfsparams);
3✔
5434
      } /* end of GetPropertyValue */
5435

5436
      /* -------------------------------------------------------------------- */
5437
      /*      Parse GetCapabilities.                                          */
5438
      /* -------------------------------------------------------------------- */
5439
      if (psGetCapabilities) {
63✔
5440
        CPLXMLNode *psAcceptVersions;
5441
        CPLXMLNode *psSections;
5442

5443
        wfsparams->pszRequest = msStrdup("GetCapabilities");
10✔
5444

5445
        pszValue = CPLGetXMLValue(psGetCapabilities, "updateSequence", NULL);
10✔
5446
        if (pszValue)
10✔
5447
          wfsparams->pszUpdateSequence = msStrdup(pszValue);
2✔
5448

5449
        /* version is optional for the GetCapabilities. If not */
5450
        /* provided, set it. */
5451
        if (wfsparams->pszVersion == NULL)
10✔
5452
          wfsparams->pszVersion = msStrdup(msWFSGetDefaultVersion(map));
7✔
5453

5454
        psAcceptVersions = CPLGetXMLNode(psGetCapabilities, "AcceptVersions");
10✔
5455
        if (psAcceptVersions != NULL) {
10✔
5456
          CPLXMLNode *psIter = psAcceptVersions->psChild;
3✔
5457
          while (psIter != NULL) {
8✔
5458
            if (psIter->eType == CXT_Element &&
5✔
5459
                strcmp(psIter->pszValue, "Version") == 0) {
5✔
5460
              pszValue = CPLGetXMLValue(psIter, NULL, NULL);
5✔
5461
              if (pszValue) {
5✔
5462
                msWFSBuildParamList(&(wfsparams->pszAcceptVersions), pszValue,
5✔
5463
                                    ",");
5464
              }
5465
            }
5466
            psIter = psIter->psNext;
5✔
5467
          }
5468
        }
5469

5470
        psSections = CPLGetXMLNode(psGetCapabilities, "Sections");
10✔
5471
        if (psSections != NULL) {
10✔
5472
          CPLXMLNode *psIter = psSections->psChild;
1✔
5473
          while (psIter != NULL) {
2✔
5474
            if (psIter->eType == CXT_Element &&
1✔
5475
                strcmp(psIter->pszValue, "Section") == 0) {
1✔
5476
              pszValue = CPLGetXMLValue(psIter, NULL, NULL);
1✔
5477
              if (pszValue) {
1✔
5478
                msWFSBuildParamList(&(wfsparams->pszSections), pszValue, ",");
1✔
5479
              }
5480
            }
5481
            psIter = psIter->psNext;
1✔
5482
          }
5483
        }
5484
      } /* end of GetCapabilities */
5485
      /* -------------------------------------------------------------------- */
5486
      /*      Parse DescribeFeatureType                                       */
5487
      /* -------------------------------------------------------------------- */
5488
      if (psDescribeFeature) {
63✔
5489
        CPLXMLNode *psIter;
5490
        wfsparams->pszRequest = msStrdup("DescribeFeatureType");
5✔
5491

5492
        pszValue = CPLGetXMLValue(psDescribeFeature, "outputFormat", NULL);
5✔
5493
        if (pszValue)
5✔
5494
          wfsparams->pszOutputFormat = msStrdup(pszValue);
2✔
5495

5496
        psIter = CPLGetXMLNode(psDescribeFeature, "TypeName");
5✔
5497
        if (psIter) {
5✔
5498
          /* free typname and filter. There may have been */
5499
          /* values if they were passed in the URL */
5500
          if (wfsparams->pszTypeName)
2✔
UNCOV
5501
            free(wfsparams->pszTypeName);
×
5502
          wfsparams->pszTypeName = NULL;
2✔
5503

5504
          while (psIter) {
5✔
5505
            if (psIter->eType == CXT_Element &&
3✔
5506
                strcasecmp(psIter->pszValue, "TypeName") == 0) {
3✔
5507
              pszValue = CPLGetXMLValue(psIter, NULL, NULL);
3✔
5508
              if (pszValue) {
3✔
5509
                msWFSBuildParamList(&(wfsparams->pszTypeName), pszValue, ",");
3✔
5510
              }
5511
            }
5512
            psIter = psIter->psNext;
3✔
5513
          }
5514
        }
5515

5516
      } /* end of DescribeFeatureType */
5517

5518
      /* -------------------------------------------------------------------- */
5519
      /*      Parse the ListStoredQueries                                     */
5520
      /* -------------------------------------------------------------------- */
5521
      if (psListStoredQueries) {
63✔
5522
        wfsparams->pszRequest = msStrdup("ListStoredQueries");
2✔
5523
      } /* end of ListStoredQueries */
5524

5525
      /* -------------------------------------------------------------------- */
5526
      /*      Parse the DescribeStoredQueries                                 */
5527
      /* -------------------------------------------------------------------- */
5528
      if (psDescribeStoredQueries) {
63✔
5529
        CPLXMLNode *psIter;
5530

5531
        wfsparams->pszRequest = msStrdup("DescribeStoredQueries");
4✔
5532

5533
        psIter = CPLGetXMLNode(psDescribeStoredQueries, "StoredQueryId");
4✔
5534
        while (psIter) {
8✔
5535
          if (psIter->eType == CXT_Element &&
4✔
5536
              strcasecmp(psIter->pszValue, "StoredQueryId") == 0) {
4✔
5537
            pszValue = CPLGetXMLValue(psIter, NULL, NULL);
4✔
5538
            if (pszValue) {
4✔
5539
              msWFSBuildParamList(&(wfsparams->pszStoredQueryId), pszValue,
4✔
5540
                                  ",");
5541
            }
5542
          }
5543
          psIter = psIter->psNext;
4✔
5544
        }
5545
      } /* end of DescribeStoredQueries */
5546

5547
      CPLDestroyXMLNode(psRoot);
63✔
5548
    }
5549

5550
#if defined(USE_LIBXML2)
5551
    {
5552
      const char *schema_location = NULL, *validate = NULL;
5553

5554
      /*do we validate the xml ?*/
5555
      validate =
5556
          msOWSLookupMetadata(&(map->web.metadata), "FO", "validate_xml");
63✔
5557
      if (validate && strcasecmp(validate, "true") == 0 &&
63✔
5558
          (schema_location = msOWSLookupMetadata(&(map->web.metadata), "FO",
×
5559
                                                 "schemas_dir"))) {
UNCOV
5560
        if ((wfsparams->pszService &&
×
5561
             strcmp(wfsparams->pszService, "WFS") == 0) ||
×
5562
            force_wfs_mode) {
5563
          char *schema_file = NULL;
5564
          if (pszSchemaLocation != NULL &&
×
5565
              strstr(pszSchemaLocation, "wfs/1.0") != NULL) {
UNCOV
5566
            schema_file = msStringConcatenate(schema_file, schema_location);
×
UNCOV
5567
            schema_file = msStringConcatenate(
×
5568
                schema_file, MS_OWSCOMMON_WFS_10_SCHEMA_LOCATION);
UNCOV
5569
          } else if (pszSchemaLocation != NULL &&
×
5570
                     strstr(pszSchemaLocation, "wfs/1.1") != NULL) {
UNCOV
5571
            schema_file = msStringConcatenate(schema_file, schema_location);
×
UNCOV
5572
            schema_file = msStringConcatenate(
×
5573
                schema_file, MS_OWSCOMMON_WFS_11_SCHEMA_LOCATION);
UNCOV
5574
          } else if (pszSchemaLocation != NULL &&
×
5575
                     strstr(pszSchemaLocation, "wfs/2.0") != NULL) {
UNCOV
5576
            schema_file = msStringConcatenate(schema_file, schema_location);
×
UNCOV
5577
            schema_file = msStringConcatenate(
×
5578
                schema_file, MS_OWSCOMMON_WFS_20_SCHEMA_LOCATION);
5579
          }
UNCOV
5580
          if (schema_file != NULL) {
×
UNCOV
5581
            if (msOWSSchemaValidation(schema_file, request->postrequest) != 0) {
×
UNCOV
5582
              msSetError(MS_WFSERR, "Invalid POST request.  XML is not valid",
×
5583
                         "msWFSParseRequest()");
UNCOV
5584
              msFree(schema_file);
×
UNCOV
5585
              return msWFSException(map, "request", "InvalidRequest", NULL);
×
5586
            }
UNCOV
5587
            msFree(schema_file);
×
5588
          }
5589
        }
5590
      }
5591
    }
5592
#endif
5593

5594
    msFree(pszSchemaLocation);
63✔
5595
  }
5596

5597
#else
5598
  (void)map;
5599
  (void)request;
5600
  (void)wfsparams;
5601
  (void)force_wfs_mode;
5602
#endif
5603
  return MS_SUCCESS;
5604
}
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
int msIsLayerSupportedForWFSOrOAPIF(layerObj *lp) {
2,486✔
5612
  /* In order to be supported, lp->type must be specified, even for
5613
  ** layers that are OGR, SDE, SDO, etc connections.
5614
  */
5615
  if ((lp->type == MS_LAYER_POINT || lp->type == MS_LAYER_LINE ||
2,486✔
5616
       lp->type == MS_LAYER_POLYGON) &&
2,469✔
5617
      lp->connectiontype != MS_WMS && lp->connectiontype != MS_GRATICULE) {
2,469✔
5618
    return 1; /* true */
2,469✔
5619
  }
5620

5621
  return 0; /* false */
5622
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc