Coverage Report

Created: 2026-04-01 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/MapServer/src/mapwfslayer.c
Line
Count
Source
1
/**********************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Implementation of WFS CONNECTIONTYPE - client to WFS servers
6
 * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7
 *
8
 **********************************************************************
9
 * Copyright (c) 2002, Daniel Morissette, DM Solutions Group Inc
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 ****************************************************************************/
28
29
#include "mapserver.h"
30
#include "maperror.h"
31
#include "mapows.h"
32
#include "mapproject.h"
33
34
#include <time.h>
35
#include <assert.h>
36
37
#if defined(_WIN32) && !defined(__CYGWIN__)
38
#include <process.h>
39
#endif
40
41
#define WFS_V_0_0_14 14
42
#define WFS_V_1_0_0 100
43
44
/*====================================================================
45
 *  Private (static) functions
46
 *====================================================================*/
47
48
#ifdef USE_WFS_LYR
49
50
/************************************************************************/
51
/*                           msBuildRequestParms                        */
52
/*                                                                      */
53
/*      Build the params object based on the metadata                   */
54
/*      information. This object will be used when building the Get     */
55
/*      and Post requsests.                                             */
56
/*      Note : Verify the connection string to extract some values      */
57
/*      for backward compatibility. (It is though deprecated).           */
58
/*      This will also set layer projection and compute BBOX in that    */
59
/*      projection.                                                     */
60
/*                                                                      */
61
/************************************************************************/
62
static wfsParamsObj *msBuildRequestParams(mapObj *map, layerObj *lp,
63
                                          rectObj *bbox_ret) {
64
  wfsParamsObj *psParams = NULL;
65
  rectObj bbox;
66
  const char *pszTmp;
67
  char *pszVersion, *pszTypeName;
68
69
  if (!map || !lp || !bbox_ret)
70
    return NULL;
71
72
  if (lp->connection == NULL)
73
    return NULL;
74
75
  psParams = msWFSCreateParamsObj();
76
  pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO", "version");
77
  if (pszTmp)
78
    psParams->pszVersion = msStrdup(pszTmp);
79
  else {
80
    pszTmp = strstr(lp->connection, "VERSION=");
81
    if (!pszTmp)
82
      pszTmp = strstr(lp->connection, "version=");
83
    if (pszTmp) {
84
      pszVersion = strchr(pszTmp, '=') + 1;
85
      if (strncmp(pszVersion, "0.0.14", 6) == 0)
86
        psParams->pszVersion = msStrdup("0.0.14");
87
      else if (strncmp(pszVersion, "1.0.0", 5) == 0)
88
        psParams->pszVersion = msStrdup("1.0.0");
89
    }
90
  }
91
92
  /*the service is always set to WFS : see bug 1302 */
93
  psParams->pszService = msStrdup("WFS");
94
95
  /*
96
  pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO", "service");
97
  if (pszTmp)
98
    psParams->pszService = msStrdup(pszTmp);
99
  else
100
  {
101
      pszTmp = strstr(lp->connection, "SERVICE=");
102
      if (!pszTmp)
103
        pszTmp = strstr(lp->connection, "service=");
104
      if (pszTmp)
105
      {
106
          pszService = strchr(pszTmp, '=')+1;
107
          if (strncmp(pszService, "WFS", 3) == 0)
108
            psParams->pszService = msStrdup("WFS");
109
      }
110
  }
111
  */
112
113
  pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO", "geometryname");
114
  if (pszTmp)
115
    psParams->pszGeometryName = msStrdup(pszTmp);
116
117
  pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO", "typename");
118
  if (pszTmp)
119
    psParams->pszTypeName = msStrdup(pszTmp);
120
  else {
121
    pszTmp = strstr(lp->connection, "TYPENAME=");
122
    if (!pszTmp)
123
      pszTmp = strstr(lp->connection, "typename=");
124
    if (pszTmp) {
125
      pszTypeName = strchr(pszTmp, '=') + 1;
126
      if (pszTypeName) {
127
        const int nLength = strlen(pszTypeName);
128
        if (nLength > 0) {
129
          int i = 0;
130
          for (; i < nLength; i++) {
131
            if (pszTypeName[i] == '&')
132
              break;
133
          }
134
135
          if (i < nLength) {
136
            char *pszTypeNameTmp = NULL;
137
            pszTypeNameTmp = msStrdup(pszTypeName);
138
            pszTypeNameTmp[i] = '\0';
139
            psParams->pszTypeName = msStrdup(pszTypeNameTmp);
140
            free(pszTypeNameTmp);
141
          } else
142
            psParams->pszTypeName = msStrdup(pszTypeName);
143
        }
144
      }
145
    }
146
  }
147
148
  pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO", "filter");
149
  if (pszTmp && strlen(pszTmp) > 0) {
150
    if (strstr(pszTmp, "<Filter>") != NULL ||
151
        strstr(pszTmp, "<ogc:Filter") != NULL)
152
      psParams->pszFilter = msStrdup(pszTmp);
153
    else {
154
      psParams->pszFilter = msStringConcatenate(
155
          psParams->pszFilter,
156
          "<ogc:Filter xmlns:ogc=\"http://www.opengis.net/ogc\">");
157
      psParams->pszFilter =
158
          msStringConcatenate(psParams->pszFilter, (char *)pszTmp);
159
      psParams->pszFilter =
160
          msStringConcatenate(psParams->pszFilter, "</ogc:Filter>");
161
    }
162
  }
163
164
  pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO", "maxfeatures");
165
  if (pszTmp)
166
    psParams->nMaxFeatures = atoi(pszTmp);
167
168
  /* Request is always GetFeature; */
169
  psParams->pszRequest = msStrdup("GetFeature");
170
171
  /* ------------------------------------------------------------------
172
   * Figure the SRS we'll use for the request.
173
   * - Fetch the map SRS (if it's EPSG)
174
   * - Check if map SRS is listed in layer wfs_srs metadata
175
   * - If map SRS is valid for this layer then use it
176
   * - Otherwise request layer in its default SRS and we'll reproject later
177
   * ------------------------------------------------------------------ */
178
179
  /* __TODO__ WFS servers support only one SRS... need to decide how we'll */
180
  /* handle this and document it well. */
181
  /* It's likely that we'll simply reproject the BBOX to the layer's projection.
182
   */
183
184
  /* ------------------------------------------------------------------
185
   * Set layer SRS and reproject map extents to the layer's SRS
186
   * ------------------------------------------------------------------ */
187
#ifdef __TODO__
188
  /* No need to set lp->proj if it's already set to the right EPSG code */
189
  if ((pszTmp = msGetEPSGProj(&(lp->projection), NULL, MS_TRUE)) == NULL ||
190
      strcasecmp(pszEPSG, pszTmp) != 0) {
191
    char szProj[20];
192
    snprintf(szProj, sizeof(szProj), "init=epsg:%s", pszEPSG + 5);
193
    if (msLoadProjectionString(&(lp->projection), szProj) != 0)
194
      return NULL;
195
  }
196
#endif
197
198
  bbox = map->extent;
199
  if (msProjectionsDiffer(&(map->projection), &(lp->projection))) {
200
    msProjectRect(&(map->projection), &(lp->projection), &bbox);
201
  }
202
203
  *bbox_ret = bbox;
204
205
  return psParams;
206
}
207
208
/**********************************************************************
209
 *                          msBuildWFSLayerPostRequest()
210
 *
211
 * Build a WFS GetFeature xml document for a Post Request.
212
 *
213
 * Returns a reference to a newly allocated string that should be freed
214
 * by the caller.
215
 **********************************************************************/
216
static char *msBuildWFSLayerPostRequest(rectObj *bbox, wfsParamsObj *psParams) {
217
  char *pszPostReq = NULL;
218
  char *pszFilter = NULL;
219
  char *pszGeometryName = "Geometry";
220
  size_t bufferSize = 0;
221
222
  if (psParams->pszVersion == NULL ||
223
      (strncmp(psParams->pszVersion, "0.0.14", 6) != 0 &&
224
       strncmp(psParams->pszVersion, "1.0.0", 5)) != 0) {
225
    msSetError(MS_WFSCONNERR,
226
               "MapServer supports only WFS 1.0.0 or 0.0.14 (please verify the "
227
               "version metadata wfs_version).",
228
               "msBuildWFSLayerPostRequest()");
229
    return NULL;
230
  }
231
232
  if (psParams->pszTypeName == NULL) {
233
    msSetError(MS_WFSCONNERR, "Metadata wfs_typename must be set in the layer",
234
               "msBuildWFSLayerPostRequest()");
235
    return NULL;
236
  }
237
238
  if (psParams->pszGeometryName) {
239
    pszGeometryName = psParams->pszGeometryName;
240
  }
241
242
  if (psParams->pszFilter)
243
    pszFilter = psParams->pszFilter;
244
  else {
245
    bufferSize = 500;
246
    pszFilter = (char *)msSmallMalloc(bufferSize);
247
    snprintf(pszFilter, bufferSize,
248
             "<ogc:Filter>\n"
249
             "<ogc:BBOX>\n"
250
             "<ogc:PropertyName>%s</ogc:PropertyName>\n"
251
             "<gml:Box>\n"
252
             "<gml:coordinates>%f,%f %f,%f</gml:coordinates>\n"
253
             "</gml:Box>\n"
254
             "</ogc:BBOX>\n"
255
             "</ogc:Filter>",
256
             pszGeometryName, bbox->minx, bbox->miny, bbox->maxx, bbox->maxy);
257
  }
258
259
  bufferSize = strlen(pszFilter) + strlen(psParams->pszTypeName) + 500;
260
  pszPostReq = (char *)msSmallMalloc(bufferSize);
261
  if (psParams->nMaxFeatures > 0)
262
    snprintf(pszPostReq, bufferSize,
263
             "<?xml version=\"1.0\" ?>\n"
264
             "<wfs:GetFeature\n"
265
             "service=\"WFS\"\n"
266
             "version=\"1.0.0\"\n"
267
             "maxFeatures=\"%d\"\n"
268
             "outputFormat=\"GML2\"\n"
269
             "xmlns:wfs=\"http://www.opengis.net/wfs\" "
270
             "xmlns:ogc=\"http://www.opengis.net/ogc\" "
271
             "xsi:schemaLocation=\"http://www.opengis.net/wfs "
272
             "http://schemas.opengis.net/wfs/1.0.0/wfs.xsd\" "
273
             "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
274
             "xmlns:gml=\"http://www.opengis.net/gml\">\n"
275
             "<wfs:Query typeName=\"%s\">\n"
276
             "%s"
277
             "</wfs:Query>\n"
278
             "</wfs:GetFeature>\n",
279
             psParams->nMaxFeatures, psParams->pszTypeName, pszFilter);
280
  else
281
    snprintf(pszPostReq, bufferSize,
282
             "<?xml version=\"1.0\" ?>\n"
283
             "<wfs:GetFeature\n"
284
             "service=\"WFS\"\n"
285
             "version=\"1.0.0\"\n"
286
             "outputFormat=\"GML2\"\n"
287
             "xmlns:wfs=\"http://www.opengis.net/wfs\" "
288
             "xmlns:ogc=\"http://www.opengis.net/ogc\" "
289
             "xsi:schemaLocation=\"http://www.opengis.net/wfs "
290
             "http://schemas.opengis.net/wfs/1.0.0/wfs.xsd\" "
291
             "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
292
             "xmlns:gml=\"http://www.opengis.net/gml\">\n"
293
             "<wfs:Query typeName=\"%s\">\n"
294
             "%s"
295
             "</wfs:Query>\n"
296
             "</wfs:GetFeature>\n",
297
             psParams->pszTypeName, pszFilter);
298
  if (psParams->pszFilter == NULL)
299
    free(pszFilter);
300
301
  return pszPostReq;
302
}
303
304
/**********************************************************************
305
 *                          msBuildWFSLayerGetURL()
306
 *
307
 * Build a WFS GetFeature URL for a Get Request.
308
 *
309
 * Returns a reference to a newly allocated string that should be freed
310
 * by the caller.
311
 **********************************************************************/
312
static char *msBuildWFSLayerGetURL(layerObj *lp, rectObj *bbox,
313
                                   wfsParamsObj *psParams) {
314
  char *pszURL = NULL, *pszOnlineResource = NULL;
315
  const char *pszTmp;
316
  char *pszVersion, *pszService, *pszTypename = NULL;
317
  int bVersionInConnection = 0;
318
  int bTypenameInConnection = 0;
319
  size_t bufferSize = 0;
320
321
  if (lp->connectiontype != MS_WFS || lp->connection == NULL) {
322
    msSetError(MS_WFSCONNERR, "Call supported only for CONNECTIONTYPE WFS",
323
               "msBuildWFSLayerGetURL()");
324
    return NULL;
325
  }
326
327
  /* -------------------------------------------------------------------- */
328
  /*      Find out request version. Look first for the wfs_version        */
329
  /*      metedata. If not available try to find out if the CONNECTION    */
330
  /*      string contains the version. This last test is done for         */
331
  /*      backward compatibility but is depericated.                       */
332
  /* -------------------------------------------------------------------- */
333
  pszVersion = psParams->pszVersion;
334
  if (!pszVersion) {
335
    if ((pszTmp = strstr(lp->connection, "VERSION=")) == NULL &&
336
        (pszTmp = strstr(lp->connection, "version=")) == NULL) {
337
      msSetError(MS_WFSCONNERR, "Metadata wfs_version must be set in the layer",
338
                 "msBuildWFSLayerGetURL()");
339
      return NULL;
340
    }
341
    pszVersion = strchr(pszTmp, '=') + 1;
342
    bVersionInConnection = 1;
343
  }
344
345
  if (strncmp(pszVersion, "0.0.14", 6) != 0 &&
346
      strncmp(pszVersion, "1.0.0", 5) != 0 &&
347
      strncmp(pszVersion, "1.1", 3) != 0) {
348
    msSetError(MS_WFSCONNERR,
349
               "MapServer supports only WFS 1.1.0, 1.0.0 or 0.0.14 (please "
350
               "verify the version metadata wfs_version).",
351
               "msBuildWFSLayerGetURL()");
352
    return NULL;
353
  }
354
355
  /* -------------------------------------------------------------------- */
356
  /*      Find out the service. It is always set to WFS in function       */
357
  /*      msBuildRequestParms  (check Bug 1302 for details).              */
358
  /* -------------------------------------------------------------------- */
359
  pszService = psParams->pszService;
360
361
  /* -------------------------------------------------------------------- */
362
  /*      Find out the typename. Look first for the wfs_tyename           */
363
  /*      metadata. If not available try to find out if the CONNECTION    */
364
  /*      string contains it. This last test is done for                  */
365
  /*      backward compatibility but is depericated.                       */
366
  /* -------------------------------------------------------------------- */
367
  pszTypename = psParams->pszTypeName;
368
  if (!pszTypename) {
369
    if (strstr(lp->connection, "TYPENAME=") == NULL &&
370
        strstr(lp->connection, "typename=") == NULL) {
371
      msSetError(MS_WFSCONNERR,
372
                 "Metadata wfs_typename must be set in the layer",
373
                 "msBuildWFSLayerGetURL()");
374
      return NULL;
375
    }
376
    bTypenameInConnection = 1;
377
  }
378
379
  /* --------------------------------------------------------------------
380
   * Build the request URL.
381
   * At this point we set only the following parameters for GetFeature:
382
   *   REQUEST
383
   *   BBOX
384
   *   VERSION
385
   *   SERVICE
386
   *   TYPENAME
387
   *   FILTER
388
   *   MAXFEATURES
389
   *
390
   * For backward compatibility the user could also have in the connection
391
   * string the following parameters (but it is deprecated):
392
   *   VERSION
393
   *   SERVICE
394
   *   TYPENAME
395
   * -------------------------------------------------------------------- */
396
  /* Make sure we have a big enough buffer for the URL */
397
  bufferSize = strlen(lp->connection) + 1024;
398
  pszURL = (char *)malloc(bufferSize);
399
  MS_CHECK_ALLOC(pszURL, bufferSize, NULL);
400
401
  /* __TODO__ We have to urlencode each value... especially the BBOX values */
402
  /* because if they end up in exponent format (123e+06) the + will be seen */
403
  /* as a space by the remote server. */
404
405
  /* -------------------------------------------------------------------- */
406
  /*      build the URL,                                                  */
407
  /* -------------------------------------------------------------------- */
408
  /* make sure connection ends with "&" or "?" */
409
  pszOnlineResource = msOWSTerminateOnlineResource(lp->connection);
410
  snprintf(pszURL, bufferSize, "%s", pszOnlineResource);
411
  msFree(pszOnlineResource);
412
413
  /* REQUEST */
414
  snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL),
415
           "&REQUEST=GetFeature");
416
417
  /* VERSION */
418
  if (!bVersionInConnection)
419
    snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL),
420
             "&VERSION=%s", pszVersion);
421
422
  /* SERVICE */
423
  snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL), "&SERVICE=%s",
424
           pszService);
425
426
  /* TYPENAME */
427
  if (!bTypenameInConnection)
428
    snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL),
429
             "&TYPENAME=%s", pszTypename);
430
431
  /* -------------------------------------------------------------------- */
432
  /*      If the filter parameter is given in the wfs_filter metadata,    */
433
  /*      we use it and do not send the BBOX parameter as they are         */
434
  /*      mutually exclusive.                                             */
435
  /* -------------------------------------------------------------------- */
436
  if (psParams->pszFilter) {
437
    char *encoded_filter = msEncodeUrl(psParams->pszFilter);
438
    snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL), "&FILTER=%s",
439
             encoded_filter);
440
    free(encoded_filter);
441
  } else {
442
    /*
443
     * take care about the axis order for WFS 1.1
444
     */
445
    char *projUrn;
446
    char *projEpsg;
447
    projUrn = msOWSGetProjURN(&(lp->projection), &(lp->metadata), "FO", 1);
448
    msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "FO", 1, &projEpsg);
449
450
    /*
451
     * WFS 1.1 supports including the SRS in the BBOX parameter, should
452
     * respect axis order in the BBOX and has a separate SRSNAME parameter for
453
     * the desired result SRS.
454
     * WFS 1.0 is always easting, northing, doesn't include the SRS as part of
455
     * the BBOX parameter and has no SRSNAME parameter: if we don't have a
456
     * URN then fallback to WFS 1.0 style */
457
    if ((strncmp(pszVersion, "1.1", 3) == 0) && projUrn) {
458
      if (projEpsg && (strncmp(projEpsg, "EPSG:", 5) == 0) &&
459
          msIsAxisInverted(atoi(projEpsg + 5))) {
460
        snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL),
461
                 "&BBOX=%.15g,%.15g,%.15g,%.15g,%s&SRSNAME=%s", bbox->miny,
462
                 bbox->minx, bbox->maxy, bbox->maxx, projUrn, projUrn);
463
      } else {
464
        snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL),
465
                 "&BBOX=%.15g,%.15g,%.15g,%.15g,%s&SRSNAME=%s", bbox->minx,
466
                 bbox->miny, bbox->maxx, bbox->maxy, projUrn, projUrn);
467
      }
468
    } else {
469
      snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL),
470
               "&BBOX=%.15g,%.15g,%.15g,%.15g", bbox->minx, bbox->miny,
471
               bbox->maxx, bbox->maxy);
472
    }
473
474
    msFree(projUrn);
475
    msFree(projEpsg);
476
  }
477
478
  if (psParams->nMaxFeatures > 0)
479
    snprintf(pszURL + strlen(pszURL), bufferSize - strlen(pszURL),
480
             "&MAXFEATURES=%d", psParams->nMaxFeatures);
481
482
  return pszURL;
483
}
484
485
/**********************************************************************
486
 *                          msWFSLayerInfo
487
 *
488
 **********************************************************************/
489
typedef struct ms_wfs_layer_info_t {
490
  char *pszGMLFilename;
491
  rectObj rect; /* set by WhichShapes */
492
  char *pszGetUrl;
493
  int nStatus;           /* HTTP status */
494
  int bLayerHasValidGML; /* False until msWFSLayerWhichShapes() is called and
495
                            determines the result GML is valid with features*/
496
} msWFSLayerInfo;
497
498
/**********************************************************************
499
 *                          msAllocWFSLayerInfo()
500
 *
501
 **********************************************************************/
502
static msWFSLayerInfo *msAllocWFSLayerInfo(void) {
503
  msWFSLayerInfo *psInfo;
504
505
  psInfo = (msWFSLayerInfo *)calloc(1, sizeof(msWFSLayerInfo));
506
  MS_CHECK_ALLOC(psInfo, sizeof(msWFSLayerInfo), NULL);
507
508
  psInfo->pszGMLFilename = NULL;
509
  psInfo->rect.minx = psInfo->rect.maxx = 0;
510
  psInfo->rect.miny = psInfo->rect.maxy = 0;
511
  psInfo->pszGetUrl = NULL;
512
  psInfo->nStatus = 0;
513
514
  return psInfo;
515
}
516
517
/**********************************************************************
518
 *                          msFreeWFSLayerInfo()
519
 *
520
 **********************************************************************/
521
static void msFreeWFSLayerInfo(msWFSLayerInfo *psInfo) {
522
  if (psInfo) {
523
    if (psInfo->pszGMLFilename)
524
      free(psInfo->pszGMLFilename);
525
    if (psInfo->pszGetUrl)
526
      free(psInfo->pszGetUrl);
527
528
    free(psInfo);
529
  }
530
}
531
532
#endif /* USE_WFS_LYR */
533
534
/*====================================================================
535
 *  Public functions
536
 *====================================================================*/
537
538
/**********************************************************************
539
 *                          msPrepareWFSLayerRequest()
540
 *
541
 **********************************************************************/
542
543
int msPrepareWFSLayerRequest(int nLayerId, mapObj *map, layerObj *lp,
544
0
                             httpRequestObj *pasReqInfo, int *numRequests) {
545
#ifdef USE_WFS_LYR
546
  char *pszURL = NULL;
547
  const char *pszTmp;
548
  rectObj bbox;
549
  int nTimeout;
550
  int nStatus = MS_SUCCESS;
551
  msWFSLayerInfo *psInfo = NULL;
552
  int bPostRequest = 0;
553
  wfsParamsObj *psParams = NULL;
554
  char *pszHTTPCookieData = NULL;
555
556
  if (lp->connectiontype != MS_WFS || lp->connection == NULL)
557
    return MS_FAILURE;
558
559
  /* ------------------------------------------------------------------
560
   * Build a params object that will be used by to build the request,
561
    this will also set layer projection and compute BBOX in that projection.
562
   * ------------------------------------------------------------------ */
563
  psParams = msBuildRequestParams(map, lp, &bbox);
564
  if (!psParams)
565
    return MS_FAILURE;
566
567
  /* -------------------------------------------------------------------- */
568
  /*      Depending on the metadata wfs_request_method, build a Get or    */
569
  /*      a Post URL.                                                     */
570
  /*    If it is a Get request the URL would contain all the parameters in*/
571
  /*      the string;                                                     */
572
  /*      If it is a Post request, the URL will only contain the          */
573
  /*      connection string coming from the layer.                       */
574
  /* -------------------------------------------------------------------- */
575
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO", "request_method")) !=
576
      NULL) {
577
    if (strncmp(pszTmp, "GET", 3) == 0) {
578
      pszURL = msBuildWFSLayerGetURL(lp, &bbox, psParams);
579
      if (!pszURL) {
580
        /* an error was already reported. */
581
        return MS_FAILURE;
582
      }
583
    }
584
  }
585
  /* else it is a post request and just get the connection string */
586
  if (!pszURL) {
587
    bPostRequest = 1;
588
    pszURL = msStrdup(lp->connection);
589
  }
590
591
  /* ------------------------------------------------------------------
592
   * check to see if a the metadata wfs_connectiontimeout is set. If it is
593
   * the case we will use it, else we use the default which is 30 seconds.
594
   * First check the metadata in the layer object and then in the map object.
595
   * ------------------------------------------------------------------ */
596
  nTimeout = 30; /* Default is 30 seconds  */
597
  if ((pszTmp = msOWSLookupMetadata2(&(lp->metadata), &(map->web.metadata),
598
                                     "FO", "connectiontimeout")) != NULL) {
599
    nTimeout = atoi(pszTmp);
600
  }
601
602
  /*------------------------------------------------------------------
603
   * Check to see if there's a HTTP Cookie to forward
604
   * If Cookie differ between the two connection, it's NOT OK to merge
605
   * the connection
606
   * ------------------------------------------------------------------ */
607
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO", "http_cookie")) !=
608
      NULL) {
609
    if (strcasecmp(pszTmp, "forward") == 0) {
610
      pszTmp = msLookupHashTable(&(map->web.metadata), "http_cookie_data");
611
      if (pszTmp != NULL) {
612
        pszHTTPCookieData = msStrdup(pszTmp);
613
      }
614
    } else {
615
      pszHTTPCookieData = msStrdup(pszTmp);
616
    }
617
  } else if ((pszTmp = msOWSLookupMetadata(&(map->web.metadata), "FO",
618
                                           "http_cookie")) != NULL) {
619
    if (strcasecmp(pszTmp, "forward") == 0) {
620
      pszTmp = msLookupHashTable(&(map->web.metadata), "http_cookie_data");
621
      if (pszTmp != NULL) {
622
        pszHTTPCookieData = msStrdup(pszTmp);
623
      }
624
    } else {
625
      pszHTTPCookieData = msStrdup(pszTmp);
626
    }
627
  }
628
629
  /* ------------------------------------------------------------------
630
   * If nLayerId == -1 then we need to figure it
631
   * ------------------------------------------------------------------ */
632
  if (nLayerId == -1) {
633
    int iLayer;
634
    for (iLayer = 0; iLayer < map->numlayers; iLayer++) {
635
      if (GET_LAYER(map, iLayer) == lp) {
636
        nLayerId = iLayer;
637
        break;
638
      }
639
    }
640
  }
641
642
  /* ------------------------------------------------------------------
643
   * Add a request to the array (already preallocated)
644
   * ------------------------------------------------------------------ */
645
  pasReqInfo[(*numRequests)].nLayerId = nLayerId;
646
  pasReqInfo[(*numRequests)].pszGetUrl = pszURL;
647
648
  if (bPostRequest) {
649
    pasReqInfo[(*numRequests)].pszPostRequest =
650
        msBuildWFSLayerPostRequest(&bbox, psParams);
651
    pasReqInfo[(*numRequests)].pszPostContentType = msStrdup("text/xml");
652
  }
653
654
  /* We'll store the remote server's response to a tmp file. */
655
  pasReqInfo[(*numRequests)].pszOutputFile =
656
      msTmpFile(map, map->mappath, NULL, "tmp.gml");
657
658
  /* TODO: Implement Caching of GML responses. There was an older caching
659
   * method, but it suffered from a race condition. See #3137.
660
   */
661
662
  pasReqInfo[(*numRequests)].pszHTTPCookieData = pszHTTPCookieData;
663
  pszHTTPCookieData = NULL;
664
  pasReqInfo[(*numRequests)].nStatus = 0;
665
  pasReqInfo[(*numRequests)].nTimeout = nTimeout;
666
  pasReqInfo[(*numRequests)].bbox = bbox;
667
  pasReqInfo[(*numRequests)].debug = lp->debug;
668
669
  if (msHTTPAuthProxySetup(&(map->web.metadata), &(lp->metadata), pasReqInfo,
670
                           *numRequests, map, "FO") != MS_SUCCESS) {
671
    msWFSFreeParamsObj(psParams);
672
    return MS_FAILURE;
673
  }
674
675
  /* ------------------------------------------------------------------
676
   * Pre-Open the layer now, (i.e. alloc and fill msWFSLayerInfo inside
677
   * layer obj).  Layer will be ready for use when the main mapserver
678
   * code calls msLayerOpen().
679
   * ------------------------------------------------------------------ */
680
  if (lp->wfslayerinfo != NULL) {
681
    psInfo = (msWFSLayerInfo *)(lp->wfslayerinfo);
682
  } else {
683
    lp->wfslayerinfo = psInfo = msAllocWFSLayerInfo();
684
  }
685
686
  if (psInfo->pszGMLFilename)
687
    free(psInfo->pszGMLFilename);
688
  psInfo->pszGMLFilename = msStrdup(pasReqInfo[(*numRequests)].pszOutputFile);
689
690
  psInfo->rect = pasReqInfo[(*numRequests)].bbox;
691
692
  if (psInfo->pszGetUrl)
693
    free(psInfo->pszGetUrl);
694
  psInfo->pszGetUrl = msStrdup(pasReqInfo[(*numRequests)].pszGetUrl);
695
696
  psInfo->nStatus = 0;
697
698
  (*numRequests)++;
699
700
  msWFSFreeParamsObj(psParams);
701
702
  return nStatus;
703
704
#else
705
  /* ------------------------------------------------------------------
706
   * WFS CONNECTION Support not included...
707
   * ------------------------------------------------------------------ */
708
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
709
0
             "msPrepareWFSLayerRequest");
710
0
  return (MS_FAILURE);
711
712
0
#endif /* USE_WFS_LYR */
713
0
}
714
715
/**********************************************************************
716
 *                          msWFSUpdateRequestInfo()
717
 *
718
 * This function is called after a WFS request has been completed so that
719
 * we can copy request result information from the httpRequestObj to the
720
 * msWFSLayerInfo struct.  Things to copy here are the HTTP status, exceptions
721
 * information, mime type, etc.
722
 **********************************************************************/
723
0
void msWFSUpdateRequestInfo(layerObj *lp, httpRequestObj *pasReqInfo) {
724
#ifdef USE_WFS_LYR
725
  if (lp->wfslayerinfo) {
726
    msWFSLayerInfo *psInfo = NULL;
727
728
    psInfo = (msWFSLayerInfo *)(lp->wfslayerinfo);
729
730
    /* Copy request results infos to msWFSLayerInfo struct */
731
    /* For now there is only nStatus, but we should eventually add */
732
    /* mime type and WFS exceptions information. */
733
    psInfo->nStatus = pasReqInfo->nStatus;
734
  }
735
#else
736
  /* ------------------------------------------------------------------
737
   * WFS CONNECTION Support not included...
738
   * ------------------------------------------------------------------ */
739
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
740
0
             "msWFSUpdateRequestInfo()");
741
0
#endif /* USE_WFS_LYR */
742
0
}
743
744
/**********************************************************************
745
 *                          msWFSLayerOpen()
746
 *
747
 * WFS layers are just a special case of OGR connection.  Only the open/close
748
 * methods differ since they have to download and maintain GML files in cache
749
 * but the rest is mapped directly to OGR function calls in maplayer.c
750
 *
751
 **********************************************************************/
752
753
int msWFSLayerOpen(layerObj *lp, const char *pszGMLFilename,
754
0
                   rectObj *defaultBBOX) {
755
#ifdef USE_WFS_LYR
756
  int status = MS_SUCCESS;
757
  msWFSLayerInfo *psInfo = NULL;
758
759
  if (msCheckParentPointer(lp->map, "map") == MS_FAILURE)
760
    return MS_FAILURE;
761
762
  if (lp->wfslayerinfo != NULL) {
763
    psInfo = (msWFSLayerInfo *)lp->wfslayerinfo;
764
765
    /* Layer already opened.  If explicit filename requested then check */
766
    /* that file was already opened with the same filename. */
767
    /* If no explicit filename requested then we'll try to reuse the */
768
    /* previously opened layer... this will happen in a msDrawMap() call. */
769
    if (pszGMLFilename == NULL ||
770
        (psInfo->pszGMLFilename &&
771
         strcmp(psInfo->pszGMLFilename, pszGMLFilename) == 0)) {
772
      if (lp->layerinfo == NULL) {
773
        if (msWFSLayerWhichShapes(lp, psInfo->rect, MS_FALSE) ==
774
            MS_FAILURE) /* no access to context (draw vs. query) here, although
775
                           I doubt it matters... */
776
          return MS_FAILURE;
777
      }
778
      return MS_SUCCESS; /* Nothing to do... layer is already opened */
779
    } else {
780
      /* Hmmm... should we produce a fatal error? */
781
      /* For now we'll just close the layer and reopen it. */
782
      if (lp->debug)
783
        msDebug("msWFSLayerOpen(): Layer already opened (%s)\n",
784
                lp->name ? lp->name : "(null)");
785
      msWFSLayerClose(lp);
786
    }
787
  }
788
789
  /* ------------------------------------------------------------------
790
   * Alloc and fill msWFSLayerInfo inside layer obj
791
   * ------------------------------------------------------------------ */
792
  lp->wfslayerinfo = psInfo = msAllocWFSLayerInfo();
793
794
  if (pszGMLFilename)
795
    psInfo->pszGMLFilename = msStrdup(pszGMLFilename);
796
  else {
797
    psInfo->pszGMLFilename =
798
        msTmpFile(lp->map, lp->map->mappath, NULL, "tmp.gml");
799
  }
800
801
  if (defaultBBOX) {
802
    /* __TODO__ If new bbox differs from current one then we should */
803
    /* invalidate current GML file in cache */
804
    psInfo->rect = *defaultBBOX;
805
  } else {
806
    /* Use map bbox by default */
807
    psInfo->rect = lp->map->extent;
808
  }
809
810
  /* We will call whichshapes() now and force downloading layer right */
811
  /* away.  This saves from having to call DescribeFeatureType and */
812
  /* parsing the response (being lazy I guess) and anyway given the */
813
  /* way we work with layers right now the bbox is unlikely to change */
814
  /* between now and the time whichshapes() would have been called by */
815
  /* the MapServer core. */
816
817
  if ((lp->map->projection.numargs > 0) && (lp->projection.numargs > 0))
818
    msProjectRect(&lp->map->projection, &lp->projection,
819
                  &psInfo->rect); /* project the searchrect to source coords */
820
821
  if (msWFSLayerWhichShapes(lp, psInfo->rect, MS_FALSE) ==
822
      MS_FAILURE) /* no access to context (draw vs. query) here, although I
823
                     doubt it matters... */
824
    status = MS_FAILURE;
825
826
  return status;
827
#else
828
  /* ------------------------------------------------------------------
829
   * WFS CONNECTION Support not included...
830
   * ------------------------------------------------------------------ */
831
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
832
0
             "msWFSLayerOpen()");
833
0
  return (MS_FAILURE);
834
835
0
#endif /* USE_WFS_LYR */
836
0
}
837
838
/**********************************************************************
839
 *                          msWFSLayerOpenVT()
840
 *
841
 * Overloaded version of msWFSLayerOpen for virtual table architecture
842
 **********************************************************************/
843
0
int msWFSLayerOpenVT(layerObj *lp) { return msWFSLayerOpen(lp, NULL, NULL); }
844
845
/**********************************************************************
846
 *                          msWFSLayerIsOpen()
847
 *
848
 * Returns MS_TRUE if layer is already open, MS_FALSE otherwise.
849
 *
850
 **********************************************************************/
851
852
63
int msWFSLayerIsOpen(layerObj *lp) {
853
#ifdef USE_WFS_LYR
854
  if (lp->wfslayerinfo != NULL)
855
    return MS_TRUE;
856
857
  return MS_FALSE;
858
#else
859
  /* ------------------------------------------------------------------
860
   * WFS CONNECTION Support not included...
861
   * ------------------------------------------------------------------ */
862
63
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
863
63
             "msWFSLayerIsOpen()");
864
63
  return (MS_FALSE);
865
866
63
#endif /* USE_WFS_LYR */
867
63
}
868
869
/**********************************************************************
870
 *                          msWFSLayerInitItemInfo()
871
 *
872
 **********************************************************************/
873
874
0
int msWFSLayerInitItemInfo(layerObj *layer) {
875
0
  (void)layer;
876
  /* Nothing to do here.  OGR will do its own initialization when it */
877
  /* opens the actual file. */
878
  /* Note that we didn't implement our own msWFSLayerFreeItemInfo() */
879
  /* so that the OGR one gets called. */
880
0
  return MS_SUCCESS;
881
0
}
882
883
/**********************************************************************
884
 *                          msWFSLayerGetShape()
885
 *
886
 **********************************************************************/
887
0
int msWFSLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record) {
888
#ifdef USE_WFS_LYR
889
  msWFSLayerInfo *psInfo = NULL;
890
891
  if (layer != NULL && layer->wfslayerinfo != NULL)
892
    psInfo = (msWFSLayerInfo *)layer->wfslayerinfo;
893
  else {
894
    msSetError(MS_WFSERR, "Layer is not opened.", "msWFSLayerGetShape()");
895
    return MS_FAILURE;
896
  }
897
898
  if (psInfo->bLayerHasValidGML)
899
    return msOGRLayerGetShape(layer, shape, record);
900
  else {
901
    /* Layer is successful, but there is no data to process */
902
    msFreeShape(shape);
903
    shape->type = MS_SHAPE_NULL;
904
    return MS_FAILURE;
905
  }
906
#else
907
  /* ------------------------------------------------------------------
908
   * WFS CONNECTION Support not included...
909
   * ------------------------------------------------------------------ */
910
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
911
0
             "msWFSLayerGetShape()");
912
0
  return (MS_FAILURE);
913
0
#endif /* USE_WFS_LYR */
914
0
}
915
916
/**********************************************************************
917
 *                          msWFSLayerGetNextShape()
918
 *
919
 **********************************************************************/
920
0
int msWFSLayerNextShape(layerObj *layer, shapeObj *shape) {
921
#ifdef USE_WFS_LYR
922
  msWFSLayerInfo *psInfo = NULL;
923
924
  if (layer != NULL && layer->wfslayerinfo != NULL)
925
    psInfo = (msWFSLayerInfo *)layer->wfslayerinfo;
926
  else {
927
    msSetError(MS_WFSERR, "Layer is not opened.", "msWFSLayerNextShape()");
928
    return MS_FAILURE;
929
  }
930
931
  if (psInfo->bLayerHasValidGML)
932
    return msOGRLayerNextShape(layer, shape);
933
  else {
934
    /* Layer is successful, but there is no data to process */
935
    msFreeShape(shape);
936
    shape->type = MS_SHAPE_NULL;
937
    return MS_FAILURE;
938
  }
939
#else
940
  /* ------------------------------------------------------------------
941
   * WFS CONNECTION Support not included...
942
   * ------------------------------------------------------------------ */
943
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
944
0
             "msWFSLayerNextShape()");
945
0
  return (MS_FAILURE);
946
0
#endif /* USE_WFS_LYR */
947
0
}
948
949
/**********************************************************************
950
 *                          msWFSLayerGetExtent()
951
 *
952
 **********************************************************************/
953
0
int msWFSLayerGetExtent(layerObj *layer, rectObj *extent) {
954
#ifdef USE_WFS_LYR
955
  msWFSLayerInfo *psInfo = NULL;
956
957
  if (layer != NULL && layer->wfslayerinfo != NULL)
958
    psInfo = (msWFSLayerInfo *)layer->wfslayerinfo;
959
  else {
960
    msSetError(MS_WFSERR, "Layer is not opened.", "msWFSLayerGetExtent()");
961
    return MS_FAILURE;
962
  }
963
964
  if (psInfo->bLayerHasValidGML)
965
    return msOGRLayerGetExtent(layer, extent);
966
  else {
967
    /* Layer is successful, but there is no data to process */
968
    msSetError(MS_WFSERR, "Unable to get extents for this layer.",
969
               "msWFSLayerGetExtent()");
970
    return MS_FAILURE;
971
  }
972
#else
973
  /* ------------------------------------------------------------------
974
   * WFS CONNECTION Support not included...
975
   * ------------------------------------------------------------------ */
976
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
977
0
             "msWFSLayerGetExtent()");
978
0
  return (MS_FAILURE);
979
0
#endif /* USE_WFS_LYR */
980
0
}
981
982
/**********************************************************************
983
 *                          msWFSLayerGetItems()
984
 *
985
 **********************************************************************/
986
987
0
int msWFSLayerGetItems(layerObj *layer) {
988
#ifdef USE_WFS_LYR
989
  /* For now this method simply lets OGR parse the GML and figure the  */
990
  /* schema itself. */
991
  /* It could also be implemented to call DescribeFeatureType for */
992
  /* this layer, but we don't need to do it so why waste resources? */
993
994
  msWFSLayerInfo *psInfo = NULL;
995
996
  if (layer != NULL && layer->wfslayerinfo != NULL)
997
    psInfo = (msWFSLayerInfo *)layer->wfslayerinfo;
998
  else {
999
    msSetError(MS_WFSERR, "Layer is not opened.", "msWFSLayerGetItems()");
1000
    return MS_FAILURE;
1001
  }
1002
1003
  if (psInfo->bLayerHasValidGML)
1004
    return msOGRLayerGetItems(layer);
1005
  else {
1006
    /* Layer is successful, but there is no data to process */
1007
    layer->numitems = 0;
1008
    layer->items = NULL;
1009
    return MS_SUCCESS;
1010
  }
1011
#else
1012
  /* ------------------------------------------------------------------
1013
   * WFS CONNECTION Support not included...
1014
   * ------------------------------------------------------------------ */
1015
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
1016
0
             "msWFSLayerGetItems()");
1017
0
  return (MS_FAILURE);
1018
0
#endif /* USE_WFS_LYR */
1019
0
}
1020
1021
/**********************************************************************
1022
 *                          msWFSLayerWhichShapes()
1023
 *
1024
 **********************************************************************/
1025
1026
0
int msWFSLayerWhichShapes(layerObj *lp, rectObj rect, int isQuery) {
1027
#ifdef USE_WFS_LYR
1028
  msWFSLayerInfo *psInfo;
1029
  int status = MS_SUCCESS;
1030
  const char *pszTmp;
1031
  FILE *fp;
1032
1033
  if (msCheckParentPointer(lp->map, "map") == MS_FAILURE)
1034
    return MS_FAILURE;
1035
1036
  psInfo = (msWFSLayerInfo *)lp->wfslayerinfo;
1037
1038
  if (psInfo == NULL) {
1039
    msSetError(MS_WFSCONNERR, "Assertion failed: WFS layer not opened!!!",
1040
               "msWFSLayerWhichShapes()");
1041
    return (MS_FAILURE);
1042
  }
1043
1044
  /* ------------------------------------------------------------------
1045
   * Check if layer overlaps current view window (using wfs_latlonboundingbox)
1046
   * ------------------------------------------------------------------ */
1047
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "FO",
1048
                                    "latlonboundingbox")) != NULL) {
1049
    char **tokens;
1050
    int n;
1051
    rectObj ext;
1052
1053
    tokens = msStringSplit(pszTmp, ' ', &n);
1054
    if (tokens == NULL || n != 4) {
1055
      msSetError(MS_WFSCONNERR,
1056
                 "Wrong number of values in 'wfs_latlonboundingbox' metadata.",
1057
                 "msWFSLayerWhichShapes()");
1058
      return MS_FAILURE;
1059
    }
1060
1061
    ext.minx = atof(tokens[0]);
1062
    ext.miny = atof(tokens[1]);
1063
    ext.maxx = atof(tokens[2]);
1064
    ext.maxy = atof(tokens[3]);
1065
1066
    msFreeCharArray(tokens, n);
1067
1068
    /* Reproject latlonboundingbox to the selected SRS for the layer and */
1069
    /* check if it overlaps the bbox that we calculated for the request */
1070
1071
    msProjectRect(&(lp->map->latlon), &(lp->projection), &ext);
1072
    if (!msRectOverlap(&rect, &ext)) {
1073
      /* No overlap... nothing to do. If layer was never opened, go open it.*/
1074
      if (lp->layerinfo)
1075
        return MS_DONE; /* No overlap. */
1076
    }
1077
  }
1078
1079
  /* ------------------------------------------------------------------
1080
   * __TODO__ If new bbox differs from current one then we should
1081
   * invalidate current GML file in cache
1082
   * ------------------------------------------------------------------ */
1083
  psInfo->rect = rect;
1084
1085
  /* ------------------------------------------------------------------
1086
   * If file not downloaded yet then do it now.
1087
   * ------------------------------------------------------------------ */
1088
  if (psInfo->nStatus == 0) {
1089
    httpRequestObj asReqInfo[2];
1090
    int numReq = 0;
1091
1092
    msHTTPInitRequestObj(asReqInfo, 2);
1093
1094
    if (msPrepareWFSLayerRequest(-1, lp->map, lp, asReqInfo, &numReq) ==
1095
            MS_FAILURE ||
1096
        msOWSExecuteRequests(asReqInfo, numReq, lp->map, MS_TRUE) ==
1097
            MS_FAILURE) {
1098
      /* Delete tmp file... we don't want it to stick around. */
1099
      unlink(asReqInfo[0].pszOutputFile);
1100
      return MS_FAILURE;
1101
    }
1102
1103
    if (psInfo->nStatus != asReqInfo[0].nStatus) {
1104
      /* For drawQuery, we may use a copy layer which needs to be updated */
1105
      msWFSUpdateRequestInfo(lp, &(asReqInfo[0]));
1106
    }
1107
1108
    /* Cleanup */
1109
    msHTTPFreeRequestObj(asReqInfo, numReq);
1110
  }
1111
1112
  if (!MS_HTTP_SUCCESS(psInfo->nStatus)) {
1113
    /* Delete tmp file... we don't want it to stick around. */
1114
    unlink(psInfo->pszGMLFilename);
1115
1116
    msSetError(MS_WFSCONNERR, "Got HTTP status %d downloading WFS layer %s",
1117
               "msWFSLayerWhichShapes()", psInfo->nStatus,
1118
               lp->name ? lp->name : "(null)");
1119
    return (MS_FAILURE);
1120
  }
1121
1122
  /* ------------------------------------------------------------------
1123
   * Check that file is really GML... it could be an exception, or just junk.
1124
   * ------------------------------------------------------------------ */
1125
  if ((fp = fopen(psInfo->pszGMLFilename, "r")) != NULL) {
1126
    char szHeader[2000];
1127
    int nBytes = 0;
1128
1129
    nBytes = fread(szHeader, 1, sizeof(szHeader) - 1, fp);
1130
    fclose(fp);
1131
1132
    if (nBytes < 0)
1133
      nBytes = 0;
1134
    szHeader[nBytes] = '\0';
1135
1136
    if (nBytes == 0) {
1137
      msSetError(MS_WFSCONNERR, "WFS request produced no oputput for layer %s.",
1138
                 "msWFSLayerWhichShapes()", lp->name ? lp->name : "(null)");
1139
      return (MS_FAILURE);
1140
    }
1141
    if (strstr(szHeader, "<WFS_Exception>") ||
1142
        strstr(szHeader, "<ServiceExceptionReport>")) {
1143
      msOWSProcessException(lp, psInfo->pszGMLFilename, MS_WFSCONNERR,
1144
                            "msWFSLayerWhichShapes()");
1145
      return MS_FAILURE;
1146
    } else if (strstr(szHeader, "opengis.net/gml") &&
1147
               strstr(szHeader, "featureMember>") == NULL) {
1148
      /* This looks like valid GML, but contains 0 features. */
1149
      return MS_DONE;
1150
    } else if (strstr(szHeader, "opengis.net/gml") == NULL ||
1151
               strstr(szHeader, "featureMember>") == NULL) {
1152
      /* This is probably just junk. */
1153
      msSetError(MS_WFSCONNERR,
1154
                 "WFS request produced unexpected output (junk?) for layer %s.",
1155
                 "msWFSLayerWhichShapes()", lp->name ? lp->name : "(null)");
1156
      return (MS_FAILURE);
1157
    }
1158
1159
    /* If we got this far, it must be a valid GML dataset... keep going */
1160
  }
1161
1162
  /* ------------------------------------------------------------------
1163
   * Open GML file using OGR.
1164
   * ------------------------------------------------------------------ */
1165
  if ((status = msOGRLayerOpen(lp, psInfo->pszGMLFilename)) != MS_SUCCESS)
1166
    return status;
1167
1168
  status = msOGRLayerWhichShapes(lp, rect, isQuery);
1169
1170
  /* Mark that the OGR Layer is valid */
1171
  psInfo->bLayerHasValidGML = MS_TRUE;
1172
1173
  return status;
1174
#else
1175
  /* ------------------------------------------------------------------
1176
   * WFS CONNECTION Support not included...
1177
   * ------------------------------------------------------------------ */
1178
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
1179
0
             "msWFSLayerWhichShapes()");
1180
0
  return (MS_FAILURE);
1181
1182
0
#endif /* USE_WFS_LYR */
1183
0
}
1184
1185
/**********************************************************************
1186
 *                          msWFSLayerClose()
1187
 *
1188
 **********************************************************************/
1189
1190
0
int msWFSLayerClose(layerObj *lp) {
1191
#ifdef USE_WFS_LYR
1192
1193
  /* ------------------------------------------------------------------
1194
   * Cleanup OGR connection
1195
   * ------------------------------------------------------------------ */
1196
  if (lp->layerinfo)
1197
    msOGRLayerClose(lp);
1198
1199
  /* ------------------------------------------------------------------
1200
   * Cleanup WFS connection info.
1201
   * __TODO__ For now we flush everything, but we should try to cache some stuff
1202
   * ------------------------------------------------------------------ */
1203
  /* __TODO__ unlink()  .gml file and OGR's schema file if they exist */
1204
  /* unlink( */
1205
1206
  msFreeWFSLayerInfo(lp->wfslayerinfo);
1207
  lp->wfslayerinfo = NULL;
1208
1209
  return MS_SUCCESS;
1210
1211
#else
1212
  /* ------------------------------------------------------------------
1213
   * WFS CONNECTION Support not included...
1214
   * ------------------------------------------------------------------ */
1215
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
1216
0
             "msWFSLayerClose()");
1217
0
  return (MS_FAILURE);
1218
1219
0
#endif /* USE_WFS_LYR */
1220
0
}
1221
1222
/**********************************************************************
1223
 *                          msWFSExecuteGetFeature()
1224
 * Returns the temporary gml file name. User shpuld free the return string.
1225
 **********************************************************************/
1226
0
char *msWFSExecuteGetFeature(layerObj *lp) {
1227
#ifdef USE_WFS_LYR
1228
  char *gmltmpfile = NULL;
1229
  msWFSLayerInfo *psInfo = NULL;
1230
1231
  if (lp == NULL || lp->connectiontype != MS_WFS)
1232
    return NULL;
1233
1234
  msWFSLayerOpen(lp, NULL, NULL);
1235
  psInfo = (msWFSLayerInfo *)lp->wfslayerinfo;
1236
  if (psInfo && psInfo->pszGMLFilename)
1237
    gmltmpfile = msStrdup(psInfo->pszGMLFilename);
1238
  msWFSLayerClose(lp);
1239
1240
  return gmltmpfile;
1241
1242
#else
1243
  /* ------------------------------------------------------------------
1244
   * WFS CONNECTION Support not included...
1245
   * ------------------------------------------------------------------ */
1246
0
  msSetError(MS_WFSCONNERR, "WFS CLIENT CONNECTION support is not available.",
1247
0
             "msExecuteWFSGetFeature()");
1248
0
  return NULL;
1249
1250
0
#endif /* USE_WFS_LYR */
1251
0
}
1252
1253
63
int msWFSLayerInitializeVirtualTable(layerObj *layer) {
1254
63
  assert(layer != NULL);
1255
63
  assert(layer->vtable != NULL);
1256
1257
63
  layer->vtable->LayerInitItemInfo = msWFSLayerInitItemInfo;
1258
63
  layer->vtable->LayerFreeItemInfo = msOGRLayerFreeItemInfo; /* yes, OGR */
1259
63
  layer->vtable->LayerOpen = msWFSLayerOpenVT;
1260
63
  layer->vtable->LayerIsOpen = msWFSLayerIsOpen;
1261
63
  layer->vtable->LayerWhichShapes = msWFSLayerWhichShapes;
1262
63
  layer->vtable->LayerNextShape = msWFSLayerNextShape;
1263
  /* layer->vtable->LayerResultsGetShape = msWFSLayerResultGetShape; */
1264
  /* layer->vtable->LayerGetShapeCount, use default */
1265
63
  layer->vtable->LayerGetShape = msWFSLayerGetShape;
1266
63
  layer->vtable->LayerClose = msWFSLayerClose;
1267
63
  layer->vtable->LayerGetItems = msWFSLayerGetItems;
1268
63
  layer->vtable->LayerGetExtent = msWFSLayerGetExtent;
1269
  /* layer->vtable->LayerGetAutoStyle, use default */
1270
  /* layer->vtable->LayerApplyFilterToLayer, use default */
1271
  /* layer->vtable->LayerCloseConnection, use default */
1272
63
  layer->vtable->LayerSetTimeFilter = msLayerMakePlainTimeFilter;
1273
  /* layer->vtable->LayerCreateItems, use default */
1274
  /* layer->vtable->LayerGetNumFeatures, use default */
1275
  /* layer->vtable->LayerGetAutoProjection, use default*/
1276
1277
63
  return MS_SUCCESS;
1278
63
}