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

geographika / mapserver / 17709373190

14 Sep 2025 09:32AM UTC coverage: 41.466% (+0.09%) from 41.375%
17709373190

push

github

geographika
Add index templates

62086 of 149729 relevant lines covered (41.47%)

25036.08 hits per line

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

62.44
/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) {
78✔
50
  wmsparams->onlineresource = NULL;
78✔
51
  wmsparams->params = msCreateHashTable();
78✔
52
  wmsparams->numparams = 0;
78✔
53
  wmsparams->httpcookiedata = NULL;
78✔
54

55
  return MS_SUCCESS;
78✔
56
}
57

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

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

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

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

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

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

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

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

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

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

134
/**********************************************************************
135
 *                          msBuildWMSParamsUrl()
136
 *
137
 **********************************************************************/
138
static char *msBuildURLFromWMSParams(wmsParamsObj *wmsparams) {
39✔
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;
39✔
147

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

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

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

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

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

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

186
  return pszURL;
39✔
187
}
188

189
#ifdef USE_WMS_LYR
190

191
static bool IsPNGFormat(const char *pszValue) {
25✔
192
  return EQUAL(pszValue, "PNG") || EQUAL(pszValue, "image/png");
25✔
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) {
64✔
206
  const char *pszFormat = msOWSLookupMetadata(&(lp->metadata), "MO", "format");
64✔
207
  if (pszFormat)
64✔
208
    return msStrdup(pszFormat);
64✔
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,
39✔
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;
39✔
282
  if (pszOnlineResource == NULL)
39✔
283
    pszOnlineResource =
284
        msOWSLookupMetadata(&(lp->metadata), "MO", "onlineresource");
×
285

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

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

309
  pszVersionKeyword = "VERSION";
310

311
  nVersion = msOWSParseVersionString(pszVersion);
39✔
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);
39✔
316

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

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

325
  if (pszStyle == NULL) {
39✔
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);
×
333
    snprintf(szBuf, sizeof(szBuf), "style_%.80s_sld_body", pszStyle);
334
    pszStyleSLDBody = msOWSLookupMetadata(&(lp->metadata), "MO", szBuf);
×
335

336
    if (pszSLD || pszStyleSLDBody) {
×
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) {
39✔
349
    msSetWMSParamString(psWMSParams, "STYLE", pszStyle, MS_TRUE, nVersion);
4✔
350
  } else {
351
    msSetWMSParamString(psWMSParams, "STYLES", pszStyle, MS_TRUE, nVersion);
35✔
352
  }
353

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

363
  if (msIsLayerQueryable(lp)) {
39✔
364
    msSetWMSParamString(psWMSParams, "QUERY_LAYERS", pszName, MS_TRUE,
22✔
365
                        nVersion);
366
  }
367
  if (pszTime && strlen(pszTime) > 0) {
39✔
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) {
39✔
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) {
39✔
395
    msSetWMSParamString(psWMSParams, "SLD", pszSLDURL, MS_TRUE, nVersion);
×
396
  }
397

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

402
  if (pszTransparent) {
39✔
403
    msSetWMSParamString(psWMSParams, "TRANSPARENT", pszTransparent, MS_TRUE,
×
404
                        nVersion);
405
  } else {
406
    msSetWMSParamString(psWMSParams, "TRANSPARENT", "TRUE", MS_TRUE, nVersion);
39✔
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,
39✔
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;
39✔
430
  const char *pszVersion, *pszRequestParam,
431
      *pszSrsParamName = "SRS", *pszLayer = NULL, *pszQueryLayers = NULL,
432
      *pszUseStrictAxisOrder;
433
  rectObj bbox;
434
  int bbox_width = map->width, bbox_height = map->height;
39✔
435
  int nVersion = OWS_VERSION_NOTSET;
436
  int bUseStrictAxisOrder = MS_FALSE; /* this is the assumption up to 1.1.0 */
437
  int bFlipAxisOrder = MS_FALSE;
438
  const char *pszTmp;
439
  int bIsEssential = MS_FALSE;
440

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

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

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

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

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

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

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

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

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

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

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

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

561
    nLen = strlen(pszEPSG);
33✔
562

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

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

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

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

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

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

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

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

633
  /* ------------------------------------------------------------------
634
   * Set layer SRS.
635
   * ------------------------------------------------------------------ */
636
  /* No need to set lp->proj if it's already set to the right EPSG code */
637
  {
638
    char *pszEPSGCodeFromLayer = NULL;
39✔
639
    msOWSGetEPSGProj(&(lp->projection), NULL, "MO", MS_TRUE,
39✔
640
                     &pszEPSGCodeFromLayer);
641
    if (pszEPSGCodeFromLayer == NULL ||
39✔
642
        strcasecmp(pszEPSG, pszEPSGCodeFromLayer) != 0) {
29✔
643
      char *ows_srs = NULL;
13✔
644
      msOWSGetEPSGProj(NULL, &(lp->metadata), "MO", MS_FALSE, &ows_srs);
13✔
645
      /* no need to set lp->proj if it is already set and there is only
646
      one item in the _srs metadata for this layer - we will assume
647
      the projection block matches the _srs metadata (the search for ' '
648
      in ows_srs is a test to see if there are multiple EPSG: codes) */
649
      if (lp->projection.numargs == 0 || ows_srs == NULL ||
13✔
650
          (strchr(ows_srs, ' ') != NULL)) {
3✔
651
        if (strncasecmp(pszEPSG, "EPSG:", 5) == 0) {
13✔
652
          char szProj[20];
653
          snprintf(szProj, sizeof(szProj), "init=epsg:%s", pszEPSG + 5);
13✔
654
          if (msLoadProjectionString(&(lp->projection), szProj) != 0) {
13✔
655
            msFree(pszEPSGCodeFromLayer);
×
656
            msFree(ows_srs);
×
657
            free(pszEPSG);
×
658
            return MS_FAILURE;
×
659
          }
660
        } else {
661
          if (msLoadProjectionString(&(lp->projection), pszEPSG) != 0) {
×
662
            msFree(pszEPSGCodeFromLayer);
×
663
            msFree(ows_srs);
×
664
            free(pszEPSG);
×
665
            return MS_FAILURE;
×
666
          }
667
        }
668
      }
669
      msFree(ows_srs);
13✔
670
    }
671
    msFree(pszEPSGCodeFromLayer);
39✔
672
  }
673

674
  /* ------------------------------------------------------------------
675
   * Adjust for MapServer EXTENT being center of pixel and WMS BBOX being
676
   * edge of pixel (#2843).
677
   * ------------------------------------------------------------------ */
678
  bbox = map->extent;
39✔
679

680
  bbox.minx -= map->cellsize * 0.5;
39✔
681
  bbox.maxx += map->cellsize * 0.5;
39✔
682
  bbox.miny -= map->cellsize * 0.5;
39✔
683
  bbox.maxy += map->cellsize * 0.5;
39✔
684

685
  /* -------------------------------------------------------------------- */
686
  /*      Reproject if needed.                                            */
687
  /* -------------------------------------------------------------------- */
688
  if (msProjectionsDiffer(&(map->projection), &(lp->projection))) {
39✔
689
    msProjectRect(&(map->projection), &(lp->projection), &bbox);
15✔
690

691
    /* -------------------------------------------------------------------- */
692
    /*      Sometimes our remote WMS only accepts square pixel              */
693
    /*      requests.  If this is the case adjust adjust the number of      */
694
    /*      pixels or lines in the request so that the pixels are           */
695
    /*      square.                                                         */
696
    /* -------------------------------------------------------------------- */
697
    {
698
      const char *nonsquare_ok =
699
          msOWSLookupMetadata(&(lp->metadata), "MO", "nonsquare_ok");
15✔
700

701
      /* assume nonsquare_ok is false */
702
      if (nonsquare_ok != NULL && (strcasecmp(nonsquare_ok, "no") == 0 ||
15✔
703
                                   strcasecmp(nonsquare_ok, "false") == 0)) {
×
704
        double cellsize_x = (bbox.maxx - bbox.minx) / bbox_width;
×
705
        double cellsize_y = (bbox.maxy - bbox.miny) / bbox_height;
×
706

707
        if (cellsize_x < cellsize_y * 0.999999) {
×
708
          int new_bbox_height = ceil((cellsize_y / cellsize_x) * bbox_height);
×
709

710
          if (lp->debug)
×
711
            msDebug("NONSQUARE_OK=%s, adjusted HEIGHT from %d to %d to "
×
712
                    "equalize cellsize at %g.\n",
713
                    nonsquare_ok, bbox_height, new_bbox_height, cellsize_x);
714
          bbox_height = new_bbox_height;
715
        } else if (cellsize_y < cellsize_x * 0.999999) {
×
716
          int new_bbox_width = ceil((cellsize_x / cellsize_y) * bbox_width);
×
717

718
          if (lp->debug)
×
719
            msDebug("NONSQUARE_OK=%s, adjusted WIDTH from %d to %d to equalize "
×
720
                    "cellsize at %g.\n",
721
                    nonsquare_ok, bbox_width, new_bbox_width, cellsize_y);
722
          bbox_width = new_bbox_width;
723
        } else {
724
          if (lp->debug)
×
725
            msDebug("NONSQUARE_OK=%s, but cellsize was already square - no "
×
726
                    "change.\n",
727
                    nonsquare_ok);
728
        }
729
      }
730
    }
731
  }
732

733
  /* -------------------------------------------------------------------- */
734
  /*      If the layer has predefined extents, and a predefined           */
735
  /*      projection that matches the request projection, then            */
736
  /*      consider restricting the BBOX to match the limits.              */
737
  /* -------------------------------------------------------------------- */
738
  if (bbox_width != 0) {
39✔
739
    char *ows_srs;
740
    rectObj layer_rect;
741

742
    msOWSGetEPSGProj(&(lp->projection), &(lp->metadata), "MO", MS_FALSE,
39✔
743
                     &ows_srs);
744

745
    if (ows_srs && strchr(ows_srs, ' ') == NULL &&
69✔
746
        msOWSGetLayerExtent(map, lp, "MO", &layer_rect) == MS_SUCCESS) {
30✔
747
      /* fulloverlap */
748
      if (msRectContained(&bbox, &layer_rect)) {
1✔
749
        /* no changes */
750
      }
751

752
      /* no overlap */
753
      else if (!msRectOverlap(&layer_rect, &bbox)) {
1✔
754
        bbox_width = 0;
755
        bbox_height = 0;
756
      }
757

758
      else {
759
        double cellsize_x = (bbox.maxx - bbox.minx) / bbox_width;
1✔
760
        double cellsize_y = (bbox.maxy - bbox.miny) / bbox_height;
1✔
761
        double cellsize = MS_MIN(cellsize_x, cellsize_y);
1✔
762

763
        msRectIntersect(&bbox, &layer_rect);
1✔
764

765
        bbox_width = round((bbox.maxx - bbox.minx) / cellsize);
1✔
766
        bbox_height = round((bbox.maxy - bbox.miny) / cellsize);
1✔
767

768
        /* Force going through the resampler if we're going to receive a clipped
769
         * BBOX (#4931) */
770
        if (msLayerGetProcessingKey(lp, "RESAMPLE") == NULL) {
1✔
771
          msLayerSetProcessingKey(lp, "RESAMPLE", "nearest");
1✔
772
        }
773
      }
774
    }
775
    msFree(ows_srs);
39✔
776
  }
777

778
  /* Set layer extent to wms bbox */
779
  lp->extent = bbox;
39✔
780

781
  /* -------------------------------------------------------------------- */
782
  /*      Potentially return the bbox.                                    */
783
  /* -------------------------------------------------------------------- */
784
  if (bbox_ret != NULL)
39✔
785
    *bbox_ret = bbox;
27✔
786

787
  if (width_ret != NULL)
39✔
788
    *width_ret = bbox_width;
27✔
789

790
  if (height_ret != NULL)
39✔
791
    *height_ret = bbox_height;
27✔
792

793
  /* ------------------------------------------------------------------
794
   * Build the request URL.
795
   * At this point we set only the following parameters for GetMap:
796
   *   REQUEST
797
   *   SRS (or CRS)
798
   *   BBOX
799
   *
800
   * And for GetFeatureInfo:
801
   *   X (I for 1.3.0)
802
   *   Y (J for 1.3.0)
803
   *   INFO_FORMAT
804
   *   FEATURE_COUNT (only if nFeatureCount > 0)
805
   *
806
   * The connection string should contain all other required params,
807
   * including:
808
   *   VERSION
809
   *   LAYERS
810
   *   FORMAT
811
   *   TRANSPARENT
812
   *   STYLES
813
   *   QUERY_LAYERS (for queryable layers only)
814
   * ------------------------------------------------------------------ */
815

816
  /* ------------------------------------------------------------------
817
   * Sometimes a requested layer is essential for the map, so if the
818
   * request fails or an error is delivered, the map has not to be drawn
819
   * ------------------------------------------------------------------ */
820
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO", "essential")) !=
39✔
821
      NULL) {
822
    if (strcasecmp(pszTmp, "true") == 0 || strcasecmp(pszTmp, "on") == 0 ||
×
823
        strcasecmp(pszTmp, "yes") == 0)
×
824
      bIsEssential = MS_TRUE;
825
    else
826
      bIsEssential = atoi(pszTmp);
827
  }
828

829
  if (nRequestType == WMS_GETFEATUREINFO) {
39✔
830
    char szBuf[100] = "";
8✔
831

832
    if (nVersion >= OWS_1_1_0)
8✔
833
      pszRequestParam = "GetFeatureInfo";
834
    else
835
      pszRequestParam = "feature_info";
836

837
    const char *pszExceptionsParam;
838
    if (nVersion >= OWS_1_3_0)
8✔
839
      pszExceptionsParam = "XML";
840
    else if (nVersion >= OWS_1_1_0) /* 1.1.0 to 1.1.0 */
4✔
841
      pszExceptionsParam = "application/vnd.ogc.se_xml";
842
    else
843
      pszExceptionsParam = "WMS_XML";
844

845
    msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE,
846
                        nVersion);
847
    msSetWMSParamInt(psWMSParams, "WIDTH", bbox_width);
8✔
848
    msSetWMSParamInt(psWMSParams, "HEIGHT", bbox_height);
8✔
849

850
    msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE,
8✔
851
                        nVersion);
852

853
    if (bFlipAxisOrder == MS_TRUE) {
8✔
854
      snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g", bbox.miny,
4✔
855
               bbox.minx, bbox.maxy, bbox.maxx);
856
    } else {
857
      snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g", bbox.minx,
4✔
858
               bbox.miny, bbox.maxx, bbox.maxy);
859
    }
860
    msSetWMSParamString(psWMSParams, "BBOX", szBuf, MS_TRUE, nVersion);
8✔
861

862
    if (nVersion >= OWS_1_3_0) {
8✔
863
      msSetWMSParamInt(psWMSParams, "I", nClickX);
4✔
864
      msSetWMSParamInt(psWMSParams, "J", nClickY);
4✔
865
    } else {
866
      msSetWMSParamInt(psWMSParams, "X", nClickX);
4✔
867
      msSetWMSParamInt(psWMSParams, "Y", nClickY);
4✔
868
    }
869

870
    msSetWMSParamString(psWMSParams, "EXCEPTIONS", pszExceptionsParam, MS_FALSE,
871
                        nVersion);
872
    msSetWMSParamString(psWMSParams, "INFO_FORMAT", pszInfoFormat, MS_TRUE,
8✔
873
                        nVersion);
874

875
    if (pszQueryLayers) { /* not set in CONNECTION string */
8✔
876
      msSetWMSParamString(psWMSParams, "QUERY_LAYERS", pszQueryLayers, MS_FALSE,
877
                          nVersion);
878
    }
879

880
    /* If FEATURE_COUNT <= 0 then don't pass this parameter */
881
    /* The spec states that FEATURE_COUNT must be greater than zero */
882
    /* and if not passed then the behavior is up to the server */
883
    if (nFeatureCount > 0) {
8✔
884
      msSetWMSParamInt(psWMSParams, "FEATURE_COUNT", nFeatureCount);
8✔
885
    }
886

887
  } else if (nRequestType == WMS_GETLEGENDGRAPHIC) {
31✔
888
    if (map->extent.maxx > map->extent.minx && map->width > 0 &&
4✔
889
        map->height > 0) {
4✔
890
      char szBuf[20] = "";
4✔
891
      double scaledenom;
892
      msCalculateScale(map->extent, map->units, map->width, map->height,
4✔
893
                       map->resolution, &scaledenom);
894
      snprintf(szBuf, 20, "%g", scaledenom);
4✔
895
      msSetWMSParamString(psWMSParams, "SCALE", szBuf, MS_FALSE, nVersion);
896
    }
897
    pszRequestParam = "GetLegendGraphic";
898

899
    /*
900
    const char* pszExceptionsParam = msOWSLookupMetadata(&(lp->metadata),
901
                         "MO", "exceptions_format");
902
    if (pszExceptionsParam == NULL) {
903
      if (nVersion >= OWS_1_1_0 && nVersion < OWS_1_3_0)
904
        pszExceptionsParam = "application/vnd.ogc.se_inimage";
905
      else
906
        pszExceptionsParam = "INIMAGE";
907
    }*/
908

909
    if (pszLayer) { /* not set in CONNECTION string */
4✔
910
      msSetWMSParamString(psWMSParams, "LAYER", pszLayer, MS_FALSE, nVersion);
911
    }
912

913
    msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE,
914
                        nVersion);
915
    msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE,
4✔
916
                        nVersion);
917

918
    if (nVersion >= OWS_1_3_0) {
4✔
919
      msSetWMSParamString(psWMSParams, "SLD_VERSION", "1.1.0", MS_FALSE,
920
                          nVersion);
921
    }
922

923
  } else { /* if (nRequestType == WMS_GETMAP) */
924
    char szBuf[100] = "";
27✔
925

926
    if (nVersion >= OWS_1_1_0)
27✔
927
      pszRequestParam = "GetMap";
928
    else
929
      pszRequestParam = "map";
930

931
    const char *pszExceptionsParam =
932
        msOWSLookupMetadata(&(lp->metadata), "MO", "exceptions_format");
27✔
933

934
    if (!bIsEssential) {
27✔
935
      if (pszExceptionsParam == NULL) {
27✔
936
        if (nVersion >= OWS_1_1_0 && nVersion < OWS_1_3_0)
27✔
937
          pszExceptionsParam = "application/vnd.ogc.se_inimage";
938
        else
939
          pszExceptionsParam = "INIMAGE";
940
      }
941
    } else {
942
      /* if layer is essential, do not emit EXCEPTIONS parameter (defaults to
943
       * XML) */
944
      pszExceptionsParam = NULL;
945
    }
946

947
    msSetWMSParamString(psWMSParams, "REQUEST", pszRequestParam, MS_FALSE,
948
                        nVersion);
949
    msSetWMSParamInt(psWMSParams, "WIDTH", bbox_width);
27✔
950
    msSetWMSParamInt(psWMSParams, "HEIGHT", bbox_height);
27✔
951
    msSetWMSParamString(psWMSParams, pszSrsParamName, pszEPSG, MS_FALSE,
27✔
952
                        nVersion);
953

954
    if (bFlipAxisOrder == MS_TRUE) {
27✔
955
      snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g", bbox.miny,
14✔
956
               bbox.minx, bbox.maxy, bbox.maxx);
957
    } else {
958
      snprintf(szBuf, sizeof(szBuf), "%.15g,%.15g,%.15g,%.15g", bbox.minx,
13✔
959
               bbox.miny, bbox.maxx, bbox.maxy);
960
    }
961
    msSetWMSParamString(psWMSParams, "BBOX", szBuf, MS_TRUE, nVersion);
27✔
962
    if (pszExceptionsParam) {
27✔
963
      msSetWMSParamString(psWMSParams, "EXCEPTIONS", pszExceptionsParam,
964
                          MS_FALSE, nVersion);
965
    }
966
  }
967

968
  free(pszEPSG);
39✔
969

970
  return MS_SUCCESS;
39✔
971

972
#else
973
  /* ------------------------------------------------------------------
974
   * WMS CONNECTION Support not included...
975
   * ------------------------------------------------------------------ */
976
  msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
977
             "msBuildWMSLayerURL()");
978
  return MS_FAILURE;
979

980
#endif /* USE_WMS_LYR */
981
}
982

983
/**********************************************************************
984
 *                          msWMSGetFeatureInfoURL()
985
 *
986
 * Build a GetFeatureInfo URL for this layer.
987
 *
988
 * Returns a reference to a newly allocated string that should be freed
989
 * by the caller.
990
 **********************************************************************/
991
char *msWMSGetFeatureInfoURL(mapObj *map, layerObj *lp, int nClickX,
×
992
                             int nClickY, int nFeatureCount,
993
                             const char *pszInfoFormat) {
994
  wmsParamsObj sThisWMSParams;
995
  char *pszURL;
996

997
  msInitWmsParamsObj(&sThisWMSParams);
×
998

999
  if (msBuildWMSLayerURL(map, lp, WMS_GETFEATUREINFO, nClickX, nClickY,
×
1000
                         nFeatureCount, pszInfoFormat, NULL, NULL, NULL,
1001
                         &sThisWMSParams) != MS_SUCCESS) {
1002
    return NULL;
1003
  }
1004

1005
  pszURL = msBuildURLFromWMSParams(&sThisWMSParams);
×
1006
  msFreeWmsParamsObj(&sThisWMSParams);
×
1007

1008
  return pszURL;
×
1009
}
1010

1011
/**********************************************************************
1012
 *                          msPrepareWMSLayerRequest()
1013
 *
1014
 **********************************************************************/
1015

1016
int msPrepareWMSLayerRequest(int nLayerId, mapObj *map, layerObj *lp,
39✔
1017
                             int nRequestType,
1018
                             enum MS_CONNECTION_TYPE lastconnectiontype,
1019
                             wmsParamsObj *psLastWMSParams, int nClickX,
1020
                             int nClickY, int nFeatureCount,
1021
                             const char *pszInfoFormat,
1022
                             httpRequestObj *pasReqInfo, int *numRequests) {
1023
#ifdef USE_WMS_LYR
1024
  char *pszURL = NULL, *pszHTTPCookieData = NULL;
1025
  const char *pszTmp;
1026
  rectObj bbox = {0};
39✔
1027
  int bbox_width = 0, bbox_height = 0;
39✔
1028
  int nTimeout, bOkToMerge, bForceSeparateRequest, bCacheToDisk;
1029
  wmsParamsObj sThisWMSParams;
1030
  int ret = MS_FAILURE;
1031

1032
  if (lp->connectiontype != MS_WMS)
39✔
1033
    return MS_FAILURE;
1034

1035
  msInitWmsParamsObj(&sThisWMSParams);
39✔
1036

1037
  /* ------------------------------------------------------------------
1038
   * Build the request URL, this will also set layer projection and
1039
   * compute BBOX in that projection.
1040
   * ------------------------------------------------------------------ */
1041

1042
  switch (nRequestType) {
39✔
1043
  case WMS_GETMAP:
27✔
1044
    ret = msBuildWMSLayerURL(map, lp, WMS_GETMAP, 0, 0, 0, NULL, &bbox,
27✔
1045
                             &bbox_width, &bbox_height, &sThisWMSParams);
1046
    break;
27✔
1047

1048
  case WMS_GETFEATUREINFO:
8✔
1049
    ret = msBuildWMSLayerURL(map, lp, WMS_GETFEATUREINFO, nClickX, nClickY,
8✔
1050
                             nFeatureCount, pszInfoFormat, NULL, NULL, NULL,
1051
                             &sThisWMSParams);
1052
    break;
8✔
1053

1054
  case WMS_GETLEGENDGRAPHIC:
4✔
1055
    ret = msBuildWMSLayerURL(map, lp, WMS_GETLEGENDGRAPHIC, 0, 0, 0, NULL, NULL,
4✔
1056
                             NULL, NULL, &sThisWMSParams);
1057
    break;
4✔
1058

1059
  default:
1060
    assert(FALSE);
1061
    break;
1062
  }
1063

1064
  if (ret != MS_SUCCESS) {
39✔
1065
    /* an error was already reported. */
1066
    msFreeWmsParamsObj(&sThisWMSParams);
×
1067
    return MS_FAILURE;
×
1068
  }
1069

1070
  /* ------------------------------------------------------------------
1071
   * Check if the request is empty, perhaps due to reprojection problems
1072
   * or wms_extents restrictions.
1073
   * ------------------------------------------------------------------ */
1074
  if ((nRequestType == WMS_GETMAP) && (bbox_width == 0 || bbox_height == 0)) {
39✔
1075
    msFreeWmsParamsObj(&sThisWMSParams);
×
1076
    return MS_SUCCESS; /* No overlap. */
×
1077
  }
1078

1079
  /* ------------------------------------------------------------------
1080
   * Check if layer overlaps current view window (using wms_latlonboundingbox)
1081
   * ------------------------------------------------------------------ */
1082
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO",
39✔
1083
                                    "latlonboundingbox")) != NULL) {
1084
    char **tokens;
1085
    int n;
1086
    rectObj ext;
1087

1088
    tokens = msStringSplit(pszTmp, ' ', &n);
×
1089
    if (tokens == NULL || n != 4) {
×
1090
      msSetError(
×
1091
          MS_WMSCONNERR,
1092
          "Wrong number of arguments for 'wms_latlonboundingbox' metadata.",
1093
          "msDrawWMSLayer()");
1094
      msFreeWmsParamsObj(&sThisWMSParams);
×
1095
      return MS_FAILURE;
×
1096
    }
1097

1098
    ext.minx = atof(tokens[0]);
×
1099
    ext.miny = atof(tokens[1]);
×
1100
    ext.maxx = atof(tokens[2]);
×
1101
    ext.maxy = atof(tokens[3]);
×
1102

1103
    msFreeCharArray(tokens, n);
×
1104

1105
    /* Reproject latlonboundingbox to the selected SRS for the layer and */
1106
    /* check if it overlaps the bbox that we calculated for the request */
1107

1108
    msProjectRect(&(map->latlon), &(lp->projection), &ext);
×
1109
    if (!msRectOverlap(&bbox, &ext)) {
×
1110
      /* No overlap... nothing to do */
1111

1112
      msFreeWmsParamsObj(&sThisWMSParams);
×
1113
      return MS_SUCCESS; /* No overlap. */
×
1114
    }
1115
  }
1116

1117
  /* ------------------------------------------------------------------
1118
   * check to see if a the metadata wms_connectiontimeout is set. If it is
1119
   * the case we will use it, else we use the default which is 30 seconds.
1120
   * First check the metadata in the layer object and then in the map object.
1121
   * ------------------------------------------------------------------ */
1122
  nTimeout = 30; /* Default is 30 seconds  */
1123
  if ((pszTmp = msOWSLookupMetadata2(&(lp->metadata), &(map->web.metadata),
39✔
1124
                                     "MO", "connectiontimeout")) != NULL) {
1125
    nTimeout = atoi(pszTmp);
1126
  }
1127

1128
  /* ------------------------------------------------------------------
1129
   * Check if we want to use in memory images instead of writing to disk.
1130
   * ------------------------------------------------------------------ */
1131
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO", "cache_to_disk")) !=
39✔
1132
      NULL) {
1133
    if (strcasecmp(pszTmp, "true") == 0 || strcasecmp(pszTmp, "on") == 0 ||
×
1134
        strcasecmp(pszTmp, "yes") == 0)
×
1135
      bCacheToDisk = MS_TRUE;
1136
    else
1137
      bCacheToDisk = atoi(pszTmp);
1138
  } else
1139
    bCacheToDisk = MS_FALSE;
1140

1141
  if (bCacheToDisk) {
×
1142
    /* We'll store the remote server's response to a tmp file. */
1143
    if (map->web.imagepath == NULL || strlen(map->web.imagepath) == 0) {
×
1144
      msSetError(MS_WMSERR,
×
1145
                 "WEB.IMAGEPATH must be set to use WMS client connections.",
1146
                 "msPrepareWMSLayerRequest()");
1147
      return MS_FAILURE;
×
1148
    }
1149
  }
1150

1151
  /* ------------------------------------------------------------------
1152
   * Check if layer can be merged with previous WMS layer requests
1153
   * Metadata wms_force_separate_request can be set to 1 to prevent this
1154
   * this layer from being combined with any other layer.
1155
   * ------------------------------------------------------------------ */
1156
  bForceSeparateRequest = MS_FALSE;
1157
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO",
39✔
1158
                                    "force_separate_request")) != NULL) {
1159
    bForceSeparateRequest = atoi(pszTmp);
1160
  }
1161
  bOkToMerge = MS_FALSE;
1162
  if (!bForceSeparateRequest && lastconnectiontype == MS_WMS &&
39✔
1163
      psLastWMSParams != NULL &&
12✔
1164
      sThisWMSParams.numparams == psLastWMSParams->numparams &&
12✔
1165
      strcmp(sThisWMSParams.onlineresource, psLastWMSParams->onlineresource) ==
×
1166
          0) {
1167
    const char *key, *value1, *value2;
1168
    bOkToMerge = MS_TRUE;
1169

1170
    key = msFirstKeyFromHashTable(sThisWMSParams.params);
×
1171
    while (key != NULL && bOkToMerge == MS_TRUE) {
×
1172
      /* Skip parameters whose values can be different */
1173
      if (!(strcmp(key, "LAYERS") == 0 || strcmp(key, "QUERY_LAYERS") == 0 ||
×
1174
            strcmp(key, "STYLES") == 0)) {
×
1175
        value1 = msLookupHashTable(psLastWMSParams->params, key);
×
1176
        value2 = msLookupHashTable(sThisWMSParams.params, key);
×
1177

1178
        if (value1 == NULL || value2 == NULL || strcmp(value1, value2) != 0) {
×
1179
          bOkToMerge = MS_FALSE;
1180
          break;
1181
        }
1182
      }
1183
      key = msNextKeyFromHashTable(sThisWMSParams.params, key);
×
1184
    }
1185
  }
1186

1187
  /*------------------------------------------------------------------
1188
   * Check to see if there's a HTTP Cookie to forward
1189
   * If Cookie differ between the two connection, it's NOT OK to merge
1190
   * the connection
1191
   * ------------------------------------------------------------------ */
1192
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO", "http_cookie")) !=
39✔
1193
      NULL) {
1194
    if (strcasecmp(pszTmp, "forward") == 0) {
×
1195
      pszTmp = msLookupHashTable(&(map->web.metadata), "http_cookie_data");
×
1196
      if (pszTmp != NULL) {
×
1197
        pszHTTPCookieData = msStrdup(pszTmp);
×
1198
      }
1199
    } else {
1200
      pszHTTPCookieData = msStrdup(pszTmp);
×
1201
    }
1202
  } else if ((pszTmp = msOWSLookupMetadata(&(map->web.metadata), "MO",
39✔
1203
                                           "http_cookie")) != NULL) {
1204
    if (strcasecmp(pszTmp, "forward") == 0) {
×
1205
      pszTmp = msLookupHashTable(&(map->web.metadata), "http_cookie_data");
×
1206
      if (pszTmp != NULL) {
×
1207
        pszHTTPCookieData = msStrdup(pszTmp);
×
1208
      }
1209
    } else {
1210
      pszHTTPCookieData = msStrdup(pszTmp);
×
1211
    }
1212
  }
1213

1214
  if (bOkToMerge && pszHTTPCookieData != sThisWMSParams.httpcookiedata) {
39✔
1215
    if (pszHTTPCookieData == NULL || sThisWMSParams.httpcookiedata == NULL) {
×
1216
      bOkToMerge = MS_FALSE;
1217
    } else if (strcmp(pszHTTPCookieData, sThisWMSParams.httpcookiedata) != 0) {
×
1218
      bOkToMerge = MS_FALSE;
1219
    }
1220
  }
1221

1222
  if (bOkToMerge) {
1223
    /* Merge both requests into sThisWMSParams
1224
     */
1225
    const char *value1, *value2;
1226
    char *keys[] = {"LAYERS", "QUERY_LAYERS", "STYLES"};
×
1227
    int i;
1228

1229
    for (i = 0; i < 3; i++) {
×
1230
      value1 = msLookupHashTable(psLastWMSParams->params, keys[i]);
×
1231
      value2 = msLookupHashTable(sThisWMSParams.params, keys[i]);
×
1232
      if (value1 && value2) {
×
1233
        char *pszBuf;
1234
        int nLen;
1235

1236
        nLen = strlen(value1) + strlen(value2) + 2;
×
1237
        pszBuf = malloc(nLen);
×
1238
        MS_CHECK_ALLOC(pszBuf, nLen, MS_FAILURE);
×
1239

1240
        snprintf(pszBuf, nLen, "%s,%s", value1, value2);
1241
        /* TODO should really send the server request version here */
1242
        msSetWMSParamString(&sThisWMSParams, keys[i], pszBuf, MS_FALSE,
1243
                            OWS_VERSION_NOTSET);
1244

1245
        /* This key existed already, we don't want it counted twice */
1246
        sThisWMSParams.numparams--;
×
1247

1248
        msFree(pszBuf);
×
1249
      }
1250
    }
1251
  }
1252

1253
  /* ------------------------------------------------------------------
1254
   * Build new request URL
1255
   * ------------------------------------------------------------------ */
1256
  pszURL = msBuildURLFromWMSParams(&sThisWMSParams);
39✔
1257

1258
  if (bOkToMerge && (*numRequests) > 0) {
39✔
1259
    /* ------------------------------------------------------------------
1260
     * Update the last request in the array:  (*numRequests)-1
1261
     * ------------------------------------------------------------------ */
1262
    msFree(pasReqInfo[(*numRequests) - 1].pszGetUrl);
×
1263
    pasReqInfo[(*numRequests) - 1].pszGetUrl = pszURL;
×
1264
    pszURL = NULL;
1265
    pasReqInfo[(*numRequests) - 1].debug |= lp->debug;
×
1266
    if (nTimeout > pasReqInfo[(*numRequests) - 1].nTimeout)
×
1267
      pasReqInfo[(*numRequests) - 1].nTimeout = nTimeout;
×
1268
  } else {
1269
    /* ------------------------------------------------------------------
1270
     * Add a request to the array (already preallocated)
1271
     * ------------------------------------------------------------------ */
1272
    pasReqInfo[(*numRequests)].nLayerId = nLayerId;
39✔
1273
    pasReqInfo[(*numRequests)].pszGetUrl = pszURL;
39✔
1274

1275
    pszURL = NULL;
1276
    pasReqInfo[(*numRequests)].pszHTTPCookieData = pszHTTPCookieData;
39✔
1277
    pszHTTPCookieData = NULL;
1278
    if (bCacheToDisk) {
39✔
1279
      pasReqInfo[(*numRequests)].pszOutputFile =
×
1280
          msTmpFile(map, map->mappath, NULL, "wms.tmp");
×
1281
    } else
1282
      pasReqInfo[(*numRequests)].pszOutputFile = NULL;
39✔
1283
    pasReqInfo[(*numRequests)].nStatus = 0;
39✔
1284
    pasReqInfo[(*numRequests)].nTimeout = nTimeout;
39✔
1285
    pasReqInfo[(*numRequests)].bbox = bbox;
39✔
1286
    pasReqInfo[(*numRequests)].width = bbox_width;
39✔
1287
    pasReqInfo[(*numRequests)].height = bbox_height;
39✔
1288
    pasReqInfo[(*numRequests)].debug = lp->debug;
39✔
1289

1290
    if (msHTTPAuthProxySetup(&(map->web.metadata), &(lp->metadata), pasReqInfo,
39✔
1291
                             *numRequests, map, "MO") != MS_SUCCESS)
1292
      return MS_FAILURE;
1293

1294
    (*numRequests)++;
39✔
1295
  }
1296

1297
  /* ------------------------------------------------------------------
1298
   * Replace contents of psLastWMSParams with sThisWMSParams
1299
   * unless bForceSeparateRequest is set in which case we make it empty
1300
   * ------------------------------------------------------------------ */
1301
  if (psLastWMSParams) {
39✔
1302
    msFreeWmsParamsObj(psLastWMSParams);
39✔
1303
    if (!bForceSeparateRequest)
39✔
1304
      *psLastWMSParams = sThisWMSParams;
39✔
1305
    else
1306
      msInitWmsParamsObj(psLastWMSParams);
×
1307
  } else {
1308
    /* Can't copy it, so we just free it */
1309
    msFreeWmsParamsObj(&sThisWMSParams);
×
1310
  }
1311

1312
  return MS_SUCCESS;
1313

1314
#else
1315
  /* ------------------------------------------------------------------
1316
   * WMS CONNECTION Support not included...
1317
   * ------------------------------------------------------------------ */
1318
  msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1319
             "msDrawWMSLayer()");
1320
  return (MS_FAILURE);
1321

1322
#endif /* USE_WMS_LYR */
1323
}
1324

1325
/**********************************************************************
1326
 *                          msDrawWMSLayerLow()
1327
 *
1328
 **********************************************************************/
1329

1330
int msDrawWMSLayerLow(int nLayerId, httpRequestObj *pasReqInfo, int numRequests,
27✔
1331
                      mapObj *map, layerObj *lp, imageObj *img) {
1332
#ifdef USE_WMS_LYR
1333
  int status = MS_SUCCESS;
1334
  int iReq = -1;
1335
  char szPath[MS_MAXPATHLEN];
1336
  int currenttype;
1337
  int currentconnectiontype;
1338
  int numclasses;
1339
  char *mem_filename = NULL;
1340
  const char *pszTmp;
1341
  int bIsEssential = MS_FALSE;
1342

1343
  /* ------------------------------------------------------------------
1344
   * Sometimes a requested layer is essential for the map, so if the
1345
   * request fails or an error is delivered, the map has not to be drawn
1346
   * ------------------------------------------------------------------ */
1347
  if ((pszTmp = msOWSLookupMetadata(&(lp->metadata), "MO", "essential")) !=
27✔
1348
      NULL) {
1349
    if (strcasecmp(pszTmp, "true") == 0 || strcasecmp(pszTmp, "on") == 0 ||
×
1350
        strcasecmp(pszTmp, "yes") == 0)
×
1351
      bIsEssential = MS_TRUE;
1352
    else
1353
      bIsEssential = atoi(pszTmp);
1354
  }
1355

1356
  /* ------------------------------------------------------------------
1357
   * Find the request info for this layer in the array, based on nLayerId
1358
   * ------------------------------------------------------------------ */
1359
  for (iReq = 0; iReq < numRequests; iReq++) {
27✔
1360
    if (pasReqInfo[iReq].nLayerId == nLayerId)
27✔
1361
      break;
1362
  }
1363

1364
  if (iReq == numRequests) {
27✔
1365
    /* This layer was skipped or was included in a multi-layers
1366
     * request ... nothing to do.
1367
     */
1368
    return MS_SUCCESS;
1369
  }
1370

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

1383
    if (!bIsEssential)
×
1384
      return MS_SUCCESS;
1385
    else
1386
      return MS_FAILURE;
1387
  }
1388

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

1403
    if (pasReqInfo[iReq].pszOutputFile) {
×
1404
      fp = fopen(pasReqInfo[iReq].pszOutputFile, "r");
×
1405
      if (fp) {
×
1406
        /* TODO: For now we'll only read the first chunk and return it
1407
         * via msSetError()... we should really try to parse the XML
1408
         * and extract the exception code/message though
1409
         */
1410
        size_t nSize;
1411

1412
        nSize = fread(szBuf, sizeof(char), MS_BUFFER_LENGTH - 1, fp);
1413
        if (nSize < MS_BUFFER_LENGTH)
×
1414
          szBuf[nSize] = '\0';
×
1415
        else {
1416
          strlcpy(szBuf, "(!!!)", sizeof(szBuf)); /* This should never happen */
1417
        }
1418

1419
        fclose(fp);
×
1420

1421
        /* We're done with the remote server's response... delete it. */
1422
        if (!lp->debug)
×
1423
          unlink(pasReqInfo[iReq].pszOutputFile);
×
1424
      } else {
1425
        strlcpy(szBuf, "(Failed to open exception response)", sizeof(szBuf));
1426
      }
1427
    } else {
1428
      strlcpy(szBuf, pasReqInfo[iReq].result_data, MS_BUFFER_LENGTH);
×
1429
    }
1430

1431
    if (lp->debug)
×
1432
      msDebug("WMS GetMap request got XML exception for layer '%s': %s.",
×
1433
              (lp->name ? lp->name : "(null)"), szBuf);
×
1434

1435
    msSetError(MS_WMSERR,
×
1436
               "WMS GetMap request got XML exception for layer '%s': %s.",
1437
               "msDrawWMSLayerLow()", (lp->name ? lp->name : "(null)"), szBuf);
×
1438

1439
    if (!bIsEssential)
×
1440
      return MS_SUCCESS;
1441
    else
1442
      return MS_FAILURE;
×
1443
  }
1444

1445
  /* ------------------------------------------------------------------
1446
   * If the output was written to a memory buffer, then we will need
1447
   * to attach a "VSI" name to this buffer.
1448
   * ------------------------------------------------------------------ */
1449
  if (pasReqInfo[iReq].pszOutputFile == NULL) {
27✔
1450
    mem_filename = msTmpFile(map, NULL, "/vsimem/msout/", "img.tmp");
27✔
1451

1452
    VSIFCloseL(VSIFileFromMemBuffer(
27✔
1453
        mem_filename, (GByte *)pasReqInfo[iReq].result_data,
27✔
1454
        (vsi_l_offset)pasReqInfo[iReq].result_size, FALSE));
27✔
1455
  }
1456

1457
  /* ------------------------------------------------------------------
1458
   * Prepare layer for drawing, reprojecting the image received from the
1459
   * server if needed...
1460
   * ------------------------------------------------------------------ */
1461
  /* keep the current type that will be restored at the end of this  */
1462
  /* function. */
1463
  currenttype = lp->type;
27✔
1464
  currentconnectiontype = lp->connectiontype;
27✔
1465
  lp->type = MS_LAYER_RASTER;
27✔
1466
  lp->connectiontype = MS_RASTER;
27✔
1467

1468
  /* set the classes to 0 so that It won't do client side */
1469
  /* classification if an sld was set. */
1470
  numclasses = lp->numclasses;
27✔
1471

1472
  /* ensure the file connection is closed right away after the layer */
1473
  /* is rendered */
1474
  msLayerSetProcessingKey(lp, "CLOSE_CONNECTION", "NORMAL");
27✔
1475

1476
  if (msOWSLookupMetadata(&(lp->metadata), "MO", "sld_body") ||
54✔
1477
      msOWSLookupMetadata(&(lp->metadata), "MO", "sld_url"))
27✔
1478
    lp->numclasses = 0;
×
1479

1480
  free(lp->data);
27✔
1481
  lp->data = mem_filename != NULL ? mem_filename
27✔
1482
                                  : msStrdup(pasReqInfo[iReq].pszOutputFile);
27✔
1483

1484
  const char *pszAllowedDrivers =
1485
      msOWSLookupMetadata(&(lp->metadata), "MO", "allowed_gdal_drivers");
27✔
1486
  if (!pszAllowedDrivers) {
27✔
1487
    char *pszFormat = msWMSLayerGetFormat(lp);
25✔
1488
    if (IsPNGFormat(pszFormat))
25✔
1489
      pszAllowedDrivers = "PNG";
1490
    else if (IsJPEGFormat(pszFormat))
11✔
1491
      pszAllowedDrivers = "JPEG";
1492
    else
1493
      pszAllowedDrivers = "PNG,JPEG";
1494
    msFree(pszFormat);
25✔
1495
  }
1496
  // This will be used by msDrawRasterLayerLow()
1497
  msLayerSetProcessingKey(lp, "ALLOWED_GDAL_DRIVERS", pszAllowedDrivers);
27✔
1498
  if (lp->debug)
27✔
1499
    msDebug("Setting ALLOWED_GDAL_DRIVERS = %s\n", pszAllowedDrivers);
11✔
1500

1501
  /* #3138 If PROCESSING "RESAMPLE=..." is set we cannot use the simple case */
1502
  if (!msProjectionsDiffer(&(map->projection), &(lp->projection)) &&
43✔
1503
      (msLayerGetProcessingKey(lp, "RESAMPLE") == NULL)) {
16✔
1504
    /* The simple case... no reprojection needed... render layer directly. */
1505
    lp->transform = MS_FALSE;
16✔
1506
    /* if (msDrawRasterLayerLow(map, lp, img) != 0) */
1507
    /* status = MS_FAILURE; */
1508
    if (msDrawLayer(map, lp, img) != 0)
16✔
1509
      status = MS_FAILURE;
1510
  } else {
1511
    VSILFILE *fp;
1512
    char *wldfile;
1513
    /* OK, we have to resample the raster to map projection... */
1514
    lp->transform = MS_TRUE;
11✔
1515
    msLayerSetProcessingKey(lp, "LOAD_WHOLE_IMAGE", "YES");
11✔
1516

1517
    /* Create a world file with raster extents */
1518
    /* One line per value, in this order: cx, 0, 0, cy, ulx, uly */
1519
    wldfile = msBuildPath(szPath, lp->map->mappath, lp->data);
11✔
1520
    if (wldfile && (strlen(wldfile) >= 3))
11✔
1521
      strcpy(wldfile + strlen(wldfile) - 3, "wld");
11✔
1522
    if (wldfile && (fp = VSIFOpenL(wldfile, "wt")) != NULL) {
22✔
1523
      double dfCellSizeX =
11✔
1524
          MS_OWS_CELLSIZE(pasReqInfo[iReq].bbox.minx,
11✔
1525
                          pasReqInfo[iReq].bbox.maxx, pasReqInfo[iReq].width);
1526
      double dfCellSizeY =
11✔
1527
          MS_OWS_CELLSIZE(pasReqInfo[iReq].bbox.maxy,
11✔
1528
                          pasReqInfo[iReq].bbox.miny, pasReqInfo[iReq].height);
1529
      char world_text[5000];
1530

1531
      sprintf(world_text, "%.12f\n0\n0\n%.12f\n%.12f\n%.12f\n", dfCellSizeX,
11✔
1532
              dfCellSizeY, pasReqInfo[iReq].bbox.minx + dfCellSizeX * 0.5,
11✔
1533
              pasReqInfo[iReq].bbox.maxy + dfCellSizeY * 0.5);
11✔
1534

1535
      VSIFWriteL(world_text, 1, strlen(world_text), fp);
11✔
1536
      VSIFCloseL(fp);
11✔
1537

1538
      /* GDAL should be called to reproject automatically. */
1539
      if (msDrawLayer(map, lp, img) != 0)
11✔
1540
        status = MS_FAILURE;
1541

1542
      if (!lp->debug || mem_filename != NULL)
11✔
1543
        VSIUnlink(wldfile);
11✔
1544
    } else {
1545
      msSetError(MS_WMSCONNERR, "Unable to create wld file for WMS slide.",
×
1546
                 "msDrawWMSLayer()");
1547
      status = MS_FAILURE;
1548
    }
1549
  }
1550

1551
  /* We're done with the remote server's response... delete it. */
1552
  if (!lp->debug || mem_filename != NULL)
27✔
1553
    VSIUnlink(lp->data);
27✔
1554

1555
  /* restore prveious type */
1556
  lp->type = currenttype;
27✔
1557
  lp->connectiontype = currentconnectiontype;
27✔
1558

1559
  /* restore previous numclasses */
1560
  lp->numclasses = numclasses;
27✔
1561

1562
  free(lp->data);
27✔
1563
  lp->data = NULL;
27✔
1564

1565
  return status;
27✔
1566

1567
#else
1568
  /* ------------------------------------------------------------------
1569
   * WMS CONNECTION Support not included...
1570
   * ------------------------------------------------------------------ */
1571
  msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1572
             "msDrawWMSLayer()");
1573
  return (MS_FAILURE);
1574

1575
#endif /* USE_WMS_LYR */
1576
}
1577

1578
int msWMSLayerExecuteRequest(mapObj *map, int nOWSLayers, int nClickX,
12✔
1579
                             int nClickY, int nFeatureCount,
1580
                             const char *pszInfoFormat, int type) {
1581
#ifdef USE_WMS_LYR
1582

1583
  msIOContext *context;
1584

1585
  httpRequestObj *pasReqInfo;
1586
  wmsParamsObj sLastWMSParams;
1587
  int i, numReq = 0;
12✔
1588

1589
  pasReqInfo = (httpRequestObj *)msSmallMalloc((nOWSLayers + 1) *
12✔
1590
                                               sizeof(httpRequestObj));
1591
  msHTTPInitRequestObj(pasReqInfo, nOWSLayers + 1);
12✔
1592
  msInitWmsParamsObj(&sLastWMSParams);
12✔
1593

1594
  /* Generate the http request */
1595
  for (i = 0; i < map->numlayers; i++) {
24✔
1596
    if (GET_LAYER(map, map->layerorder[i])->status == MS_ON) {
12✔
1597
      if (type == WMS_GETFEATUREINFO) {
12✔
1598
        if (msPrepareWMSLayerRequest(
8✔
1599
                map->layerorder[i], map, GET_LAYER(map, map->layerorder[i]),
1600
                WMS_GETFEATUREINFO, MS_WMS, &sLastWMSParams, nClickX, nClickY,
1601
                nFeatureCount, pszInfoFormat, pasReqInfo,
1602
                &numReq) == MS_FAILURE) {
1603
          msFreeWmsParamsObj(&sLastWMSParams);
×
1604
          msFree(pasReqInfo);
×
1605
          return MS_FAILURE;
×
1606
        }
1607
      } else if (msPrepareWMSLayerRequest(map->layerorder[i], map,
4✔
1608
                                          GET_LAYER(map, map->layerorder[i]),
1609
                                          WMS_GETLEGENDGRAPHIC, MS_WMS,
1610
                                          &sLastWMSParams, 0, 0, 0, NULL,
1611
                                          pasReqInfo, &numReq) == MS_FAILURE) {
1612
        msFreeWmsParamsObj(&sLastWMSParams);
×
1613
        msFree(pasReqInfo);
×
1614
        return MS_FAILURE;
×
1615
      }
1616
    }
1617
  }
1618

1619
  if (msOWSExecuteRequests(pasReqInfo, numReq, map, MS_FALSE) == MS_FAILURE) {
12✔
1620
    msHTTPFreeRequestObj(pasReqInfo, numReq);
×
1621
    msFree(pasReqInfo);
×
1622
    msFreeWmsParamsObj(&sLastWMSParams);
×
1623
    return MS_FAILURE;
×
1624
  }
1625

1626
  context = msIO_getHandler(stdout);
12✔
1627
  if (context == NULL) {
12✔
1628
    msHTTPFreeRequestObj(pasReqInfo, numReq);
×
1629
    msFree(pasReqInfo);
×
1630
    msFreeWmsParamsObj(&sLastWMSParams);
×
1631
    return MS_FAILURE;
×
1632
  }
1633

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

1636
  if (pasReqInfo[0].pszOutputFile) {
12✔
1637
    FILE *fp;
1638
    char szBuf[MS_BUFFER_LENGTH];
1639

1640
    fp = fopen(pasReqInfo[0].pszOutputFile, "r");
×
1641
    if (fp) {
×
1642
      while (1) {
1643
        size_t nSize;
1644
        nSize = fread(szBuf, sizeof(char), MS_BUFFER_LENGTH - 1, fp);
1645
        if (nSize > 0)
×
1646
          msIO_contextWrite(context, szBuf, nSize);
×
1647
        if (nSize != MS_BUFFER_LENGTH - 1)
×
1648
          break;
1649
      }
1650
      fclose(fp);
×
1651
      if (!map->debug)
×
1652
        unlink(pasReqInfo[0].pszOutputFile);
×
1653
    } else {
1654
      msSetError(MS_IOERR, "'%s'.", "msWMSLayerExecuteRequest()",
×
1655
                 pasReqInfo[0].pszOutputFile);
1656
      return MS_FAILURE;
×
1657
    }
1658
  } else {
1659
    msIO_contextWrite(context, pasReqInfo[0].result_data,
12✔
1660
                      pasReqInfo[0].result_size);
1661
  }
1662

1663
  msHTTPFreeRequestObj(pasReqInfo, numReq);
12✔
1664
  msFree(pasReqInfo);
12✔
1665
  msFreeWmsParamsObj(&sLastWMSParams);
12✔
1666

1667
  return MS_SUCCESS;
12✔
1668
#else
1669
  /* ------------------------------------------------------------------
1670
   * WMS CONNECTION Support not included...
1671
   * ------------------------------------------------------------------ */
1672
  msSetError(MS_WMSCONNERR, "WMS CLIENT CONNECTION support is not available.",
1673
             "msWMSLayerExecuteRequest()");
1674
  return (MS_FAILURE);
1675

1676
#endif /* USE_WMS_LYR */
1677
}
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