Coverage Report

Created: 2025-06-13 06:29

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