1 /*****************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  Implementation of WMS CONNECTIONTYPE - client to WMS servers
6  * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7  *
8  *****************************************************************************
9  * Copyright (c) 2001-2004, 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  * DEALINGS IN THE SOFTWARE.
28  *****************************************************************************/
29 
30 #include "mapserver.h"
31 #include "maperror.h"
32 #include "mapogcsld.h"
33 #include "mapows.h"
34 
35 #include <time.h>
36 #include <ctype.h>
37 
38 #if defined(_WIN32) && !defined(__CYGWIN__)
39 #include <process.h>
40 #include <stdio.h>
41 #endif
42 
43 #include "cpl_vsi.h"
44 
45 /**********************************************************************
46  *                          msInitWmsParamsObj()
47  *
48  **********************************************************************/
msInitWmsParamsObj(wmsParamsObj * wmsparams)49 int msInitWmsParamsObj(wmsParamsObj *wmsparams)
50 {
51   wmsparams->onlineresource = NULL;
52   wmsparams->params = msCreateHashTable();
53   wmsparams->numparams=0;
54   wmsparams->httpcookiedata = NULL;
55 
56   return MS_SUCCESS;
57 }
58 
59 /**********************************************************************
60  *                          msFreeWmsParamsObj()
61  *
62  * Frees the contents of the object, but not the object itself.
63  **********************************************************************/
msFreeWmsParamsObj(wmsParamsObj * wmsparams)64 void msFreeWmsParamsObj(wmsParamsObj *wmsparams)
65 {
66   msFree(wmsparams->onlineresource);
67   wmsparams->onlineresource = NULL;
68 
69   msFreeHashTable(wmsparams->params);
70   wmsparams->params = NULL;
71 
72   msFree(wmsparams->httpcookiedata);
73 
74   wmsparams->numparams=0;
75 }
76 
77 /**********************************************************************
78  *                          msSetWMSParamString()
79  *
80  **********************************************************************/
81 
82 #ifdef USE_WMS_LYR
msSetWMSParamString(wmsParamsObj * psWMSParams,const char * name,const char * value,int urlencode,int nVersion)83 static int msSetWMSParamString(wmsParamsObj *psWMSParams,
84                                const char *name, const char * value,
85                                int urlencode, int nVersion)
86 {
87   if (urlencode) {
88     char *pszTmp;
89 
90     /*
91      *  Special case handling for characters the WMS specification
92      *  says should not be encoded, when they occur in certain
93      *  parameters.
94      *
95      *  Note: WMS 1.3 removes SRS and FORMAT from the set of
96      *        exceptional cases, but renames SRS as CRS in any case.
97      */
98     if( strcmp(name,"LAYERS") == 0 ||
99         strcmp(name,"STYLES") == 0 ||
100         strcmp(name,"BBOX") == 0 ) {
101       pszTmp = msEncodeUrlExcept(value,',');
102     } else if ( strcmp(name,"SRS") == 0 ) {
103       pszTmp = msEncodeUrlExcept(value,':');
104     } else if ( nVersion < OWS_1_3_0 && strcmp(name,"FORMAT") == 0 ) {
105       pszTmp = msEncodeUrlExcept(value,'/');
106     } else {
107       pszTmp = msEncodeUrl(value);
108     }
109 
110     msInsertHashTable(psWMSParams->params, name, pszTmp);
111     msFree(pszTmp);
112   } else {
113     msInsertHashTable(psWMSParams->params, name, value);
114   }
115   psWMSParams->numparams++;
116 
117   return MS_SUCCESS;
118 }
119 #endif /* def USE_WMS_LYR */
120 
121 /**********************************************************************
122  *                          msSetWMSParamInt()
123  *
124  **********************************************************************/
125 
126 #ifdef USE_WMS_LYR
msSetWMSParamInt(wmsParamsObj * wmsparams,const char * name,int value)127 static int msSetWMSParamInt(wmsParamsObj *wmsparams,
128                             const char *name, int value)
129 {
130   char szBuf[100];
131 
132   snprintf(szBuf, sizeof(szBuf), "%d", value);
133   msInsertHashTable(wmsparams->params, name, szBuf);
134   wmsparams->numparams++;
135 
136   return MS_SUCCESS;
137 }
138 #endif /* def USE_WMS_LYR */
139 
140 /**********************************************************************
141  *                          msBuildWMSParamsUrl()
142  *
143  **********************************************************************/
msBuildURLFromWMSParams(wmsParamsObj * wmsparams)144 static char *msBuildURLFromWMSParams(wmsParamsObj *wmsparams)
145 {
146   const char *key, *value;
147   size_t bufferSize = 0;
148   int nLen;
149   char *pszURL;
150 
151   /* Compute size required for URL buffer
152    */
153   nLen = strlen(wmsparams->onlineresource) + 3;
154 
155   key = msFirstKeyFromHashTable(wmsparams->params);
156   while (key != NULL) {
157     value = msLookupHashTable(wmsparams->params, key);
158     nLen += strlen(key) + strlen(value) + 2;
159 
160     key = msNextKeyFromHashTable(wmsparams->params, key);
161   }
162 
163   bufferSize = nLen+1;
164   pszURL = (char*)msSmallMalloc(bufferSize);
165 
166   /* Start with the onlineresource value and append trailing '?' or '&'
167    * if missing.
168    */
169   strcpy(pszURL, wmsparams->onlineresource);
170   if (strchr(pszURL, '?') == NULL)
171     strcat(pszURL, "?");
172   else {
173     char *c;
174     c = pszURL+strlen(pszURL)-1;
175     if (*c != '?' && *c != '&')
176       strcpy(c+1, "&");
177   }
178 
179   /* Now add all the parameters
180    */
181   nLen = strlen(pszURL);
182   key = msFirstKeyFromHashTable(wmsparams->params);
183   while (key != NULL) {
184     value = msLookupHashTable(wmsparams->params, key);
185     snprintf(pszURL+nLen, bufferSize-nLen, "%s=%s&", key, value);
186     nLen += strlen(key) + strlen(value) + 2;
187     key = msNextKeyFromHashTable(wmsparams->params, key);
188   }
189 
190   /* Get rid of trailing '&'*/
191   pszURL[nLen-1] = '\0';
192 
193 
194   return pszURL;
195 }
196 
197 
198 
199 
200 #ifdef USE_WMS_LYR
201 /**********************************************************************
202  *                          msBuildWMSLayerURLBase()
203  *
204  * Build the base of a GetMap or GetFeatureInfo URL using metadata.
205  * The parameters to set are:
206  *   VERSION
207  *   LAYERS
208  *   FORMAT
209  *   TRANSPARENT
210  *   STYLES
211  *   QUERY_LAYERS (for queriable layers only)
212  *
213  * Returns a reference to a newly allocated string that should be freed
214  * by the caller.
215  **********************************************************************/
msBuildWMSLayerURLBase(mapObj * map,layerObj * lp,wmsParamsObj * psWMSParams,int nRequestType)216 static int msBuildWMSLayerURLBase(mapObj *map, layerObj *lp,
217                                   wmsParamsObj *psWMSParams, int nRequestType)
218 {
219   const char *pszOnlineResource, *pszVersion, *pszName, *pszFormat;
220   const char *pszFormatList, *pszStyle, /* *pszStyleList,*/ *pszTime;
221   const char *pszBgColor, *pszTransparent;
222   const char *pszSLD=NULL, *pszStyleSLDBody=NULL, *pszVersionKeyword=NULL;
223   const char *pszSLDBody=NULL, *pszSLDURL = NULL;
224   char *pszSLDGenerated = NULL;
225   int nVersion=OWS_VERSION_NOTSET;
226 
227   /* If lp->connection is not set then use wms_onlineresource metadata */
228   pszOnlineResource = lp->connection;
229   if (pszOnlineResource == NULL)
230     pszOnlineResource = msOWSLookupMetadata(&(lp->metadata),
231                                             "MO", "onlineresource");
232 
233   pszVersion =        msOWSLookupMetadata(&(lp->metadata), "MO", "server_version");
234   pszName =           msOWSLookupMetadata(&(lp->metadata), "MO", "name");
235   pszFormat =         msOWSLookupMetadata(&(lp->metadata), "MO", "format");
236   pszFormatList =     msOWSLookupMetadata(&(lp->metadata), "MO", "formatlist");
237   pszStyle =          msOWSLookupMetadata(&(lp->metadata), "MO", "style");
238   /*pszStyleList =      msOWSLookupMetadata(&(lp->metadata), "MO", "stylelist");*/
239   pszTime =           msOWSLookupMetadata(&(lp->metadata), "MO", "time");
240   pszSLDBody =        msOWSLookupMetadata(&(lp->metadata), "MO", "sld_body");
241   pszSLDURL =         msOWSLookupMetadata(&(lp->metadata), "MO", "sld_url");
242   pszBgColor =        msOWSLookupMetadata(&(lp->metadata), "MO", "bgcolor");
243   pszTransparent =    msOWSLookupMetadata(&(lp->metadata), "MO", "transparent");
244 
245   if (pszOnlineResource==NULL || pszVersion==NULL || pszName==NULL) {
246     msSetError(MS_WMSCONNERR,
247                "One of wms_onlineresource, wms_server_version, wms_name "
248                "metadata is missing in layer %s.  "
249                "Please either provide a valid CONNECTION URL, or provide "
250                "those values in the layer's metadata.\n",
251                "msBuildWMSLayerURLBase()", lp->name);
252     return MS_FAILURE;
253   }
254 
255   psWMSParams->onlineresource = msStrdup(pszOnlineResource);
256 
257   if (strncmp(pszVersion, "1.0.7", 5) < 0)
258     pszVersionKeyword = "WMTVER";
259   else
260     pszVersionKeyword = "VERSION";
261 
262   nVersion = msOWSParseVersionString(pszVersion);
263   /* WMS 1.0.8 is really just 1.1.0 */
264   if (nVersion == OWS_1_0_8) nVersion = OWS_1_1_0;
265 
266   msSetWMSParamString(psWMSParams, pszVersionKeyword, pszVersion, MS_FALSE, nVersion);
267   msSetWMSParamString(psWMSParams, "SERVICE", "WMS",     MS_FALSE, nVersion);
268   msSetWMSParamString(psWMSParams, "LAYERS",  pszName,   MS_TRUE, nVersion);
269 
270   if (pszFormat==NULL && pszFormatList==NULL) {
271     msSetError(MS_WMSCONNERR,
272                "At least wms_format or wms_formatlist is required for "
273                "layer %s.  "
274                "Please either provide a valid CONNECTION URL, or provide "
275                "those values in the layer's metadata.\n",
276                "msBuildWMSLayerURLBase()", lp->name);
277     return MS_FAILURE;
278   }
279 
280   if (pszFormat != NULL) {
281     msSetWMSParamString(psWMSParams, "FORMAT",  pszFormat, MS_TRUE, nVersion);
282   } else {
283     /* Look for the first format in list that matches */
284     char **papszTok;
285     int i, n;
286     papszTok = msStringSplit(pszFormatList, ',', &n);
287 
288     for(i=0; pszFormat==NULL && i<n; i++) {
289       if (0
290 #if defined USE_PNG
291           || strcasecmp(papszTok[i], "PNG")
292           || strcasecmp(papszTok[i], "image/png")
293 #endif
294 #if defined USE_JPEG
295           || strcasecmp(papszTok[i], "JPEG")
296           || strcasecmp(papszTok[i], "image/jpeg")
297 #endif
298          ) {
299         pszFormat = papszTok[i];
300       }
301     }
302 
303     if (pszFormat) {
304       msSetWMSParamString(psWMSParams, "FORMAT",  pszFormat, MS_TRUE, nVersion);
305       msFreeCharArray(papszTok, n);
306     } else {
307       msSetError(MS_WMSCONNERR,
308                  "Could not find a format that matches supported input "
309                  "formats in wms_formatlist metdata in layer %s.  "
310                  "Please either provide a valid CONNECTION URL, or "
311                  "provide the required layer metadata.\n",
312                  "msBuildWMSLayerURLBase()", lp->name);
313       msFreeCharArray(papszTok, n);
314       return MS_FAILURE;
315     }
316   }
317 
318   if (pszStyle==NULL) {
319     /* When no style is selected, use "" which is a valid default. */
320     pszStyle = "";
321   } else {
322     /* Was a wms_style_..._sld URL provided? */
323     char szBuf[100];
324     snprintf(szBuf, sizeof(szBuf), "style_%.80s_sld", pszStyle);
325     pszSLD = msOWSLookupMetadata(&(lp->metadata), "MO", szBuf);
326     snprintf(szBuf, sizeof(szBuf), "style_%.80s_sld_body", pszStyle);
327     pszStyleSLDBody = msOWSLookupMetadata(&(lp->metadata), "MO", szBuf);
328 
329     if (pszSLD || pszStyleSLDBody) {
330       /* SLD URL is set.  If this defn. came from a map context then */
331       /* the style name may just be an internal name: "Style{%d}" if */
332       /* that's the case then we should not pass this name via the URL */
333       if (strncmp(pszStyle, "Style{", 6) == 0)
334         pszStyle = "";
335     }
336   }
337 
338   /*  set STYLE parameter no matter what, even if it's empty (i.e. "STYLES=")
339    *  GetLegendGraphic doesn't support multiple styles and is named STYLE
340    */
341   if (nRequestType == WMS_GETLEGENDGRAPHIC) {
342     msSetWMSParamString(psWMSParams, "STYLE", pszStyle, MS_TRUE, nVersion);
343   } else {
344     msSetWMSParamString(psWMSParams, "STYLES", pszStyle, MS_TRUE, nVersion);
345   }
346 
347   if (pszSLD != NULL) {
348     /* Only SLD is set */
349     msSetWMSParamString(psWMSParams, "SLD",    pszSLD,   MS_TRUE, nVersion);
350   } else if (pszStyleSLDBody != NULL) {
351     /* SLDBODY are set */
352     msSetWMSParamString(psWMSParams, "SLD_BODY", pszStyleSLDBody, MS_TRUE, nVersion);
353   }
354 
355   if (msIsLayerQueryable(lp)) {
356     msSetWMSParamString(psWMSParams, "QUERY_LAYERS", pszName, MS_TRUE, nVersion);
357   }
358   if (pszTime && strlen(pszTime) > 0) {
359     msSetWMSParamString(psWMSParams, "TIME",   pszTime,  MS_TRUE, nVersion);
360   }
361 
362   /* if  the metadata wms_sld_body is set to AUTO, we generate
363    * the sld based on classes found in the map file and send
364    * it in the URL. If diffrent from AUTO, we are assuming that
365    * it is a valid sld.
366    */
367   if (pszSLDBody) {
368     if (strcasecmp(pszSLDBody, "AUTO") == 0) {
369       if (pszVersion && strncmp(pszVersion, "1.3.0", 5) == 0)
370         pszSLDGenerated = msSLDGenerateSLD(map, lp->index, "1.1.0");
371       else
372         pszSLDGenerated = msSLDGenerateSLD(map, lp->index, NULL);
373 
374       if (pszSLDGenerated) {
375         msSetWMSParamString(psWMSParams, "SLD_BODY",
376                             pszSLDGenerated, MS_TRUE, nVersion);
377         free(pszSLDGenerated);
378       }
379     } else {
380       msSetWMSParamString(psWMSParams, "SLD_BODY", pszSLDBody, MS_TRUE, nVersion);
381     }
382 
383   }
384 
385   if (pszSLDURL) {
386     msSetWMSParamString(psWMSParams, "SLD", pszSLDURL, MS_TRUE, nVersion);
387   }
388 
389   if (pszBgColor) {
390     msSetWMSParamString(psWMSParams, "BGCOLOR", pszBgColor, MS_TRUE, nVersion);
391   }
392 
393   if (pszTransparent) {
394     msSetWMSParamString(psWMSParams, "TRANSPARENT", pszTransparent, MS_TRUE, nVersion);
395   } else {
396     msSetWMSParamString(psWMSParams, "TRANSPARENT", "TRUE", MS_TRUE, nVersion);
397   }
398 
399   return MS_SUCCESS;
400 }
401 
402 #endif /* USE_WMS_LYR */
403 
404 
405 /**********************************************************************
406  *                          msBuildWMSLayerURL()
407  *
408  * Build a GetMap or GetFeatureInfo URL.
409  *
410  * Returns a reference to a newly allocated string that should be freed
411  * by the caller.
412  **********************************************************************/
413 
414 static int
msBuildWMSLayerURL(mapObj * map,layerObj * lp,int nRequestType,int nClickX,int nClickY,int nFeatureCount,const char * pszInfoFormat,rectObj * bbox_ret,int * width_ret,int * height_ret,wmsParamsObj * psWMSParams)415 msBuildWMSLayerURL(mapObj *map, layerObj *lp, int nRequestType,
416                    int nClickX, int nClickY, int nFeatureCount,
417                    const char *pszInfoFormat, rectObj *bbox_ret,
418                    int *width_ret, int *height_ret,
419                    wmsParamsObj *psWMSParams)
420 {
421 #ifdef USE_WMS_LYR
422   char *pszEPSG = NULL;
423   const char *pszVersion, *pszRequestParam, *pszExceptionsParam,
424         *pszSrsParamName="SRS", *pszLayer=NULL, *pszQueryLayers=NULL,
425         *pszUseStrictAxisOrder;
426   rectObj bbox;
427   int bbox_width = map->width, bbox_height = map->height;
428   int nVersion=OWS_VERSION_NOTSET;
429   int bUseStrictAxisOrder = MS_FALSE; /* this is the assumption up to 1.1.0 */
430   int bFlipAxisOrder = MS_FALSE;
431   const char *pszTmp;
432   int bIsEssential = MS_FALSE;
433 
434   if (lp->connectiontype != MS_WMS) {
435     msSetError(MS_WMSCONNERR, "Call supported only for CONNECTIONTYPE WMS",
436                "msBuildWMSLayerURL()");
437     return MS_FAILURE;
438   }
439 
440   /* ------------------------------------------------------------------
441    * Find out request version
442    * ------------------------------------------------------------------ */
443   if (lp->connection == NULL ||
444       ((pszVersion = strstr(lp->connection, "VERSION=")) == NULL &&
445        (pszVersion = strstr(lp->connection, "version=")) == NULL &&
446        (pszVersion = strstr(lp->connection, "WMTVER=")) == NULL &&
447        (pszVersion = strstr(lp->connection, "wmtver=")) == NULL ) ) {
448     /* CONNECTION missing or seems incomplete... try to build from metadata */
449     if (msBuildWMSLayerURLBase(map, lp, psWMSParams, nRequestType) != MS_SUCCESS)
450       return MS_FAILURE;  /* An error already produced. */
451 
452     /* If we received MS_SUCCESS then version must have been set */
453     pszVersion = msLookupHashTable(psWMSParams->params, "VERSION");
454     if (pszVersion ==NULL)
455       pszVersion = msLookupHashTable(psWMSParams->params, "WMTVER");
456 
457     nVersion = msOWSParseVersionString(pszVersion);
458   } else {
459     /* CONNECTION string seems complete, start with that. */
460     char *pszDelimiter;
461     psWMSParams->onlineresource = msStrdup(lp->connection);
462 
463     /* Fetch version info */
464     pszVersion = strchr(pszVersion, '=')+1;
465     pszDelimiter = strchr(pszVersion, '&');
466     if (pszDelimiter != NULL)
467       *pszDelimiter = '\0';
468     nVersion = msOWSParseVersionString(pszVersion);
469     if (pszDelimiter != NULL)
470       *pszDelimiter = '&';
471   }
472 
473   switch (nVersion) {
474     case OWS_1_0_8:
475       nVersion = OWS_1_1_0;    /* 1.0.8 == 1.1.0 */
476       break;
477     case OWS_1_0_0:
478     case OWS_1_0_1:
479     case OWS_1_0_7:
480     case OWS_1_1_0:
481     case OWS_1_1_1:
482       /* All is good, this is a supported version. */
483       break;
484     case OWS_1_3_0:
485       /* 1.3.0 introduces a few changes... */
486       pszSrsParamName = "CRS";
487       bUseStrictAxisOrder = MS_TRUE; /* this is the assumption for 1.3.0 */
488       break;
489     default:
490       /* Not a supported version */
491       msSetError(MS_WMSCONNERR, "MapServer supports only WMS 1.0.0 to 1.3.0 (please verify the VERSION parameter in the connection string).", "msBuildWMSLayerURL()");
492       return MS_FAILURE;
493   }
494 
495   /* ------------------------------------------------------------------
496    * For GetFeatureInfo requests, make sure QUERY_LAYERS is included
497    * ------------------------------------------------------------------ */
498   if  (nRequestType == WMS_GETFEATUREINFO &&
499        strstr(psWMSParams->onlineresource, "QUERY_LAYERS=") == NULL &&
500        strstr(psWMSParams->onlineresource, "query_layers=") == NULL &&
501        msLookupHashTable(psWMSParams->params, "QUERY_LAYERS") == NULL) {
502     pszQueryLayers = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
503 
504     if (pszQueryLayers == NULL) {
505       msSetError(MS_WMSCONNERR, "wms_name not set or WMS Connection String must contain the QUERY_LAYERS parameter to support GetFeatureInfo requests (with name in uppercase).", "msBuildWMSLayerURL()");
506       return MS_FAILURE;
507     }
508   }
509 
510   /* ------------------------------------------------------------------
511    * For GetLegendGraphic requests, make sure LAYER is included
512    * ------------------------------------------------------------------ */
513   if  (nRequestType == WMS_GETLEGENDGRAPHIC &&
514        strstr(psWMSParams->onlineresource, "LAYER=") == NULL &&
515        strstr(psWMSParams->onlineresource, "layer=") == NULL &&
516        msLookupHashTable(psWMSParams->params, "LAYER") == NULL) {
517     pszLayer = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
518 
519     if (pszLayer == NULL) {
520       msSetError(MS_WMSCONNERR, "wms_name not set or WMS Connection String must contain the LAYER parameter to support GetLegendGraphic requests (with name in uppercase).", "msBuildWMSLayerURL()");
521       return MS_FAILURE;
522     }
523   }
524 
525   /* ------------------------------------------------------------------
526    * Figure the SRS we'll use for the request.
527    * - Fetch the map SRS (if it's EPSG)
528    * - Check if map SRS is listed in layer wms_srs metadata
529    * - If map SRS is valid for this layer then use it
530    * - Otherwise request layer in its default SRS and we'll reproject later
531    * ------------------------------------------------------------------ */
532   msOWSGetEPSGProj(&(map->projection),NULL, NULL, MS_TRUE, &pszEPSG);
533   if ( pszEPSG &&
534       (strncasecmp(pszEPSG, "EPSG:", 5) == 0 ||
535        strncasecmp(pszEPSG, "AUTO:", 5) == 0) ) {
536     const char *pszFound;
537     char *pszLyrEPSG;
538     int nLen;
539     char *pszPtr = NULL;
540 
541     /* If it's an AUTO projection then keep only id and strip off  */
542     /* the parameters for now (we'll restore them at the end) */
543     if (strncasecmp(pszEPSG, "AUTO:", 5) == 0) {
544       if ((pszPtr = strchr(pszEPSG, ',')))
545         *pszPtr = '\0';
546     }
547 
548     nLen = strlen(pszEPSG);
549 
550     msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "MO", MS_FALSE, &pszLyrEPSG);
551 
552     if (pszLyrEPSG == NULL ||
553         (pszFound = strstr(pszLyrEPSG, pszEPSG)) == NULL ||
554         ! ((*(pszFound+nLen) == '\0') || isspace(*(pszFound+nLen))) ) {
555       /* Not found in Layer's list of SRS (including projection object) */
556       free(pszEPSG);
557       pszEPSG = NULL;
558     }
559     msFree(pszLyrEPSG);
560     if (pszEPSG && pszPtr)
561       *pszPtr = ',';  /* Restore full AUTO:... definition */
562   }
563 
564   if (pszEPSG == NULL) {
565       msOWSGetEPSGProj(&(lp->projection), &(lp->metadata),"MO", MS_TRUE, &pszEPSG);
566       if( pszEPSG == NULL || (strncasecmp(pszEPSG, "EPSG:", 5) != 0 && strncasecmp(pszEPSG, "AUTO:", 5) != 0) )  {
567         msSetError(MS_WMSCONNERR, "Layer must have an EPSG or AUTO projection code (in its PROJECTION object or wms_srs metadata)", "msBuildWMSLayerURL()");
568         msFree(pszEPSG);
569         return MS_FAILURE;
570       }
571   }
572 
573   /* ------------------------------------------------------------------
574    * For an AUTO projection, set the Units,lon0,lat0 if not already set
575    * ------------------------------------------------------------------ */
576   if (strncasecmp(pszEPSG, "AUTO:", 5) == 0 &&
577       strchr(pszEPSG, ',') == NULL) {
578     pointObj oPoint;
579     char *pszNewEPSG;
580 
581     /* Use center of map view for lon0,lat0 */
582     oPoint.x = (map->extent.minx + map->extent.maxx)/2.0;
583     oPoint.y = (map->extent.miny + map->extent.maxy)/2.0;
584     msProjectPoint(&(map->projection), &(map->latlon), &oPoint);
585 
586     pszNewEPSG = (char*)msSmallMalloc(101*sizeof(char));
587 
588     snprintf(pszNewEPSG, 100, "%s,9001,%.16g,%.16g",
589              pszEPSG, oPoint.x, oPoint.y);
590     pszNewEPSG[100]='\0';
591     free(pszEPSG);
592     pszEPSG=pszNewEPSG;
593   }
594 
595   /*
596    * Work out whether we'll be wanting to flip the axis order for the request
597    */
598   pszUseStrictAxisOrder = msOWSLookupMetadata(&(lp->metadata), "MO", "strict_axis_order");
599   if (pszUseStrictAxisOrder != NULL) {
600     if (strncasecmp(pszUseStrictAxisOrder, "1", 1) == 0 ||
601         strncasecmp(pszUseStrictAxisOrder, "true", 4) == 0) {
602       bUseStrictAxisOrder = MS_TRUE;
603     } else if (strncasecmp(pszUseStrictAxisOrder, "0", 1) == 0 ||
604         strncasecmp(pszUseStrictAxisOrder, "false", 5) == 0) {
605       bUseStrictAxisOrder = MS_FALSE;
606     }
607   }
608   if (bUseStrictAxisOrder == MS_TRUE && pszEPSG &&
609       strncasecmp(pszEPSG, "EPSG:", 5) == 0 &&
610       msIsAxisInverted(atoi(pszEPSG + 5))) {
611     bFlipAxisOrder = MS_TRUE;
612   }
613 
614   /* ------------------------------------------------------------------
615    * Set layer SRS.
616    * ------------------------------------------------------------------ */
617   /* No need to set lp->proj if it's already set to the right EPSG code */
618   {
619     char* pszEPSGCodeFromLayer = NULL;
620     msOWSGetEPSGProj(&(lp->projection), NULL, "MO", MS_TRUE, &pszEPSGCodeFromLayer);
621     if (pszEPSGCodeFromLayer == NULL || strcasecmp(pszEPSG, pszEPSGCodeFromLayer) != 0) {
622       char *ows_srs = NULL;
623       msOWSGetEPSGProj(NULL, &(lp->metadata), "MO", MS_FALSE, &ows_srs);
624       /* no need to set lp->proj if it is already set and there is only
625       one item in the _srs metadata for this layer - we will assume
626       the projection block matches the _srs metadata (the search for ' '
627       in ows_srs is a test to see if there are multiple EPSG: codes) */
628       if( lp->projection.numargs == 0 || ows_srs == NULL || (strchr(ows_srs,' ') != NULL) ) {
629         if (strncasecmp(pszEPSG, "EPSG:", 5) == 0) {
630           char szProj[20];
631           snprintf(szProj, sizeof(szProj), "init=epsg:%s", pszEPSG+5);
632           if (msLoadProjectionString(&(lp->projection), szProj) != 0) {
633             msFree(pszEPSGCodeFromLayer);
634             msFree(ows_srs);
635             return MS_FAILURE;
636           }
637         } else {
638           if (msLoadProjectionString(&(lp->projection), pszEPSG) != 0) {
639             msFree(pszEPSGCodeFromLayer);
640             msFree(ows_srs);
641             return MS_FAILURE;
642           }
643         }
644       }
645       msFree(ows_srs);
646     }
647     msFree(pszEPSGCodeFromLayer);
648   }
649 
650   /* ------------------------------------------------------------------
651    * Adjust for MapServer EXTENT being center of pixel and WMS BBOX being
652    * edge of pixel (#2843).
653    * ------------------------------------------------------------------ */
654   bbox = map->extent;
655 
656   bbox.minx -= map->cellsize * 0.5;
657   bbox.maxx += map->cellsize * 0.5;
658   bbox.miny -= map->cellsize * 0.5;
659   bbox.maxy += map->cellsize * 0.5;
660 
661   /* -------------------------------------------------------------------- */
662   /*      Reproject if needed.                                            */
663   /* -------------------------------------------------------------------- */
664   if (msProjectionsDiffer(&(map->projection), &(lp->projection))) {
665     msProjectRect(&(map->projection), &(lp->projection), &bbox);
666 
667     /* -------------------------------------------------------------------- */
668     /*      Sometimes our remote WMS only accepts square pixel              */
669     /*      requests.  If this is the case adjust adjust the number of      */
670     /*      pixels or lines in the request so that the pixels are           */
671     /*      square.                                                         */
672     /* -------------------------------------------------------------------- */
673     {
674       const char *nonsquare_ok =
675         msOWSLookupMetadata(&(lp->metadata),
676                             "MO", "nonsquare_ok");
677 
678       /* assume nonsquare_ok is false */
679       if( nonsquare_ok != NULL
680           && (strcasecmp(nonsquare_ok,"no") == 0
681               || strcasecmp(nonsquare_ok,"false") == 0) ) {
682         double cellsize_x = (bbox.maxx-bbox.minx) / bbox_width;
683         double cellsize_y = (bbox.maxy-bbox.miny) / bbox_height;
684 
685         if( cellsize_x < cellsize_y * 0.999999 ) {
686           int new_bbox_height =
687             ceil((cellsize_y/cellsize_x) * bbox_height);
688 
689           if (lp->debug)
690             msDebug("NONSQUARE_OK=%s, adjusted HEIGHT from %d to %d to equalize cellsize at %g.\n",
691                     nonsquare_ok,
692                     bbox_height, new_bbox_height, cellsize_x );
693           bbox_height = new_bbox_height;
694         } else if( cellsize_y < cellsize_x * 0.999999 ) {
695           int new_bbox_width =
696             ceil((cellsize_x/cellsize_y) * bbox_width);
697 
698           if (lp->debug)
699             msDebug("NONSQUARE_OK=%s, adjusted WIDTH from %d to %d to equalize cellsize at %g.\n",
700                     nonsquare_ok,
701                     bbox_width, new_bbox_width, cellsize_y );
702           bbox_width = new_bbox_width;
703         } else {
704           if (lp->debug)
705             msDebug("NONSQUARE_OK=%s, but cellsize was already square - no change.\n",
706                     nonsquare_ok );
707         }
708       }
709     }
710   }
711 
712   /* -------------------------------------------------------------------- */
713   /*      If the layer has predefined extents, and a predefined           */
714   /*      projection that matches the request projection, then            */
715   /*      consider restricting the BBOX to match the limits.              */
716   /* -------------------------------------------------------------------- */
717   if( bbox_width != 0 ) {
718     char *ows_srs;
719     rectObj  layer_rect;
720 
721     msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "MO", MS_FALSE, &ows_srs);
722 
723     if( ows_srs && strchr(ows_srs,' ') == NULL
724         && msOWSGetLayerExtent( map, lp, "MO", &layer_rect) == MS_SUCCESS ) {
725       /* fulloverlap */
726       if( msRectContained( &bbox, &layer_rect ) ) {
727         /* no changes */
728       }
729 
730       /* no overlap */
731       else if( !msRectOverlap( &layer_rect, &bbox ) ) {
732         bbox_width = 0;
733         bbox_height = 0;
734       }
735 
736       else {
737         double cellsize_x = (bbox.maxx-bbox.minx) / bbox_width;
738         double cellsize_y = (bbox.maxy-bbox.miny) / bbox_height;
739         double cellsize = MS_MIN(cellsize_x,cellsize_y);
740 
741         msRectIntersect( &bbox, &layer_rect );
742 
743         bbox_width = round((bbox.maxx - bbox.minx) / cellsize);
744         bbox_height = round((bbox.maxy - bbox.miny) / cellsize);
745 
746         /* Force going through the resampler if we're going to receive a clipped BBOX (#4931) */
747         if(msLayerGetProcessingKey(lp, "RESAMPLE") == NULL) {
748           msLayerSetProcessingKey(lp, "RESAMPLE", "nearest");
749         }
750       }
751     }
752     msFree(ows_srs);
753   }
754 
755   /* -------------------------------------------------------------------- */
756   /*      Potentially return the bbox.                                    */
757   /* -------------------------------------------------------------------- */
758   if (bbox_ret != NULL)
759     *bbox_ret = bbox;
760 
761   if( width_ret != NULL )
762     *width_ret = bbox_width;
763 
764   if( height_ret != NULL )
765     *height_ret = bbox_height;
766 
767   /* ------------------------------------------------------------------
768    * Build the request URL.
769    * At this point we set only the following parameters for GetMap:
770    *   REQUEST
771    *   SRS (or CRS)
772    *   BBOX
773    *
774    * And for GetFeatureInfo:
775    *   X (I for 1.3.0)
776    *   Y (J for 1.3.0)
777    *   INFO_FORMAT
778    *   FEATURE_COUNT (only if nFeatureCount > 0)
779    *
780    * The connection string should contain all other required params,
781    * including:
782    *   VERSION
783    *   LAYERS
784    *   FORMAT
785    *   TRANSPARENT
786    *   STYLES
787    *   QUERY_LAYERS (for queryable layers only)
788    * ------------------------------------------------------------------ */
789 
790   /* ------------------------------------------------------------------
791    * Sometimes a requested layer is essential for the map, so if the
792    * request fails or an error is delivered, the map has not to be drawn
793    * ------------------------------------------------------------------ */
794   if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
795                                     "MO", "essential")) != NULL) {
796     if( strcasecmp(pszTmp,"true") == 0
797         || strcasecmp(pszTmp,"on") == 0
798         || strcasecmp(pszTmp,"yes") == 0 )
799       bIsEssential = MS_TRUE;
800     else
801       bIsEssential = atoi(pszTmp);
802   }
803 
804   if (nRequestType == WMS_GETFEATUREINFO) {
805     char szBuf[100] = "";
806 
807     if (nVersion >= OWS_1_0_7)
808       pszRequestParam = "GetFeatureInfo";
809     else
810       pszRequestParam = "feature_info";
811 
812     if (nVersion >= OWS_1_3_0)
813       pszExceptionsParam = "XML";
814     else if (nVersion >= OWS_1_1_0) /* 1.1.0 to 1.1.0 */
815       pszExceptionsParam = "application/vnd.ogc.se_xml";
816     else if (nVersion > OWS_1_0_0)  /* 1.0.1 to 1.0.7 */
817       pszExceptionsParam = "SE_XML";
818     else
819       pszExceptionsParam = "WMS_XML";
820 
821     msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE, nVersion);
822     msSetWMSParamInt(   psWMSParams, "WIDTH",   bbox_width);
823     msSetWMSParamInt(   psWMSParams, "HEIGHT",  bbox_height);
824 
825     msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE, nVersion);
826 
827     if (bFlipAxisOrder == MS_TRUE) {
828       snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g",
829                bbox.miny, bbox.minx, bbox.maxy, bbox.maxx);
830     } else {
831       snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g",
832                bbox.minx, bbox.miny, bbox.maxx, bbox.maxy);
833     }
834     msSetWMSParamString(psWMSParams, "BBOX",    szBuf, MS_TRUE, nVersion);
835 
836     if (nVersion >= OWS_1_3_0) {
837       msSetWMSParamInt(   psWMSParams, "I",       nClickX);
838       msSetWMSParamInt(   psWMSParams, "J",       nClickY);
839     } else {
840       msSetWMSParamInt(   psWMSParams, "X",       nClickX);
841       msSetWMSParamInt(   psWMSParams, "Y",       nClickY);
842     }
843 
844     msSetWMSParamString(psWMSParams, "EXCEPTIONS", pszExceptionsParam, MS_FALSE, nVersion);
845     msSetWMSParamString(psWMSParams, "INFO_FORMAT", pszInfoFormat, MS_TRUE, nVersion);
846 
847     if (pszQueryLayers) { /* not set in CONNECTION string */
848       msSetWMSParamString(psWMSParams, "QUERY_LAYERS", pszQueryLayers, MS_FALSE, nVersion);
849     }
850 
851     /* If FEATURE_COUNT <= 0 then don't pass this parameter */
852     /* The spec states that FEATURE_COUNT must be greater than zero */
853     /* and if not passed then the behavior is up to the server */
854     if (nFeatureCount > 0) {
855       msSetWMSParamInt(psWMSParams, "FEATURE_COUNT", nFeatureCount);
856     }
857 
858   } else if (nRequestType == WMS_GETLEGENDGRAPHIC) {
859     if(map->extent.maxx > map->extent.minx && map->width > 0 && map->height > 0) {
860       char szBuf[20] = "";
861       double scaledenom;
862       msCalculateScale(map->extent, map->units, map->width, map->height,
863                      map->resolution, &scaledenom);
864       snprintf(szBuf, 20, "%g",scaledenom);
865       msSetWMSParamString(psWMSParams, "SCALE", szBuf, MS_FALSE, nVersion);
866     }
867     pszRequestParam = "GetLegendGraphic";
868 
869     pszExceptionsParam = msOWSLookupMetadata(&(lp->metadata),
870                          "MO", "exceptions_format");
871     if (pszExceptionsParam == NULL) {
872       if (nVersion >= OWS_1_1_0 && nVersion < OWS_1_3_0)
873         pszExceptionsParam = "application/vnd.ogc.se_inimage";
874       else
875         pszExceptionsParam = "INIMAGE";
876     }
877 
878     if (pszLayer) { /* not set in CONNECTION string */
879       msSetWMSParamString(psWMSParams, "LAYER", pszLayer, MS_FALSE, nVersion);
880     }
881 
882     msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE, nVersion);
883     msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE, nVersion);
884 
885     if (nVersion >= OWS_1_3_0) {
886       msSetWMSParamString(psWMSParams, "SLD_VERSION", "1.1.0", MS_FALSE, nVersion);
887     }
888 
889   } else { /* if (nRequestType == WMS_GETMAP) */
890     char szBuf[100] = "";
891 
892     if (nVersion >= OWS_1_0_7)
893       pszRequestParam = "GetMap";
894     else
895       pszRequestParam = "map";
896 
897     pszExceptionsParam = msOWSLookupMetadata(&(lp->metadata),
898                          "MO", "exceptions_format");
899 
900     if (!bIsEssential) {
901       if (pszExceptionsParam == NULL) {
902         if (nVersion >= OWS_1_1_0 && nVersion < OWS_1_3_0)
903           pszExceptionsParam = "application/vnd.ogc.se_inimage";
904         else
905           pszExceptionsParam = "INIMAGE";
906       }
907     } else {
908       /* if layer is essential, do not emit EXCEPTIONS parameter (defaults to XML) */
909       pszExceptionsParam = NULL;
910     }
911 
912     msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE, nVersion);
913     msSetWMSParamInt(   psWMSParams, "WIDTH",   bbox_width);
914     msSetWMSParamInt(   psWMSParams, "HEIGHT",  bbox_height);
915     msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE, nVersion);
916 
917     if (bFlipAxisOrder == MS_TRUE) {
918       snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g",
919                bbox.miny, bbox.minx, bbox.maxy, bbox.maxx);
920     } else {
921       snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g",
922                bbox.minx, bbox.miny, bbox.maxx, bbox.maxy);
923     }
924     msSetWMSParamString(psWMSParams, "BBOX",    szBuf, MS_TRUE, nVersion);
925     if( pszExceptionsParam ) {
926       msSetWMSParamString(psWMSParams, "EXCEPTIONS",  pszExceptionsParam, MS_FALSE, nVersion);
927     }
928   }
929 
930   free(pszEPSG);
931 
932   return MS_SUCCESS;
933 
934 #else
935   /* ------------------------------------------------------------------
936    * WMS CONNECTION Support not included...
937    * ------------------------------------------------------------------ */
938   msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
939              "msBuildWMSLayerURL()");
940   return MS_FAILURE;
941 
942 #endif /* USE_WMS_LYR */
943 
944 }
945 
946 
947 /**********************************************************************
948  *                          msWMSGetFeatureInfoURL()
949  *
950  * Build a GetFeatureInfo URL for this layer.
951  *
952  * Returns a reference to a newly allocated string that should be freed
953  * by the caller.
954  **********************************************************************/
msWMSGetFeatureInfoURL(mapObj * map,layerObj * lp,int nClickX,int nClickY,int nFeatureCount,const char * pszInfoFormat)955 char *msWMSGetFeatureInfoURL(mapObj *map, layerObj *lp,
956                              int nClickX, int nClickY, int nFeatureCount,
957                              const char *pszInfoFormat)
958 {
959   wmsParamsObj sThisWMSParams;
960   char *pszURL;
961 
962   msInitWmsParamsObj(&sThisWMSParams);
963 
964   if (msBuildWMSLayerURL(map, lp, WMS_GETFEATUREINFO,
965                          nClickX, nClickY, nFeatureCount,
966                          pszInfoFormat, NULL, NULL, NULL,
967                          &sThisWMSParams)!= MS_SUCCESS) {
968     return NULL;
969   }
970 
971   pszURL = msBuildURLFromWMSParams(&sThisWMSParams);
972   msFreeWmsParamsObj(&sThisWMSParams);
973 
974   return pszURL;
975 }
976 
977 /**********************************************************************
978  *                          msPrepareWMSLayerRequest()
979  *
980  **********************************************************************/
981 
msPrepareWMSLayerRequest(int nLayerId,mapObj * map,layerObj * lp,int nRequestType,enum MS_CONNECTION_TYPE lastconnectiontype,wmsParamsObj * psLastWMSParams,int nClickX,int nClickY,int nFeatureCount,const char * pszInfoFormat,httpRequestObj * pasReqInfo,int * numRequests)982 int msPrepareWMSLayerRequest(int nLayerId, mapObj *map, layerObj *lp,
983                              int nRequestType, enum MS_CONNECTION_TYPE lastconnectiontype,
984                              wmsParamsObj *psLastWMSParams,
985                              int nClickX, int nClickY, int nFeatureCount, const char *pszInfoFormat,
986                              httpRequestObj *pasReqInfo, int *numRequests)
987 {
988 #ifdef USE_WMS_LYR
989   char *pszURL = NULL, *pszHTTPCookieData = NULL;
990   const char *pszTmp;
991   rectObj bbox = { 0 };
992   int bbox_width = 0, bbox_height = 0;
993   int nTimeout, bOkToMerge, bForceSeparateRequest, bCacheToDisk;
994   wmsParamsObj sThisWMSParams;
995 
996   if (lp->connectiontype != MS_WMS)
997     return MS_FAILURE;
998 
999   msInitWmsParamsObj(&sThisWMSParams);
1000 
1001   /* ------------------------------------------------------------------
1002    * Build the request URL, this will also set layer projection and
1003    * compute BBOX in that projection.
1004    * ------------------------------------------------------------------ */
1005 
1006 
1007   if (nRequestType == WMS_GETMAP &&
1008       ( msBuildWMSLayerURL(map, lp, WMS_GETMAP,
1009                            0, 0, 0, NULL, &bbox, &bbox_width, &bbox_height,
1010                            &sThisWMSParams) != MS_SUCCESS) ) {
1011     /* an error was already reported. */
1012     msFreeWmsParamsObj(&sThisWMSParams);
1013     return MS_FAILURE;
1014   }
1015 
1016   else if (nRequestType == WMS_GETFEATUREINFO &&
1017            msBuildWMSLayerURL(map, lp, WMS_GETFEATUREINFO,
1018                               nClickX, nClickY, nFeatureCount, pszInfoFormat,
1019                               NULL, NULL, NULL,
1020                               &sThisWMSParams) != MS_SUCCESS ) {
1021     /* an error was already reported. */
1022     msFreeWmsParamsObj(&sThisWMSParams);
1023     return MS_FAILURE;
1024   } else if (nRequestType == WMS_GETLEGENDGRAPHIC &&
1025              msBuildWMSLayerURL(map, lp, WMS_GETLEGENDGRAPHIC,
1026                                 0, 0, 0, NULL,
1027                                 NULL, NULL, NULL,
1028                                 &sThisWMSParams) != MS_SUCCESS ) {
1029     /* an error was already reported. */
1030     msFreeWmsParamsObj(&sThisWMSParams);
1031     return MS_FAILURE;
1032   }
1033 
1034 
1035   /* ------------------------------------------------------------------
1036    * Check if the request is empty, perhaps due to reprojection problems
1037    * or wms_extents restrictions.
1038    * ------------------------------------------------------------------ */
1039   if ((nRequestType == WMS_GETMAP) && (bbox_width == 0 || bbox_height == 0) ) {
1040     msFreeWmsParamsObj(&sThisWMSParams);
1041     return MS_SUCCESS;  /* No overlap. */
1042   }
1043 
1044   /* ------------------------------------------------------------------
1045    * Check if layer overlaps current view window (using wms_latlonboundingbox)
1046    * ------------------------------------------------------------------ */
1047   if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1048                                     "MO", "latlonboundingbox")) != NULL) {
1049     char **tokens;
1050     int n;
1051     rectObj ext;
1052 
1053     tokens = msStringSplit(pszTmp, ' ', &n);
1054     if (tokens==NULL || n != 4) {
1055       msSetError(MS_WMSCONNERR, "Wrong number of arguments for 'wms_latlonboundingbox' metadata.",
1056                  "msDrawWMSLayer()");
1057       msFreeWmsParamsObj(&sThisWMSParams);
1058       return MS_FAILURE;
1059     }
1060 
1061     ext.minx = atof(tokens[0]);
1062     ext.miny = atof(tokens[1]);
1063     ext.maxx = atof(tokens[2]);
1064     ext.maxy = atof(tokens[3]);
1065 
1066     msFreeCharArray(tokens, n);
1067 
1068     /* Reproject latlonboundingbox to the selected SRS for the layer and */
1069     /* check if it overlaps the bbox that we calculated for the request */
1070 
1071     msProjectRect(&(map->latlon), &(lp->projection), &ext);
1072     if (!msRectOverlap(&bbox, &ext)) {
1073       /* No overlap... nothing to do */
1074 
1075       msFreeWmsParamsObj(&sThisWMSParams);
1076       return MS_SUCCESS;  /* No overlap. */
1077     }
1078   }
1079 
1080   /* ------------------------------------------------------------------
1081    * check to see if a the metadata wms_connectiontimeout is set. If it is
1082    * the case we will use it, else we use the default which is 30 seconds.
1083    * First check the metadata in the layer object and then in the map object.
1084    * ------------------------------------------------------------------ */
1085   nTimeout = 30;  /* Default is 30 seconds  */
1086   if ((pszTmp = msOWSLookupMetadata2(&(lp->metadata), &(map->web.metadata),
1087                                     "MO", "connectiontimeout")) != NULL) {
1088     nTimeout = atoi(pszTmp);
1089   }
1090 
1091   /* ------------------------------------------------------------------
1092    * Check if we want to use in memory images instead of writing to disk.
1093    * ------------------------------------------------------------------ */
1094   if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1095                                     "MO", "cache_to_disk")) != NULL) {
1096     if( strcasecmp(pszTmp,"true") == 0
1097         || strcasecmp(pszTmp,"on") == 0
1098         || strcasecmp(pszTmp,"yes") == 0 )
1099       bCacheToDisk = MS_TRUE;
1100     else
1101       bCacheToDisk = atoi(pszTmp);
1102   } else
1103     bCacheToDisk = MS_FALSE;
1104 
1105   if( bCacheToDisk ) {
1106     /* We'll store the remote server's response to a tmp file. */
1107     if (map->web.imagepath == NULL || strlen(map->web.imagepath) == 0) {
1108       msSetError(MS_WMSERR,
1109                  "WEB.IMAGEPATH must be set to use WMS client connections.",
1110                  "msPrepareWMSLayerRequest()");
1111       return MS_FAILURE;
1112     }
1113   }
1114 
1115   /* ------------------------------------------------------------------
1116    * Check if layer can be merged with previous WMS layer requests
1117    * Metadata wms_force_separate_request can be set to 1 to prevent this
1118    * this layer from being combined with any other layer.
1119    * ------------------------------------------------------------------ */
1120   bForceSeparateRequest = MS_FALSE;
1121   if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1122                                     "MO", "force_separate_request")) != NULL) {
1123     bForceSeparateRequest = atoi(pszTmp);
1124   }
1125   bOkToMerge = MS_FALSE;
1126   if (!bForceSeparateRequest &&
1127       lastconnectiontype == MS_WMS &&
1128       psLastWMSParams != NULL &&
1129       sThisWMSParams.numparams == psLastWMSParams->numparams &&
1130       strcmp(sThisWMSParams.onlineresource,
1131              psLastWMSParams->onlineresource) == 0) {
1132     const char *key, *value1, *value2;
1133     bOkToMerge = MS_TRUE;
1134 
1135     key = msFirstKeyFromHashTable(sThisWMSParams.params);
1136     while (key != NULL && bOkToMerge == MS_TRUE) {
1137       /* Skip parameters whose values can be different */
1138       if (!(strcmp(key, "LAYERS") == 0 ||
1139             strcmp(key, "QUERY_LAYERS") == 0 ||
1140             strcmp(key, "STYLES") == 0) ) {
1141         value1 = msLookupHashTable(psLastWMSParams->params, key);
1142         value2 = msLookupHashTable(sThisWMSParams.params, key);
1143 
1144         if (value1==NULL || value2==NULL ||
1145             strcmp(value1, value2) != 0) {
1146           bOkToMerge = MS_FALSE;
1147           break;
1148         }
1149       }
1150       key = msNextKeyFromHashTable(sThisWMSParams.params, key);
1151     }
1152   }
1153 
1154   /*------------------------------------------------------------------
1155    * Check to see if there's a HTTP Cookie to forward
1156    * If Cookie differ between the two connection, it's NOT OK to merge
1157    * the connection
1158    * ------------------------------------------------------------------ */
1159   if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1160                                     "MO", "http_cookie")) != NULL) {
1161     if(strcasecmp(pszTmp, "forward") == 0) {
1162       pszTmp= msLookupHashTable(&(map->web.metadata),"http_cookie_data");
1163       if(pszTmp != NULL) {
1164         pszHTTPCookieData = msStrdup(pszTmp);
1165       }
1166     } else {
1167       pszHTTPCookieData = msStrdup(pszTmp);
1168     }
1169   } else if ((pszTmp = msOWSLookupMetadata(&(map->web.metadata),
1170                        "MO", "http_cookie")) != NULL) {
1171     if(strcasecmp(pszTmp, "forward") == 0) {
1172       pszTmp= msLookupHashTable(&(map->web.metadata),"http_cookie_data");
1173       if(pszTmp != NULL) {
1174         pszHTTPCookieData = msStrdup(pszTmp);
1175       }
1176     } else {
1177       pszHTTPCookieData = msStrdup(pszTmp);
1178     }
1179   }
1180 
1181   if(bOkToMerge && pszHTTPCookieData != sThisWMSParams.httpcookiedata) {
1182     if(pszHTTPCookieData == NULL || sThisWMSParams.httpcookiedata == NULL) {
1183       bOkToMerge = MS_FALSE;
1184     }
1185     if(strcmp(pszHTTPCookieData, sThisWMSParams.httpcookiedata) != 0) {
1186       bOkToMerge = MS_FALSE;
1187     }
1188   }
1189 
1190   if (bOkToMerge) {
1191     /* Merge both requests into sThisWMSParams
1192      */
1193     const char *value1, *value2;
1194     char *keys[] = {"LAYERS", "QUERY_LAYERS", "STYLES"};
1195     int i;
1196 
1197     for(i=0; i<3; i++) {
1198       value1 = msLookupHashTable(psLastWMSParams->params, keys[i]);
1199       value2 = msLookupHashTable(sThisWMSParams.params, keys[i]);
1200       if (value1 && value2) {
1201         char *pszBuf;
1202         int nLen;
1203 
1204         nLen = strlen(value1) + strlen(value2) +2;
1205         pszBuf = malloc(nLen);
1206         MS_CHECK_ALLOC(pszBuf, nLen, MS_FAILURE);
1207 
1208         snprintf(pszBuf, nLen, "%s,%s", value1, value2);
1209         /* TODO should really send the server request version here */
1210         msSetWMSParamString(&sThisWMSParams, keys[i], pszBuf,MS_FALSE, OWS_VERSION_NOTSET);
1211 
1212         /* This key existed already, we don't want it counted twice */
1213         sThisWMSParams.numparams--;
1214 
1215         msFree(pszBuf);
1216       }
1217     }
1218   }
1219 
1220 
1221   /* ------------------------------------------------------------------
1222    * Build new request URL
1223    * ------------------------------------------------------------------ */
1224   pszURL = msBuildURLFromWMSParams(&sThisWMSParams);
1225 
1226   if (bOkToMerge && (*numRequests)>0) {
1227     /* ------------------------------------------------------------------
1228      * Update the last request in the array:  (*numRequests)-1
1229      * ------------------------------------------------------------------ */
1230     msFree(pasReqInfo[(*numRequests)-1].pszGetUrl);
1231     pasReqInfo[(*numRequests)-1].pszGetUrl = pszURL;
1232     pszURL = NULL;
1233     pasReqInfo[(*numRequests)-1].debug |= lp->debug;
1234     if (nTimeout > pasReqInfo[(*numRequests)-1].nTimeout)
1235       pasReqInfo[(*numRequests)-1].nTimeout = nTimeout;
1236   } else {
1237     /* ------------------------------------------------------------------
1238      * Add a request to the array (already preallocated)
1239      * ------------------------------------------------------------------ */
1240     pasReqInfo[(*numRequests)].nLayerId = nLayerId;
1241     pasReqInfo[(*numRequests)].pszGetUrl = pszURL;
1242 
1243     pszURL = NULL;
1244     pasReqInfo[(*numRequests)].pszHTTPCookieData = pszHTTPCookieData;
1245     pszHTTPCookieData = NULL;
1246     if( bCacheToDisk ) {
1247       pasReqInfo[(*numRequests)].pszOutputFile =
1248         msTmpFile(map, map->mappath, NULL, "wms.tmp");
1249     } else
1250       pasReqInfo[(*numRequests)].pszOutputFile = NULL;
1251     pasReqInfo[(*numRequests)].nStatus = 0;
1252     pasReqInfo[(*numRequests)].nTimeout = nTimeout;
1253     pasReqInfo[(*numRequests)].bbox   = bbox;
1254     pasReqInfo[(*numRequests)].width  = bbox_width;
1255     pasReqInfo[(*numRequests)].height = bbox_height;
1256     pasReqInfo[(*numRequests)].debug = lp->debug;
1257 
1258     if (msHTTPAuthProxySetup(&(map->web.metadata), &(lp->metadata),
1259                              pasReqInfo, *numRequests, map, "MO") != MS_SUCCESS)
1260       return MS_FAILURE;
1261 
1262     (*numRequests)++;
1263   }
1264 
1265 
1266   /* ------------------------------------------------------------------
1267    * Replace contents of psLastWMSParams with sThisWMSParams
1268    * unless bForceSeparateRequest is set in which case we make it empty
1269    * ------------------------------------------------------------------ */
1270   if (psLastWMSParams) {
1271     msFreeWmsParamsObj(psLastWMSParams);
1272     if (!bForceSeparateRequest)
1273       *psLastWMSParams = sThisWMSParams;
1274     else
1275       msInitWmsParamsObj(psLastWMSParams);
1276   } else {
1277     /* Can't copy it, so we just free it */
1278     msFreeWmsParamsObj(&sThisWMSParams);
1279   }
1280 
1281   return MS_SUCCESS;
1282 
1283 #else
1284   /* ------------------------------------------------------------------
1285    * WMS CONNECTION Support not included...
1286    * ------------------------------------------------------------------ */
1287   msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1288              "msDrawWMSLayer()");
1289   return(MS_FAILURE);
1290 
1291 #endif /* USE_WMS_LYR */
1292 
1293 }
1294 
1295 /**********************************************************************
1296  *                          msDrawWMSLayerLow()
1297  *
1298  **********************************************************************/
1299 
msDrawWMSLayerLow(int nLayerId,httpRequestObj * pasReqInfo,int numRequests,mapObj * map,layerObj * lp,imageObj * img)1300 int msDrawWMSLayerLow(int nLayerId, httpRequestObj *pasReqInfo,
1301                       int numRequests, mapObj *map, layerObj *lp, imageObj *img)
1302 {
1303 #ifdef USE_WMS_LYR
1304   int status = MS_SUCCESS;
1305   int iReq = -1;
1306   char szPath[MS_MAXPATHLEN];
1307   int currenttype;
1308   int currentconnectiontype;
1309   int numclasses;
1310   char *mem_filename = NULL;
1311   const char *pszTmp;
1312   int bIsEssential = MS_FALSE;
1313 
1314   /* ------------------------------------------------------------------
1315    * Sometimes a requested layer is essential for the map, so if the
1316    * request fails or an error is delivered, the map has not to be drawn
1317    * ------------------------------------------------------------------ */
1318   if ((pszTmp = msOWSLookupMetadata(&(lp->metadata),
1319                                     "MO", "essential")) != NULL) {
1320     if( strcasecmp(pszTmp,"true") == 0
1321         || strcasecmp(pszTmp,"on") == 0
1322         || strcasecmp(pszTmp,"yes") == 0 )
1323       bIsEssential = MS_TRUE;
1324     else
1325       bIsEssential = atoi(pszTmp);
1326   }
1327 
1328   /* ------------------------------------------------------------------
1329    * Find the request info for this layer in the array, based on nLayerId
1330    * ------------------------------------------------------------------ */
1331   for(iReq=0; iReq<numRequests; iReq++) {
1332     if (pasReqInfo[iReq].nLayerId == nLayerId)
1333       break;
1334   }
1335 
1336   if (iReq == numRequests) {
1337     /* This layer was skipped or was included in a multi-layers
1338      * request ... nothing to do.
1339      */
1340     return MS_SUCCESS;
1341   }
1342 
1343   if ( !MS_HTTP_SUCCESS( pasReqInfo[iReq].nStatus ) ) {
1344     /* ====================================================================
1345           Failed downloading layer... we log an error but we still return
1346           SUCCESS here so that the layer is only skipped instead of aborting
1347           the whole draw map.
1348           If the layer is essential the map is not to be drawn.
1349      ==================================================================== */
1350     msSetError(MS_WMSERR,
1351                "WMS GetMap request failed for layer '%s' (Status %d: %s).",
1352                "msDrawWMSLayerLow()",
1353                (lp->name?lp->name:"(null)"),
1354                pasReqInfo[iReq].nStatus, pasReqInfo[iReq].pszErrBuf );
1355 
1356     if (!bIsEssential)
1357       return MS_SUCCESS;
1358     else
1359       return MS_FAILURE;
1360   }
1361 
1362   /* ------------------------------------------------------------------
1363    * Check the Content-Type of the response to see if we got an exception,
1364    * if yes then try to parse it and pass the info to msSetError().
1365    * We log an error but we still return SUCCESS here so that the layer
1366    * is only skipped instead of aborting the whole draw map.
1367    * If the layer is essential the map is not to be drawn.
1368    * ------------------------------------------------------------------ */
1369   if (pasReqInfo[iReq].pszContentType &&
1370       (strcmp(pasReqInfo[iReq].pszContentType, "text/xml") == 0 ||
1371        strcmp(pasReqInfo[iReq].pszContentType, "application/vnd.ogc.se_xml") == 0)) {
1372     FILE *fp;
1373     char szBuf[MS_BUFFER_LENGTH];
1374 
1375     if( pasReqInfo[iReq].pszOutputFile ) {
1376       fp = fopen(pasReqInfo[iReq].pszOutputFile, "r");
1377       if (fp) {
1378         /* TODO: For now we'll only read the first chunk and return it
1379          * via msSetError()... we should really try to parse the XML
1380          * and extract the exception code/message though
1381          */
1382         size_t nSize;
1383 
1384         nSize = fread(szBuf, sizeof(char), MS_BUFFER_LENGTH-1, fp);
1385         if (nSize < MS_BUFFER_LENGTH)
1386           szBuf[nSize] = '\0';
1387         else {
1388           strlcpy(szBuf, "(!!!)", sizeof(szBuf)); /* This should never happen */
1389         }
1390 
1391         fclose(fp);
1392 
1393         /* We're done with the remote server's response... delete it. */
1394         if (!lp->debug)
1395           unlink(pasReqInfo[iReq].pszOutputFile);
1396       } else {
1397         strlcpy(szBuf, "(Failed to open exception response)", sizeof(szBuf));
1398       }
1399     } else {
1400       strlcpy( szBuf, pasReqInfo[iReq].result_data, MS_BUFFER_LENGTH );
1401     }
1402 
1403     if (lp->debug)
1404       msDebug("WMS GetMap request got XML exception for layer '%s': %s.",
1405               (lp->name?lp->name:"(null)"), szBuf );
1406 
1407     msSetError(MS_WMSERR,
1408                "WMS GetMap request got XML exception for layer '%s': %s.",
1409                "msDrawWMSLayerLow()",
1410                (lp->name?lp->name:"(null)"), szBuf );
1411 
1412     if (!bIsEssential)
1413       return MS_SUCCESS;
1414     else
1415       return MS_FAILURE;
1416   }
1417 
1418   /* ------------------------------------------------------------------
1419    * If the output was written to a memory buffer, then we will need
1420    * to attach a "VSI" name to this buffer.
1421    * ------------------------------------------------------------------ */
1422   if( pasReqInfo[iReq].pszOutputFile == NULL ) {
1423     mem_filename = msTmpFile(map, NULL, "/vsimem/msout/", "img.tmp" );
1424 
1425     VSIFCloseL(
1426       VSIFileFromMemBuffer( mem_filename,
1427                             (GByte*) pasReqInfo[iReq].result_data,
1428                             (vsi_l_offset) pasReqInfo[iReq].result_size,
1429                             FALSE ) );
1430   }
1431 
1432   /* ------------------------------------------------------------------
1433    * Prepare layer for drawing, reprojecting the image received from the
1434    * server if needed...
1435    * ------------------------------------------------------------------ */
1436   /* keep the current type that will be restored at the end of this  */
1437   /* function. */
1438   currenttype = lp->type;
1439   currentconnectiontype = lp->connectiontype;
1440   lp->type = MS_LAYER_RASTER;
1441   lp->connectiontype = MS_SHAPEFILE;
1442 
1443   /* set the classes to 0 so that It won't do client side */
1444   /* classification if an sld was set. */
1445   numclasses = lp->numclasses;
1446 
1447   /* ensure the file connection is closed right away after the layer */
1448   /* is rendered */
1449   msLayerSetProcessingKey( lp, "CLOSE_CONNECTION", "NORMAL");
1450 
1451   if (msOWSLookupMetadata(&(lp->metadata), "MO", "sld_body") ||
1452       msOWSLookupMetadata(&(lp->metadata), "MO", "sld_url"))
1453     lp->numclasses = 0;
1454 
1455   if (lp->data) free(lp->data);
1456   if( mem_filename != NULL )
1457     lp->data = mem_filename;
1458   else
1459     lp->data =  msStrdup(pasReqInfo[iReq].pszOutputFile);
1460 
1461   /* #3138 If PROCESSING "RESAMPLE=..." is set we cannot use the simple case */
1462   if (!msProjectionsDiffer(&(map->projection), &(lp->projection)) &&
1463       (msLayerGetProcessingKey(lp, "RESAMPLE") == NULL) ) {
1464     /* The simple case... no reprojection needed... render layer directly. */
1465     lp->transform = MS_FALSE;
1466     /* if (msDrawRasterLayerLow(map, lp, img) != 0) */
1467     /* status = MS_FAILURE; */
1468     if (msDrawLayer(map, lp, img) != 0)
1469       status = MS_FAILURE;
1470   } else {
1471     FILE *fp;
1472     char *wldfile;
1473     /* OK, we have to resample the raster to map projection... */
1474     lp->transform = MS_TRUE;
1475     msLayerSetProcessingKey( lp, "LOAD_WHOLE_IMAGE", "YES" );
1476 
1477     /* Create a world file with raster extents */
1478     /* One line per value, in this order: cx, 0, 0, cy, ulx, uly */
1479     wldfile = msBuildPath(szPath, lp->map->mappath, lp->data);
1480     if (wldfile && (strlen(wldfile)>=3))
1481       strcpy(wldfile+strlen(wldfile)-3, "wld");
1482     if (wldfile && (fp = VSIFOpenL(wldfile, "wt")) != NULL) {
1483       double dfCellSizeX = MS_OWS_CELLSIZE(pasReqInfo[iReq].bbox.minx,
1484                                            pasReqInfo[iReq].bbox.maxx,
1485                                            pasReqInfo[iReq].width);
1486       double dfCellSizeY = MS_OWS_CELLSIZE(pasReqInfo[iReq].bbox.maxy,
1487                                            pasReqInfo[iReq].bbox.miny,
1488                                            pasReqInfo[iReq].height);
1489       char world_text[5000];
1490 
1491       sprintf( world_text, "%.12f\n0\n0\n%.12f\n%.12f\n%.12f\n",
1492                dfCellSizeX,
1493                dfCellSizeY,
1494                pasReqInfo[iReq].bbox.minx + dfCellSizeX * 0.5,
1495                pasReqInfo[iReq].bbox.maxy + dfCellSizeY * 0.5 );
1496 
1497       VSIFWriteL( world_text, 1, strlen(world_text), fp );
1498       VSIFCloseL( fp );
1499 
1500       /* GDAL should be called to reproject automatically. */
1501       if (msDrawLayer(map, lp, img) != 0)
1502         status = MS_FAILURE;
1503 
1504       if (!lp->debug || mem_filename != NULL)
1505         VSIUnlink( wldfile );
1506     } else {
1507       msSetError(MS_WMSCONNERR,
1508                  "Unable to create wld file for WMS slide.",
1509                  "msDrawWMSLayer()");
1510       status = MS_FAILURE;
1511     }
1512 
1513   }
1514 
1515   /* We're done with the remote server's response... delete it. */
1516   if (!lp->debug || mem_filename != NULL)
1517     VSIUnlink(lp->data);
1518 
1519   /* restore prveious type */
1520   lp->type = currenttype;
1521   lp->connectiontype = currentconnectiontype;
1522 
1523   /* restore previous numclasses */
1524   lp->numclasses = numclasses;
1525 
1526   free(lp->data);
1527   lp->data = NULL;
1528 
1529   return status;
1530 
1531 #else
1532   /* ------------------------------------------------------------------
1533    * WMS CONNECTION Support not included...
1534    * ------------------------------------------------------------------ */
1535   msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1536              "msDrawWMSLayer()");
1537   return(MS_FAILURE);
1538 
1539 #endif /* USE_WMS_LYR */
1540 
1541 }
1542 
1543 
msWMSLayerExecuteRequest(mapObj * map,int nOWSLayers,int nClickX,int nClickY,int nFeatureCount,const char * pszInfoFormat,int type)1544 int msWMSLayerExecuteRequest(mapObj *map, int nOWSLayers, int nClickX, int nClickY,
1545                              int nFeatureCount, const char *pszInfoFormat, int type)
1546 {
1547 #ifdef USE_WMS_LYR
1548 
1549   msIOContext *context;
1550 
1551   httpRequestObj *pasReqInfo;
1552   wmsParamsObj sLastWMSParams;
1553   int i, numReq = 0;
1554 
1555   pasReqInfo = (httpRequestObj *)msSmallMalloc((nOWSLayers+1)*sizeof(httpRequestObj));
1556   msHTTPInitRequestObj(pasReqInfo, nOWSLayers+1);
1557   msInitWmsParamsObj(&sLastWMSParams);
1558 
1559   /* Generate the http request */
1560   for (i=0; i<map->numlayers; i++) {
1561     if (GET_LAYER(map,map->layerorder[i])->status == MS_ON) {
1562       if (type == WMS_GETFEATUREINFO ) {
1563         if( msPrepareWMSLayerRequest(map->layerorder[i], map, GET_LAYER(map,map->layerorder[i]),
1564                                    WMS_GETFEATUREINFO,
1565                                    MS_WMS, &sLastWMSParams,
1566                                    nClickX, nClickY, nFeatureCount, pszInfoFormat,
1567                                    pasReqInfo, &numReq) == MS_FAILURE) {
1568           msFreeWmsParamsObj(&sLastWMSParams);
1569           msFree(pasReqInfo);
1570           return MS_FAILURE;
1571         }
1572       } else if (msPrepareWMSLayerRequest(map->layerorder[i], map, GET_LAYER(map,map->layerorder[i]),
1573                                           WMS_GETLEGENDGRAPHIC,
1574                                           MS_WMS, &sLastWMSParams,
1575                                           0, 0, 0, NULL,
1576                                           pasReqInfo, &numReq) == MS_FAILURE) {
1577         msFreeWmsParamsObj(&sLastWMSParams);
1578         msFree(pasReqInfo);
1579         return MS_FAILURE;
1580       }
1581     }
1582   }
1583 
1584   if (msOWSExecuteRequests(pasReqInfo, numReq, map, MS_FALSE) == MS_FAILURE) {
1585     msHTTPFreeRequestObj(pasReqInfo, numReq);
1586     msFree(pasReqInfo);
1587     msFreeWmsParamsObj(&sLastWMSParams);
1588     return MS_FAILURE;
1589   }
1590 
1591   context = msIO_getHandler( stdout );
1592   if( context == NULL ) {
1593     msHTTPFreeRequestObj(pasReqInfo, numReq);
1594     msFree(pasReqInfo);
1595     msFreeWmsParamsObj(&sLastWMSParams);
1596     return MS_FAILURE;
1597   }
1598 
1599   msIO_printf("Content-Type: %s%c%c",pasReqInfo[0].pszContentType, 10,10);
1600 
1601   if( pasReqInfo[0].pszOutputFile ) {
1602     FILE *fp;
1603     char szBuf[MS_BUFFER_LENGTH];
1604 
1605     fp = fopen(pasReqInfo[0].pszOutputFile, "r");
1606     if (fp) {
1607       while(1) {
1608         size_t nSize;
1609         nSize = fread(szBuf, sizeof(char), MS_BUFFER_LENGTH-1, fp);
1610         if (nSize > 0)
1611           msIO_contextWrite( context,
1612                              szBuf,
1613                              nSize);
1614         if (nSize != MS_BUFFER_LENGTH-1)
1615           break;
1616       }
1617       fclose(fp);
1618       if (!map->debug)
1619         unlink(pasReqInfo[0].pszOutputFile);
1620     } else {
1621       msSetError(MS_IOERR, "'%s'.",
1622                  "msWMSLayerExecuteRequest()", pasReqInfo[0].pszOutputFile);
1623       return MS_FAILURE;
1624     }
1625   } else {
1626     msIO_contextWrite( context,
1627                        pasReqInfo[0].result_data,
1628                        pasReqInfo[0].result_size );
1629   }
1630 
1631   msHTTPFreeRequestObj(pasReqInfo, numReq);
1632   msFree(pasReqInfo);
1633   msFreeWmsParamsObj(&sLastWMSParams);
1634 
1635   return MS_SUCCESS;
1636 #else
1637   /* ------------------------------------------------------------------
1638    * WMS CONNECTION Support not included...
1639    * ------------------------------------------------------------------ */
1640   msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1641              "msWMSLayerExecuteRequest()");
1642   return(MS_FAILURE);
1643 
1644 #endif /* USE_WMS_LYR */
1645 
1646 }
1647