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

MapServer / MapServer / 20718230642

05 Jan 2026 02:18PM UTC coverage: 41.754% (+0.03%) from 41.725%
20718230642

push

github

web-flow
WMS: rework building of the layer tree to make it faster, and understandable! (#7410)

* WMS: rework building of the layer tree to make it faster, and understandable!

Avoid quadratic performance in the number of layers.

On a 103 MB .map file with 6130 layers and 3 level of nesting, WMS
GetCapabilities response generation goes from 56 seconds to 5 seconds.

* msRenameLayer(): avoid potential overflow

* mapows.cpp: minimal conversion to C++

* msOWSMakeAllLayersUnique(): avoid quadratic performance in number of layers

* loadMap(): optimize CRS creation when they are all the same

360 of 422 new or added lines in 4 files covered. (85.31%)

76 existing lines in 4 files now uncovered.

62888 of 150616 relevant lines covered (41.75%)

25229.23 hits per line

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

70.77
/src/mapows.cpp
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  OGC Web Services (WMS, WFS) support functions
6
 * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
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
22
 * OR 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 "maptime.h"
32
#include "maptemplate.h"
33
#include "mapows.h"
34

35
#if defined(USE_LIBXML2)
36
#include "maplibxml2.h"
37
#else
38
#include "cpl_minixml.h"
39
#include "cpl_error.h"
40
#endif
41
#include "mapowscommon.h"
42

43
#include <ctype.h> /* isalnum() */
44
#include <stdarg.h>
45
#include <stdbool.h>
46
#include <assert.h>
47

48
#include <map>
49

50
/*
51
** msOWSInitRequestObj() initializes an owsRequestObj; i.e: sets
52
** all internal pointers to NULL.
53
*/
54
void msOWSInitRequestObj(owsRequestObj *ows_request) {
1,858✔
55
  ows_request->numlayers = 0;
1,858✔
56
  ows_request->numwmslayerargs = 0;
1,858✔
57
  ows_request->enabled_layers = NULL;
1,858✔
58
  ows_request->layerwmsfilterindex = NULL;
1,858✔
59

60
  ows_request->service = NULL;
1,858✔
61
  ows_request->version = NULL;
1,858✔
62
  ows_request->request = NULL;
1,858✔
63
  ows_request->document = NULL;
1,858✔
64
}
1,858✔
65

66
/*
67
** msOWSClearRequestObj() releases all resources associated with an
68
** owsRequestObj.
69
*/
70
void msOWSClearRequestObj(owsRequestObj *ows_request) {
1,858✔
71
  msFree(ows_request->enabled_layers);
1,858✔
72
  msFree(ows_request->layerwmsfilterindex);
1,858✔
73
  msFree(ows_request->service);
1,858✔
74
  msFree(ows_request->version);
1,858✔
75
  msFree(ows_request->request);
1,858✔
76
  if (ows_request->document) {
1,858✔
77
#if defined(USE_LIBXML2)
78
    xmlFreeDoc(static_cast<xmlDocPtr>(ows_request->document));
157✔
79
    xmlCleanupParser();
157✔
80
#else
81
    CPLDestroyXMLNode(ows_request->document);
82
#endif
83
  }
84
}
1,858✔
85

86
#if defined(USE_LIBXML2) && LIBXML_VERSION < 20900
87
static int bExternalEntityAsked = MS_FALSE;
88
static xmlParserInputPtr dummyEntityLoader(const char *URL, const char *ID,
89
                                           xmlParserCtxtPtr context) {
90
  bExternalEntityAsked = MS_TRUE;
91
  return NULL;
92
}
93
#endif
94

95
/*
96
** msOWSPreParseRequest() parses a cgiRequestObj either with GET/KVP
97
** or with POST/XML. Only SERVICE, VERSION (or WMTVER) and REQUEST are
98
** being determined, all WxS (or SOS) specific parameters are parsed
99
** within the according handler.
100
** The results are saved within an owsRequestObj. If the request was
101
** transmitted with POST/XML, either the document (if compiled with
102
** libxml2) or the root CPLXMLNode is saved to the ows_request->document
103
** field.
104
** Returns MS_SUCCESS upon success, MS_FAILURE if severe errors occurred
105
** or MS_DONE, if the service could not be determined.
106
*/
107
static int msOWSPreParseRequest(cgiRequestObj *request,
1,857✔
108
                                owsRequestObj *ows_request) {
109
  /* decide if KVP or XML */
110
  if (request->type == MS_GET_REQUEST ||
1,857✔
111
      (request->type == MS_POST_REQUEST && request->contenttype &&
159✔
112
       strncmp(request->contenttype, "application/x-www-form-urlencoded",
159✔
113
               strlen("application/x-www-form-urlencoded")) == 0)) {
114
    int i;
115
    /* parse KVP parameters service, version and request */
116
    for (i = 0; i < request->NumParams; ++i) {
6,926✔
117
      if (ows_request->service == NULL &&
6,855✔
118
          EQUAL(request->ParamNames[i], "SERVICE")) {
3,565✔
119
        ows_request->service = msStrdup(request->ParamValues[i]);
1,676✔
120
      } else if (ows_request->version == NULL &&
5,179✔
121
                 (EQUAL(request->ParamNames[i], "VERSION") ||
3,543✔
122
                  EQUAL(request->ParamNames[i], "WMTVER"))) { /* for WMS */
1,896✔
123
        ows_request->version = msStrdup(request->ParamValues[i]);
1,647✔
124
      } else if (ows_request->request == NULL &&
3,532✔
125
                 EQUAL(request->ParamNames[i], "REQUEST")) {
3,485✔
126
        ows_request->request = msStrdup(request->ParamValues[i]);
1,670✔
127
      }
128

129
      /* stop if we have all necessary parameters */
130
      if (ows_request->service && ows_request->version &&
6,855✔
131
          ows_request->request) {
3,246✔
132
        break;
133
      }
134
    }
135
  } else if (request->type == MS_POST_REQUEST) {
157✔
136
#if defined(USE_LIBXML2)
137
    xmlNodePtr root = NULL;
138
#if LIBXML_VERSION < 20900
139
    xmlExternalEntityLoader oldExternalEntityLoader;
140
#endif
141
#else
142
    CPLXMLNode *temp;
143
#endif
144
    if (!request->postrequest || !strlen(request->postrequest)) {
157✔
145
      msSetError(MS_OWSERR, "POST request is empty.", "msOWSPreParseRequest()");
×
146
      return MS_FAILURE;
×
147
    }
148
#if defined(USE_LIBXML2)
149
#if LIBXML_VERSION < 20900
150
    oldExternalEntityLoader = xmlGetExternalEntityLoader();
151
    /* to avoid  XML External Entity vulnerability with libxml2 < 2.9 */
152
    xmlSetExternalEntityLoader(dummyEntityLoader);
153
    bExternalEntityAsked = MS_FALSE;
154
#endif
155
    /* parse to DOM-Structure with libxml2 and get the root element */
156
    ows_request->document =
157✔
157
        xmlParseMemory(request->postrequest, strlen(request->postrequest));
157✔
158
#if LIBXML_VERSION < 20900
159
    xmlSetExternalEntityLoader(oldExternalEntityLoader);
160
    if (bExternalEntityAsked) {
161
      msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",
162
                 "External entity fetch");
163
      return MS_FAILURE;
164
    }
165
#endif
166
    if (ows_request->document == NULL ||
314✔
167
        (root = xmlDocGetRootElement(
157✔
168
             static_cast<const xmlDoc *>(ows_request->document))) == NULL) {
169
      const xmlError *error = xmlGetLastError();
×
170
      msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",
×
171
                 error->message);
×
172
      return MS_FAILURE;
×
173
    }
174

175
    /* Get service, version and request from root */
176
    xmlChar *serviceTmp = xmlGetProp(root, BAD_CAST "service");
157✔
177
    if (serviceTmp != NULL) {
157✔
178
      ows_request->service = msStrdup((char *)serviceTmp);
157✔
179
      xmlFree(serviceTmp);
157✔
180
    }
181

182
    xmlChar *versionTmp = xmlGetProp(root, BAD_CAST "version");
157✔
183
    if (versionTmp != NULL) {
157✔
184
      ows_request->version = msStrdup((char *)versionTmp);
150✔
185
      xmlFree(versionTmp);
150✔
186
    }
187

188
    ows_request->request = msStrdup((char *)root->name);
157✔
189

190
#else
191
    /* parse with CPLXML */
192
    ows_request->document = CPLParseXMLString(request->postrequest);
193
    if (ows_request->document == NULL) {
194
      msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",
195
                 CPLGetLastErrorMsg());
196
      return MS_FAILURE;
197
    }
198

199
    /* remove all namespaces */
200
    CPLStripXMLNamespace(ows_request->document, NULL, 1);
201
    for (temp = ows_request->document; temp != NULL; temp = temp->psNext) {
202

203
      if (temp->eType == CXT_Element) {
204
        const char *service, *version;
205
        ows_request->request = msStrdup(temp->pszValue);
206

207
        if ((service = CPLGetXMLValue(temp, "service", NULL)) != NULL) {
208
          ows_request->service = msStrdup(service);
209
        }
210
        if ((version = CPLGetXMLValue(temp, "version", NULL)) != NULL) {
211
          ows_request->version = msStrdup(version);
212
        }
213
        continue;
214
      }
215
    }
216
#endif /* defined(USE_LIBXML2) */
217
  } else {
218
    msSetError(MS_OWSERR, "Unknown request method. Use either GET or POST.",
×
219
               "msOWSPreParseRequest()");
220
    return MS_FAILURE;
×
221
  }
222

223
  /* certain WMS requests do not require the service parameter */
224
  /* see: http://trac.osgeo.org/mapserver/ticket/2531          */
225
  if (ows_request->service == NULL && ows_request->request != NULL) {
1,857✔
226
    if (EQUAL(ows_request->request, "GetMap") ||
12✔
227
        EQUAL(ows_request->request, "GetFeatureInfo")) {
9✔
228
      ows_request->service = msStrdup("WMS");
3✔
229
    } else { /* service could not be determined */
230
      return MS_DONE;
231
    }
232
  }
233

234
  return MS_SUCCESS;
235
}
236

237
/*
238
** msOWSStrictCompliance()
239
** Check whether we need to strictly comply to OGC standards.
240
*/
241
bool msOWSStrictCompliance(mapObj *map) {
2,423✔
242
  const char *compliance_mode_str = NULL;
243

244
  compliance_mode_str =
245
      msOWSLookupMetadata(&(map->web.metadata), "MO", "compliance_mode");
2,423✔
246
  return (compliance_mode_str != NULL) &&
2,423✔
247
         (strcasecmp(compliance_mode_str, "true") == 0);
9✔
248
}
249

250
/*
251
** msOWSServiceParameterException()
252
** Used when the service parameter is either missing
253
** or invalid.
254
*/
255
static int msOWSServiceParameterException(mapObj *map,
3✔
256
                                          const char *exceptionCode) {
257
  int size = 0;
3✔
258
  char *errorString = NULL;
259

260
  xmlDocPtr psDoc = NULL;
261
  xmlNodePtr psRootNode = NULL;
262
  xmlNsPtr psNsOws = NULL;
263
  xmlChar *buffer = NULL;
3✔
264

265
  psNsOws = xmlNewNs(NULL, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI,
3✔
266
                     BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
267

268
  errorString = msGetErrorString("\n");
3✔
269

270
  psDoc = xmlNewDoc(BAD_CAST "1.0");
3✔
271

272
  psRootNode = msOWSCommonExceptionReport(
3✔
273
      psNsOws, OWS_1_0_0, msOWSGetSchemasLocation(map), "1.0.0",
274
      msOWSGetLanguage(map, "exception"), exceptionCode, "service",
275
      errorString);
276

277
  xmlDocSetRootElement(psDoc, psRootNode);
3✔
278

279
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI,
3✔
280
           BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
281

282
  msIO_setHeader("Status", "200 OK");
3✔
283
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
3✔
284
  msIO_sendHeaders();
3✔
285

286
  xmlDocDumpFormatMemoryEnc(psDoc, &buffer, &size, "UTF-8", 1);
3✔
287

288
  msIO_printf("%s", buffer);
3✔
289

290
  /*free buffer and the document */
291
  free(errorString);
3✔
292
  xmlFree(buffer);
3✔
293
  xmlFreeDoc(psDoc);
3✔
294
  xmlFreeNs(psNsOws);
3✔
295

296
  /* clear error since we have already reported it */
297
  msResetErrorList();
3✔
298

299
  return MS_FAILURE;
3✔
300
}
301

302
/*
303
** msOWSDispatch() is the entry point for any OWS request (WMS, WFS, ...)
304
** - If this is a valid request then it is processed and MS_SUCCESS is returned
305
**   on success, or MS_FAILURE on failure.
306
** - If force_ows_mode is true then an exception will be produced if the
307
**   request is not recognized as a valid request.
308
** - If force_ows_mode is false and this does not appear to be a valid OWS
309
**   request then MS_DONE is returned and MapServer is expected to process
310
**   this as a regular MapServer (traditional CGI) request.
311
*/
312
int msOWSDispatch(mapObj *map, cgiRequestObj *request, int ows_mode) {
1,857✔
313
  int status = MS_DONE, force_ows_mode = 0;
314
  owsRequestObj ows_request;
315
  bool compliance_mode = false;
316

317
  if (!request) {
1,857✔
318
    return status;
319
  }
320

321
  force_ows_mode = (ows_mode == OWS || ows_mode == WFS);
1,857✔
322

323
  msOWSInitRequestObj(&ows_request);
1,857✔
324
  switch (msOWSPreParseRequest(request, &ows_request)) {
1,857✔
325
  case MS_FAILURE: /* a severe error occurred */
326
    return MS_FAILURE;
327
  case MS_DONE:
328
    /* OWS Service could not be determined              */
329
    /* continue for now                                 */
330
    status = MS_DONE;
331
  }
332

333
  compliance_mode = msOWSStrictCompliance(map);
1,857✔
334

335
  if (ows_request.service == NULL) {
1,857✔
336
#ifdef USE_LIBXML2
337
    if (ows_request.request && EQUAL(ows_request.request, "GetMetadata")) {
21✔
338
      status = msMetadataDispatch(map, request);
6✔
339
      msOWSClearRequestObj(&ows_request);
6✔
340
      return status;
6✔
341
    }
342
#endif
343
#ifdef USE_WFS_SVR
344
    if (msOWSLookupMetadata(&(map->web.metadata), "FO", "cite_wfs2") != NULL) {
15✔
345
      status = msWFSException(map, "service",
2✔
346
                              MS_OWS_ERROR_MISSING_PARAMETER_VALUE, NULL);
347
    } else
348
#endif
349
        if (compliance_mode) {
13✔
350
      msSetError(MS_MISCERR,
1✔
351
                 "OWS Common exception: exceptionCode=MissingParameterValue, "
352
                 "locator=SERVICE, ExceptionText=SERVICE parameter missing.",
353
                 "msOWSDispatch()");
354
      status = msOWSServiceParameterException(
1✔
355
          map, MS_OWS_ERROR_MISSING_PARAMETER_VALUE);
356
    } else
357

358
      /* exit if service is not set */
359
      if (force_ows_mode) {
12✔
360
        msSetError(MS_MISCERR,
×
361
                   "OWS Common exception: exceptionCode=MissingParameterValue, "
362
                   "locator=SERVICE, ExceptionText=SERVICE parameter missing.",
363
                   "msOWSDispatch()");
364
        status = MS_FAILURE;
365
      } else {
366
        status = MS_DONE;
367
      }
368
  } else if (EQUAL(ows_request.service, "WMS")) {
1,836✔
369
#ifdef USE_WMS_SVR
370
    status = msWMSDispatch(map, request, &ows_request, MS_FALSE);
737✔
371
#else
372
    msSetError(
373
        MS_WMSERR,
374
        "SERVICE=WMS requested, but WMS support not configured in MapServer.",
375
        "msOWSDispatch()");
376
#endif
377
  } else if (EQUAL(ows_request.service, "WFS")) {
1,099✔
378
#ifdef USE_WFS_SVR
379
    status = msWFSDispatch(map, request, &ows_request, (ows_mode == WFS));
752✔
380
#else
381
    msSetError(
382
        MS_WFSERR,
383
        "SERVICE=WFS requested, but WFS support not configured in MapServer.",
384
        "msOWSDispatch()");
385
#endif
386
  } else if (EQUAL(ows_request.service, "WCS")) {
347✔
387
#ifdef USE_WCS_SVR
388
    status = msWCSDispatch(map, request, &ows_request);
310✔
389
#else
390
    msSetError(
391
        MS_WCSERR,
392
        "SERVICE=WCS requested, but WCS support not configured in MapServer.",
393
        "msOWSDispatch()");
394
#endif
395
  } else if (EQUAL(ows_request.service, "SOS")) {
37✔
396
#ifdef USE_SOS_SVR
397
    status = msSOSDispatch(map, request, &ows_request);
35✔
398
#else
399
    msSetError(
400
        MS_SOSERR,
401
        "SERVICE=SOS requested, but SOS support not configured in MapServer.",
402
        "msOWSDispatch()");
403
#endif
404
  } else if (compliance_mode) {
2✔
405
    msSetError(
2✔
406
        MS_MISCERR,
407
        "OWS Common exception: exceptionCode=InvalidParameterValue, "
408
        "locator=SERVICE, ExceptionText=SERVICE parameter value invalid.",
409
        "msOWSDispatch()");
410
    status = msOWSServiceParameterException(
2✔
411
        map, MS_OWS_ERROR_INVALID_PARAMETER_VALUE);
412
  } else if (force_ows_mode) {
×
413
    msSetError(
×
414
        MS_MISCERR,
415
        "OWS Common exception: exceptionCode=InvalidParameterValue, "
416
        "locator=SERVICE, ExceptionText=SERVICE parameter value invalid.",
417
        "msOWSDispatch()");
418
    status = MS_FAILURE;
419
  }
420

421
  msOWSClearRequestObj(&ows_request);
1,851✔
422
  return status;
423
}
424

425
/*
426
** msOWSIpParse()
427
**
428
** Parse the IP address or range into a binary array.
429
** Supports ipv4 and ipv6 addresses
430
** Ranges can be specified using the CIDR notation (ie: 192.100.100.0/24)
431
**
432
** Returns the parsed of the IP (4 or 16).
433
*/
434
int msOWSIpParse(const char *ip, unsigned char *ip1, unsigned char *mask) {
×
435
  int len = 0, masklen, seps;
436

437
  if (msCountChars((char *)ip, '.') == 3) {
×
438
    /* ipv4 */
439
    unsigned char *val = ip1;
440
    len = 1;
441
    masklen = 32;
442
    *val = 0;
×
443
    while (*ip) {
×
444
      if (*ip >= '0' && *ip <= '9')
×
445
        (*val) = 10 * (*val) + (*ip - '0');
×
446
      else if (*ip == '.') {
×
447
        ++val;
×
448
        *val = 0;
×
449
        ++len;
×
450
      } else if (*ip == '/') {
×
451
        masklen = atoi(ip + 1);
×
452
        if (masklen > 32)
×
453
          masklen = 32;
454
        break;
455
      } else
456
        break;
457
      ++ip;
×
458
    }
459
    if (len != 4)
×
460
      return 0;
461
    /* write mask */
462
    if (mask) {
×
463
      memset(mask, 0, len);
464
      val = mask;
465
      while (masklen) {
×
466
        if (masklen >= 8) {
×
467
          *val = 0xff;
×
468
          masklen -= 8;
×
469
        } else {
470
          *val = -((unsigned char)pow(2, 8 - masklen));
×
471
          break;
×
472
        }
473
        ++val;
×
474
      }
475
    }
476
  } else if ((seps = msCountChars((char *)ip, ':')) > 1 && seps < 8) {
×
477
    /* ipv6 */
478
    unsigned short *val = (unsigned short *)ip1;
479
    len = 2;
480
    masklen = 128;
481
    *val = 0;
×
482
    while (*ip) {
×
483
      if (*ip >= '0' && *ip <= '9')
×
484
        (*val) = 16 * (*val) + (*ip - '0');
×
485
      else if (*ip >= 'a' && *ip <= 'f')
×
486
        (*val) = 16 * (*val) + (*ip - 'a' + 10);
×
487
      else if (*ip >= 'A' && *ip <= 'F')
×
488
        (*val) = 16 * (*val) + (*ip - 'A' + 10);
×
489
      else if (*ip == ':') {
×
490
        ++ip;
×
491
        ++val;
×
492
        len += 2;
×
493
        *val = 0;
×
494
        if (*ip == ':') {
×
495
          /* insert 0 values */
496
          while (seps <= 7) {
×
497
            ++val;
×
498
            len += 2;
×
499
            *val = 0;
×
500
            ++seps;
×
501
          }
502
        } else
503
          continue;
×
504
      } else if (*ip == '/') {
×
505
        masklen = atoi(ip + 1);
×
506
        if (masklen > 128)
×
507
          masklen = 128;
508
        break;
509
      } else
510
        break;
511
      ++ip;
×
512
    }
513
    if (len != 16)
×
514
      return 0;
515
    /* write mask */
516
    if (mask) {
×
517
      memset(mask, 0, len);
518
      val = (unsigned short *)mask;
519
      while (masklen) {
×
520
        if (masklen >= 16) {
×
521
          *val = 0xffff;
×
522
          masklen -= 16;
×
523
        } else {
524
          *val = -((unsigned short)pow(2, 16 - masklen));
×
525
          break;
×
526
        }
527
        ++val;
×
528
      }
529
    }
530
  }
531

532
  return len;
533
}
534

535
/*
536
** msOWSIpInList()
537
**
538
** Check if an ip is in a space separated list of IP addresses/ranges.
539
** Supports ipv4 and ipv6 addresses
540
** Ranges can be specified using the CIDR notation (ie: 192.100.100.0/24)
541
**
542
** Returns MS_TRUE if the IP is found.
543
*/
544
int msOWSIpInList(const char *ip_list, const char *ip) {
×
545
  int i, j, numips, iplen;
546
  unsigned char ip1[16];
547
  unsigned char ip2[16];
548
  unsigned char mask[16];
549
  char **ips;
550

551
  /* Parse input IP */
552
  iplen = msOWSIpParse(ip, (unsigned char *)&ip1, NULL);
×
553
  if (iplen != 4 && iplen != 16) /* ipv4 or ipv6 */
×
554
    return MS_FALSE;
555

556
  ips = msStringSplit(ip_list, ' ', &numips);
×
557
  if (ips) {
×
558
    for (i = 0; i < numips; i++) {
×
559
      if (msOWSIpParse(ips[i], (unsigned char *)&ip2, (unsigned char *)&mask) ==
×
560
          iplen) {
561
        for (j = 0; j < iplen; j++) {
×
562
          if ((ip1[j] & mask[j]) != (ip2[j] & mask[j]))
×
563
            break;
564
        }
565
        if (j == iplen) {
×
566
          msFreeCharArray(ips, numips);
×
567
          return MS_TRUE; /* match found */
×
568
        }
569
      }
570
    }
571
    msFreeCharArray(ips, numips);
×
572
  }
573

574
  return MS_FALSE;
575
}
576

577
/*
578
** msOWSIpDisabled()
579
**
580
** Check if an ip is in a list specified in the metadata section.
581
**
582
** Returns MS_TRUE if the IP is found.
583
*/
584
int msOWSIpInMetadata(const char *ip_list, const char *ip) {
×
585
  FILE *stream;
586
  char buffer[MS_BUFFER_LENGTH];
587
  int found = MS_FALSE;
588

589
  if (strncasecmp(ip_list, "file:", 5) == 0) {
×
590
    stream = fopen(ip_list + 5, "r");
×
591
    if (stream) {
×
592
      found = MS_FALSE;
593
      while (fgets(buffer, MS_BUFFER_LENGTH, stream)) {
×
594
        if (msOWSIpInList(buffer, ip)) {
×
595
          found = MS_TRUE;
596
          break;
597
        }
598
      }
599
      fclose(stream);
×
600
    }
601
  } else {
602
    if (msOWSIpInList(ip_list, ip))
×
603
      found = MS_TRUE;
604
  }
605
  return found;
×
606
}
607

608
/*
609
** msOWSIpDisabled()
610
**
611
** Check if the layers are enabled or disabled by IP list.
612
**
613
** 'namespaces' is a string with a letter for each namespace to lookup
614
** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_
615
** If namespaces is NULL then this function just does a regular metadata
616
** lookup.
617
**
618
** Returns the disabled flag.
619
*/
620
int msOWSIpDisabled(hashTableObj *metadata, const char *namespaces,
9,965✔
621
                    const char *ip) {
622
  const char *ip_list;
623
  int disabled = MS_FALSE;
624

625
  if (!ip)
9,965✔
626
    return MS_FALSE; /* no endpoint ip */
627

628
  ip_list = msOWSLookupMetadata(metadata, namespaces, "allowed_ip_list");
×
629
  if (!ip_list)
×
630
    ip_list = msOWSLookupMetadata(metadata, "O", "allowed_ip_list");
×
631

632
  if (ip_list) {
×
633
    disabled = MS_TRUE;
634
    if (msOWSIpInMetadata(ip_list, ip))
×
635
      disabled = MS_FALSE;
636
  }
637

638
  ip_list = msOWSLookupMetadata(metadata, namespaces, "denied_ip_list");
×
639
  if (!ip_list)
×
640
    ip_list = msOWSLookupMetadata(metadata, "O", "denied_ip_list");
×
641

642
  if (ip_list && msOWSIpInMetadata(ip_list, ip))
×
643
    disabled = MS_TRUE;
644

645
  return disabled;
646
}
647

648
/*
649
** msOWSRequestIsEnabled()
650
**
651
** Check if a layer is visible for a specific OWS request.
652
**
653
** 'namespaces' is a string with a letter for each namespace to lookup in
654
** the order they should be looked up. e.g. "MO" to lookup wms_ and ows_ If
655
** namespaces is NULL then this function just does a regular metadata
656
** lookup. If check_all_layers is set to MS_TRUE, the function will check
657
** all layers to see if the request is enable. (returns as soon as one is found)
658
*/
659
int msOWSRequestIsEnabled(mapObj *map, layerObj *layer, const char *namespaces,
718✔
660
                          const char *request, int check_all_layers) {
661
  int disabled = MS_FALSE; /* explicitly disabled flag */
718✔
662
  const char *enable_request;
663
  const char *remote_ip;
664

665
  if (request == NULL)
718✔
666
    return MS_FALSE;
667

668
  remote_ip = getenv("REMOTE_ADDR");
718✔
669

670
  /* First, we check in the layer metadata */
671
  if (layer && check_all_layers == MS_FALSE) {
718✔
672
    enable_request =
673
        msOWSLookupMetadata(&layer->metadata, namespaces, "enable_request");
40✔
674
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
40✔
675
      return MS_TRUE;
676
    if (disabled)
40✔
677
      return MS_FALSE;
678

679
    enable_request =
680
        msOWSLookupMetadata(&layer->metadata, "O", "enable_request");
40✔
681
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
40✔
682
      return MS_TRUE;
683
    if (disabled)
40✔
684
      return MS_FALSE;
685

686
    if (msOWSIpDisabled(&layer->metadata, namespaces, remote_ip))
40✔
687
      return MS_FALSE;
688
  }
689

690
  if (map && (check_all_layers == MS_FALSE || map->numlayers == 0)) {
718✔
691
    /* then we check in the map metadata */
692
    enable_request =
693
        msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
439✔
694
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
439✔
695
      return MS_TRUE;
696
    if (disabled)
318✔
697
      return MS_FALSE;
698

699
    enable_request =
700
        msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
314✔
701
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
314✔
702
      return MS_TRUE;
703
    if (disabled)
19✔
704
      return MS_FALSE;
705

706
    if (msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
19✔
707
      return MS_FALSE;
708
  }
709

710
  if (map && check_all_layers == MS_TRUE) {
298✔
711
    int i, globally_enabled = MS_FALSE;
712
    enable_request =
713
        msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
279✔
714
    globally_enabled =
715
        msOWSParseRequestMetadata(enable_request, request, &disabled);
279✔
716

717
    if (!globally_enabled && !disabled) {
279✔
718
      enable_request =
719
          msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
258✔
720
      globally_enabled =
721
          msOWSParseRequestMetadata(enable_request, request, &disabled);
258✔
722
    }
723

724
    if (globally_enabled &&
537✔
725
        msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
276✔
726
      globally_enabled = MS_FALSE;
727

728
    /* Check all layers */
729
    for (i = 0; i < map->numlayers; i++) {
282✔
730
      int result = MS_FALSE;
731
      layerObj *lp;
732
      lp = (GET_LAYER(map, i));
282✔
733

734
      enable_request =
735
          msOWSLookupMetadata(&lp->metadata, namespaces, "enable_request");
282✔
736
      result = msOWSParseRequestMetadata(enable_request, request, &disabled);
282✔
737
      if (!result && disabled)
282✔
738
        continue; /* if the request has been explicitly set to disabled,
3✔
739
                     continue */
740

741
      if (!result && !disabled) { /* if the request has not been found in the
742
                                     wms metadata, */
743
        /* check the ows namespace  */
744

745
        enable_request =
746
            msOWSLookupMetadata(&lp->metadata, "O", "enable_request");
272✔
747
        result = msOWSParseRequestMetadata(enable_request, request, &disabled);
272✔
748
        if (!result && disabled)
272✔
749
          continue;
×
750
      }
751

752
      if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))
279✔
753
        continue;
×
754

755
      if (result || (!disabled && globally_enabled))
279✔
756
        return MS_TRUE;
757
    }
758

759
    if (!disabled && globally_enabled)
×
760
      return MS_TRUE;
761
  }
762

763
  return MS_FALSE;
764
}
765

766
/*
767
** msOWSRequestLayersEnabled()
768
**
769
** Check if the layers are visible for a specific OWS request.
770
**
771
** 'namespaces' is a string with a letter for each namespace to lookup
772
** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_
773
** If namespaces is NULL then this function just does a regular metadata
774
** lookup.
775
**
776
** Generates an array of the layer ids enabled.
777
*/
778
void msOWSRequestLayersEnabled(mapObj *map, const char *namespaces,
1,815✔
779
                               const char *request,
780
                               owsRequestObj *ows_request) {
781
  int disabled = MS_FALSE; /* explicitly disabled flag */
1,815✔
782
  int globally_enabled = MS_FALSE;
783
  const char *enable_request;
784
  const char *remote_ip;
785

786
  if (ows_request->numlayers > 0)
1,815✔
787
    msFree(ows_request->enabled_layers);
9✔
788

789
  ows_request->numlayers = 0;
1,815✔
790
  ows_request->enabled_layers = NULL;
1,815✔
791

792
  if (request == NULL || (map == NULL) || (map->numlayers <= 0))
1,815✔
793
    return;
9✔
794

795
  remote_ip = getenv("REMOTE_ADDR");
1,806✔
796

797
  enable_request =
798
      msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
1,806✔
799
  globally_enabled =
800
      msOWSParseRequestMetadata(enable_request, request, &disabled);
1,806✔
801

802
  if (!globally_enabled && !disabled) {
1,806✔
803
    enable_request =
804
        msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
1,577✔
805
    globally_enabled =
806
        msOWSParseRequestMetadata(enable_request, request, &disabled);
1,577✔
807
  }
808

809
  if (globally_enabled &&
3,370✔
810
      msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
1,783✔
811
    globally_enabled = MS_FALSE;
812

813
  if (map->numlayers) {
1,806✔
814
    int i, layers_size = map->numlayers; /* for most of cases, this will be
815
                                            relatively small */
816

817
    ows_request->enabled_layers =
1,806✔
818
        (int *)msSmallMalloc(sizeof(int) * layers_size);
1,806✔
819

820
    for (i = 0; i < map->numlayers; i++) {
9,536✔
821
      int result = MS_FALSE;
822
      layerObj *lp;
823
      lp = (GET_LAYER(map, i));
7,730✔
824

825
      enable_request =
826
          msOWSLookupMetadata(&lp->metadata, namespaces, "enable_request");
7,730✔
827
      result = msOWSParseRequestMetadata(enable_request, request, &disabled);
7,730✔
828
      if (!result && disabled)
7,730✔
829
        continue; /* if the request has been explicitly set to disabled,
158✔
830
                     continue */
831

832
      if (!result && !disabled) { /* if the request has not been found in the
833
                                     wms metadata, */
834
        /* check the ows namespace  */
835

836
        enable_request =
837
            msOWSLookupMetadata(&lp->metadata, "O", "enable_request");
7,505✔
838
        result = msOWSParseRequestMetadata(enable_request, request, &disabled);
7,505✔
839
        if (!result && disabled)
7,505✔
840
          continue;
4✔
841
      }
842

843
      if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))
7,568✔
844
        continue;
×
845

846
      if (result || (!disabled && globally_enabled)) {
7,568✔
847
        ows_request->enabled_layers[ows_request->numlayers] = lp->index;
7,556✔
848
        ows_request->numlayers++;
7,556✔
849
      }
850
    }
851

852
    if (ows_request->numlayers == 0) {
1,806✔
853
      msFree(ows_request->enabled_layers);
15✔
854
      ows_request->enabled_layers = NULL;
15✔
855
    }
856
  }
857
}
858

859
/* msOWSParseRequestMetadata
860
 *
861
 * This function parse a enable_request metadata string and check if the
862
 * given request is present and enabled.
863
 */
864
int msOWSParseRequestMetadata(const char *metadata, const char *request,
20,786✔
865
                              int *disabled) {
866
  char requestBuffer[32];
867
  int wordFlag = MS_FALSE;
868
  int disableFlag = MS_FALSE;
869
  int allFlag = MS_FALSE;
870
  char *bufferPtr, *ptr = NULL;
871

872
  *disabled = MS_FALSE;
20,786✔
873

874
  if (metadata == NULL)
20,786✔
875
    return MS_FALSE;
876

877
  ptr = (char *)metadata;
878
  const size_t len = strlen(ptr);
3,021✔
879
  requestBuffer[0] = '\0';
3,021✔
880
  bufferPtr = requestBuffer;
881

882
  for (size_t i = 0; i <= len; ++i, ++ptr) {
13,783✔
883

884
    if (!wordFlag && isspace(*ptr))
10,942✔
885
      continue;
×
886

887
    wordFlag = MS_TRUE;
888

889
    if (*ptr == '!') {
10,942✔
890
      disableFlag = MS_TRUE;
891
      continue;
354✔
892
    } else if ((*ptr == ' ') ||
10,588✔
893
               (*ptr != '\0' && ptr[1] == '\0')) { /* end of word */
7,573✔
894
      if (ptr[1] == '\0' && *ptr != ' ') {
3,152✔
895
        *bufferPtr = *ptr;
2,978✔
896
        ++bufferPtr;
2,978✔
897
      }
898

899
      *bufferPtr = '\0';
3,152✔
900
      if (strcasecmp(request, requestBuffer) == 0) {
3,152✔
901
        *disabled = MS_TRUE; /* explicitly found, will stop the process in
180✔
902
                                msOWSRequestIsEnabled() */
903
        return (disableFlag ? MS_FALSE : MS_TRUE);
180✔
904
      } else {
905
        if (strcmp("*", requestBuffer) ==
2,972✔
906
            0) { /* check if we read the all flag */
907
          if (disableFlag)
2,741✔
908
            *disabled = MS_TRUE;
135✔
909
          allFlag = disableFlag ? MS_FALSE : MS_TRUE;
2,741✔
910
        }
911
        /* reset flags */
912
        wordFlag = MS_FALSE;
913
        disableFlag = MS_FALSE;
914
        bufferPtr = requestBuffer;
915
      }
916
    } else {
917
      *bufferPtr = *ptr;
7,436✔
918
      ++bufferPtr;
7,436✔
919
    }
920
  }
921

922
  return allFlag;
923
}
924

925
/*
926
** msOWSGetPrefixFromNamespace()
927
**
928
** Return the metadata name prefix from a character identifying the OWS
929
** namespace.
930
*/
931
static const char *msOWSGetPrefixFromNamespace(char chNamespace) {
252,215✔
932
  // Return should be a 3 character string, otherwise breaks assumption
933
  // in msOWSLookupMetadata()
934
  switch (chNamespace) {
252,215✔
935
  case 'O':
936
    return "ows";
937
  case 'A':
1,574✔
938
    return "oga"; /* oga_... (OGC Geospatial API) */
1,574✔
939
  case 'M':
51,474✔
940
    return "wms";
51,474✔
941
  case 'F':
25,874✔
942
    return "wfs";
25,874✔
943
  case 'C':
11,818✔
944
    return "wcs";
11,818✔
945
  case 'G':
95,324✔
946
    return "gml";
95,324✔
947
  case 'S':
1,404✔
948
    return "sos";
1,404✔
949
  default:
×
950
    /* We should never get here unless an invalid code (typo) is */
951
    /* present in the code, but since this happened before... */
952
    msSetError(MS_WMSERR, "Unsupported metadata namespace code (%c).",
×
953
               "msOWSGetPrefixFromNamespace()", chNamespace);
954
    assert(MS_FALSE);
955
    return NULL;
×
956
  }
957
}
958

959
/*
960
** msOWSLookupMetadata()
961
**
962
** Attempts to lookup a given metadata name in multiple OWS namespaces.
963
**
964
** 'namespaces' is a string with a letter for each namespace to lookup
965
** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_
966
** If namespaces is NULL then this function just does a regular metadata
967
** lookup.
968
*/
969
const char *msOWSLookupMetadata(hashTableObj *metadata, const char *namespaces,
185,205✔
970
                                const char *name) {
971
  const char *value = NULL;
972

973
  if (namespaces == NULL) {
185,205✔
974
    value = msLookupHashTable(metadata, (char *)name);
×
975
  } else {
976
    char buf[100] = "ows_";
185,205✔
977

978
    strlcpy(buf + 4, name, 96);
979

980
    while (value == NULL && *namespaces != '\0') {
432,186✔
981
      const char *prefix = msOWSGetPrefixFromNamespace(*namespaces);
246,981✔
982
      if (prefix == NULL)
246,981✔
983
        return NULL;
×
984
      assert(strlen(prefix) == 3);
985
      memcpy(buf, prefix, 3);
986
      value = msLookupHashTable(metadata, buf);
246,981✔
987
      namespaces++;
246,981✔
988
    }
989
  }
990

991
  return value;
992
}
993

994
/*
995
** msOWSLookupMetadataWithLanguage()
996
**
997
** Attempts to lookup a given metadata name in multiple OWS namespaces
998
** for a specific language.
999
*/
1000
const char *msOWSLookupMetadataWithLanguage(hashTableObj *metadata,
15,055✔
1001
                                            const char *namespaces,
1002
                                            const char *name,
1003
                                            const char *validated_language) {
1004
  const char *value = NULL;
1005

1006
  if (name && validated_language && validated_language[0]) {
15,055✔
1007
    size_t bufferSize = strlen(name) + strlen(validated_language) + 2;
1,218✔
1008
    char *name2 = (char *)msSmallMalloc(bufferSize);
1,218✔
1009
    snprintf(name2, bufferSize, "%s.%s", name, validated_language);
1010
    value = msOWSLookupMetadata(metadata, namespaces, name2);
1,218✔
1011
    free(name2);
1,218✔
1012
  }
1013

1014
  if (name && !value) {
15,055✔
1015
    value = msOWSLookupMetadata(metadata, namespaces, name);
14,594✔
1016
  }
1017

1018
  return value;
15,055✔
1019
}
1020

1021
/*
1022
** msOWSLookupMetadata2()
1023
**
1024
** Attempts to lookup a given metadata name in multiple hashTables, and
1025
** in multiple OWS namespaces within each. First searches the primary
1026
** table and if no result is found, attempts the search using the
1027
** secondary (fallback) table.
1028
**
1029
** 'namespaces' is a string with a letter for each namespace to lookup
1030
** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_
1031
** If namespaces is NULL then this function just does a regular metadata
1032
** lookup.
1033
*/
1034
const char *msOWSLookupMetadata2(hashTableObj *pri, hashTableObj *sec,
2,216✔
1035
                                 const char *namespaces, const char *name) {
1036
  const char *result;
1037

1038
  if ((result = msOWSLookupMetadata(pri, namespaces, name)) == NULL) {
2,216✔
1039
    /* Try the secondary table */
1040
    result = msOWSLookupMetadata(sec, namespaces, name);
2,209✔
1041
  }
1042

1043
  return result;
2,216✔
1044
}
1045

1046
/* msOWSParseVersionString()
1047
**
1048
** Parse a version string in the format "a.b.c" or "a.b" and return an
1049
** integer in the format 0x0a0b0c suitable for regular integer comparisons.
1050
**
1051
** Returns one of OWS_VERSION_NOTSET or OWS_VERSION_BADFORMAT if version
1052
** could not be parsed.
1053
*/
1054
int msOWSParseVersionString(const char *pszVersion) {
2,315✔
1055
  char **digits = NULL;
1056
  int numDigits = 0;
2,315✔
1057

1058
  if (pszVersion) {
2,315✔
1059
    int nVersion = 0;
1060
    digits = msStringSplit(pszVersion, '.', &numDigits);
2,303✔
1061
    if (digits == NULL || numDigits < 2 || numDigits > 3) {
2,303✔
1062
      msSetError(MS_OWSERR,
15✔
1063
                 "Invalid version (%s). Version must be in the "
1064
                 "format 'x.y' or 'x.y.z'",
1065
                 "msOWSParseVersionString()", pszVersion);
1066
      if (digits)
15✔
1067
        msFreeCharArray(digits, numDigits);
15✔
1068
      return OWS_VERSION_BADFORMAT;
15✔
1069
    }
1070

1071
    nVersion = atoi(digits[0]) * 0x010000;
2,288✔
1072
    nVersion += atoi(digits[1]) * 0x0100;
2,288✔
1073
    if (numDigits > 2)
2,288✔
1074
      nVersion += atoi(digits[2]);
2,173✔
1075

1076
    msFreeCharArray(digits, numDigits);
2,288✔
1077

1078
    return nVersion;
2,288✔
1079
  }
1080

1081
  return OWS_VERSION_NOTSET;
1082
}
1083

1084
/* msOWSGetVersionString()
1085
**
1086
** Returns a OWS version string in the format a.b.c from the integer
1087
** version value passed as argument (0x0a0b0c)
1088
**
1089
** Fills in the pszBuffer and returns a reference to it. Recommended buffer
1090
** size is OWS_VERSION_MAXLEN chars.
1091
*/
1092
const char *msOWSGetVersionString(int nVersion, char *pszBuffer) {
578✔
1093

1094
  if (pszBuffer)
578✔
1095
    snprintf(pszBuffer, OWS_VERSION_MAXLEN - 1, "%d.%d.%d",
578✔
1096
             (nVersion / 0x10000) % 0x100, (nVersion / 0x100) % 0x100,
578✔
1097
             nVersion % 0x100);
1098

1099
  return pszBuffer;
578✔
1100
}
1101

1102
/*
1103
** msOWSGetEPSGProj()
1104
**
1105
** Extract projection code for this layer/map.
1106
**
1107
** First look for a xxx_srs metadata. If not found then look for an EPSG
1108
** code in projectionObj, and if not found then return NULL.
1109
**
1110
** If bReturnOnlyFirstOne=TRUE and metadata contains multiple EPSG codes
1111
** then only the first one (which is assumed to be the layer's default
1112
** projection) is returned.
1113
*/
1114
void msOWSGetEPSGProj(projectionObj *proj, hashTableObj *metadata,
4,007✔
1115
                      const char *namespaces, int bReturnOnlyFirstOne,
1116
                      char **epsgCode) {
1117
  const char *value;
1118
  *epsgCode = NULL;
4,007✔
1119

1120
  /* metadata value should already be in format "EPSG:n" or "AUTO:..." */
1121
  if (metadata &&
7,015✔
1122
      ((value = msOWSLookupMetadata(metadata, namespaces, "srs")) != NULL)) {
3,008✔
1123
    const char *space_ptr;
1124
    if (!bReturnOnlyFirstOne || (space_ptr = strchr(value, ' ')) == NULL) {
2,411✔
1125
      *epsgCode = msStrdup(value);
1,242✔
1126
      return;
1,242✔
1127
    }
1128

1129
    *epsgCode = static_cast<char *>(
1,169✔
1130
        msSmallMalloc((space_ptr - value + 1) * sizeof(char)));
1,169✔
1131
    /* caller requested only first projection code, copy up to the first space
1132
     * character*/
1133
    strlcpy(*epsgCode, value, space_ptr - value + 1);
1134
    return;
1,169✔
1135
  } else if (proj && proj->numargs > 0 &&
1,596✔
1136
             (value = strstr(proj->args[0], "init=epsg:")) != NULL) {
1,567✔
1137
    *epsgCode = static_cast<char *>(msSmallMalloc(
1,457✔
1138
        (strlen("EPSG:") + strlen(value + 10) + 1) * sizeof(char)));
1,457✔
1139
    sprintf(*epsgCode, "EPSG:%s", value + 10);
1140
    return;
1,457✔
1141
  } else if (proj && proj->numargs > 0 &&
139✔
1142
             (value = strstr(proj->args[0], "init=crs:")) != NULL) {
110✔
NEW
1143
    *epsgCode = static_cast<char *>(
×
NEW
1144
        msSmallMalloc((strlen("CRS:") + strlen(value + 9) + 1) * sizeof(char)));
×
1145
    sprintf(*epsgCode, "CRS:%s", value + 9);
1146
    return;
×
1147
  } else if (proj && proj->numargs > 0 &&
139✔
1148
             (strncasecmp(proj->args[0], "AUTO:", 5) == 0 ||
110✔
1149
              strncasecmp(proj->args[0], "AUTO2:", 6) == 0)) {
110✔
1150
    *epsgCode = msStrdup(proj->args[0]);
×
1151
    return;
×
1152
  }
1153
}
1154

1155
/*
1156
** msOWSProjectToWGS84()
1157
**
1158
** Reprojects the extent to WGS84.
1159
**
1160
*/
1161
void msOWSProjectToWGS84(projectionObj *srcproj, rectObj *ext) {
301✔
1162
  if (srcproj->proj && !msProjIsGeographicCRS(srcproj)) {
301✔
1163
    projectionObj wgs84;
1164
    msInitProjection(&wgs84);
218✔
1165
    msProjectionInheritContextFrom(&wgs84, srcproj);
218✔
1166
    msLoadProjectionString(&wgs84, "+proj=longlat +ellps=WGS84 +datum=WGS84");
218✔
1167
    msProjectRect(srcproj, &wgs84, ext);
218✔
1168
    msFreeProjection(&wgs84);
218✔
1169
  }
1170
}
301✔
1171

1172
/* msOWSGetLanguage()
1173
**
1174
** returns the language via MAP/WEB/METADATA/ows_language
1175
**
1176
** Use value of "ows_language" metadata, if not set then
1177
** return "undefined" as a default
1178
*/
1179
const char *msOWSGetLanguage(mapObj *map, const char *context) {
145✔
1180
  const char *language;
1181

1182
  /* if this is an exception, MapServer always returns Exception
1183
     messages in en-US
1184
  */
1185
  if (strcmp(context, "exception") == 0) {
145✔
1186
    language = MS_ERROR_LANGUAGE;
1187
  }
1188
  /* if not, fetch language from mapfile metadata */
1189
  else {
1190
    language = msLookupHashTable(&(map->web.metadata), "ows_language");
×
1191

1192
    if (language == NULL) {
×
1193
      language = "undefined";
1194
    }
1195
  }
1196
  return language;
145✔
1197
}
1198

1199
/* msOWSGetSchemasLocation()
1200
**
1201
** schemas location is the root of the web tree where all WFS-related
1202
** schemas can be found on this server.  These URLs must exist in order
1203
** to validate xml.
1204
**
1205
** Use value of "ows_schemas_location" metadata, if not set then
1206
** return ".." as a default
1207
*/
1208
const char *msOWSGetSchemasLocation(mapObj *map) {
1,192✔
1209
  const char *schemas_location;
1210

1211
  schemas_location =
1212
      msLookupHashTable(&(map->web.metadata), "ows_schemas_location");
1,192✔
1213
  if (schemas_location == NULL)
1,192✔
1214
    schemas_location = OWS_DEFAULT_SCHEMAS_LOCATION;
1215

1216
  return schemas_location;
1,192✔
1217
}
1218

1219
/*
1220
** msOWSGetExpandedMetadataKey()
1221
*/
1222
char *msOWSGetExpandedMetadataKey(const char *namespaces,
2,601✔
1223
                                  const char *metadata_name) {
1224
  char *pszRet = msStringConcatenate(NULL, "");
2,601✔
1225
  for (int i = 0; namespaces[i] != '\0'; ++i) {
7,835✔
1226
    if (i > 0)
5,234✔
1227
      pszRet = msStringConcatenate(pszRet, " or ");
2,633✔
1228
    pszRet = msStringConcatenate(pszRet, "\"");
5,234✔
1229
    pszRet =
1230
        msStringConcatenate(pszRet, msOWSGetPrefixFromNamespace(namespaces[i]));
5,234✔
1231
    pszRet = msStringConcatenate(pszRet, "_");
5,234✔
1232
    pszRet = msStringConcatenate(pszRet, metadata_name);
5,234✔
1233
    pszRet = msStringConcatenate(pszRet, "\"");
5,234✔
1234
  }
1235
  return pszRet;
2,601✔
1236
}
1237

1238
/*
1239
** msOWSGetOnlineResource()
1240
**
1241
** Return the online resource for this service.  First try to lookup
1242
** specified metadata, and if not found then try to build the URL ourselves.
1243
**
1244
** Returns a newly allocated string that should be freed by the caller or
1245
** NULL in case of error.
1246
*/
1247
char *msOWSGetOnlineResource(mapObj *map, const char *namespaces,
748✔
1248
                             const char *metadata_name, cgiRequestObj *req) {
1249
  const char *value;
1250
  char *online_resource = NULL;
1251

1252
  /* We need this script's URL, including hostname. */
1253
  /* Default to use the value of the "onlineresource" metadata, and if not */
1254
  /* set then build it: "http://$(SERVER_NAME):$(SERVER_PORT)$(SCRIPT_NAME)?" */
1255
  /* (+append the map=... param if it was explicitly passed in QUERY_STRING) */
1256
  /*  */
1257
  if ((value = msOWSLookupMetadata(&(map->web.metadata), namespaces,
748✔
1258
                                   metadata_name))) {
1259
    online_resource = msOWSTerminateOnlineResource(value);
733✔
1260
  } else {
1261
    if ((online_resource = msBuildOnlineResource(map, req)) == NULL) {
15✔
1262
      char *pszExpandedMetadataKey =
1263
          msOWSGetExpandedMetadataKey(namespaces, metadata_name);
15✔
1264
      msSetError(MS_CGIERR, "Please set %s metadata.",
15✔
1265
                 "msOWSGetOnlineResource()", pszExpandedMetadataKey);
1266
      msFree(pszExpandedMetadataKey);
15✔
1267
      return NULL;
15✔
1268
    }
1269
  }
1270

1271
  return online_resource;
1272
}
1273

1274
/*
1275
** msOWSTerminateOnlineResource()
1276
**
1277
** Append trailing "?" or "&" to an onlineresource URL if it doesn't have
1278
** one already. The returned string is then ready to append GET parameters
1279
** to it.
1280
**
1281
** Returns a newly allocated string that should be freed by the caller or
1282
** NULL in case of error.
1283
*/
1284
char *msOWSTerminateOnlineResource(const char *src_url) {
733✔
1285
  char *online_resource = NULL;
1286
  size_t buffer_size = 0;
1287

1288
  if (src_url == NULL)
733✔
1289
    return NULL;
1290

1291
  buffer_size = strlen(src_url) + 2;
733✔
1292
  online_resource = (char *)malloc(buffer_size);
733✔
1293

1294
  if (online_resource == NULL) {
733✔
1295
    msSetError(MS_MEMERR, NULL, "msOWSTerminateOnlineResource()");
×
1296
    return NULL;
×
1297
  }
1298

1299
  strlcpy(online_resource, src_url, buffer_size);
1300

1301
  /* Append trailing '?' or '&' if missing. */
1302
  if (strchr(online_resource, '?') == NULL)
733✔
1303
    strlcat(online_resource, "?", buffer_size);
1304
  else {
1305
    char *c;
1306
    c = online_resource + strlen(online_resource) - 1;
638✔
1307
    if (*c != '?' && *c != '&')
638✔
1308
      strlcpy(c + 1, "&", buffer_size - strlen(online_resource));
6✔
1309
  }
1310

1311
  return online_resource;
1312
}
1313

1314
/************************************************************************/
1315
/*                         msUpdateGMLFieldMetadata                     */
1316
/*                                                                      */
1317
/*      Updates a fields GML metadata if it has not already             */
1318
/*      been set. Nullable is not implemented for all drivers           */
1319
/*      and can be set to 0 if unknown                                  */
1320
/************************************************************************/
1321
int msUpdateGMLFieldMetadata(layerObj *layer, const char *field_name,
2,882✔
1322
                             const char *gml_type, const char *gml_width,
1323
                             const char *gml_precision, const short nullable) {
1324

1325
  char md_item_name[256];
1326

1327
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_type", field_name);
1328
  if (msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
2,882✔
1329
    msInsertHashTable(&(layer->metadata), md_item_name, gml_type);
1,329✔
1330

1331
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_width", field_name);
1332
  if (strlen(gml_width) > 0 &&
4,088✔
1333
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
1,206✔
1334
    msInsertHashTable(&(layer->metadata), md_item_name, gml_width);
645✔
1335

1336
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_precision", field_name);
1337
  if (strlen(gml_precision) > 0 &&
3,011✔
1338
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
129✔
1339
    msInsertHashTable(&(layer->metadata), md_item_name, gml_precision);
68✔
1340

1341
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_nillable", field_name);
1342
  if (nullable > 0 &&
2,882✔
1343
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
×
1344
    msInsertHashTable(&(layer->metadata), md_item_name, "true");
×
1345

1346
  return MS_TRUE;
2,882✔
1347
}
1348

1349
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1350
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
1351

1352
/*
1353
** msRenameLayer()
1354
*/
1355
static int msRenameLayer(layerObj *lp, int count) {
×
1356
  char *newname;
NEW
1357
  const size_t nSize = strlen(lp->name) + 16;
×
NEW
1358
  newname = (char *)malloc(nSize);
×
1359
  if (!newname) {
×
1360
    msSetError(MS_MEMERR, NULL, "msRenameLayer()");
×
1361
    return MS_FAILURE;
×
1362
  }
1363
  snprintf(newname, nSize, "%s_%2.2d", lp->name, count);
1364
  free(lp->name);
×
1365
  lp->name = newname;
×
1366

1367
  return MS_SUCCESS;
×
1368
}
1369

1370
/*
1371
** msOWSMakeAllLayersUnique()
1372
*/
1373
int msOWSMakeAllLayersUnique(mapObj *map) {
1,468✔
1374
  std::map<std::string, int> namesToIdx;
1375
  std::map<std::string, int> namesToCount;
1376

1377
  /* Make sure all layers in the map file have valid and unique names */
1378
  for (int i = 0; i < map->numlayers; i++) {
8,691✔
1379
    layerObj *lp = GET_LAYER(map, i);
7,223✔
1380
    if (!lp->name)
7,223✔
NEW
1381
      continue;
×
1382
    const std::string name = msStringToLower(std::string(lp->name));
14,446✔
1383
    auto iterToIdx = namesToIdx.find(name);
1384
    if (iterToIdx == namesToIdx.end()) {
7,223✔
1385
      namesToIdx[name] = i;
7,223✔
1386
      namesToCount[name] = 1;
7,223✔
1387
    } else {
1388
      auto iterToCount = namesToCount.find(name);
1389
      assert(iterToCount != namesToCount.end());
NEW
1390
      if (iterToCount->second == 1) {
×
NEW
1391
        if (msRenameLayer(GET_LAYER(map, iterToIdx->second), 1) != MS_SUCCESS)
×
1392
          return MS_FAILURE;
1393
      }
NEW
1394
      ++iterToCount->second;
×
NEW
1395
      if (msRenameLayer(lp, iterToCount->second) != MS_SUCCESS)
×
1396
        return MS_FAILURE;
1397
    }
1398
  }
1399

1400
  return MS_SUCCESS;
1401
}
1402

1403
/*
1404
** msOWSNegotiateVersion()
1405
**
1406
** returns the most suitable version an OWS is to support given a client
1407
** version parameter.
1408
**
1409
** supported_versions must be ordered from highest to lowest
1410
**
1411
** Returns a version integer of the supported version
1412
**
1413
*/
1414

1415
int msOWSNegotiateVersion(int requested_version, const int supported_versions[],
101✔
1416
                          int num_supported_versions) {
1417
  int i;
1418

1419
  /* if version is not set return highest version */
1420
  if (!requested_version)
101✔
1421
    return supported_versions[0];
×
1422

1423
  /* if the requested version is lower than the lowest version return the lowest
1424
   * version  */
1425
  if (requested_version < supported_versions[num_supported_versions - 1])
101✔
1426
    return supported_versions[num_supported_versions - 1];
1427

1428
  /* return the first entry that's lower than or equal to the requested version
1429
   */
1430
  for (i = 0; i < num_supported_versions; i++) {
282✔
1431
    if (supported_versions[i] <= requested_version)
282✔
1432
      return supported_versions[i];
101✔
1433
  }
1434

1435
  return requested_version;
1436
}
1437

1438
/*
1439
** msOWSGetOnlineResource()
1440
**
1441
** Return the online resource for this service and add language parameter.
1442
**
1443
** Returns a newly allocated string that should be freed by the caller or
1444
** NULL in case of error.
1445
*/
1446
char *msOWSGetOnlineResource2(mapObj *map, const char *namespaces,
88✔
1447
                              const char *metadata_name, cgiRequestObj *req,
1448
                              const char *validated_language) {
1449
  char *online_resource =
1450
      msOWSGetOnlineResource(map, namespaces, metadata_name, req);
88✔
1451

1452
  if (online_resource && validated_language && validated_language[0]) {
88✔
1453
    /* online_resource is already terminated, so we can simply add language=...&
1454
     */
1455
    /* but first we need to make sure that online_resource has enough capacity
1456
     */
1457
    online_resource = (char *)msSmallRealloc(
20✔
1458
        online_resource,
1459
        strlen(online_resource) + strlen(validated_language) + 11);
20✔
1460
    strcat(online_resource, "language=");
1461
    strcat(online_resource, validated_language);
1462
    strcat(online_resource, "&");
1463
  }
1464

1465
  return online_resource;
88✔
1466
}
1467

1468
/* msOWSGetInspireSchemasLocation()
1469
**
1470
** schemas location is the root of the web tree where all Inspire-related
1471
** schemas can be found on this server.  These URLs must exist in order
1472
** to validate xml.
1473
**
1474
** Use value of "inspire_schemas_location" metadata
1475
*/
1476
const char *msOWSGetInspireSchemasLocation(mapObj *map) {
41✔
1477
  const char *schemas_location;
1478

1479
  schemas_location =
1480
      msLookupHashTable(&(map->web.metadata), "inspire_schemas_location");
41✔
1481
  if (schemas_location == NULL)
41✔
1482
    schemas_location = "http://inspire.ec.europa.eu/schemas";
1483

1484
  return schemas_location;
41✔
1485
}
1486

1487
/* msOWSGetLanguageList
1488
**
1489
** Returns the list of languages that this service supports
1490
**
1491
** Use value of "languages" metadata (comma-separated list), or NULL if not set
1492
**
1493
** Returns a malloced char** of length numitems which must be freed
1494
** by the caller, or NULL (with numitems = 0)
1495
*/
1496
char **msOWSGetLanguageList(mapObj *map, const char *namespaces,
1,426✔
1497
                            int *numitems) {
1498

1499
  const char *languages = NULL;
1500

1501
  languages =
1502
      msOWSLookupMetadata(&(map->web.metadata), namespaces, "languages");
1,426✔
1503
  if (languages && strlen(languages) > 0) {
1,426✔
1504
    return msStringSplit(languages, ',', numitems);
132✔
1505
  } else {
1506
    *numitems = 0;
1,294✔
1507
    return NULL;
1,294✔
1508
  }
1509
}
1510

1511
/* msOWSGetLanguageFromList
1512
**
1513
** Returns a language according to the language requested by the client
1514
**
1515
** If the requested language is in the languages metadata then use it,
1516
** otherwise ignore it and use the default language, which is the first entry in
1517
** the languages metadata list. Calling with a NULL requested_langauge
1518
** therefore returns this default language. If the language metadata list is
1519
** not defined then the language is set to NULL.
1520
**
1521
** Returns a malloced char* which must be freed by the caller, or NULL
1522
*/
1523
char *msOWSGetLanguageFromList(mapObj *map, const char *namespaces,
1,385✔
1524
                               const char *requested_language) {
1525
  int num_items = 0;
1,385✔
1526
  char **languages = msOWSGetLanguageList(map, namespaces, &num_items);
1,385✔
1527
  char *language = NULL;
1528

1529
  if (languages && num_items > 0) {
1,385✔
1530
    if (!requested_language ||
128✔
1531
        !msStringInArray(requested_language, languages, num_items)) {
16✔
1532
      language = msStrdup(languages[0]);
98✔
1533
    } else {
1534
      language = msStrdup(requested_language);
14✔
1535
    }
1536
  }
1537
  msFreeCharArray(languages, num_items);
1,385✔
1538

1539
  return language;
1,385✔
1540
}
1541

1542
/* msOWSLanguageNegotiation
1543
**
1544
** Returns a language according to the accepted languages requested by the
1545
*client
1546
**
1547
** Returns a malloced char* which must be freed by the caller, or NULL
1548
*/
1549
char *msOWSLanguageNegotiation(mapObj *map, const char *namespaces,
41✔
1550
                               char **accept_languages,
1551
                               int num_accept_languages) {
1552
  int num_languages = 0;
41✔
1553
  char **languages = NULL;
1554
  char *result_language = NULL;
1555

1556
  languages = msOWSGetLanguageList(map, namespaces, &num_languages);
41✔
1557

1558
  if (languages && num_languages > 0) {
41✔
1559
    int i;
1560
    for (i = 0; i < num_accept_languages; ++i) {
28✔
1561
      const char *accept_language = accept_languages[i];
20✔
1562

1563
      /* '*' means any language */
1564
      if (EQUAL(accept_language, "*")) {
20✔
1565
        result_language = msStrdup(languages[0]);
×
1566
        break;
1567
      } else if (msStringInArray(accept_language, languages, num_languages)) {
20✔
1568
        result_language = msStrdup(accept_language);
12✔
1569
        break;
1570
      }
1571
    }
1572

1573
    if (result_language == NULL) {
20✔
1574
      result_language = msStrdup(languages[0]);
8✔
1575
    }
1576
  }
1577

1578
  msFreeCharArray(languages, num_languages);
41✔
1579
  return result_language;
41✔
1580
}
1581

1582
/* msOWSPrintInspireCommonExtendedCapabilities
1583
**
1584
** Output INSPIRE common extended capabilities items to stream
1585
** The currently supported items are metadata and languages
1586
**
1587
** tag_name is the name (including ns prefix) of the tag to include the whole
1588
** extended capabilities block in
1589
**
1590
** service is currently included for future compatibility when differing
1591
** extended capabilities elements are included for different service types
1592
**
1593
** Returns a status code; MS_NOERR if all ok, action_if_not_found otherwise
1594
*/
1595
int msOWSPrintInspireCommonExtendedCapabilities(
41✔
1596
    FILE *stream, mapObj *map, const char *namespaces, int action_if_not_found,
1597
    const char *tag_name, const char *tag_ns, const char *validated_language,
1598
    const OWSServiceType service) {
1599

1600
  int metadataStatus = 0;
1601
  int languageStatus = 0;
1602

1603
  if (tag_ns)
41✔
1604
    msIO_fprintf(stream, "  <%s %s>\n", tag_name, tag_ns);
26✔
1605
  else
1606
    msIO_fprintf(stream, "  <%s>\n", tag_name);
15✔
1607

1608
  metadataStatus = msOWSPrintInspireCommonMetadata(
41✔
1609
      stream, map, namespaces, action_if_not_found, service);
1610
  languageStatus = msOWSPrintInspireCommonLanguages(
41✔
1611
      stream, map, namespaces, action_if_not_found, validated_language);
1612

1613
  msIO_fprintf(stream, "  </%s>\n", tag_name);
41✔
1614

1615
  return (metadataStatus != MS_NOERR) ? metadataStatus : languageStatus;
41✔
1616
}
1617

1618
/* msOWSPrintInspireCommonMetadata
1619
**
1620
** Output INSPIRE common metadata items to a stream
1621
**
1622
** Returns a status code; MS_NOERR if all OK, action_if_not_found otherwise
1623
*/
1624
int msOWSPrintInspireCommonMetadata(FILE *stream, mapObj *map,
41✔
1625
                                    const char *namespaces,
1626
                                    int action_if_not_found,
1627
                                    const OWSServiceType service) {
1628

1629
  int status = MS_NOERR;
1630
  const char *inspire_capabilities = NULL;
1631

1632
  inspire_capabilities = msOWSLookupMetadata(&(map->web.metadata), namespaces,
41✔
1633
                                             "inspire_capabilities");
1634

1635
  if (!inspire_capabilities) {
41✔
1636
    if (OWS_WARN == action_if_not_found) {
×
1637
      msIO_fprintf(
×
1638
          stream,
1639
          "<!-- WARNING: missing metadata entry for 'inspire_capabilities', "
1640
          "one of 'embed' and 'url' must be supplied. -->\n");
1641
    }
1642
    return action_if_not_found;
×
1643
  }
1644
  if (strcasecmp("url", inspire_capabilities) == 0) {
41✔
1645
    if (msOWSLookupMetadata(&(map->web.metadata), namespaces,
20✔
1646
                            "inspire_metadataurl_href") != NULL) {
1647
      msIO_fprintf(stream,
20✔
1648
                   "    <inspire_common:MetadataUrl "
1649
                   "xsi:type=\"inspire_common:resourceLocatorType\">\n");
1650
      msOWSPrintEncodeMetadata(
20✔
1651
          stream, &(map->web.metadata), namespaces, "inspire_metadataurl_href",
1652
          OWS_WARN, "      <inspire_common:URL>%s</inspire_common:URL>\n", "");
1653
      msOWSPrintEncodeMetadata(
20✔
1654
          stream, &(map->web.metadata), namespaces,
1655
          "inspire_metadataurl_format", OWS_WARN,
1656
          "      <inspire_common:MediaType>%s</inspire_common:MediaType>\n",
1657
          "");
1658
      msIO_fprintf(stream, "    </inspire_common:MetadataUrl>\n");
20✔
1659
    } else {
1660
      status = action_if_not_found;
1661
      if (OWS_WARN == action_if_not_found) {
×
1662
        char *pszExpandedMetadataKey =
1663
            msOWSGetExpandedMetadataKey(namespaces, "inspire_metadataurl_href");
×
1664
        msIO_fprintf(stream,
×
1665
                     "<!-- WARNING: Mandatory metadata %s was missing in this "
1666
                     "context. -->\n",
1667
                     pszExpandedMetadataKey);
1668
        msFree(pszExpandedMetadataKey);
×
1669
      }
1670
    }
1671
  } else if (strcasecmp("embed", inspire_capabilities) == 0) {
21✔
1672
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1673
                             "inspire_resourcelocator", OWS_WARN,
1674
                             "    <inspire_common:ResourceLocator>\n      "
1675
                             "<inspire_common:URL>%s</inspire_common:URL>\n    "
1676
                             "</inspire_common:ResourceLocator>\n",
1677
                             NULL);
1678
    msIO_fprintf(
21✔
1679
        stream,
1680
        "    "
1681
        "<inspire_common:ResourceType>service</inspire_common:ResourceType>\n");
1682
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1683
                             "inspire_temporal_reference", OWS_WARN,
1684
                             "    <inspire_common:TemporalReference>\n      "
1685
                             "<inspire_common:DateOfLastRevision>%s</"
1686
                             "inspire_common:DateOfLastRevision>\n    "
1687
                             "</inspire_common:TemporalReference>\n",
1688
                             "");
1689
    msIO_fprintf(stream, "    <inspire_common:Conformity>\n");
21✔
1690
    msIO_fprintf(stream, "      <inspire_common:Specification>\n");
21✔
1691
    msIO_fprintf(stream,
21✔
1692
                 "        <inspire_common:Title>-</inspire_common:Title>\n");
1693
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1694
                             "inspire_temporal_reference", OWS_NOERR,
1695
                             "        "
1696
                             "<inspire_common:DateOfLastRevision>%s</"
1697
                             "inspire_common:DateOfLastRevision>\n",
1698
                             "");
1699
    msIO_fprintf(stream, "      </inspire_common:Specification>\n");
21✔
1700
    msIO_fprintf(
21✔
1701
        stream,
1702
        "      <inspire_common:Degree>notEvaluated</inspire_common:Degree>\n");
1703
    msIO_fprintf(stream, "    </inspire_common:Conformity>\n");
21✔
1704
    msIO_fprintf(stream, "    <inspire_common:MetadataPointOfContact>\n");
21✔
1705
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1706
                             "inspire_mpoc_name", OWS_WARN,
1707
                             "      "
1708
                             "<inspire_common:OrganisationName>%s</"
1709
                             "inspire_common:OrganisationName>\n",
1710
                             "");
1711
    msOWSPrintEncodeMetadata(
21✔
1712
        stream, &(map->web.metadata), namespaces, "inspire_mpoc_email",
1713
        OWS_WARN,
1714
        "      <inspire_common:EmailAddress>%s</inspire_common:EmailAddress>\n",
1715
        "");
1716
    msIO_fprintf(stream, "    </inspire_common:MetadataPointOfContact>\n");
21✔
1717
    msOWSPrintEncodeMetadata(
21✔
1718
        stream, &(map->web.metadata), namespaces, "inspire_metadatadate",
1719
        OWS_WARN,
1720
        "      <inspire_common:MetadataDate>%s</inspire_common:MetadataDate>\n",
1721
        "");
1722
    if (service == OWS_WFS || service == OWS_WCS)
21✔
1723
      msIO_fprintf(stream, "    "
15✔
1724
                           "<inspire_common:SpatialDataServiceType>download</"
1725
                           "inspire_common:SpatialDataServiceType>\n");
1726
    else
1727
      msIO_fprintf(stream, "    "
6✔
1728
                           "<inspire_common:SpatialDataServiceType>view</"
1729
                           "inspire_common:SpatialDataServiceType>\n");
1730
    msOWSPrintEncodeMetadata(
21✔
1731
        stream, &(map->web.metadata), namespaces, "inspire_keyword", OWS_WARN,
1732
        "    <inspire_common:MandatoryKeyword>\n      "
1733
        "<inspire_common:KeywordValue>%s</inspire_common:KeywordValue>\n    "
1734
        "</inspire_common:MandatoryKeyword>\n",
1735
        "");
1736
  } else {
1737
    status = action_if_not_found;
1738
    if (OWS_WARN == action_if_not_found) {
×
1739
      msIO_fprintf(
×
1740
          stream,
1741
          "<!-- WARNING: invalid value '%s' for 'inspire_capabilities', only "
1742
          "'embed' and 'url' are supported. -->\n",
1743
          inspire_capabilities);
1744
    }
1745
  }
1746

1747
  return status;
1748
}
1749

1750
/* msOWSPrintInspireCommonLanguages
1751
**
1752
** Output INSPIRE supported languages block to stream
1753
**
1754
** Returns a status code; MS_NOERR if all OK; action_if_not_found otherwise
1755
*/
1756
int msOWSPrintInspireCommonLanguages(FILE *stream, mapObj *map,
41✔
1757
                                     const char *namespaces,
1758
                                     int action_if_not_found,
1759
                                     const char *validated_language) {
1760
  char *buffer =
1761
      NULL; /* temp variable for malloced strings that will need freeing */
1762
  int status = MS_NOERR;
1763

1764
  char *default_language = msOWSGetLanguageFromList(map, namespaces, NULL);
41✔
1765

1766
  if (validated_language && validated_language[0] && default_language) {
41✔
1767
    msIO_fprintf(stream, "    <inspire_common:SupportedLanguages>\n");
40✔
1768
    msIO_fprintf(
40✔
1769
        stream,
1770
        "      <inspire_common:DefaultLanguage><inspire_common:Language>%s"
1771
        "</inspire_common:Language></inspire_common:DefaultLanguage>\n",
1772
        buffer = msEncodeHTMLEntities(default_language));
40✔
1773
    msFree(buffer);
40✔
1774

1775
    /* append _exclude to our default_language*/
1776
    default_language = static_cast<char *>(msSmallRealloc(
40✔
1777
        default_language, strlen(default_language) + strlen("_exclude") + 1));
40✔
1778
    strcat(default_language, "_exclude");
1779

1780
    msOWSPrintEncodeMetadataList(
40✔
1781
        stream, &(map->web.metadata), namespaces, "languages", NULL, NULL,
1782
        "      <inspire_common:SupportedLanguage><inspire_common:Language>%s"
1783
        "</inspire_common:Language></inspire_common:SupportedLanguage>\n",
1784
        default_language);
1785
    msIO_fprintf(stream, "    </inspire_common:SupportedLanguages>\n");
40✔
1786
    msIO_fprintf(
40✔
1787
        stream,
1788
        "    <inspire_common:ResponseLanguage><inspire_common:Language>%s"
1789
        "</inspire_common:Language></inspire_common:ResponseLanguage>\n",
1790
        validated_language);
1791
  } else {
1792
    status = action_if_not_found;
1793
    if (OWS_WARN == action_if_not_found) {
1✔
1794
      char *pszExpandedMetadataKey =
1795
          msOWSGetExpandedMetadataKey(namespaces, "languages");
1✔
1796
      msIO_fprintf(stream,
1✔
1797
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1798
                   "context. -->\n",
1799
                   pszExpandedMetadataKey);
1800
      msFree(pszExpandedMetadataKey);
1✔
1801
    }
1802
  }
1803

1804
  msFree(default_language);
41✔
1805

1806
  return status;
41✔
1807
}
1808

1809
/*
1810
** msOWSPrintMetadata()
1811
**
1812
** Attempt to output a capability item.  If corresponding metadata is not
1813
** found then one of a number of predefined actions will be taken.
1814
** If a default value is provided and metadata is absent then the
1815
** default will be used.
1816
*/
1817

1818
int msOWSPrintMetadata(FILE *stream, hashTableObj *metadata,
×
1819
                       const char *namespaces, const char *name,
1820
                       int action_if_not_found, const char *format,
1821
                       const char *default_value) {
1822
  const char *value = NULL;
1823
  int status = MS_NOERR;
1824

1825
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)) != NULL) {
×
1826
    msIO_fprintf(stream, format, value);
×
1827
  } else {
1828
    if (action_if_not_found == OWS_WARN) {
×
1829
      char *pszExpandedMetadataKey =
1830
          msOWSGetExpandedMetadataKey(namespaces, name);
×
1831
      msIO_fprintf(stream,
×
1832
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1833
                   "context. -->\n",
1834
                   pszExpandedMetadataKey);
1835
      msFree(pszExpandedMetadataKey);
×
1836
      status = action_if_not_found;
1837
    }
1838

1839
    if (default_value)
×
1840
      msIO_fprintf(stream, format, default_value);
×
1841
  }
1842

1843
  return status;
×
1844
}
1845

1846
/*
1847
** msOWSPrintEncodeMetadata()
1848
**
1849
** Attempt to output a capability item.  If corresponding metadata is not
1850
** found then one of a number of predefined actions will be taken.
1851
** If a default value is provided and metadata is absent then the
1852
** default will be used.
1853
** Also encode the value with msEncodeHTMLEntities.
1854
*/
1855

1856
int msOWSPrintEncodeMetadata(FILE *stream, hashTableObj *metadata,
1,402✔
1857
                             const char *namespaces, const char *name,
1858
                             int action_if_not_found, const char *format,
1859
                             const char *default_value) {
1860
  return msOWSPrintEncodeMetadata2(stream, metadata, namespaces, name,
1,402✔
1861
                                   action_if_not_found, format, default_value,
1862
                                   NULL);
1,402✔
1863
}
1864

1865
/*
1866
** msOWSPrintEncodeMetadata2()
1867
**
1868
** Attempt to output a capability item in the requested language.
1869
** Fallback using no language parameter.
1870
*/
1871
int msOWSPrintEncodeMetadata2(FILE *stream, hashTableObj *metadata,
2,167✔
1872
                              const char *namespaces, const char *name,
1873
                              int action_if_not_found, const char *format,
1874
                              const char *default_value,
1875
                              const char *validated_language) {
1876
  const char *value;
1877
  char *pszEncodedValue = NULL;
1878
  int status = MS_NOERR;
1879

1880
  if ((value = msOWSLookupMetadataWithLanguage(metadata, namespaces, name,
2,167✔
1881
                                               validated_language))) {
1882
    pszEncodedValue = msEncodeHTMLEntities(value);
1,636✔
1883
    msIO_fprintf(stream, format, pszEncodedValue);
1,636✔
1884
    free(pszEncodedValue);
1,636✔
1885
  } else {
1886
    if (action_if_not_found == OWS_WARN) {
531✔
1887
      char *pszExpandedName = msStringConcatenate(NULL, name);
55✔
1888
      if (validated_language && validated_language[0]) {
55✔
1889
        pszExpandedName = msStringConcatenate(pszExpandedName, ".");
×
1890
        pszExpandedName =
1891
            msStringConcatenate(pszExpandedName, validated_language);
×
1892
      }
1893
      char *pszExpandedMetadataKey =
1894
          msOWSGetExpandedMetadataKey(namespaces, pszExpandedName);
55✔
1895
      msIO_fprintf(stream,
55✔
1896
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1897
                   "context. -->\n",
1898
                   pszExpandedMetadataKey);
1899
      msFree(pszExpandedName);
55✔
1900
      msFree(pszExpandedMetadataKey);
55✔
1901
      status = action_if_not_found;
1902
    }
1903

1904
    if (default_value) {
531✔
1905
      pszEncodedValue = msEncodeHTMLEntities(default_value);
205✔
1906
      msIO_fprintf(stream, format, default_value);
205✔
1907
      free(pszEncodedValue);
205✔
1908
    }
1909
  }
1910

1911
  return status;
2,167✔
1912
}
1913

1914
/*
1915
** msOWSGetEncodeMetadata()
1916
**
1917
** Equivalent to msOWSPrintEncodeMetadata. Returns en encoded value of the
1918
** metadata or the default value.
1919
** Caller should free the returned string.
1920
*/
1921
char *msOWSGetEncodeMetadata(hashTableObj *metadata, const char *namespaces,
384✔
1922
                             const char *name, const char *default_value) {
1923
  const char *value;
1924
  char *pszEncodedValue = NULL;
1925
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)))
384✔
1926
    pszEncodedValue = msEncodeHTMLEntities(value);
49✔
1927
  else if (default_value)
335✔
1928
    pszEncodedValue = msEncodeHTMLEntities(default_value);
277✔
1929

1930
  return pszEncodedValue;
384✔
1931
}
1932

1933
/*
1934
** msOWSPrintValidateMetadata()
1935
**
1936
** Attempt to output a capability item.  If corresponding metadata is not
1937
** found then one of a number of predefined actions will be taken.
1938
** If a default value is provided and metadata is absent then the
1939
** default will be used.
1940
** Also validate the value with msIsXMLTagValid.
1941
*/
1942

1943
int msOWSPrintValidateMetadata(FILE *stream, hashTableObj *metadata,
298✔
1944
                               const char *namespaces, const char *name,
1945
                               int action_if_not_found, const char *format,
1946
                               const char *default_value) {
1947
  const char *value;
1948
  int status = MS_NOERR;
1949

1950
  if ((value = msOWSLookupMetadata(metadata, namespaces, name))) {
298✔
1951
    if (msIsXMLTagValid(value) == MS_FALSE)
×
1952
      msIO_fprintf(stream,
×
1953
                   "<!-- WARNING: The value '%s' is not valid in a "
1954
                   "XML tag context. -->\n",
1955
                   value);
1956
    msIO_fprintf(stream, format, value);
×
1957
  } else {
1958
    if (action_if_not_found == OWS_WARN) {
298✔
1959
      char *pszExpandedMetadataKey =
1960
          msOWSGetExpandedMetadataKey(namespaces, name);
×
1961
      msIO_fprintf(stream,
×
1962
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1963
                   "context. -->\n",
1964
                   pszExpandedMetadataKey);
1965
      msFree(pszExpandedMetadataKey);
×
1966
      status = action_if_not_found;
1967
    }
1968

1969
    if (default_value) {
298✔
1970
      if (msIsXMLTagValid(default_value) == MS_FALSE)
298✔
1971
        msIO_fprintf(stream,
×
1972
                     "<!-- WARNING: The value '%s' is not valid "
1973
                     "in a XML tag context. -->\n",
1974
                     default_value);
1975
      msIO_fprintf(stream, format, default_value);
298✔
1976
    }
1977
  }
1978

1979
  return status;
298✔
1980
}
1981

1982
/*
1983
** msOWSPrintGroupMetadata()
1984
**
1985
** Attempt to output a capability item.  If corresponding metadata is not
1986
** found then one of a number of predefined actions will be taken.
1987
** If a default value is provided and metadata is absent then the
1988
** default will be used.
1989
*/
1990
int msOWSPrintGroupMetadata(FILE *stream, mapObj *map, char *pszGroupName,
×
1991
                            const char *namespaces, const char *name,
1992
                            int action_if_not_found, const char *format,
1993
                            const char *default_value) {
1994
  return msOWSPrintGroupMetadata2(stream, map, pszGroupName, namespaces, name,
×
1995
                                  action_if_not_found, format, default_value,
1996
                                  NULL);
×
1997
}
1998

1999
/*
2000
** msOWSPrintGroupMetadata2()
2001
**
2002
** Attempt to output a capability item in the requested language.
2003
** Fallback using no language parameter.
2004
*/
2005
int msOWSPrintGroupMetadata2(FILE *stream, mapObj *map, char *pszGroupName,
×
2006
                             const char *namespaces, const char *name,
2007
                             int action_if_not_found, const char *format,
2008
                             const char *default_value,
2009
                             const char *validated_language) {
2010
  const char *value;
2011
  char *encoded;
2012
  int status = MS_NOERR;
2013
  int i;
2014

2015
  for (i = 0; i < map->numlayers; i++) {
×
2016
    if (GET_LAYER(map, i)->group &&
×
2017
        (strcmp(GET_LAYER(map, i)->group, pszGroupName) == 0)) {
×
2018
      if ((value = msOWSLookupMetadataWithLanguage(
×
2019
               &(GET_LAYER(map, i)->metadata), namespaces, name,
2020
               validated_language))) {
2021
        encoded = msEncodeHTMLEntities(value);
×
2022
        msIO_fprintf(stream, format, encoded);
×
2023
        msFree(encoded);
×
2024
        return status;
×
2025
      }
2026
    }
2027
  }
2028

2029
  if (action_if_not_found == OWS_WARN) {
×
2030
    char *pszExpandedMetadataKey =
2031
        msOWSGetExpandedMetadataKey(namespaces, name);
×
2032
    msIO_fprintf(stream,
×
2033
                 "<!-- WARNING: Mandatory metadata %s was missing in this "
2034
                 "context. -->\n",
2035
                 pszExpandedMetadataKey);
2036
    msFree(pszExpandedMetadataKey);
×
2037
    status = action_if_not_found;
2038
  }
2039

2040
  if (default_value) {
×
2041
    encoded = msEncodeHTMLEntities(default_value);
×
2042
    msIO_fprintf(stream, format, encoded);
×
2043
    msFree(encoded);
×
2044
  }
2045

2046
  return status;
2047
}
2048

2049
/* msOWSPrintURLType()
2050
**
2051
** Attempt to output a URL item in capabilities.  If corresponding metadata
2052
** is not found then one of a number of predefined actions will be taken.
2053
** Since it's a capability item, five metadata will be used to populate the
2054
** XML elements.
2055
**
2056
** The 'name' argument is the basename of the metadata items relating to this
2057
** URL type and the suffixes _type, _width, _height, _format and _href will
2058
** be appended to the name in the metadata search.
2059
** e.g. passing name=metadataurl will result in the following medata entries
2060
** being used:
2061
**    ows_metadataurl_type
2062
**    ows_metadataurl_format
2063
**    ows_metadataurl_href
2064
**    ... (width and height are unused for metadata)
2065
**
2066
** As for all the msOWSPrint*() functions, the namespace argument specifies
2067
** which prefix (ows_, wms_, wcs_, etc.) is used for the metadata names above.
2068
**
2069
** Then the final string will be built from
2070
** the tag_name and the five metadata. The template is:
2071
** <tag_name%type%width%height%format>%href</tag_name>
2072
**
2073
** For example the width format will usually be " width=\"%s\"".
2074
** An extern format will be "> <Format>%s</Format"
2075
**
2076
** Another template template may be used, but it needs to contains 5 %s,
2077
** otherwise leave it to NULL. If tag_format is used then you don't need the
2078
** tag_name and the tabspace.
2079
**
2080
** Note that all values will be HTML-encoded.
2081
**/
2082
int msOWSPrintURLType(FILE *stream, hashTableObj *metadata,
707✔
2083
                      const char *namespaces, const char *name,
2084
                      int action_if_not_found, const char *tag_format,
2085
                      const char *tag_name, const char *type_format,
2086
                      const char *width_format, const char *height_format,
2087
                      const char *urlfrmt_format, const char *href_format,
2088
                      int type_is_mandatory, int width_is_mandatory,
2089
                      int height_is_mandatory, int format_is_mandatory,
2090
                      int href_is_mandatory, const char *default_type,
2091
                      const char *default_width, const char *default_height,
2092
                      const char *default_urlfrmt, const char *default_href,
2093
                      const char *tabspace) {
2094
  const char *value;
2095
  char *metadata_name;
2096
  char *encoded;
2097
  int status = MS_NOERR;
2098
  char *type = NULL, *width = NULL, *height = NULL, *urlfrmt = NULL,
2099
       *href = NULL;
2100

2101
  const size_t buffer_size = strlen(name) + 10;
707✔
2102
  metadata_name = (char *)malloc(buffer_size);
707✔
2103

2104
  /* Get type */
2105
  if (type_format != NULL) {
707✔
2106
    snprintf(metadata_name, buffer_size, "%s_type", name);
2107
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
293✔
2108
    if (value != NULL) {
293✔
2109
      encoded = msEncodeHTMLEntities(value);
231✔
2110
      const size_t buffer_size_tmp = strlen(type_format) + strlen(encoded) + 1;
231✔
2111
      type = (char *)malloc(buffer_size_tmp);
231✔
2112
      snprintf(type, buffer_size_tmp, type_format, encoded);
2113
      msFree(encoded);
231✔
2114
    }
2115
  }
2116

2117
  /* Get width */
2118
  if (width_format != NULL) {
707✔
2119
    snprintf(metadata_name, buffer_size, "%s_width", name);
2120
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
173✔
2121
    if (value != NULL) {
173✔
2122
      encoded = msEncodeHTMLEntities(value);
19✔
2123
      const size_t buffer_size_tmp = strlen(width_format) + strlen(encoded) + 1;
19✔
2124
      width = (char *)malloc(buffer_size_tmp);
19✔
2125
      snprintf(width, buffer_size_tmp, width_format, encoded);
2126
      msFree(encoded);
19✔
2127
    }
2128
  }
2129

2130
  /* Get height */
2131
  if (height_format != NULL) {
707✔
2132
    snprintf(metadata_name, buffer_size, "%s_height", name);
2133
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
173✔
2134
    if (value != NULL) {
173✔
2135
      encoded = msEncodeHTMLEntities(value);
19✔
2136
      const size_t buffer_size_tmp =
19✔
2137
          strlen(height_format) + strlen(encoded) + 1;
19✔
2138
      height = (char *)malloc(buffer_size_tmp);
19✔
2139
      snprintf(height, buffer_size_tmp, height_format, encoded);
2140
      msFree(encoded);
19✔
2141
    }
2142
  }
2143

2144
  /* Get format */
2145
  if (urlfrmt_format != NULL) {
707✔
2146
    snprintf(metadata_name, buffer_size, "%s_format", name);
2147
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
681✔
2148
    if (value != NULL) {
681✔
2149
      encoded = msEncodeHTMLEntities(value);
246✔
2150
      const size_t buffer_size_tmp =
246✔
2151
          strlen(urlfrmt_format) + strlen(encoded) + 1;
246✔
2152
      urlfrmt = (char *)malloc(buffer_size_tmp);
246✔
2153
      snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, encoded);
2154
      msFree(encoded);
246✔
2155
    }
2156
  }
2157

2158
  /* Get href */
2159
  if (href_format != NULL) {
707✔
2160
    snprintf(metadata_name, buffer_size, "%s_href", name);
2161
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
707✔
2162
    if (value != NULL) {
707✔
2163
      encoded = msEncodeHTMLEntities(value);
268✔
2164
      const size_t buffer_size_tmp = strlen(href_format) + strlen(encoded) + 1;
268✔
2165
      href = (char *)malloc(buffer_size_tmp);
268✔
2166
      snprintf(href, buffer_size_tmp, href_format, encoded);
2167
      msFree(encoded);
268✔
2168
    }
2169
  }
2170

2171
  msFree(metadata_name);
707✔
2172

2173
  if (type || width || height || urlfrmt || href ||
707✔
2174
      (!metadata && (default_type || default_width || default_height ||
154✔
2175
                     default_urlfrmt || default_href))) {
×
2176
    if ((!type && type_is_mandatory) || (!width && width_is_mandatory) ||
422✔
2177
        (!height && height_is_mandatory) || (!urlfrmt && format_is_mandatory) ||
422✔
2178
        (!href && href_is_mandatory)) {
422✔
2179
      msIO_fprintf(stream,
×
2180
                   "<!-- WARNING: Some mandatory elements for '%s' are missing "
2181
                   "in this context. -->\n",
2182
                   tag_name);
2183
      if (action_if_not_found == OWS_WARN) {
×
2184
        char *pszExpandedMetadataKey =
2185
            msOWSGetExpandedMetadataKey(namespaces, name);
×
2186
        msIO_fprintf(stream,
×
2187
                     "<!-- WARNING: Mandatory metadata %s was missing in this "
2188
                     "context. -->\n",
2189
                     pszExpandedMetadataKey);
2190
        msFree(pszExpandedMetadataKey);
×
2191
        status = action_if_not_found;
2192
      }
2193
    } else {
2194
      if (!type && type_format && default_type) {
422✔
2195
        const size_t buffer_size_tmp =
13✔
2196
            strlen(type_format) + strlen(default_type) + 2;
13✔
2197
        type = (char *)malloc(buffer_size_tmp);
13✔
2198
        snprintf(type, buffer_size_tmp, type_format, default_type);
2199
      } else if (!type)
409✔
2200
        type = msStrdup("");
178✔
2201
      if (!width && width_format && default_width) {
422✔
2202
        const size_t buffer_size_tmp =
154✔
2203
            strlen(width_format) + strlen(default_width) + 2;
154✔
2204
        width = (char *)malloc(buffer_size_tmp);
154✔
2205
        snprintf(width, buffer_size_tmp, width_format, default_width);
2206
      } else if (!width)
268✔
2207
        width = msStrdup("");
249✔
2208
      if (!height && height_format && default_height) {
422✔
2209
        const size_t buffer_size_tmp =
154✔
2210
            strlen(height_format) + strlen(default_height) + 2;
154✔
2211
        height = (char *)malloc(buffer_size_tmp);
154✔
2212
        snprintf(height, buffer_size_tmp, height_format, default_height);
2213
      } else if (!height)
268✔
2214
        height = msStrdup("");
249✔
2215
      if (!urlfrmt && urlfrmt_format && default_urlfrmt) {
422✔
2216
        const size_t buffer_size_tmp =
154✔
2217
            strlen(urlfrmt_format) + strlen(default_urlfrmt) + 2;
154✔
2218
        urlfrmt = (char *)malloc(buffer_size_tmp);
154✔
2219
        snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, default_urlfrmt);
2220
      } else if (!urlfrmt)
268✔
2221
        urlfrmt = msStrdup("");
22✔
2222
      if (!href && href_format && default_href) {
422✔
2223
        const size_t buffer_size_tmp =
154✔
2224
            strlen(href_format) + strlen(default_href) + 2;
154✔
2225
        href = (char *)malloc(buffer_size_tmp);
154✔
2226
        snprintf(href, buffer_size_tmp, href_format, default_href);
2227
      } else if (!href)
268✔
2228
        href = msStrdup("");
×
2229

2230
      if (tag_format == NULL)
422✔
2231
        msIO_fprintf(stream, "%s<%s%s%s%s%s>%s</%s>\n", tabspace, tag_name,
400✔
2232
                     type, width, height, urlfrmt, href, tag_name);
2233
      else
2234
        msIO_fprintf(stream, tag_format, type, width, height, urlfrmt, href);
22✔
2235
    }
2236

2237
    msFree(type);
422✔
2238
    msFree(width);
422✔
2239
    msFree(height);
422✔
2240
    msFree(urlfrmt);
422✔
2241
    msFree(href);
422✔
2242
  } else {
422✔
2243
    if (action_if_not_found == OWS_WARN) {
285✔
2244
      char *pszExpandedMetadataKey =
2245
          msOWSGetExpandedMetadataKey(namespaces, name);
×
2246
      msIO_fprintf(stream,
×
2247
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
2248
                   "context. -->\n",
2249
                   pszExpandedMetadataKey);
2250
      msFree(pszExpandedMetadataKey);
×
2251
      status = action_if_not_found;
2252
    }
2253
  }
2254

2255
  return status;
707✔
2256
}
2257

2258
/* msOWSPrintParam()
2259
**
2260
** Same as printMetadata() but applied to mapfile parameters.
2261
**/
2262
int msOWSPrintParam(FILE *stream, const char *name, const char *value,
×
2263
                    int action_if_not_found, const char *format,
2264
                    const char *default_value) {
2265
  int status = MS_NOERR;
2266

2267
  if (value && strlen(value) > 0) {
×
2268
    msIO_fprintf(stream, format, value);
×
2269
  } else {
2270
    if (action_if_not_found == OWS_WARN) {
×
2271
      msIO_fprintf(stream,
×
2272
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2273
                   "in this context. -->\n",
2274
                   name);
2275
      status = action_if_not_found;
2276
    }
2277

2278
    if (default_value)
×
2279
      msIO_fprintf(stream, format, default_value);
×
2280
  }
2281

2282
  return status;
×
2283
}
2284

2285
/* msOWSPrintEncodeParam()
2286
**
2287
** Same as printEncodeMetadata() but applied to mapfile parameters.
2288
**/
2289
int msOWSPrintEncodeParam(FILE *stream, const char *name, const char *value,
312✔
2290
                          int action_if_not_found, const char *format,
2291
                          const char *default_value) {
2292
  int status = MS_NOERR;
2293
  char *encode;
2294

2295
  if (value && strlen(value) > 0) {
312✔
2296
    encode = msEncodeHTMLEntities(value);
312✔
2297
    msIO_fprintf(stream, format, encode);
312✔
2298
    msFree(encode);
312✔
2299
  } else {
2300
    if (action_if_not_found == OWS_WARN) {
×
2301
      msIO_fprintf(stream,
×
2302
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2303
                   "in this context. -->\n",
2304
                   name);
2305
      status = action_if_not_found;
2306
    }
2307

2308
    if (default_value) {
×
2309
      encode = msEncodeHTMLEntities(default_value);
×
2310
      msIO_fprintf(stream, format, encode);
×
2311
      msFree(encode);
×
2312
    }
2313
  }
2314

2315
  return status;
312✔
2316
}
2317

2318
/* msOWSPrintMetadataList()
2319
**
2320
** Prints comma-separated lists metadata.  (e.g. keywordList)
2321
** default_value serves 2 purposes if specified:
2322
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2323
**  (exclusion)
2324
** - will be printed if MetadataList is empty (fallback)
2325
**/
2326
int msOWSPrintMetadataList(FILE *stream, hashTableObj *metadata,
×
2327
                           const char *namespaces, const char *name,
2328
                           const char *startTag, const char *endTag,
2329
                           const char *itemFormat, const char *default_value) {
2330
  const char *value;
2331

2332
  value = msOWSLookupMetadata(metadata, namespaces, name);
×
2333

2334
  if (value == NULL) {
×
2335
    value = default_value;
2336
    default_value = NULL;
2337
  }
2338

2339
  if (value != NULL) {
×
2340
    char **keywords;
2341
    int numkeywords;
2342

2343
    keywords = msStringSplit(value, ',', &numkeywords);
×
2344
    if (keywords && numkeywords > 0) {
×
2345
      int kw;
2346
      if (startTag)
×
2347
        msIO_fprintf(stream, "%s", startTag);
×
2348
      for (kw = 0; kw < numkeywords; kw++) {
×
2349
        if (default_value != NULL &&
×
2350
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
×
2351
                0 &&
×
2352
            strncasecmp("_exclude", default_value + strlen(default_value) - 8,
×
2353
                        8) == 0)
2354
          continue;
×
2355

2356
        msIO_fprintf(stream, itemFormat, keywords[kw]);
×
2357
      }
2358
      if (endTag)
×
2359
        msIO_fprintf(stream, "%s", endTag);
×
2360
    }
2361
    msFreeCharArray(keywords, numkeywords);
×
2362
    return MS_TRUE;
2363
  }
2364
  return MS_FALSE;
2365
}
2366

2367
/* msOWSPrintEncodeMetadataList()
2368
**
2369
** Prints comma-separated lists metadata.  (e.g. keywordList)
2370
** This will print HTML encoded values.
2371
** default_value serves 2 purposes if specified:
2372
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2373
**  (exclusion)
2374
** - will be printed if MetadataList is empty (fallback)
2375
**/
2376
int msOWSPrintEncodeMetadataList(FILE *stream, hashTableObj *metadata,
512✔
2377
                                 const char *namespaces, const char *name,
2378
                                 const char *startTag, const char *endTag,
2379
                                 const char *itemFormat,
2380
                                 const char *default_value) {
2381
  const char *value;
2382
  char *encoded;
2383
  size_t default_value_len = 0;
2384

2385
  value = msOWSLookupMetadata(metadata, namespaces, name);
512✔
2386

2387
  if (value == NULL) {
512✔
2388
    value = default_value;
2389
    default_value = NULL;
2390
  }
2391
  if (default_value)
270✔
2392
    default_value_len = strlen(default_value);
47✔
2393

2394
  if (value != NULL) {
512✔
2395
    char **keywords;
2396
    int numkeywords;
2397

2398
    keywords = msStringSplit(value, ',', &numkeywords);
273✔
2399
    if (keywords && numkeywords > 0) {
273✔
2400
      int kw;
2401
      if (startTag)
273✔
2402
        msIO_fprintf(stream, "%s", startTag);
45✔
2403
      for (kw = 0; kw < numkeywords; kw++) {
806✔
2404
        if (default_value != NULL && default_value_len > 8 &&
533✔
2405
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
103✔
2406
                0 &&
80✔
2407
            strncasecmp("_exclude", default_value + default_value_len - 8, 8) ==
40✔
2408
                0)
2409
          continue;
40✔
2410

2411
        encoded = msEncodeHTMLEntities(keywords[kw]);
493✔
2412
        msIO_fprintf(stream, itemFormat, encoded);
493✔
2413
        msFree(encoded);
493✔
2414
      }
2415
      if (endTag)
273✔
2416
        msIO_fprintf(stream, "%s", endTag);
45✔
2417
    }
2418
    msFreeCharArray(keywords, numkeywords);
273✔
2419
    return MS_TRUE;
2420
  }
2421
  return MS_FALSE;
2422
}
2423

2424
/* msOWSPrintEncodeParamList()
2425
**
2426
** Same as msOWSPrintEncodeMetadataList() but applied to mapfile parameters.
2427
**/
2428
int msOWSPrintEncodeParamList(FILE *stream, const char *name, const char *value,
220✔
2429
                              int action_if_not_found, char delimiter,
2430
                              const char *startTag, const char *endTag,
2431
                              const char *format, const char *default_value) {
2432
  int status = MS_NOERR;
2433
  char *encoded;
2434
  char **items = NULL;
2435
  int numitems = 0, i;
220✔
2436

2437
  if (value && strlen(value) > 0)
220✔
2438
    items = msStringSplit(value, delimiter, &numitems);
164✔
2439
  else {
2440
    if (action_if_not_found == OWS_WARN) {
56✔
2441
      msIO_fprintf(stream,
×
2442
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2443
                   "in this context. -->\n",
2444
                   name);
2445
      status = action_if_not_found;
2446
    }
2447

2448
    if (default_value)
56✔
2449
      items = msStringSplit(default_value, delimiter, &numitems);
×
2450
  }
2451

2452
  if (items && numitems > 0) {
164✔
2453
    if (startTag)
164✔
2454
      msIO_fprintf(stream, "%s", startTag);
×
2455
    for (i = 0; i < numitems; i++) {
472✔
2456
      encoded = msEncodeHTMLEntities(items[i]);
308✔
2457
      msIO_fprintf(stream, format, encoded);
308✔
2458
      msFree(encoded);
308✔
2459
    }
2460
    if (endTag)
164✔
2461
      msIO_fprintf(stream, "%s", endTag);
×
2462
  }
2463
  msFreeCharArray(items, numitems);
220✔
2464

2465
  return status;
220✔
2466
}
2467

2468
/*
2469
** msOWSPrintEX_GeographicBoundingBox()
2470
**
2471
** Print a EX_GeographicBoundingBox tag for WMS1.3.0
2472
**
2473
*/
2474
void msOWSPrintEX_GeographicBoundingBox(FILE *stream, const char *tabspace,
115✔
2475
                                        rectObj *extent, projectionObj *srcproj)
2476

2477
{
2478
  const char *pszTag = "EX_GeographicBoundingBox"; /* The default for WMS */
2479
  rectObj ext;
2480

2481
  ext = *extent;
115✔
2482

2483
  /* always project to lat long */
2484
  msOWSProjectToWGS84(srcproj, &ext);
115✔
2485

2486
  msIO_fprintf(stream, "%s<%s>\n", tabspace, pszTag);
115✔
2487
  msIO_fprintf(stream, "%s    <westBoundLongitude>%.6f</westBoundLongitude>\n",
115✔
2488
               tabspace, ext.minx);
2489
  msIO_fprintf(stream, "%s    <eastBoundLongitude>%.6f</eastBoundLongitude>\n",
115✔
2490
               tabspace, ext.maxx);
2491
  msIO_fprintf(stream, "%s    <southBoundLatitude>%.6f</southBoundLatitude>\n",
115✔
2492
               tabspace, ext.miny);
2493
  msIO_fprintf(stream, "%s    <northBoundLatitude>%.6f</northBoundLatitude>\n",
115✔
2494
               tabspace, ext.maxy);
2495
  msIO_fprintf(stream, "%s</%s>\n", tabspace, pszTag);
115✔
2496

2497
  /* msIO_fprintf(stream, "%s<%s minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"
2498
     />\n", tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy); */
2499
}
115✔
2500

2501
/*
2502
** msOWSPrintLatLonBoundingBox()
2503
**
2504
** Print a LatLonBoundingBox tag for WMS, or LatLongBoundingBox for WFS
2505
** ... yes, the tag name differs between WMS and WFS, yuck!
2506
**
2507
*/
2508
void msOWSPrintLatLonBoundingBox(FILE *stream, const char *tabspace,
139✔
2509
                                 rectObj *extent, projectionObj *srcproj,
2510
                                 projectionObj *wfsproj,
2511
                                 OWSServiceType nService) {
2512
  const char *pszTag = "LatLonBoundingBox"; /* The default for WMS */
2513
  rectObj ext;
2514

2515
  ext = *extent;
139✔
2516

2517
  if (nService == OWS_WMS) { /* always project to lat long */
139✔
2518
    msOWSProjectToWGS84(srcproj, &ext);
114✔
2519
  } else if (nService == OWS_WFS) { /* called from wfs 1.0.0 only: project to
25✔
2520
                                       map srs, if set */
2521
    pszTag = "LatLongBoundingBox";
2522
    if (wfsproj) {
25✔
2523
      if (msProjectionsDiffer(srcproj, wfsproj) == MS_TRUE)
25✔
2524
        msProjectRect(srcproj, wfsproj, &ext);
16✔
2525
    }
2526
  }
2527

2528
  msIO_fprintf(
139✔
2529
      stream,
2530
      "%s<%s minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" maxy=\"%.6f\" />\n",
2531
      tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy);
2532
}
139✔
2533

2534
/*
2535
** Emit a bounding box if we can find projection information.
2536
** If <namespaces>_bbox_extended is not set, emit a single bounding box
2537
** using the layer's native SRS (ignoring any <namespaces>_srs metadata).
2538
**
2539
** If <namespaces>_bbox_extended is set to true, emit a bounding box
2540
** for every projection listed in the <namespaces>_srs list.
2541
** Check the map level metadata for both _bbox_extended and _srs,
2542
** if there is no such metadata at the layer level.
2543
** (These settings make more sense at the global/map level anyways)
2544
*/
2545
void msOWSPrintBoundingBox(FILE *stream, const char *tabspace, rectObj *extent,
229✔
2546
                           projectionObj *srcproj, hashTableObj *layer_meta,
2547
                           hashTableObj *map_meta, const char *namespaces,
2548
                           int wms_version) {
2549
  const char *value, *resx, *resy, *wms_bbox_extended;
2550
  char *encoded, *encoded_resx, *encoded_resy, *epsg_str;
2551
  char **epsgs;
2552
  int i, num_epsgs;
2553
  projectionObj proj;
2554
  rectObj ext;
2555

2556
  wms_bbox_extended =
2557
      msOWSLookupMetadata2(layer_meta, map_meta, namespaces, "bbox_extended");
229✔
2558
  if (wms_bbox_extended && strncasecmp(wms_bbox_extended, "true", 5) == 0) {
229✔
2559
    /* get a list of all projections from the metadata
2560
       try the layer metadata first, otherwise use the map's */
2561
    if (msOWSLookupMetadata(layer_meta, namespaces, "srs")) {
126✔
2562
      msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_FALSE, &epsg_str);
×
2563
    } else {
2564
      msOWSGetEPSGProj(srcproj, map_meta, namespaces, MS_FALSE, &epsg_str);
126✔
2565
    }
2566
    epsgs = msStringSplit(epsg_str, ' ', &num_epsgs);
126✔
2567
    msFree(epsg_str);
126✔
2568
  } else {
2569
    /* Look for EPSG code in PROJECTION block only.  "wms_srs" metadata cannot
2570
     * be used to establish the native projection of a layer for BoundingBox
2571
     * purposes.
2572
     */
2573
    epsgs = (char **)msSmallMalloc(sizeof(char *));
103✔
2574
    num_epsgs = 1;
103✔
2575
    msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_TRUE, &(epsgs[0]));
103✔
2576
  }
2577

2578
  for (i = 0; i < num_epsgs; i++) {
1,790✔
2579
    value = epsgs[i];
1,561✔
2580
    if (value && *value) {
1,561✔
2581
      memcpy(&ext, extent, sizeof(rectObj));
2582

2583
      /* reproject the extents for each SRS's bounding box */
2584
      msInitProjection(&proj);
1,545✔
2585
      msProjectionInheritContextFrom(&proj, srcproj);
1,545✔
2586
      if (msLoadProjectionStringEPSG(&proj, (char *)value) == 0) {
1,545✔
2587
        if (msProjectionsDiffer(srcproj, &proj) == MS_TRUE) {
1,527✔
2588
          msProjectRect(srcproj, &proj, &ext);
1,439✔
2589
        }
2590
        /*for wms 1.3.0 we need to make sure that we present the BBOX with
2591
          a reversed axes for some espg codes*/
2592
        if (wms_version >= OWS_1_3_0 && strncasecmp(value, "EPSG:", 5) == 0) {
1,527✔
2593
          msAxisNormalizePoints(&proj, 1, &(ext.minx), &(ext.miny));
775✔
2594
          msAxisNormalizePoints(&proj, 1, &(ext.maxx), &(ext.maxy));
775✔
2595
        }
2596
      }
2597

2598
      encoded = msEncodeHTMLEntities(value);
1,545✔
2599
      if (msProjIsGeographicCRS(&proj))
1,545✔
2600
        msIO_fprintf(stream,
584✔
2601
                     "%s<BoundingBox %s=\"%s\"\n"
2602
                     "%s            minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" "
2603
                     "maxy=\"%.6f\"",
2604
                     tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS",
2605
                     encoded, tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2606
      else
2607
        msIO_fprintf(
1,720✔
2608
            stream,
2609
            "%s<BoundingBox %s=\"%s\"\n"
2610
            "%s            minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"",
2611
            tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS", encoded,
2612
            tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2613

2614
      msFree(encoded);
1,545✔
2615
      msFreeProjection(&proj);
1,545✔
2616

2617
      if ((resx = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resx")) !=
1,545✔
2618
              NULL &&
1,547✔
2619
          (resy = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resy")) !=
2✔
2620
              NULL) {
2621
        encoded_resx = msEncodeHTMLEntities(resx);
2✔
2622
        encoded_resy = msEncodeHTMLEntities(resy);
2✔
2623
        msIO_fprintf(stream, "\n%s            resx=\"%s\" resy=\"%s\"",
2✔
2624
                     tabspace, encoded_resx, encoded_resy);
2625
        msFree(encoded_resx);
2✔
2626
        msFree(encoded_resy);
2✔
2627
      }
2628

2629
      msIO_fprintf(stream, " />\n");
1,545✔
2630
    }
2631
  }
2632
  msFreeCharArray(epsgs, num_epsgs);
229✔
2633
}
229✔
2634

2635
/*
2636
** Print the contact information
2637
*/
2638
void msOWSPrintContactInfo(FILE *stream, const char *tabspace, int nVersion,
60✔
2639
                           hashTableObj *metadata, const char *namespaces) {
2640
  /* contact information is a required element in 1.0.7 but the */
2641
  /* sub-elements such as ContactPersonPrimary, etc. are not! */
2642
  /* In 1.1.0, ContactInformation becomes optional. */
2643
  if (nVersion > OWS_1_0_0) {
60✔
2644
    msIO_fprintf(stream, "%s<ContactInformation>\n", tabspace);
58✔
2645

2646
    /* ContactPersonPrimary is optional, but when present then all its  */
2647
    /* sub-elements are mandatory */
2648

2649
    if (msOWSLookupMetadata(metadata, namespaces, "contactperson") ||
82✔
2650
        msOWSLookupMetadata(metadata, namespaces, "contactorganization")) {
24✔
2651
      msIO_fprintf(stream, "%s  <ContactPersonPrimary>\n", tabspace);
34✔
2652

2653
      msOWSPrintEncodeMetadata(
34✔
2654
          stream, metadata, namespaces, "contactperson", OWS_WARN,
2655
          "      <ContactPerson>%s</ContactPerson>\n", NULL);
2656
      msOWSPrintEncodeMetadata(
34✔
2657
          stream, metadata, namespaces, "contactorganization", OWS_WARN,
2658
          "      <ContactOrganization>%s</ContactOrganization>\n", NULL);
2659
      msIO_fprintf(stream, "%s  </ContactPersonPrimary>\n", tabspace);
34✔
2660
    }
2661

2662
    if (msOWSLookupMetadata(metadata, namespaces, "contactposition")) {
58✔
2663
      msOWSPrintEncodeMetadata(
34✔
2664
          stream, metadata, namespaces, "contactposition", OWS_NOERR,
2665
          "      <ContactPosition>%s</ContactPosition>\n", NULL);
2666
    }
2667

2668
    /* ContactAddress is optional, but when present then all its  */
2669
    /* sub-elements are mandatory */
2670
    if (msOWSLookupMetadata(metadata, namespaces, "addresstype") ||
82✔
2671
        msOWSLookupMetadata(metadata, namespaces, "address") ||
48✔
2672
        msOWSLookupMetadata(metadata, namespaces, "city") ||
48✔
2673
        msOWSLookupMetadata(metadata, namespaces, "stateorprovince") ||
48✔
2674
        msOWSLookupMetadata(metadata, namespaces, "postcode") ||
106✔
2675
        msOWSLookupMetadata(metadata, namespaces, "country")) {
24✔
2676
      msIO_fprintf(stream, "%s  <ContactAddress>\n", tabspace);
34✔
2677

2678
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "addresstype",
34✔
2679
                               OWS_WARN,
2680
                               "        <AddressType>%s</AddressType>\n", NULL);
2681
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "address",
34✔
2682
                               OWS_WARN, "        <Address>%s</Address>\n",
2683
                               NULL);
2684
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "city", OWS_WARN,
34✔
2685
                               "        <City>%s</City>\n", NULL);
2686
      msOWSPrintEncodeMetadata(
34✔
2687
          stream, metadata, namespaces, "stateorprovince", OWS_WARN,
2688
          "        <StateOrProvince>%s</StateOrProvince>\n", NULL);
2689
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "postcode",
34✔
2690
                               OWS_WARN, "        <PostCode>%s</PostCode>\n",
2691
                               NULL);
2692
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "country",
34✔
2693
                               OWS_WARN, "        <Country>%s</Country>\n",
2694
                               NULL);
2695
      msIO_fprintf(stream, "%s  </ContactAddress>\n", tabspace);
34✔
2696
    }
2697

2698
    if (msOWSLookupMetadata(metadata, namespaces, "contactvoicetelephone")) {
58✔
2699
      msOWSPrintEncodeMetadata(
34✔
2700
          stream, metadata, namespaces, "contactvoicetelephone", OWS_NOERR,
2701
          "      <ContactVoiceTelephone>%s</ContactVoiceTelephone>\n", NULL);
2702
    }
2703

2704
    if (msOWSLookupMetadata(metadata, namespaces,
58✔
2705
                            "contactfacsimiletelephone")) {
2706
      msOWSPrintEncodeMetadata(
28✔
2707
          stream, metadata, namespaces, "contactfacsimiletelephone", OWS_NOERR,
2708
          "      <ContactFacsimileTelephone>%s</ContactFacsimileTelephone>\n",
2709
          NULL);
2710
    }
2711

2712
    if (msOWSLookupMetadata(metadata, namespaces,
58✔
2713
                            "contactelectronicmailaddress")) {
2714
      msOWSPrintEncodeMetadata(
34✔
2715
          stream, metadata, namespaces, "contactelectronicmailaddress",
2716
          OWS_NOERR,
2717
          "  <ContactElectronicMailAddress>%s</ContactElectronicMailAddress>\n",
2718
          NULL);
2719
    }
2720
    msIO_fprintf(stream, "%s</ContactInformation>\n", tabspace);
58✔
2721
  }
2722
}
60✔
2723

2724
/*
2725
** msOWSGetLayerExtent()
2726
**
2727
** Try to establish layer extent, first looking for "ows_extent" metadata, and
2728
** if not found then call msLayerGetExtent() which will lookup the
2729
** layer->extent member, and if not found will open layer to read extent.
2730
**
2731
*/
2732
int msOWSGetLayerExtent(mapObj *map, layerObj *lp, const char *namespaces,
937✔
2733
                        rectObj *ext) {
2734
  (void)map;
2735
  const char *value;
2736

2737
  if ((value = msOWSLookupMetadata(&(lp->metadata), namespaces, "extent")) !=
937✔
2738
      NULL) {
2739
    char **tokens;
2740
    int n;
2741

2742
    tokens = msStringSplit(value, ' ', &n);
291✔
2743
    if (tokens == NULL || n != 4) {
291✔
2744
      msSetError(MS_WMSERR, "Wrong number of arguments for EXTENT metadata.",
×
2745
                 "msOWSGetLayerExtent()");
2746
      return MS_FAILURE;
×
2747
    }
2748
    ext->minx = atof(tokens[0]);
291✔
2749
    ext->miny = atof(tokens[1]);
291✔
2750
    ext->maxx = atof(tokens[2]);
291✔
2751
    ext->maxy = atof(tokens[3]);
291✔
2752

2753
    msFreeCharArray(tokens, n);
291✔
2754
    return MS_SUCCESS;
291✔
2755
  } else {
2756
    return msLayerGetExtent(lp, ext);
646✔
2757
  }
2758

2759
  return MS_FAILURE;
2760
}
2761

2762
/**********************************************************************
2763
 *                          msOWSExecuteRequests()
2764
 *
2765
 * Execute a number of WFS/WMS HTTP requests in parallel, and then
2766
 * update layerObj information with the result of the requests.
2767
 **********************************************************************/
2768
int msOWSExecuteRequests(httpRequestObj *pasReqInfo, int numRequests,
44✔
2769
                         mapObj *map, int bCheckLocalCache) {
2770
  int nStatus, iReq;
2771

2772
  /* Execute requests */
2773
#if defined(USE_CURL)
2774
  nStatus = msHTTPExecuteRequests(pasReqInfo, numRequests, bCheckLocalCache);
44✔
2775
#else
2776
  msSetError(MS_WMSERR,
2777
             "msOWSExecuteRequests() called apparently without libcurl "
2778
             "configured, msHTTPExecuteRequests() not available.",
2779
             "msOWSExecuteRequests()");
2780
  return MS_FAILURE;
2781
#endif
2782

2783
  /* Scan list of layers and call the handler for each layer type to */
2784
  /* pass them the request results. */
2785
  for (iReq = 0; iReq < numRequests; iReq++) {
88✔
2786
    if (pasReqInfo[iReq].nLayerId >= 0 &&
44✔
2787
        pasReqInfo[iReq].nLayerId < map->numlayers) {
44✔
2788
      layerObj *lp;
2789

2790
      lp = GET_LAYER(map, pasReqInfo[iReq].nLayerId);
44✔
2791

2792
      if (lp->connectiontype == MS_WFS)
44✔
2793
        msWFSUpdateRequestInfo(lp, &(pasReqInfo[iReq]));
3✔
2794
    }
2795
  }
2796

2797
  return nStatus;
44✔
2798
}
2799

2800
/**********************************************************************
2801
 *                          msOWSProcessException()
2802
 *
2803
 **********************************************************************/
2804
void msOWSProcessException(layerObj *lp, const char *pszFname, int nErrorCode,
×
2805
                           const char *pszFuncName) {
2806
  FILE *fp;
2807

2808
  if ((fp = fopen(pszFname, "r")) != NULL) {
×
2809
    char *pszBuf = NULL;
2810
    int nBufSize = 0;
2811
    char *pszStart, *pszEnd;
2812

2813
    fseek(fp, 0, SEEK_END);
×
2814
    nBufSize = ftell(fp);
×
2815
    if (nBufSize < 0) {
×
2816
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
×
2817
      fclose(fp);
×
2818
      return;
×
2819
    }
2820
    rewind(fp);
×
2821
    pszBuf = (char *)malloc((nBufSize + 1) * sizeof(char));
×
2822
    if (pszBuf == NULL) {
×
2823
      msSetError(MS_MEMERR, NULL, "msOWSProcessException()");
×
2824
      fclose(fp);
×
2825
      return;
×
2826
    }
2827

2828
    if ((int)fread(pszBuf, 1, nBufSize, fp) != nBufSize) {
×
2829
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
×
2830
      free(pszBuf);
×
2831
      fclose(fp);
×
2832
      return;
×
2833
    }
2834

2835
    pszBuf[nBufSize] = '\0';
×
2836

2837
    /* OK, got the data in the buffer.  Look for the <Message> tags */
2838
    if ((strstr(pszBuf, "<WFS_Exception>") && /* WFS style */
×
2839
         (pszStart = strstr(pszBuf, "<Message>")) &&
×
2840
         (pszEnd = strstr(pszStart, "</Message>"))) ||
×
2841
        (strstr(pszBuf, "<ServiceExceptionReport>") && /* WMS style */
×
2842
         (pszStart = strstr(pszBuf, "<ServiceException>")) &&
×
2843
         (pszEnd = strstr(pszStart, "</ServiceException>")))) {
2844
      pszStart = strchr(pszStart, '>') + 1;
×
2845
      *pszEnd = '\0';
×
2846
      msSetError(nErrorCode, "Got Remote Server Exception for layer %s: %s",
×
2847
                 pszFuncName, lp->name ? lp->name : "(null)", pszStart);
×
2848
    } else {
2849
      msSetError(
×
2850
          MS_WFSCONNERR,
2851
          "Unable to parse Remote Server Exception Message for layer %s.",
2852
          pszFuncName, lp->name ? lp->name : "(null)");
×
2853
    }
2854

2855
    free(pszBuf);
×
2856
    fclose(fp);
×
2857
  }
2858
}
2859

2860
/**********************************************************************
2861
 *                          msOWSBuildURLFilename()
2862
 *
2863
 * Build a unique filename for this URL to use in caching remote server
2864
 * requests.  Slashes and illegal characters will be turned into '_'
2865
 *
2866
 * Returns a newly allocated buffer that should be freed by the caller or
2867
 * NULL in case of error.
2868
 **********************************************************************/
2869
char *msOWSBuildURLFilename(const char *pszPath, const char *pszURL,
×
2870
                            const char *pszExt) {
2871
  char *pszBuf, *pszPtr;
2872
  int i;
2873
  size_t nBufLen = 0;
2874

2875
  nBufLen = strlen(pszURL) + strlen(pszExt) + 2;
×
2876
  if (pszPath)
×
2877
    nBufLen += (strlen(pszPath) + 1);
×
2878

2879
  pszBuf = (char *)malloc(nBufLen);
×
2880
  if (pszBuf == NULL) {
×
2881
    msSetError(MS_MEMERR, NULL, "msOWSBuildURLFilename()");
×
2882
    return NULL;
×
2883
  }
2884
  pszBuf[0] = '\0';
×
2885

2886
  if (pszPath) {
×
2887
#ifdef _WIN32
2888
    if (pszPath[strlen(pszPath) - 1] != '/' &&
2889
        pszPath[strlen(pszPath) - 1] != '\\')
2890
      snprintf(pszBuf, nBufLen, "%s\\", pszPath);
2891
    else
2892
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2893
#else
2894
    if (pszPath[strlen(pszPath) - 1] != '/')
×
2895
      snprintf(pszBuf, nBufLen, "%s/", pszPath);
2896
    else
2897
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2898
#endif
2899
  }
2900

2901
  pszPtr = pszBuf + strlen(pszBuf);
×
2902

2903
  for (i = 0; pszURL[i] != '\0'; i++) {
×
2904
    if (isalnum(pszURL[i]))
×
2905
      *pszPtr = pszURL[i];
×
2906
    else
2907
      *pszPtr = '_';
×
2908
    pszPtr++;
×
2909
  }
2910

2911
  strlcpy(pszPtr, pszExt, nBufLen);
2912

2913
  return pszBuf;
2914
}
2915

2916
/*
2917
** msOWSGetProjURN()
2918
**
2919
** Fetch an OGC URN for this layer or map.  Similar to msOWSGetEPSGProj()
2920
** but returns the result in the form "urn:ogc:def:crs:EPSG::27700".
2921
** The returned buffer is dynamically allocated, and must be freed by the
2922
** caller.
2923
*/
2924
char *msOWSGetProjURN(projectionObj *proj, hashTableObj *metadata,
457✔
2925
                      const char *namespaces, int bReturnOnlyFirstOne) {
2926
  char *result;
2927
  char **tokens;
2928
  int numtokens, i;
2929
  char *oldStyle = NULL;
457✔
2930

2931
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
457✔
2932

2933
  if (oldStyle == NULL || strncmp(oldStyle, "CRS:", 4) == 0 ||
457✔
2934
      strncmp(oldStyle, "AUTO:", 5) == 0 ||
451✔
2935
      strncmp(oldStyle, "AUTO2:", 6) == 0) {
451✔
2936
    msFree(oldStyle);
6✔
2937
    return NULL;
6✔
2938
  }
2939

2940
  result = msStrdup("");
451✔
2941

2942
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
451✔
2943
  msFree(oldStyle);
451✔
2944
  for (i = 0; tokens != NULL && i < numtokens; i++) {
956✔
2945
    char urn[100];
2946
    char *colon = strchr(tokens[i], ':');
505✔
2947

2948
    if (colon != NULL && strchr(colon + 1, ':') == NULL) {
505✔
2949
      *colon = 0;
505✔
2950
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:%s::%s", tokens[i],
505✔
2951
               colon + 1);
2952
    } else if (strcasecmp(tokens[i], "imageCRS") == 0)
×
2953
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:OGC::imageCRS");
2954
    else if (strncmp(tokens[i], "urn:ogc:def:crs:", 16) == 0) {
×
2955
      strlcpy(urn, tokens[i], sizeof(urn));
2956
    } else {
2957
      strlcpy(urn, "", sizeof(urn));
2958
    }
2959

2960
    if (strlen(urn) > 0) {
505✔
2961
      const size_t bufferSize = strlen(result) + strlen(urn) + 2;
505✔
2962
      result = (char *)msSmallRealloc(result, bufferSize);
505✔
2963

2964
      if (strlen(result) > 0)
505✔
2965
        strlcat(result, " ", bufferSize);
2966
      strlcat(result, urn, bufferSize);
2967
    } else {
2968
      msDebug("msOWSGetProjURN(): Failed to process SRS '%s', ignored.",
×
2969
              tokens[i]);
2970
    }
2971
  }
2972

2973
  msFreeCharArray(tokens, numtokens);
451✔
2974

2975
  if (strlen(result) == 0) {
451✔
2976
    msFree(result);
×
2977
    return NULL;
×
2978
  } else
2979
    return result;
2980
}
2981

2982
/*
2983
** msOWSGetProjURI()
2984
**
2985
** Fetch an OGC URI for this layer or map.  Similar to msOWSGetEPSGProj()
2986
** but returns the result in the form
2987
*"http://www.opengis.net/def/crs/EPSG/0/27700".
2988
** The returned buffer is dynamically allocated, and must be freed by the
2989
** caller.
2990
*/
2991
char *msOWSGetProjURI(projectionObj *proj, hashTableObj *metadata,
242✔
2992
                      const char *namespaces, int bReturnOnlyFirstOne) {
2993
  char *result;
2994
  char **tokens;
2995
  int numtokens, i;
2996
  char *oldStyle = NULL;
242✔
2997

2998
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
242✔
2999

3000
  if (oldStyle == NULL || !EQUALN(oldStyle, "EPSG:", 5)) {
242✔
3001
    msFree(oldStyle); // avoid leak
2✔
3002
    return NULL;
2✔
3003
  }
3004

3005
  result = msStrdup("");
240✔
3006

3007
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
240✔
3008
  msFree(oldStyle);
240✔
3009
  for (i = 0; tokens != NULL && i < numtokens; i++) {
513✔
3010
    char urn[100];
3011

3012
    if (strncmp(tokens[i], "EPSG:", 5) == 0)
273✔
3013
      snprintf(urn, sizeof(urn), "http://www.opengis.net/def/crs/EPSG/0/%s",
273✔
3014
               tokens[i] + 5);
3015
    else if (strcasecmp(tokens[i], "imageCRS") == 0)
×
3016
      snprintf(urn, sizeof(urn),
3017
               "http://www.opengis.net/def/crs/OGC/0/imageCRS");
3018
    else if (strncmp(tokens[i], "http://www.opengis.net/def/crs/", 16) == 0)
×
3019
      snprintf(urn, sizeof(urn), "%s", tokens[i]);
3020
    else
3021
      strlcpy(urn, "", sizeof(urn));
3022

3023
    if (strlen(urn) > 0) {
273✔
3024
      result = (char *)msSmallRealloc(result, strlen(result) + strlen(urn) + 2);
273✔
3025

3026
      if (strlen(result) > 0)
273✔
3027
        strcat(result, " ");
3028
      strcat(result, urn);
3029
    } else {
3030
      msDebug("msOWSGetProjURI(): Failed to process SRS '%s', ignored.",
×
3031
              tokens[i]);
3032
    }
3033
  }
3034

3035
  msFreeCharArray(tokens, numtokens);
240✔
3036

3037
  if (strlen(result) == 0) {
240✔
3038
    msFree(result);
×
3039
    return NULL;
×
3040
  } else
3041
    return result;
3042
}
3043

3044
/*
3045
** msOWSGetDimensionInfo()
3046
**
3047
** Extract dimension information from a layer's metadata
3048
**
3049
** Before 4.9, only the time dimension was support. With the addition of
3050
** Web Map Context 1.1.0, we need to support every dimension types.
3051
** This function get the dimension information from special metadata in
3052
** the layer, but can also return default values for the time dimension.
3053
**
3054
*/
3055
void msOWSGetDimensionInfo(layerObj *layer, const char *pszDimension,
×
3056
                           const char **papszDimUserValue,
3057
                           const char **papszDimUnits,
3058
                           const char **papszDimDefault,
3059
                           const char **papszDimNearValue,
3060
                           const char **papszDimUnitSymbol,
3061
                           const char **papszDimMultiValue) {
3062
  char *pszDimensionItem;
3063
  size_t bufferSize = 0;
3064

3065
  if (pszDimension == NULL || layer == NULL)
×
3066
    return;
3067

3068
  bufferSize = strlen(pszDimension) + 50;
×
3069
  pszDimensionItem = (char *)malloc(bufferSize);
×
3070

3071
  /* units (mandatory in map context) */
3072
  if (papszDimUnits != NULL) {
×
3073
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_units", pszDimension);
3074
    *papszDimUnits =
×
3075
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3076
  }
3077
  /* unitSymbol (mandatory in map context) */
3078
  if (papszDimUnitSymbol != NULL) {
×
3079
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_unitsymbol",
3080
             pszDimension);
3081
    *papszDimUnitSymbol =
×
3082
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3083
  }
3084
  /* userValue (mandatory in map context) */
3085
  if (papszDimUserValue != NULL) {
×
3086
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_uservalue",
3087
             pszDimension);
3088
    *papszDimUserValue =
×
3089
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3090
  }
3091
  /* default */
3092
  if (papszDimDefault != NULL) {
×
3093
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_default",
3094
             pszDimension);
3095
    *papszDimDefault =
×
3096
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3097
  }
3098
  /* multipleValues */
3099
  if (papszDimMultiValue != NULL) {
×
3100
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_multiplevalues",
3101
             pszDimension);
3102
    *papszDimMultiValue =
×
3103
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3104
  }
3105
  /* nearestValue */
3106
  if (papszDimNearValue != NULL) {
×
3107
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_nearestvalue",
3108
             pszDimension);
3109
    *papszDimNearValue =
×
3110
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3111
  }
3112

3113
  /* Use default time value if necessary */
3114
  if (strcasecmp(pszDimension, "time") == 0) {
×
3115
    if (papszDimUserValue != NULL && *papszDimUserValue == NULL)
×
3116
      *papszDimUserValue =
×
3117
          msOWSLookupMetadata(&(layer->metadata), "MO", "time");
×
3118
    if (papszDimDefault != NULL && *papszDimDefault == NULL)
×
3119
      *papszDimDefault =
×
3120
          msOWSLookupMetadata(&(layer->metadata), "MO", "timedefault");
×
3121
    if (papszDimUnits != NULL && *papszDimUnits == NULL)
×
3122
      *papszDimUnits = "ISO8601";
×
3123
    if (papszDimUnitSymbol != NULL && *papszDimUnitSymbol == NULL)
×
3124
      *papszDimUnitSymbol = "t";
×
3125
    if (papszDimNearValue != NULL && *papszDimNearValue == NULL)
×
3126
      *papszDimNearValue = "0";
×
3127
  }
3128

3129
  free(pszDimensionItem);
×
3130

3131
  return;
×
3132
}
3133

3134
/**
3135
 * msOWSNegotiateUpdateSequence()
3136
 *
3137
 * returns the updateSequence value for an OWS
3138
 *
3139
 * @param requested_updatesequence the updatesequence passed by the client
3140
 * @param updatesequence the updatesequence set by the server
3141
 *
3142
 * @return result of comparison (-1, 0, 1)
3143
 * -1: lower / higher OR values not set by client or server
3144
 *  1: higher / lower
3145
 *  0: equal
3146
 */
3147

3148
int msOWSNegotiateUpdateSequence(const char *requested_updatesequence,
38✔
3149
                                 const char *updatesequence) {
3150
  int valtype1 = 1; /* default datatype for updatesequence passed by client */
3151
  int valtype2 = 1; /* default datatype for updatesequence set by server */
3152
  struct tm tm_requested_updatesequence, tm_updatesequence;
3153

3154
  /* if not specified by client, or set by server,
3155
     server responds with latest Capabilities XML */
3156
  if (!requested_updatesequence || !updatesequence)
38✔
3157
    return -1;
3158

3159
  /* test to see if server value is an integer (1), string (2) or timestamp (3)
3160
   */
3161
  if (msStringIsInteger(updatesequence) == MS_FAILURE)
38✔
3162
    valtype1 = 2;
3163

3164
  if (valtype1 == 2) { /* test if timestamp */
3165
    msTimeInit(&tm_updatesequence);
15✔
3166
    if (msParseTime(updatesequence, &tm_updatesequence) == MS_TRUE)
15✔
3167
      valtype1 = 3;
3168
    msResetErrorList();
15✔
3169
  }
3170

3171
  /* test to see if client value is an integer (1), string (2) or timestamp (3)
3172
   */
3173
  if (msStringIsInteger(requested_updatesequence) == MS_FAILURE)
38✔
3174
    valtype2 = 2;
3175

3176
  if (valtype2 == 2) { /* test if timestamp */
3177
    msTimeInit(&tm_requested_updatesequence);
15✔
3178
    if (msParseTime(requested_updatesequence, &tm_requested_updatesequence) ==
15✔
3179
        MS_TRUE)
3180
      valtype2 = 3;
3181
    msResetErrorList();
15✔
3182
  }
3183

3184
  /* if the datatypes do not match, do not compare, */
3185
  if (valtype1 != valtype2)
38✔
3186
    return -1;
3187

3188
  if (valtype1 == 1) { /* integer */
38✔
3189
    const int requested_updatesequence_i = atoi(requested_updatesequence);
3190
    const int updatesequence_i = atoi(updatesequence);
3191
    if (requested_updatesequence_i < updatesequence_i)
23✔
3192
      return -1;
3193

3194
    if (requested_updatesequence_i > updatesequence_i)
15✔
3195
      return 1;
3196

3197
    return 0;
8✔
3198
  }
3199

3200
  if (valtype1 == 2) /* string */
15✔
3201
    return strcasecmp(requested_updatesequence, updatesequence);
3✔
3202

3203
  assert(valtype1 == 3); /* timestamp */
3204
  /* compare timestamps */
3205
  return msDateCompare(&tm_requested_updatesequence, &tm_updatesequence) +
12✔
3206
         msTimeCompare(&tm_requested_updatesequence, &tm_updatesequence);
12✔
3207
}
3208

3209
/************************************************************************/
3210
/*                         msOwsIsOutputFormatValid                     */
3211
/*                                                                      */
3212
/*      Utility function to parse a comma separated list in a            */
3213
/*      metedata object and select and outputformat.                    */
3214
/************************************************************************/
3215
outputFormatObj *msOwsIsOutputFormatValid(mapObj *map, const char *format,
109✔
3216
                                          hashTableObj *metadata,
3217
                                          const char *namespaces,
3218
                                          const char *name) {
3219
  char **tokens = NULL;
3220
  int i, n;
3221
  outputFormatObj *psFormat = NULL;
3222
  const char *format_list = NULL;
3223

3224
  if (map && format && metadata && namespaces && name) {
109✔
3225
    msApplyDefaultOutputFormats(map);
109✔
3226
    format_list = msOWSLookupMetadata(metadata, namespaces, name);
109✔
3227
    n = 0;
109✔
3228
    if (format_list)
109✔
3229
      tokens = msStringSplit(format_list, ',', &n);
109✔
3230

3231
    if (tokens && n > 0) {
109✔
3232
      for (i = 0; i < n; i++) {
241✔
3233
        int iFormat = msGetOutputFormatIndex(map, tokens[i]);
241✔
3234
        const char *mimetype;
3235
        if (iFormat == -1)
241✔
3236
          continue;
28✔
3237

3238
        mimetype = map->outputformatlist[iFormat]->mimetype;
213✔
3239

3240
        msStringTrim(tokens[i]);
213✔
3241
        if (strcasecmp(tokens[i], format) == 0)
213✔
3242
          break;
3243
        if (mimetype && strcasecmp(mimetype, format) == 0)
106✔
3244
          break;
3245
      }
3246
      if (i < n)
109✔
3247
        psFormat = msSelectOutputFormat(map, format);
109✔
3248
    }
3249
    if (tokens)
109✔
3250
      msFreeCharArray(tokens, n);
109✔
3251
  }
3252

3253
  return psFormat;
109✔
3254
}
3255

3256
#endif /* defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined             \
3257
          (USE_WCS_SVR) || defined(USE_SOS_SVR) || defined(USE_WMS_LYR) ||     \
3258
          defined(USE_WFS_LYR) */
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