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