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