• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MapServer / MapServer / 18964964805

31 Oct 2025 06:44AM UTC coverage: 41.688% (+0.02%) from 41.673%
18964964805

push

github

web-flow
Remove legacy bootstrap4 files (#7363)

62724 of 150460 relevant lines covered (41.69%)

25214.53 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.71
/src/mapwmslayer.c
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
int msInitWmsParamsObj(wmsParamsObj *wmsparams) {
82✔
50
  wmsparams->onlineresource = NULL;
82✔
51
  wmsparams->params = msCreateHashTable();
82✔
52
  wmsparams->numparams = 0;
82✔
53
  wmsparams->httpcookiedata = NULL;
82✔
54

55
  return MS_SUCCESS;
82✔
56
}
57

58
/**********************************************************************
59
 *                          msFreeWmsParamsObj()
60
 *
61
 * Frees the contents of the object, but not the object itself.
62
 **********************************************************************/
63
void msFreeWmsParamsObj(wmsParamsObj *wmsparams) {
82✔
64
  msFree(wmsparams->onlineresource);
82✔
65
  wmsparams->onlineresource = NULL;
82✔
66

67
  msFreeHashTable(wmsparams->params);
82✔
68
  wmsparams->params = NULL;
82✔
69

70
  msFree(wmsparams->httpcookiedata);
82✔
71

72
  wmsparams->numparams = 0;
82✔
73
}
82✔
74

75
/**********************************************************************
76
 *                          msSetWMSParamString()
77
 *
78
 **********************************************************************/
79

80
#ifdef USE_WMS_LYR
81
static int msSetWMSParamString(wmsParamsObj *psWMSParams, const char *name,
233✔
82
                               const char *value, int urlencode, int nVersion) {
83
  if (urlencode) {
233✔
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 ||
233✔
95
        strcmp(name, "BBOX") == 0) {
155✔
96
      pszTmp = msEncodeUrlExcept(value, ',');
115✔
97
    } else if (strcmp(name, "SRS") == 0) {
118✔
98
      pszTmp = msEncodeUrlExcept(value, ':');
×
99
    } else if (nVersion < OWS_1_3_0 && strcmp(name, "FORMAT") == 0) {
118✔
100
      pszTmp = msEncodeUrlExcept(value, '/');
17✔
101
    } else {
102
      pszTmp = msEncodeUrl(value);
101✔
103
    }
104

105
    msInsertHashTable(psWMSParams->params, name, pszTmp);
233✔
106
    msFree(pszTmp);
233✔
107
  } else {
108
    msInsertHashTable(psWMSParams->params, name, value);
129✔
109
  }
110
  psWMSParams->numparams++;
45✔
111

112
  return MS_SUCCESS;
35✔
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,
98✔
123
                            int value) {
124
  char szBuf[100];
125

126
  snprintf(szBuf, sizeof(szBuf), "%d", value);
127
  msInsertHashTable(wmsparams->params, name, szBuf);
98✔
128
  wmsparams->numparams++;
98✔
129

130
  return MS_SUCCESS;
98✔
131
}
132
#endif /* def USE_WMS_LYR */
133

134
/**********************************************************************
135
 *                          msBuildWMSParamsUrl()
136
 *
137
 **********************************************************************/
138
static char *msBuildURLFromWMSParams(wmsParamsObj *wmsparams) {
41✔
139
  const char *key, *value;
140
  size_t bufferSize = 0;
141
  int nLen;
142
  char *pszURL;
143

144
  /* Compute size required for URL buffer
145
   */
146
  nLen = strlen(wmsparams->onlineresource) + 3;
41✔
147

148
  key = msFirstKeyFromHashTable(wmsparams->params);
41✔
149
  while (key != NULL) {
583✔
150
    value = msLookupHashTable(wmsparams->params, key);
542✔
151
    nLen += strlen(key) + strlen(value) + 2;
542✔
152

153
    key = msNextKeyFromHashTable(wmsparams->params, key);
542✔
154
  }
155

156
  bufferSize = nLen + 1;
41✔
157
  pszURL = (char *)msSmallMalloc(bufferSize);
41✔
158

159
  /* Start with the onlineresource value and append trailing '?' or '&'
160
   * if missing.
161
   */
162
  strcpy(pszURL, wmsparams->onlineresource);
41✔
163
  if (strchr(pszURL, '?') == NULL)
41✔
164
    strcat(pszURL, "?");
165
  else {
166
    char *c;
167
    c = pszURL + strlen(pszURL) - 1;
41✔
168
    if (*c != '?' && *c != '&')
41✔
169
      strcpy(c + 1, "&");
×
170
  }
171

172
  /* Now add all the parameters
173
   */
174
  nLen = strlen(pszURL);
41✔
175
  key = msFirstKeyFromHashTable(wmsparams->params);
41✔
176
  while (key != NULL) {
583✔
177
    value = msLookupHashTable(wmsparams->params, key);
542✔
178
    snprintf(pszURL + nLen, bufferSize - nLen, "%s=%s&", key, value);
542✔
179
    nLen += strlen(key) + strlen(value) + 2;
542✔
180
    key = msNextKeyFromHashTable(wmsparams->params, key);
542✔
181
  }
182

183
  /* Get rid of trailing '&'*/
184
  pszURL[nLen - 1] = '\0';
41✔
185

186
  return pszURL;
41✔
187
}
188

189
#ifdef USE_WMS_LYR
190

191
static bool IsPNGFormat(const char *pszValue) {
27✔
192
  return EQUAL(pszValue, "PNG") || EQUAL(pszValue, "image/png");
27✔
193
}
194

195
static bool IsJPEGFormat(const char *pszValue) {
11✔
196
  return EQUAL(pszValue, "JPEG") || EQUAL(pszValue, "image/jpeg");
11✔
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) {
68✔
206
  const char *pszFormat = msOWSLookupMetadata(&(lp->metadata), "MO", "format");
68✔
207
  if (pszFormat)
68✔
208
    return msStrdup(pszFormat);
68✔
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,
41✔
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;
41✔
282
  if (pszOnlineResource == NULL)
41✔
283
    pszOnlineResource =
284
        msOWSLookupMetadata(&(lp->metadata), "MO", "onlineresource");
×
285

286
  pszVersion = msOWSLookupMetadata(&(lp->metadata), "MO", "server_version");
41✔
287
  pszName = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
41✔
288
  pszStyle = msOWSLookupMetadata(&(lp->metadata), "MO", "style");
41✔
289
  /*pszStyleList =      msOWSLookupMetadata(&(lp->metadata), "MO",
290
   * "stylelist");*/
291
  pszTime = msOWSLookupMetadata(&(lp->metadata), "MO", "time");
41✔
292
  pszSLDBody = msOWSLookupMetadata(&(lp->metadata), "MO", "sld_body");
41✔
293
  pszSLDURL = msOWSLookupMetadata(&(lp->metadata), "MO", "sld_url");
41✔
294
  pszBgColor = msOWSLookupMetadata(&(lp->metadata), "MO", "bgcolor");
41✔
295
  pszTransparent = msOWSLookupMetadata(&(lp->metadata), "MO", "transparent");
41✔
296

297
  if (pszOnlineResource == NULL || pszVersion == NULL || pszName == NULL) {
41✔
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);
41✔
308

309
  pszVersionKeyword = "VERSION";
310

311
  nVersion = msOWSParseVersionString(pszVersion);
41✔
312
  msSetWMSParamString(psWMSParams, pszVersionKeyword, pszVersion, MS_FALSE,
313
                      nVersion);
314
  msSetWMSParamString(psWMSParams, "SERVICE", "WMS", MS_FALSE, nVersion);
315
  msSetWMSParamString(psWMSParams, "LAYERS", pszName, MS_TRUE, nVersion);
41✔
316

317
  char *pszFormat = msWMSLayerGetFormat(lp);
41✔
318
  if (!pszFormat) {
41✔
319
    return MS_FAILURE;
320
  }
321

322
  msSetWMSParamString(psWMSParams, "FORMAT", pszFormat, MS_TRUE, nVersion);
41✔
323
  msFree(pszFormat);
41✔
324

325
  if (pszStyle == NULL) {
41✔
326
    /* When no style is selected, use "" which is a valid default. */
327
    pszStyle = "";
328
  } else {
329
    /* Was a wms_style_..._sld URL provided? */
330
    char szBuf[100];
331
    snprintf(szBuf, sizeof(szBuf), "style_%.80s_sld", pszStyle);
332
    pszSLD = msOWSLookupMetadata(&(lp->metadata), "MO", szBuf);
1✔
333
    snprintf(szBuf, sizeof(szBuf), "style_%.80s_sld_body", pszStyle);
334
    pszStyleSLDBody = msOWSLookupMetadata(&(lp->metadata), "MO", szBuf);
1✔
335

336
    if (pszSLD || pszStyleSLDBody) {
1✔
337
      /* SLD URL is set.  If this defn. came from a map context then */
338
      /* the style name may just be an internal name: "Style{%d}" if */
339
      /* that's the case then we should not pass this name via the URL */
340
      if (strncmp(pszStyle, "Style{", 6) == 0)
×
341
        pszStyle = "";
342
    }
343
  }
344

345
  /*  set STYLE parameter no matter what, even if it's empty (i.e. "STYLES=")
346
   *  GetLegendGraphic doesn't support multiple styles and is named STYLE
347
   */
348
  if (nRequestType == WMS_GETLEGENDGRAPHIC) {
41✔
349
    msSetWMSParamString(psWMSParams, "STYLE", pszStyle, MS_TRUE, nVersion);
4✔
350
  } else {
351
    msSetWMSParamString(psWMSParams, "STYLES", pszStyle, MS_TRUE, nVersion);
37✔
352
  }
353

354
  if (pszSLD != NULL) {
41✔
355
    /* Only SLD is set */
356
    msSetWMSParamString(psWMSParams, "SLD", pszSLD, MS_TRUE, nVersion);
×
357
  } else if (pszStyleSLDBody != NULL) {
41✔
358
    /* SLDBODY are set */
359
    msSetWMSParamString(psWMSParams, "SLD_BODY", pszStyleSLDBody, MS_TRUE,
×
360
                        nVersion);
361
  }
362

363
  if (msIsLayerQueryable(lp)) {
41✔
364
    msSetWMSParamString(psWMSParams, "QUERY_LAYERS", pszName, MS_TRUE,
24✔
365
                        nVersion);
366
  }
367
  if (pszTime && strlen(pszTime) > 0) {
41✔
368
    msSetWMSParamString(psWMSParams, "TIME", pszTime, MS_TRUE, nVersion);
×
369
  }
370

371
  /* if  the metadata wms_sld_body is set to AUTO, we generate
372
   * the sld based on classes found in the map file and send
373
   * it in the URL. If different from AUTO, we are assuming that
374
   * it is a valid sld.
375
   */
376
  if (pszSLDBody) {
41✔
377
    if (strcasecmp(pszSLDBody, "AUTO") == 0) {
×
378
      if (strncmp(pszVersion, "1.3.0", 5) == 0)
×
379
        pszSLDGenerated = msSLDGenerateSLD(map, lp->index, "1.1.0");
×
380
      else
381
        pszSLDGenerated = msSLDGenerateSLD(map, lp->index, NULL);
×
382

383
      if (pszSLDGenerated) {
×
384
        msSetWMSParamString(psWMSParams, "SLD_BODY", pszSLDGenerated, MS_TRUE,
×
385
                            nVersion);
386
        free(pszSLDGenerated);
×
387
      }
388
    } else {
389
      msSetWMSParamString(psWMSParams, "SLD_BODY", pszSLDBody, MS_TRUE,
×
390
                          nVersion);
391
    }
392
  }
393

394
  if (pszSLDURL) {
41✔
395
    msSetWMSParamString(psWMSParams, "SLD", pszSLDURL, MS_TRUE, nVersion);
×
396
  }
397

398
  if (pszBgColor) {
41✔
399
    msSetWMSParamString(psWMSParams, "BGCOLOR", pszBgColor, MS_TRUE, nVersion);
×
400
  }
401

402
  if (pszTransparent) {
41✔
403
    msSetWMSParamString(psWMSParams, "TRANSPARENT", pszTransparent, MS_TRUE,
×
404
                        nVersion);
405
  } else {
406
    msSetWMSParamString(psWMSParams, "TRANSPARENT", "TRUE", MS_TRUE, nVersion);
41✔
407
  }
408

409
  return MS_SUCCESS;
410
}
411

412
#endif /* USE_WMS_LYR */
413

414
/**********************************************************************
415
 *                          msBuildWMSLayerURL()
416
 *
417
 * Build a GetMap or GetFeatureInfo URL.
418
 *
419
 * Returns a reference to a newly allocated string that should be freed
420
 * by the caller.
421
 **********************************************************************/
422

423
static int msBuildWMSLayerURL(mapObj *map, layerObj *lp, int nRequestType,
41✔
424
                              int nClickX, int nClickY, int nFeatureCount,
425
                              const char *pszInfoFormat, rectObj *bbox_ret,
426
                              int *width_ret, int *height_ret,
427
                              wmsParamsObj *psWMSParams) {
428
#ifdef USE_WMS_LYR
429
  char *pszEPSG = NULL;
41✔
430
  const char *pszVersion, *pszRequestParam,
431
      *pszSrsParamName = "SRS", *pszLayer = NULL, *pszQueryLayers = NULL,
432
      *pszUseStrictAxisOrder;
433
  rectObj bbox;
434
  projectionObj layerRequestProjection;
435
  int bbox_width = map->width, bbox_height = map->height;
41✔
436
  int nVersion = OWS_VERSION_NOTSET;
437
  int bUseStrictAxisOrder = MS_FALSE; /* this is the assumption up to 1.1.0 */
438
  int bFlipAxisOrder = MS_FALSE;
439
  const char *pszTmp;
440
  int bIsEssential = MS_FALSE;
441

442
  if (lp->connectiontype != MS_WMS) {
41✔
443
    msSetError(MS_WMSCONNERR, "Call supported only for CONNECTIONTYPE WMS",
×
444
               "msBuildWMSLayerURL()");
445
    return MS_FAILURE;
×
446
  }
447

448
  /* ------------------------------------------------------------------
449
   * Find out request version
450
   * ------------------------------------------------------------------ */
451
  if (lp->connection == NULL ||
41✔
452
      ((pszVersion = strstr(lp->connection, "VERSION=")) == NULL &&
41✔
453
       (pszVersion = strstr(lp->connection, "version=")) == NULL &&
41✔
454
       (pszVersion = strstr(lp->connection, "WMTVER=")) == NULL &&
41✔
455
       (pszVersion = strstr(lp->connection, "wmtver=")) == NULL)) {
41✔
456
    /* CONNECTION missing or seems incomplete... try to build from metadata */
457
    if (msBuildWMSLayerURLBase(map, lp, psWMSParams, nRequestType) !=
41✔
458
        MS_SUCCESS)
459
      return MS_FAILURE; /* An error already produced. */
460

461
    /* If we received MS_SUCCESS then version must have been set */
462
    pszVersion = msLookupHashTable(psWMSParams->params, "VERSION");
41✔
463
    if (pszVersion == NULL)
41✔
464
      pszVersion = msLookupHashTable(psWMSParams->params, "WMTVER");
×
465

466
    nVersion = msOWSParseVersionString(pszVersion);
41✔
467
  } else {
468
    /* CONNECTION string seems complete, start with that. */
469
    char *pszDelimiter;
470
    psWMSParams->onlineresource = msStrdup(lp->connection);
×
471

472
    /* Fetch version info */
473
    pszVersion = strchr(pszVersion, '=') + 1;
×
474
    pszDelimiter = strchr(pszVersion, '&');
×
475
    if (pszDelimiter != NULL)
×
476
      *pszDelimiter = '\0';
×
477
    nVersion = msOWSParseVersionString(pszVersion);
×
478
    if (pszDelimiter != NULL)
×
479
      *pszDelimiter = '&';
×
480
  }
481

482
  switch (nVersion) {
41✔
483
  case OWS_1_0_0:
484
  case OWS_1_1_0:
485
  case OWS_1_1_1:
486
    /* All is good, this is a supported version. */
487
    break;
488
  case OWS_1_3_0:
24✔
489
    /* 1.3.0 introduces a few changes... */
490
    pszSrsParamName = "CRS";
491
    bUseStrictAxisOrder = MS_TRUE; /* this is the assumption for 1.3.0 */
492
    break;
24✔
493
  default:
×
494
    /* Not a supported version */
495
    msSetError(MS_WMSCONNERR,
×
496
               "MapServer supports only WMS 1.0.0 to 1.3.0 (please verify the "
497
               "VERSION parameter in the connection string).",
498
               "msBuildWMSLayerURL()");
499
    return MS_FAILURE;
×
500
  }
501

502
  /* ------------------------------------------------------------------
503
   * For GetFeatureInfo requests, make sure QUERY_LAYERS is included
504
   * ------------------------------------------------------------------ */
505
  if (nRequestType == WMS_GETFEATUREINFO &&
41✔
506
      strstr(psWMSParams->onlineresource, "QUERY_LAYERS=") == NULL &&
8✔
507
      strstr(psWMSParams->onlineresource, "query_layers=") == NULL &&
16✔
508
      msLookupHashTable(psWMSParams->params, "QUERY_LAYERS") == NULL) {
8✔
509
    pszQueryLayers = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
×
510

511
    if (pszQueryLayers == NULL) {
×
512
      msSetError(MS_WMSCONNERR,
×
513
                 "wms_name not set or WMS Connection String must contain the "
514
                 "QUERY_LAYERS parameter to support GetFeatureInfo requests "
515
                 "(with name in uppercase).",
516
                 "msBuildWMSLayerURL()");
517
      return MS_FAILURE;
×
518
    }
519
  }
520

521
  /* ------------------------------------------------------------------
522
   * For GetLegendGraphic requests, make sure LAYER is included
523
   * ------------------------------------------------------------------ */
524
  if (nRequestType == WMS_GETLEGENDGRAPHIC &&
41✔
525
      strstr(psWMSParams->onlineresource, "LAYER=") == NULL &&
4✔
526
      strstr(psWMSParams->onlineresource, "layer=") == NULL &&
8✔
527
      msLookupHashTable(psWMSParams->params, "LAYER") == NULL) {
4✔
528
    pszLayer = msOWSLookupMetadata(&(lp->metadata), "MO", "name");
4✔
529

530
    if (pszLayer == NULL) {
4✔
531
      msSetError(MS_WMSCONNERR,
×
532
                 "wms_name not set or WMS Connection String must contain the "
533
                 "LAYER parameter to support GetLegendGraphic requests (with "
534
                 "name in uppercase).",
535
                 "msBuildWMSLayerURL()");
536
      return MS_FAILURE;
×
537
    }
538
  }
539

540
  /* ------------------------------------------------------------------
541
   * Figure the SRS we'll use for the request.
542
   * - Fetch the map SRS (if it's EPSG)
543
   * - Check if map SRS is listed in layer wms_srs metadata
544
   * - If map SRS is valid for this layer then use it
545
   * - Otherwise request layer in its default SRS and we'll reproject later
546
   * ------------------------------------------------------------------ */
547
  msOWSGetEPSGProj(&(map->projection), NULL, NULL, MS_TRUE, &pszEPSG);
41✔
548
  if (pszEPSG && (strncasecmp(pszEPSG, "EPSG:", 5) == 0 ||
41✔
549
                  strncasecmp(pszEPSG, "AUTO:", 5) == 0)) {
×
550
    const char *pszFound;
551
    char *pszLyrEPSG;
552
    int nLen;
553
    char *pszPtr = NULL;
554

555
    /* If it's an AUTO projection then keep only id and strip off  */
556
    /* the parameters for now (we'll restore them at the end) */
557
    if (strncasecmp(pszEPSG, "AUTO:", 5) == 0) {
35✔
558
      if ((pszPtr = strchr(pszEPSG, ',')))
×
559
        *pszPtr = '\0';
×
560
    }
561

562
    nLen = strlen(pszEPSG);
35✔
563

564
    msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "MO", MS_FALSE,
35✔
565
                     &pszLyrEPSG);
566

567
    if (pszLyrEPSG == NULL ||
35✔
568
        (pszFound = strstr(pszLyrEPSG, pszEPSG)) == NULL ||
35✔
569
        !((*(pszFound + nLen) == '\0') || isspace(*(pszFound + nLen)))) {
35✔
570
      /* Not found in Layer's list of SRS (including projection object) */
571
      free(pszEPSG);
×
572
      pszEPSG = NULL;
×
573
    }
574
    msFree(pszLyrEPSG);
35✔
575
    if (pszEPSG && pszPtr)
35✔
576
      *pszPtr = ','; /* Restore full AUTO:... definition */
×
577
  }
578

579
  if (pszEPSG == NULL) {
41✔
580
    msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "MO", MS_TRUE,
6✔
581
                     &pszEPSG);
582
    if (pszEPSG == NULL || (strncasecmp(pszEPSG, "EPSG:", 5) != 0 &&
6✔
583
                            strncasecmp(pszEPSG, "AUTO:", 5) != 0)) {
×
584
      msSetError(MS_WMSCONNERR,
×
585
                 "Layer must have an EPSG or AUTO projection code (in its "
586
                 "PROJECTION object or wms_srs metadata)",
587
                 "msBuildWMSLayerURL()");
588
      msFree(pszEPSG);
×
589
      return MS_FAILURE;
×
590
    }
591
  }
592

593
  /* ------------------------------------------------------------------
594
   * For an AUTO projection, set the Units,lon0,lat0 if not already set
595
   * ------------------------------------------------------------------ */
596
  if (strncasecmp(pszEPSG, "AUTO:", 5) == 0 && strchr(pszEPSG, ',') == NULL) {
41✔
597
    pointObj oPoint;
598
    char *pszNewEPSG;
599

600
    /* Use center of map view for lon0,lat0 */
601
    oPoint.x = (map->extent.minx + map->extent.maxx) / 2.0;
×
602
    oPoint.y = (map->extent.miny + map->extent.maxy) / 2.0;
×
603
    msProjectPoint(&(map->projection), &(map->latlon), &oPoint);
×
604

605
    pszNewEPSG = (char *)msSmallMalloc(101 * sizeof(char));
×
606

607
    snprintf(pszNewEPSG, 100, "%s,9001,%.16g,%.16g", pszEPSG, oPoint.x,
×
608
             oPoint.y);
609
    pszNewEPSG[100] = '\0';
×
610
    free(pszEPSG);
×
611
    pszEPSG = pszNewEPSG;
×
612
  }
613

614
  /*
615
   * Work out whether we'll be wanting to flip the axis order for the request
616
   */
617
  pszUseStrictAxisOrder =
618
      msOWSLookupMetadata(&(lp->metadata), "MO", "strict_axis_order");
41✔
619
  if (pszUseStrictAxisOrder != NULL) {
41✔
620
    if (strncasecmp(pszUseStrictAxisOrder, "1", 1) == 0 ||
8✔
621
        strncasecmp(pszUseStrictAxisOrder, "true", 4) == 0) {
8✔
622
      bUseStrictAxisOrder = MS_TRUE;
623
    } else if (strncasecmp(pszUseStrictAxisOrder, "0", 1) == 0 ||
4✔
624
               strncasecmp(pszUseStrictAxisOrder, "false", 5) == 0) {
×
625
      bUseStrictAxisOrder = MS_FALSE;
626
    }
627
  }
628
  if (bUseStrictAxisOrder == MS_TRUE && pszEPSG &&
37✔
629
      strncasecmp(pszEPSG, "EPSG:", 5) == 0 &&
48✔
630
      msIsAxisInverted(atoi(pszEPSG + 5))) {
24✔
631
    bFlipAxisOrder = MS_TRUE;
632
  }
633

634
  msInitProjection(&layerRequestProjection);
41✔
635
  if (msLoadProjectionString(&layerRequestProjection, pszEPSG) != 0) {
41✔
636
    free(pszEPSG);
×
637
    msFreeProjection(&layerRequestProjection);
×
638
    return MS_FAILURE;
×
639
  }
640

641
  /* ------------------------------------------------------------------
642
   * Adjust for MapServer EXTENT being center of pixel and WMS BBOX being
643
   * edge of pixel (#2843).
644
   * ------------------------------------------------------------------ */
645
  bbox = map->extent;
41✔
646

647
  bbox.minx -= map->cellsize * 0.5;
41✔
648
  bbox.maxx += map->cellsize * 0.5;
41✔
649
  bbox.miny -= map->cellsize * 0.5;
41✔
650
  bbox.maxy += map->cellsize * 0.5;
41✔
651

652
  /* -------------------------------------------------------------------- */
653
  /*      Reproject if needed.                                            */
654
  /* -------------------------------------------------------------------- */
655
  if (msProjectionsDiffer(&(map->projection), &layerRequestProjection)) {
41✔
656
    msProjectRect(&(map->projection), &layerRequestProjection, &bbox);
17✔
657

658
    /* -------------------------------------------------------------------- */
659
    /*      Sometimes our remote WMS only accepts square pixel              */
660
    /*      requests.  If this is the case adjust adjust the number of      */
661
    /*      pixels or lines in the request so that the pixels are           */
662
    /*      square.                                                         */
663
    /* -------------------------------------------------------------------- */
664
    {
665
      const char *nonsquare_ok =
666
          msOWSLookupMetadata(&(lp->metadata), "MO", "nonsquare_ok");
17✔
667

668
      /* assume nonsquare_ok is false */
669
      if (nonsquare_ok != NULL && (strcasecmp(nonsquare_ok, "no") == 0 ||
17✔
670
                                   strcasecmp(nonsquare_ok, "false") == 0)) {
×
671
        double cellsize_x = (bbox.maxx - bbox.minx) / bbox_width;
×
672
        double cellsize_y = (bbox.maxy - bbox.miny) / bbox_height;
×
673

674
        if (cellsize_x < cellsize_y * 0.999999) {
×
675
          int new_bbox_height = ceil((cellsize_y / cellsize_x) * bbox_height);
×
676

677
          if (lp->debug)
×
678
            msDebug("NONSQUARE_OK=%s, adjusted HEIGHT from %d to %d to "
×
679
                    "equalize cellsize at %g.\n",
680
                    nonsquare_ok, bbox_height, new_bbox_height, cellsize_x);
681
          bbox_height = new_bbox_height;
682
        } else if (cellsize_y < cellsize_x * 0.999999) {
×
683
          int new_bbox_width = ceil((cellsize_x / cellsize_y) * bbox_width);
×
684

685
          if (lp->debug)
×
686
            msDebug("NONSQUARE_OK=%s, adjusted WIDTH from %d to %d to equalize "
×
687
                    "cellsize at %g.\n",
688
                    nonsquare_ok, bbox_width, new_bbox_width, cellsize_y);
689
          bbox_width = new_bbox_width;
690
        } else {
691
          if (lp->debug)
×
692
            msDebug("NONSQUARE_OK=%s, but cellsize was already square - no "
×
693
                    "change.\n",
694
                    nonsquare_ok);
695
        }
696
      }
697
    }
698
  }
699

700
  /* -------------------------------------------------------------------- */
701
  /*      If the layer has predefined extents, and a predefined           */
702
  /*      projection that matches the request projection, then            */
703
  /*      consider restricting the BBOX to match the limits.              */
704
  /* -------------------------------------------------------------------- */
705
  if (bbox_width != 0) {
41✔
706
    char *ows_srs;
707
    rectObj layer_rect;
708

709
    msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "MO", MS_FALSE,
41✔
710
                     &ows_srs);
711

712
    if (ows_srs && strchr(ows_srs, ' ') == NULL &&
71✔
713
        msOWSGetLayerExtent(map, lp, "MO", &layer_rect) == MS_SUCCESS &&
31✔
714
        !msProjectionsDiffer(&(lp->projection), &layerRequestProjection)) {
1✔
715
      /* fulloverlap */
716
      if (msRectContained(&bbox, &layer_rect)) {
1✔
717
        /* no changes */
718
      }
719

720
      /* no overlap */
721
      else if (!msRectOverlap(&layer_rect, &bbox)) {
1✔
722
        bbox_width = 0;
723
        bbox_height = 0;
724
      }
725

726
      else {
727
        double cellsize_x = (bbox.maxx - bbox.minx) / bbox_width;
1✔
728
        double cellsize_y = (bbox.maxy - bbox.miny) / bbox_height;
1✔
729
        double cellsize = MS_MIN(cellsize_x, cellsize_y);
1✔
730

731
        msRectIntersect(&bbox, &layer_rect);
1✔
732

733
        bbox_width = round((bbox.maxx - bbox.minx) / cellsize);
1✔
734
        bbox_height = round((bbox.maxy - bbox.miny) / cellsize);
1✔
735

736
        /* Force going through the resampler if we're going to receive a clipped
737
         * BBOX (#4931) */
738
        if (msLayerGetProcessingKey(lp, "RESAMPLE") == NULL) {
1✔
739
          msLayerSetProcessingKey(lp, "RESAMPLE", "nearest");
1✔
740
        }
741
      }
742
    }
743
    msFree(ows_srs);
41✔
744
  }
745

746
  /* -------------------------------------------------------------------- */
747
  /*      Potentially return the bbox.                                    */
748
  /* -------------------------------------------------------------------- */
749
  if (bbox_ret != NULL)
41✔
750
    *bbox_ret = bbox;
29✔
751

752
  if (width_ret != NULL)
41✔
753
    *width_ret = bbox_width;
29✔
754

755
  if (height_ret != NULL)
41✔
756
    *height_ret = bbox_height;
29✔
757

758
  /* ------------------------------------------------------------------
759
   * Build the request URL.
760
   * At this point we set only the following parameters for GetMap:
761
   *   REQUEST
762
   *   SRS (or CRS)
763
   *   BBOX
764
   *
765
   * And for GetFeatureInfo:
766
   *   X (I for 1.3.0)
767
   *   Y (J for 1.3.0)
768
   *   INFO_FORMAT
769
   *   FEATURE_COUNT (only if nFeatureCount > 0)
770
   *
771
   * The connection string should contain all other required params,
772
   * including:
773
   *   VERSION
774
   *   LAYERS
775
   *   FORMAT
776
   *   TRANSPARENT
777
   *   STYLES
778
   *   QUERY_LAYERS (for queryable layers only)
779
   * ------------------------------------------------------------------ */
780

781
  /* ------------------------------------------------------------------
782
   * Sometimes a requested layer is essential for the map, so if the
783
   * request fails or an error is delivered, the map has not to be drawn
784
   * ------------------------------------------------------------------ */
785
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO", "essential")) !=
41✔
786
      NULL) {
787
    if (strcasecmp(pszTmp, "true") == 0 || strcasecmp(pszTmp, "on") == 0 ||
×
788
        strcasecmp(pszTmp, "yes") == 0)
×
789
      bIsEssential = MS_TRUE;
790
    else
791
      bIsEssential = atoi(pszTmp);
792
  }
793

794
  if (nRequestType == WMS_GETFEATUREINFO) {
41✔
795
    char szBuf[100] = "";
8✔
796

797
    if (nVersion >= OWS_1_1_0)
8✔
798
      pszRequestParam = "GetFeatureInfo";
799
    else
800
      pszRequestParam = "feature_info";
801

802
    const char *pszExceptionsParam;
803
    if (nVersion >= OWS_1_3_0)
8✔
804
      pszExceptionsParam = "XML";
805
    else if (nVersion >= OWS_1_1_0) /* 1.1.0 to 1.1.0 */
4✔
806
      pszExceptionsParam = "application/vnd.ogc.se_xml";
807
    else
808
      pszExceptionsParam = "WMS_XML";
809

810
    msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE,
811
                        nVersion);
812
    msSetWMSParamInt(psWMSParams, "WIDTH", bbox_width);
8✔
813
    msSetWMSParamInt(psWMSParams, "HEIGHT", bbox_height);
8✔
814

815
    msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE,
8✔
816
                        nVersion);
817

818
    if (bFlipAxisOrder == MS_TRUE) {
8✔
819
      snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g", bbox.miny,
4✔
820
               bbox.minx, bbox.maxy, bbox.maxx);
821
    } else {
822
      snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g", bbox.minx,
4✔
823
               bbox.miny, bbox.maxx, bbox.maxy);
824
    }
825
    msSetWMSParamString(psWMSParams, "BBOX", szBuf, MS_TRUE, nVersion);
8✔
826

827
    if (nVersion >= OWS_1_3_0) {
8✔
828
      msSetWMSParamInt(psWMSParams, "I", nClickX);
4✔
829
      msSetWMSParamInt(psWMSParams, "J", nClickY);
4✔
830
    } else {
831
      msSetWMSParamInt(psWMSParams, "X", nClickX);
4✔
832
      msSetWMSParamInt(psWMSParams, "Y", nClickY);
4✔
833
    }
834

835
    msSetWMSParamString(psWMSParams, "EXCEPTIONS", pszExceptionsParam, MS_FALSE,
836
                        nVersion);
837
    msSetWMSParamString(psWMSParams, "INFO_FORMAT", pszInfoFormat, MS_TRUE,
8✔
838
                        nVersion);
839

840
    if (pszQueryLayers) { /* not set in CONNECTION string */
8✔
841
      msSetWMSParamString(psWMSParams, "QUERY_LAYERS", pszQueryLayers, MS_FALSE,
842
                          nVersion);
843
    }
844

845
    /* If FEATURE_COUNT <= 0 then don't pass this parameter */
846
    /* The spec states that FEATURE_COUNT must be greater than zero */
847
    /* and if not passed then the behavior is up to the server */
848
    if (nFeatureCount > 0) {
8✔
849
      msSetWMSParamInt(psWMSParams, "FEATURE_COUNT", nFeatureCount);
8✔
850
    }
851

852
  } else if (nRequestType == WMS_GETLEGENDGRAPHIC) {
33✔
853
    if (map->extent.maxx > map->extent.minx && map->width > 0 &&
4✔
854
        map->height > 0) {
4✔
855
      char szBuf[20] = "";
4✔
856
      double scaledenom;
857
      msCalculateScale(map->extent, map->units, map->width, map->height,
4✔
858
                       map->resolution, &scaledenom);
859
      snprintf(szBuf, 20, "%g", scaledenom);
4✔
860
      msSetWMSParamString(psWMSParams, "SCALE", szBuf, MS_FALSE, nVersion);
861
    }
862
    pszRequestParam = "GetLegendGraphic";
863

864
    /*
865
    const char* pszExceptionsParam = msOWSLookupMetadata(&(lp->metadata),
866
                         "MO", "exceptions_format");
867
    if (pszExceptionsParam == NULL) {
868
      if (nVersion >= OWS_1_1_0 && nVersion < OWS_1_3_0)
869
        pszExceptionsParam = "application/vnd.ogc.se_inimage";
870
      else
871
        pszExceptionsParam = "INIMAGE";
872
    }*/
873

874
    if (pszLayer) { /* not set in CONNECTION string */
4✔
875
      msSetWMSParamString(psWMSParams, "LAYER", pszLayer, MS_FALSE, nVersion);
876
    }
877

878
    msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE,
879
                        nVersion);
880
    msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE,
4✔
881
                        nVersion);
882

883
    if (nVersion >= OWS_1_3_0) {
4✔
884
      msSetWMSParamString(psWMSParams, "SLD_VERSION", "1.1.0", MS_FALSE,
885
                          nVersion);
886
    }
887

888
  } else { /* if (nRequestType == WMS_GETMAP) */
889
    char szBuf[100] = "";
29✔
890

891
    if (nVersion >= OWS_1_1_0)
29✔
892
      pszRequestParam = "GetMap";
893
    else
894
      pszRequestParam = "map";
895

896
    const char *pszExceptionsParam =
897
        msOWSLookupMetadata(&(lp->metadata), "MO", "exceptions_format");
29✔
898

899
    if (!bIsEssential) {
29✔
900
      if (pszExceptionsParam == NULL) {
29✔
901
        if (nVersion >= OWS_1_1_0 && nVersion < OWS_1_3_0)
29✔
902
          pszExceptionsParam = "application/vnd.ogc.se_inimage";
903
        else
904
          pszExceptionsParam = "INIMAGE";
905
      }
906
    } else {
907
      /* if layer is essential, do not emit EXCEPTIONS parameter (defaults to
908
       * XML) */
909
      pszExceptionsParam = NULL;
910
    }
911

912
    msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE,
913
                        nVersion);
914
    msSetWMSParamInt(psWMSParams, "WIDTH", bbox_width);
29✔
915
    msSetWMSParamInt(psWMSParams, "HEIGHT", bbox_height);
29✔
916
    msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE,
29✔
917
                        nVersion);
918

919
    if (bFlipAxisOrder == MS_TRUE) {
29✔
920
      snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g", bbox.miny,
15✔
921
               bbox.minx, bbox.maxy, bbox.maxx);
922
    } else {
923
      snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g", bbox.minx,
14✔
924
               bbox.miny, bbox.maxx, bbox.maxy);
925
    }
926
    msSetWMSParamString(psWMSParams, "BBOX", szBuf, MS_TRUE, nVersion);
29✔
927
    if (pszExceptionsParam) {
29✔
928
      msSetWMSParamString(psWMSParams, "EXCEPTIONS", pszExceptionsParam,
929
                          MS_FALSE, nVersion);
930
    }
931
  }
932

933
  free(pszEPSG);
41✔
934
  msFreeProjection(&layerRequestProjection);
41✔
935
  return MS_SUCCESS;
41✔
936

937
#else
938
  /* ------------------------------------------------------------------
939
   * WMS CONNECTION Support not included...
940
   * ------------------------------------------------------------------ */
941
  msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
942
             "msBuildWMSLayerURL()");
943
  return MS_FAILURE;
944

945
#endif /* USE_WMS_LYR */
946
}
947

948
/**********************************************************************
949
 *                          msWMSGetFeatureInfoURL()
950
 *
951
 * Build a GetFeatureInfo URL for this layer.
952
 *
953
 * Returns a reference to a newly allocated string that should be freed
954
 * by the caller.
955
 **********************************************************************/
956
char *msWMSGetFeatureInfoURL(mapObj *map, layerObj *lp, int nClickX,
×
957
                             int nClickY, int nFeatureCount,
958
                             const char *pszInfoFormat) {
959
  wmsParamsObj sThisWMSParams;
960
  char *pszURL;
961

962
  msInitWmsParamsObj(&sThisWMSParams);
×
963

964
  if (msBuildWMSLayerURL(map, lp, WMS_GETFEATUREINFO, nClickX, nClickY,
×
965
                         nFeatureCount, pszInfoFormat, NULL, NULL, NULL,
966
                         &sThisWMSParams) != MS_SUCCESS) {
967
    return NULL;
968
  }
969

970
  pszURL = msBuildURLFromWMSParams(&sThisWMSParams);
×
971
  msFreeWmsParamsObj(&sThisWMSParams);
×
972

973
  return pszURL;
×
974
}
975

976
/**********************************************************************
977
 *                          msPrepareWMSLayerRequest()
978
 *
979
 **********************************************************************/
980

981
int msPrepareWMSLayerRequest(int nLayerId, mapObj *map, layerObj *lp,
41✔
982
                             int nRequestType,
983
                             enum MS_CONNECTION_TYPE lastconnectiontype,
984
                             wmsParamsObj *psLastWMSParams, int nClickX,
985
                             int nClickY, int nFeatureCount,
986
                             const char *pszInfoFormat,
987
                             httpRequestObj *pasReqInfo, int *numRequests) {
988
#ifdef USE_WMS_LYR
989
  char *pszURL = NULL, *pszHTTPCookieData = NULL;
990
  const char *pszTmp;
991
  rectObj bbox = {0};
41✔
992
  projectionObj lyrRequestProjection;
993
  const char *pszEPSG;
994
  int bbox_width = 0, bbox_height = 0;
41✔
995
  int nTimeout, bOkToMerge, bForceSeparateRequest, bCacheToDisk;
996
  wmsParamsObj sThisWMSParams;
997
  int ret = MS_FAILURE;
998

999
  if (lp->connectiontype != MS_WMS)
41✔
1000
    return MS_FAILURE;
1001

1002
  msInitWmsParamsObj(&sThisWMSParams);
41✔
1003

1004
  /* ------------------------------------------------------------------
1005
   * Build the request URL
1006
   * ------------------------------------------------------------------ */
1007

1008
  switch (nRequestType) {
41✔
1009
  case WMS_GETMAP:
29✔
1010
    ret = msBuildWMSLayerURL(map, lp, WMS_GETMAP, 0, 0, 0, NULL, &bbox,
29✔
1011
                             &bbox_width, &bbox_height, &sThisWMSParams);
1012
    break;
29✔
1013

1014
  case WMS_GETFEATUREINFO:
8✔
1015
    ret = msBuildWMSLayerURL(map, lp, WMS_GETFEATUREINFO, nClickX, nClickY,
8✔
1016
                             nFeatureCount, pszInfoFormat, NULL, NULL, NULL,
1017
                             &sThisWMSParams);
1018
    break;
8✔
1019

1020
  case WMS_GETLEGENDGRAPHIC:
4✔
1021
    ret = msBuildWMSLayerURL(map, lp, WMS_GETLEGENDGRAPHIC, 0, 0, 0, NULL, NULL,
4✔
1022
                             NULL, NULL, &sThisWMSParams);
1023
    break;
4✔
1024

1025
  default:
1026
    assert(FALSE);
1027
    break;
1028
  }
1029

1030
  if (ret != MS_SUCCESS) {
41✔
1031
    /* an error was already reported. */
1032
    msFreeWmsParamsObj(&sThisWMSParams);
×
1033
    return MS_FAILURE;
×
1034
  }
1035

1036
  /* Load projection that we will request, it may differ from current layer
1037
   * projection */
1038
  char *srsKeys[] = {"SRS", "CRS"};
41✔
1039
  int i;
1040
  for (i = 0; i < 2; i++) {
65✔
1041
    pszEPSG = msLookupHashTable(sThisWMSParams.params, srsKeys[i]);
65✔
1042
    if (pszEPSG != NULL && strlen(pszEPSG) > 0)
65✔
1043
      break;
1044
  }
1045

1046
  msInitProjection(&lyrRequestProjection);
41✔
1047
  if (pszEPSG == NULL || msLoadProjectionStringEPSG(&lyrRequestProjection,
41✔
1048
                                                    pszEPSG) != MS_SUCCESS) {
1049
    msFreeWmsParamsObj(&sThisWMSParams);
×
1050
    msFreeProjection(&lyrRequestProjection);
×
1051
    return MS_FAILURE;
×
1052
  }
1053

1054
  /* ------------------------------------------------------------------
1055
   * Check if the request is empty, perhaps due to reprojection problems
1056
   * or wms_extents restrictions.
1057
   * ------------------------------------------------------------------ */
1058
  if ((nRequestType == WMS_GETMAP) && (bbox_width == 0 || bbox_height == 0)) {
41✔
1059
    msFreeWmsParamsObj(&sThisWMSParams);
×
1060
    msFreeProjection(&lyrRequestProjection);
×
1061
    return MS_SUCCESS; /* No overlap. */
×
1062
  }
1063

1064
  /* ------------------------------------------------------------------
1065
   * Check if layer overlaps current view window (using wms_latlonboundingbox)
1066
   * ------------------------------------------------------------------ */
1067
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO",
41✔
1068
                                    "latlonboundingbox")) != NULL) {
1069
    char **tokens;
1070
    int n;
1071
    rectObj ext;
1072

1073
    tokens = msStringSplit(pszTmp, ' ', &n);
×
1074
    if (tokens == NULL || n != 4) {
×
1075
      msSetError(
×
1076
          MS_WMSCONNERR,
1077
          "Wrong number of arguments for 'wms_latlonboundingbox' metadata.",
1078
          "msDrawWMSLayer()");
1079
      msFreeWmsParamsObj(&sThisWMSParams);
×
1080
      return MS_FAILURE;
×
1081
    }
1082

1083
    ext.minx = atof(tokens[0]);
×
1084
    ext.miny = atof(tokens[1]);
×
1085
    ext.maxx = atof(tokens[2]);
×
1086
    ext.maxy = atof(tokens[3]);
×
1087

1088
    msFreeCharArray(tokens, n);
×
1089

1090
    /* Reproject latlonboundingbox to the selected SRS for the layer and */
1091
    /* check if it overlaps the bbox that we calculated for the request */
1092

1093
    msProjectRect(&(map->latlon), &lyrRequestProjection, &ext);
×
1094
    if (!msRectOverlap(&bbox, &ext)) {
×
1095
      /* No overlap... nothing to do */
1096
      msFreeProjection(&lyrRequestProjection);
×
1097
      msFreeWmsParamsObj(&sThisWMSParams);
×
1098
      return MS_SUCCESS; /* No overlap. */
×
1099
    }
1100
  }
1101

1102
  /* ------------------------------------------------------------------
1103
   * check to see if a the metadata wms_connectiontimeout is set. If it is
1104
   * the case we will use it, else we use the default which is 30 seconds.
1105
   * First check the metadata in the layer object and then in the map object.
1106
   * ------------------------------------------------------------------ */
1107
  nTimeout = 30; /* Default is 30 seconds  */
1108
  if ((pszTmp = msOWSLookupMetadata2(&(lp->metadata), &(map->web.metadata),
41✔
1109
                                     "MO", "connectiontimeout")) != NULL) {
1110
    nTimeout = atoi(pszTmp);
1111
  }
1112

1113
  /* ------------------------------------------------------------------
1114
   * Check if we want to use in memory images instead of writing to disk.
1115
   * ------------------------------------------------------------------ */
1116
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO", "cache_to_disk")) !=
41✔
1117
      NULL) {
1118
    if (strcasecmp(pszTmp, "true") == 0 || strcasecmp(pszTmp, "on") == 0 ||
×
1119
        strcasecmp(pszTmp, "yes") == 0)
×
1120
      bCacheToDisk = MS_TRUE;
1121
    else
1122
      bCacheToDisk = atoi(pszTmp);
1123
  } else
1124
    bCacheToDisk = MS_FALSE;
1125

1126
  if (bCacheToDisk) {
×
1127
    /* We'll store the remote server's response to a tmp file. */
1128
    if (map->web.imagepath == NULL || strlen(map->web.imagepath) == 0) {
×
1129
      msSetError(MS_WMSERR,
×
1130
                 "WEB.IMAGEPATH must be set to use WMS client connections.",
1131
                 "msPrepareWMSLayerRequest()");
1132
      msFreeProjection(&lyrRequestProjection);
×
1133
      msFreeWmsParamsObj(&sThisWMSParams);
×
1134
      return MS_FAILURE;
×
1135
    }
1136
  }
1137

1138
  /* ------------------------------------------------------------------
1139
   * Check if layer can be merged with previous WMS layer requests
1140
   * Metadata wms_force_separate_request can be set to 1 to prevent this
1141
   * this layer from being combined with any other layer.
1142
   * ------------------------------------------------------------------ */
1143
  bForceSeparateRequest = MS_FALSE;
1144
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO",
41✔
1145
                                    "force_separate_request")) != NULL) {
1146
    bForceSeparateRequest = atoi(pszTmp);
1147
  }
1148
  bOkToMerge = MS_FALSE;
1149
  if (!bForceSeparateRequest && lastconnectiontype == MS_WMS &&
41✔
1150
      psLastWMSParams != NULL &&
12✔
1151
      sThisWMSParams.numparams == psLastWMSParams->numparams &&
12✔
1152
      strcmp(sThisWMSParams.onlineresource, psLastWMSParams->onlineresource) ==
×
1153
          0) {
1154
    const char *key, *value1, *value2;
1155
    bOkToMerge = MS_TRUE;
1156

1157
    key = msFirstKeyFromHashTable(sThisWMSParams.params);
×
1158
    while (key != NULL && bOkToMerge == MS_TRUE) {
×
1159
      /* Skip parameters whose values can be different */
1160
      if (!(strcmp(key, "LAYERS") == 0 || strcmp(key, "QUERY_LAYERS") == 0 ||
×
1161
            strcmp(key, "STYLES") == 0)) {
×
1162
        value1 = msLookupHashTable(psLastWMSParams->params, key);
×
1163
        value2 = msLookupHashTable(sThisWMSParams.params, key);
×
1164

1165
        if (value1 == NULL || value2 == NULL || strcmp(value1, value2) != 0) {
×
1166
          bOkToMerge = MS_FALSE;
1167
          break;
1168
        }
1169
      }
1170
      key = msNextKeyFromHashTable(sThisWMSParams.params, key);
×
1171
    }
1172
  }
1173

1174
  /*------------------------------------------------------------------
1175
   * Check to see if there's a HTTP Cookie to forward
1176
   * If Cookie differ between the two connection, it's NOT OK to merge
1177
   * the connection
1178
   * ------------------------------------------------------------------ */
1179
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO", "http_cookie")) !=
41✔
1180
      NULL) {
1181
    if (strcasecmp(pszTmp, "forward") == 0) {
×
1182
      pszTmp = msLookupHashTable(&(map->web.metadata), "http_cookie_data");
×
1183
      if (pszTmp != NULL) {
×
1184
        pszHTTPCookieData = msStrdup(pszTmp);
×
1185
      }
1186
    } else {
1187
      pszHTTPCookieData = msStrdup(pszTmp);
×
1188
    }
1189
  } else if ((pszTmp = msOWSLookupMetadata(&(map->web.metadata), "MO",
41✔
1190
                                           "http_cookie")) != NULL) {
1191
    if (strcasecmp(pszTmp, "forward") == 0) {
×
1192
      pszTmp = msLookupHashTable(&(map->web.metadata), "http_cookie_data");
×
1193
      if (pszTmp != NULL) {
×
1194
        pszHTTPCookieData = msStrdup(pszTmp);
×
1195
      }
1196
    } else {
1197
      pszHTTPCookieData = msStrdup(pszTmp);
×
1198
    }
1199
  }
1200

1201
  if (bOkToMerge && pszHTTPCookieData != sThisWMSParams.httpcookiedata) {
41✔
1202
    if (pszHTTPCookieData == NULL || sThisWMSParams.httpcookiedata == NULL) {
×
1203
      bOkToMerge = MS_FALSE;
1204
    } else if (strcmp(pszHTTPCookieData, sThisWMSParams.httpcookiedata) != 0) {
×
1205
      bOkToMerge = MS_FALSE;
1206
    }
1207
  }
1208

1209
  if (bOkToMerge) {
1210
    /* Merge both requests into sThisWMSParams
1211
     */
1212
    const char *value1, *value2;
1213
    char *keys[] = {"LAYERS", "QUERY_LAYERS", "STYLES"};
×
1214
    int i;
1215

1216
    for (i = 0; i < 3; i++) {
×
1217
      value1 = msLookupHashTable(psLastWMSParams->params, keys[i]);
×
1218
      value2 = msLookupHashTable(sThisWMSParams.params, keys[i]);
×
1219
      if (value1 && value2) {
×
1220
        char *pszBuf;
1221
        int nLen;
1222

1223
        nLen = strlen(value1) + strlen(value2) + 2;
×
1224
        pszBuf = malloc(nLen);
×
1225
        MS_CHECK_ALLOC(pszBuf, nLen, MS_FAILURE);
×
1226

1227
        snprintf(pszBuf, nLen, "%s,%s", value1, value2);
1228
        /* TODO should really send the server request version here */
1229
        msSetWMSParamString(&sThisWMSParams, keys[i], pszBuf, MS_FALSE,
1230
                            OWS_VERSION_NOTSET);
1231

1232
        /* This key existed already, we don't want it counted twice */
1233
        sThisWMSParams.numparams--;
×
1234

1235
        msFree(pszBuf);
×
1236
      }
1237
    }
1238
  }
1239

1240
  /* ------------------------------------------------------------------
1241
   * Build new request URL
1242
   * ------------------------------------------------------------------ */
1243
  pszURL = msBuildURLFromWMSParams(&sThisWMSParams);
41✔
1244

1245
  if (bOkToMerge && (*numRequests) > 0) {
41✔
1246
    /* ------------------------------------------------------------------
1247
     * Update the last request in the array:  (*numRequests)-1
1248
     * ------------------------------------------------------------------ */
1249
    msFree(pasReqInfo[(*numRequests) - 1].pszGetUrl);
×
1250
    pasReqInfo[(*numRequests) - 1].pszGetUrl = pszURL;
×
1251
    pszURL = NULL;
1252
    pasReqInfo[(*numRequests) - 1].debug |= lp->debug;
×
1253
    if (nTimeout > pasReqInfo[(*numRequests) - 1].nTimeout)
×
1254
      pasReqInfo[(*numRequests) - 1].nTimeout = nTimeout;
×
1255
  } else {
1256
    /* ------------------------------------------------------------------
1257
     * Add a request to the array (already preallocated)
1258
     * ------------------------------------------------------------------ */
1259
    pasReqInfo[(*numRequests)].nLayerId = nLayerId;
41✔
1260
    pasReqInfo[(*numRequests)].pszGetUrl = pszURL;
41✔
1261

1262
    pszURL = NULL;
1263
    pasReqInfo[(*numRequests)].pszHTTPCookieData = pszHTTPCookieData;
41✔
1264
    pszHTTPCookieData = NULL;
1265
    if (bCacheToDisk) {
41✔
1266
      pasReqInfo[(*numRequests)].pszOutputFile =
×
1267
          msTmpFile(map, map->mappath, NULL, "wms.tmp");
×
1268
    } else
1269
      pasReqInfo[(*numRequests)].pszOutputFile = NULL;
41✔
1270
    pasReqInfo[(*numRequests)].nStatus = 0;
41✔
1271
    pasReqInfo[(*numRequests)].nTimeout = nTimeout;
41✔
1272
    pasReqInfo[(*numRequests)].bbox = bbox;
41✔
1273
    pasReqInfo[(*numRequests)].width = bbox_width;
41✔
1274
    pasReqInfo[(*numRequests)].height = bbox_height;
41✔
1275
    pasReqInfo[(*numRequests)].debug = lp->debug;
41✔
1276
    pasReqInfo[(*numRequests)].pszEPSG = msStrdup(pszEPSG);
41✔
1277

1278
    if (msHTTPAuthProxySetup(&(map->web.metadata), &(lp->metadata), pasReqInfo,
41✔
1279
                             *numRequests, map, "MO") != MS_SUCCESS)
1280
      return MS_FAILURE;
1281

1282
    (*numRequests)++;
41✔
1283
  }
1284

1285
  /* ------------------------------------------------------------------
1286
   * Replace contents of psLastWMSParams with sThisWMSParams
1287
   * unless bForceSeparateRequest is set in which case we make it empty
1288
   * ------------------------------------------------------------------ */
1289
  if (psLastWMSParams) {
41✔
1290
    msFreeWmsParamsObj(psLastWMSParams);
41✔
1291
    if (!bForceSeparateRequest)
41✔
1292
      *psLastWMSParams = sThisWMSParams;
41✔
1293
    else
1294
      msInitWmsParamsObj(psLastWMSParams);
×
1295
  } else {
1296
    /* Can't copy it, so we just free it */
1297
    msFreeWmsParamsObj(&sThisWMSParams);
×
1298
  }
1299

1300
  msFreeProjection(&lyrRequestProjection);
41✔
1301

1302
  return MS_SUCCESS;
41✔
1303

1304
#else
1305
  /* ------------------------------------------------------------------
1306
   * WMS CONNECTION Support not included...
1307
   * ------------------------------------------------------------------ */
1308
  msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1309
             "msDrawWMSLayer()");
1310
  return (MS_FAILURE);
1311

1312
#endif /* USE_WMS_LYR */
1313
}
1314

1315
/**********************************************************************
1316
 *                          msDrawWMSLayerLow()
1317
 *
1318
 **********************************************************************/
1319

1320
int msDrawWMSLayerLow(int nLayerId, httpRequestObj *pasReqInfo, int numRequests,
29✔
1321
                      mapObj *map, layerObj *lp, imageObj *img) {
1322
#ifdef USE_WMS_LYR
1323
  int status = MS_SUCCESS;
1324
  int iReq = -1;
1325
  char szPath[MS_MAXPATHLEN];
1326
  int currenttype;
1327
  int currentconnectiontype;
1328
  projectionObj currentprojectionobject;
1329
  rectObj currentextent;
1330
  int numclasses;
1331
  char *mem_filename = NULL;
1332
  const char *pszTmp;
1333
  int bIsEssential = MS_FALSE;
1334

1335
  /* ------------------------------------------------------------------
1336
   * Sometimes a requested layer is essential for the map, so if the
1337
   * request fails or an error is delivered, the map has not to be drawn
1338
   * ------------------------------------------------------------------ */
1339
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO", "essential")) !=
29✔
1340
      NULL) {
1341
    if (strcasecmp(pszTmp, "true") == 0 || strcasecmp(pszTmp, "on") == 0 ||
×
1342
        strcasecmp(pszTmp, "yes") == 0)
×
1343
      bIsEssential = MS_TRUE;
1344
    else
1345
      bIsEssential = atoi(pszTmp);
1346
  }
1347

1348
  /* ------------------------------------------------------------------
1349
   * Find the request info for this layer in the array, based on nLayerId
1350
   * ------------------------------------------------------------------ */
1351
  for (iReq = 0; iReq < numRequests; iReq++) {
29✔
1352
    if (pasReqInfo[iReq].nLayerId == nLayerId)
29✔
1353
      break;
1354
  }
1355

1356
  if (iReq == numRequests) {
29✔
1357
    /* This layer was skipped or was included in a multi-layers
1358
     * request ... nothing to do.
1359
     */
1360
    return MS_SUCCESS;
1361
  }
1362

1363
  if (!MS_HTTP_SUCCESS(pasReqInfo[iReq].nStatus)) {
29✔
1364
    /* ====================================================================
1365
          Failed downloading layer... we log an error but we still return
1366
          SUCCESS here so that the layer is only skipped instead of aborting
1367
          the whole draw map.
1368
          If the layer is essential the map is not to be drawn.
1369
     ==================================================================== */
1370
    msSetError(MS_WMSERR,
×
1371
               "WMS GetMap request failed for layer '%s' (Status %d: %s).",
1372
               "msDrawWMSLayerLow()", (lp->name ? lp->name : "(null)"),
×
1373
               pasReqInfo[iReq].nStatus, pasReqInfo[iReq].pszErrBuf);
1374

1375
    if (!bIsEssential)
×
1376
      return MS_SUCCESS;
1377
    else
1378
      return MS_FAILURE;
1379
  }
1380

1381
  /* ------------------------------------------------------------------
1382
   * Check the Content-Type of the response to see if we got an exception,
1383
   * if yes then try to parse it and pass the info to msSetError().
1384
   * We log an error but we still return SUCCESS here so that the layer
1385
   * is only skipped instead of aborting the whole draw map.
1386
   * If the layer is essential the map is not to be drawn.
1387
   * ------------------------------------------------------------------ */
1388
  if (pasReqInfo[iReq].pszContentType &&
29✔
1389
      (strcmp(pasReqInfo[iReq].pszContentType, "text/xml") == 0 ||
29✔
1390
       strcmp(pasReqInfo[iReq].pszContentType, "application/vnd.ogc.se_xml") ==
29✔
1391
           0)) {
1392
    FILE *fp;
1393
    char szBuf[MS_BUFFER_LENGTH];
1394

1395
    if (pasReqInfo[iReq].pszOutputFile) {
×
1396
      fp = fopen(pasReqInfo[iReq].pszOutputFile, "r");
×
1397
      if (fp) {
×
1398
        /* TODO: For now we'll only read the first chunk and return it
1399
         * via msSetError()... we should really try to parse the XML
1400
         * and extract the exception code/message though
1401
         */
1402
        size_t nSize;
1403

1404
        nSize = fread(szBuf, sizeof(char), MS_BUFFER_LENGTH - 1, fp);
1405
        if (nSize < MS_BUFFER_LENGTH)
×
1406
          szBuf[nSize] = '\0';
×
1407
        else {
1408
          strlcpy(szBuf, "(!!!)", sizeof(szBuf)); /* This should never happen */
1409
        }
1410

1411
        fclose(fp);
×
1412

1413
        /* We're done with the remote server's response... delete it. */
1414
        if (!lp->debug)
×
1415
          unlink(pasReqInfo[iReq].pszOutputFile);
×
1416
      } else {
1417
        strlcpy(szBuf, "(Failed to open exception response)", sizeof(szBuf));
1418
      }
1419
    } else {
1420
      strlcpy(szBuf, pasReqInfo[iReq].result_data, MS_BUFFER_LENGTH);
×
1421
    }
1422

1423
    if (lp->debug)
×
1424
      msDebug("WMS GetMap request got XML exception for layer '%s': %s.",
×
1425
              (lp->name ? lp->name : "(null)"), szBuf);
×
1426

1427
    msSetError(MS_WMSERR,
×
1428
               "WMS GetMap request got XML exception for layer '%s': %s.",
1429
               "msDrawWMSLayerLow()", (lp->name ? lp->name : "(null)"), szBuf);
×
1430

1431
    if (!bIsEssential)
×
1432
      return MS_SUCCESS;
1433
    else
1434
      return MS_FAILURE;
×
1435
  }
1436

1437
  /* ------------------------------------------------------------------
1438
   * If the output was written to a memory buffer, then we will need
1439
   * to attach a "VSI" name to this buffer.
1440
   * ------------------------------------------------------------------ */
1441
  if (pasReqInfo[iReq].pszOutputFile == NULL) {
29✔
1442
    mem_filename = msTmpFile(map, NULL, "/vsimem/msout/", "img.tmp");
29✔
1443

1444
    VSIFCloseL(VSIFileFromMemBuffer(
29✔
1445
        mem_filename, (GByte *)pasReqInfo[iReq].result_data,
29✔
1446
        (vsi_l_offset)pasReqInfo[iReq].result_size, FALSE));
29✔
1447
  }
1448

1449
  /* ------------------------------------------------------------------
1450
   * Prepare layer for drawing, reprojecting the image received from the
1451
   * server if needed...
1452
   * ------------------------------------------------------------------ */
1453

1454
  // Aassign requested projection temporarily to layer
1455
  currentprojectionobject = lp->projection;
29✔
1456
  msInitProjection(&(lp->projection));
29✔
1457
  if (pasReqInfo[iReq].pszEPSG == NULL ||
58✔
1458
      msLoadProjectionString(&(lp->projection), pasReqInfo[iReq].pszEPSG) !=
29✔
1459
          MS_SUCCESS) {
1460
    msSetError(
×
1461
        MS_WMSERR, "WMS GetMap failed to load projection for layer '%s': %s.",
1462
        "msDrawWMSLayerLow()", (lp->name ? lp->name : "(null)"),
×
1463
        (pasReqInfo[iReq].pszEPSG ? pasReqInfo[iReq].pszEPSG : "(null)"));
×
1464

1465
    // Restore layer projection
1466
    msFreeProjection(&(lp->projection));
×
1467
    lp->projection = currentprojectionobject;
×
1468
    if (!bIsEssential)
×
1469
      return MS_SUCCESS;
1470
    else
1471
      return MS_FAILURE;
1472
  }
1473

1474
  /* keep the current type that will be restored at the end of this  */
1475
  /* function. */
1476
  currenttype = lp->type;
29✔
1477
  currentconnectiontype = lp->connectiontype;
29✔
1478
  currentextent = lp->extent;
29✔
1479
  lp->type = MS_LAYER_RASTER;
29✔
1480
  lp->connectiontype = MS_RASTER;
29✔
1481
  lp->extent = pasReqInfo[iReq].bbox;
29✔
1482

1483
  /* set the classes to 0 so that It won't do client side */
1484
  /* classification if an sld was set. */
1485
  numclasses = lp->numclasses;
29✔
1486

1487
  /* ensure the file connection is closed right away after the layer */
1488
  /* is rendered */
1489
  msLayerSetProcessingKey(lp, "CLOSE_CONNECTION", "NORMAL");
29✔
1490

1491
  if (msOWSLookupMetadata(&(lp->metadata), "MO", "sld_body") ||
58✔
1492
      msOWSLookupMetadata(&(lp->metadata), "MO", "sld_url"))
29✔
1493
    lp->numclasses = 0;
×
1494

1495
  free(lp->data);
29✔
1496
  lp->data = mem_filename != NULL ? mem_filename
29✔
1497
                                  : msStrdup(pasReqInfo[iReq].pszOutputFile);
29✔
1498

1499
  const char *pszAllowedDrivers =
1500
      msOWSLookupMetadata(&(lp->metadata), "MO", "allowed_gdal_drivers");
29✔
1501
  if (!pszAllowedDrivers) {
29✔
1502
    char *pszFormat = msWMSLayerGetFormat(lp);
27✔
1503
    if (IsPNGFormat(pszFormat))
27✔
1504
      pszAllowedDrivers = "PNG";
1505
    else if (IsJPEGFormat(pszFormat))
11✔
1506
      pszAllowedDrivers = "JPEG";
1507
    else
1508
      pszAllowedDrivers = "PNG,JPEG";
1509
    msFree(pszFormat);
27✔
1510
  }
1511
  // This will be used by msDrawRasterLayerLow()
1512
  msLayerSetProcessingKey(lp, "ALLOWED_GDAL_DRIVERS", pszAllowedDrivers);
29✔
1513
  if (lp->debug)
29✔
1514
    msDebug("Setting ALLOWED_GDAL_DRIVERS = %s\n", pszAllowedDrivers);
11✔
1515

1516
  /* #3138 If PROCESSING "RESAMPLE=..." is set we cannot use the simple case */
1517
  if (!msProjectionsDiffer(&(map->projection), &(lp->projection)) &&
45✔
1518
      (msLayerGetProcessingKey(lp, "RESAMPLE") == NULL)) {
16✔
1519
    /* The simple case... no reprojection needed... render layer directly. */
1520
    lp->transform = MS_FALSE;
16✔
1521
    /* if (msDrawRasterLayerLow(map, lp, img) != 0) */
1522
    /* status = MS_FAILURE; */
1523
    if (msDrawLayer(map, lp, img) != 0)
16✔
1524
      status = MS_FAILURE;
1525
  } else {
1526
    VSILFILE *fp;
1527
    char *wldfile;
1528
    /* OK, we have to resample the raster to map projection... */
1529
    lp->transform = MS_TRUE;
13✔
1530
    msLayerSetProcessingKey(lp, "LOAD_WHOLE_IMAGE", "YES");
13✔
1531

1532
    /* Create a world file with raster extents */
1533
    /* One line per value, in this order: cx, 0, 0, cy, ulx, uly */
1534
    wldfile = msBuildPath(szPath, lp->map->mappath, lp->data);
13✔
1535
    if (wldfile && (strlen(wldfile) >= 3))
13✔
1536
      strcpy(wldfile + strlen(wldfile) - 3, "wld");
13✔
1537
    if (wldfile && (fp = VSIFOpenL(wldfile, "wt")) != NULL) {
26✔
1538
      double dfCellSizeX =
13✔
1539
          MS_OWS_CELLSIZE(pasReqInfo[iReq].bbox.minx,
13✔
1540
                          pasReqInfo[iReq].bbox.maxx, pasReqInfo[iReq].width);
1541
      double dfCellSizeY =
13✔
1542
          MS_OWS_CELLSIZE(pasReqInfo[iReq].bbox.maxy,
13✔
1543
                          pasReqInfo[iReq].bbox.miny, pasReqInfo[iReq].height);
1544
      char world_text[5000];
1545

1546
      sprintf(world_text, "%.12f\n0\n0\n%.12f\n%.12f\n%.12f\n", dfCellSizeX,
13✔
1547
              dfCellSizeY, pasReqInfo[iReq].bbox.minx + dfCellSizeX * 0.5,
13✔
1548
              pasReqInfo[iReq].bbox.maxy + dfCellSizeY * 0.5);
13✔
1549

1550
      VSIFWriteL(world_text, 1, strlen(world_text), fp);
13✔
1551
      VSIFCloseL(fp);
13✔
1552

1553
      /* GDAL should be called to reproject automatically. */
1554
      if (msDrawLayer(map, lp, img) != 0)
13✔
1555
        status = MS_FAILURE;
1556

1557
      if (!lp->debug || mem_filename != NULL)
13✔
1558
        VSIUnlink(wldfile);
13✔
1559
    } else {
1560
      msSetError(MS_WMSCONNERR, "Unable to create wld file for WMS slide.",
×
1561
                 "msDrawWMSLayer()");
1562
      status = MS_FAILURE;
1563
    }
1564
  }
1565

1566
  /* We're done with the remote server's response... delete it. */
1567
  if (!lp->debug || mem_filename != NULL)
29✔
1568
    VSIUnlink(lp->data);
29✔
1569

1570
  /* restore prveious type */
1571
  lp->type = currenttype;
29✔
1572
  lp->connectiontype = currentconnectiontype;
29✔
1573
  lp->extent = currentextent;
29✔
1574

1575
  msFreeProjection(&(lp->projection));
29✔
1576
  lp->projection = currentprojectionobject;
29✔
1577

1578
  /* restore previous numclasses */
1579
  lp->numclasses = numclasses;
29✔
1580

1581
  free(lp->data);
29✔
1582
  lp->data = NULL;
29✔
1583

1584
  return status;
29✔
1585

1586
#else
1587
  /* ------------------------------------------------------------------
1588
   * WMS CONNECTION Support not included...
1589
   * ------------------------------------------------------------------ */
1590
  msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1591
             "msDrawWMSLayer()");
1592
  return (MS_FAILURE);
1593

1594
#endif /* USE_WMS_LYR */
1595
}
1596

1597
int msWMSLayerExecuteRequest(mapObj *map, int nOWSLayers, int nClickX,
12✔
1598
                             int nClickY, int nFeatureCount,
1599
                             const char *pszInfoFormat, int type) {
1600
#ifdef USE_WMS_LYR
1601

1602
  msIOContext *context;
1603

1604
  httpRequestObj *pasReqInfo;
1605
  wmsParamsObj sLastWMSParams;
1606
  int i, numReq = 0;
12✔
1607

1608
  pasReqInfo = (httpRequestObj *)msSmallMalloc((nOWSLayers + 1) *
12✔
1609
                                               sizeof(httpRequestObj));
1610
  msHTTPInitRequestObj(pasReqInfo, nOWSLayers + 1);
12✔
1611
  msInitWmsParamsObj(&sLastWMSParams);
12✔
1612

1613
  /* Generate the http request */
1614
  for (i = 0; i < map->numlayers; i++) {
28✔
1615
    if (GET_LAYER(map, map->layerorder[i])->status == MS_ON) {
16✔
1616
      if (type == WMS_GETFEATUREINFO) {
12✔
1617
        if (msPrepareWMSLayerRequest(
8✔
1618
                map->layerorder[i], map, GET_LAYER(map, map->layerorder[i]),
1619
                WMS_GETFEATUREINFO, MS_WMS, &sLastWMSParams, nClickX, nClickY,
1620
                nFeatureCount, pszInfoFormat, pasReqInfo,
1621
                &numReq) == MS_FAILURE) {
1622
          msFreeWmsParamsObj(&sLastWMSParams);
×
1623
          msFree(pasReqInfo);
×
1624
          return MS_FAILURE;
×
1625
        }
1626
      } else if (msPrepareWMSLayerRequest(map->layerorder[i], map,
4✔
1627
                                          GET_LAYER(map, map->layerorder[i]),
1628
                                          WMS_GETLEGENDGRAPHIC, MS_WMS,
1629
                                          &sLastWMSParams, 0, 0, 0, NULL,
1630
                                          pasReqInfo, &numReq) == MS_FAILURE) {
1631
        msFreeWmsParamsObj(&sLastWMSParams);
×
1632
        msFree(pasReqInfo);
×
1633
        return MS_FAILURE;
×
1634
      }
1635
    }
1636
  }
1637

1638
  if (msOWSExecuteRequests(pasReqInfo, numReq, map, MS_FALSE) == MS_FAILURE) {
12✔
1639
    msHTTPFreeRequestObj(pasReqInfo, numReq);
×
1640
    msFree(pasReqInfo);
×
1641
    msFreeWmsParamsObj(&sLastWMSParams);
×
1642
    return MS_FAILURE;
×
1643
  }
1644

1645
  context = msIO_getHandler(stdout);
12✔
1646
  if (context == NULL) {
12✔
1647
    msHTTPFreeRequestObj(pasReqInfo, numReq);
×
1648
    msFree(pasReqInfo);
×
1649
    msFreeWmsParamsObj(&sLastWMSParams);
×
1650
    return MS_FAILURE;
×
1651
  }
1652

1653
  msIO_printf("Content-Type: %s%c%c", pasReqInfo[0].pszContentType, 10, 10);
12✔
1654

1655
  if (pasReqInfo[0].pszOutputFile) {
12✔
1656
    FILE *fp;
1657
    char szBuf[MS_BUFFER_LENGTH];
1658

1659
    fp = fopen(pasReqInfo[0].pszOutputFile, "r");
×
1660
    if (fp) {
×
1661
      while (1) {
1662
        size_t nSize;
1663
        nSize = fread(szBuf, sizeof(char), MS_BUFFER_LENGTH - 1, fp);
1664
        if (nSize > 0)
×
1665
          msIO_contextWrite(context, szBuf, nSize);
×
1666
        if (nSize != MS_BUFFER_LENGTH - 1)
×
1667
          break;
1668
      }
1669
      fclose(fp);
×
1670
      if (!map->debug)
×
1671
        unlink(pasReqInfo[0].pszOutputFile);
×
1672
    } else {
1673
      msSetError(MS_IOERR, "'%s'.", "msWMSLayerExecuteRequest()",
×
1674
                 pasReqInfo[0].pszOutputFile);
1675
      return MS_FAILURE;
×
1676
    }
1677
  } else {
1678
    msIO_contextWrite(context, pasReqInfo[0].result_data,
12✔
1679
                      pasReqInfo[0].result_size);
1680
  }
1681

1682
  msHTTPFreeRequestObj(pasReqInfo, numReq);
12✔
1683
  msFree(pasReqInfo);
12✔
1684
  msFreeWmsParamsObj(&sLastWMSParams);
12✔
1685

1686
  return MS_SUCCESS;
12✔
1687
#else
1688
  /* ------------------------------------------------------------------
1689
   * WMS CONNECTION Support not included...
1690
   * ------------------------------------------------------------------ */
1691
  msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1692
             "msWMSLayerExecuteRequest()");
1693
  return (MS_FAILURE);
1694

1695
#endif /* USE_WMS_LYR */
1696
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc