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

geographika / mapserver / 23004945429

12 Mar 2026 01:39PM UTC coverage: 42.423% (+0.003%) from 42.42%
23004945429

push

github

geographika
Use std::string and avoid strlcpy

8 of 9 new or added lines in 1 file covered. (88.89%)

955 existing lines in 4 files now uncovered.

64589 of 152250 relevant lines covered (42.42%)

27323.37 hits per line

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

71.7
/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
#include "cpl_string.h"
35

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

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

49
#include <map>
50

51
#define OGC_CRS_PREFIX "http://www.opengis.net/def/crs/"
52

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

63
  ows_request->service = NULL;
1,893✔
64
  ows_request->version = NULL;
1,893✔
65
  ows_request->request = NULL;
1,893✔
66
  ows_request->document = NULL;
1,893✔
67
}
1,893✔
68

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

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

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

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

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

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

191
    ows_request->request = msStrdup((char *)root->name);
157✔
192

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

202
    /* remove all namespaces */
203
    CPLStripXMLNamespace(ows_request->document, NULL, 1);
204
    for (temp = ows_request->document; temp != NULL; temp = temp->psNext) {
205

206
      if (temp->eType == CXT_Element) {
207
        const char *service, *version;
208
        ows_request->request = msStrdup(temp->pszValue);
209

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

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

237
  return MS_SUCCESS;
238
}
239

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

247
  compliance_mode_str =
248
      msOWSLookupMetadata(&(map->web.metadata), "MO", "compliance_mode");
2,466✔
249
  return (compliance_mode_str != NULL) &&
2,466✔
250
         (strcasecmp(compliance_mode_str, "true") == 0);
9✔
251
}
252

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

263
  xmlDocPtr psDoc = NULL;
264
  xmlNodePtr psRootNode = NULL;
265
  xmlNsPtr psNsOws = NULL;
266
  xmlChar *buffer = NULL;
3✔
267

268
  psNsOws = xmlNewNs(NULL, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI,
3✔
269
                     BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
270

271
  errorString = msGetErrorString("\n");
3✔
272

273
  psDoc = xmlNewDoc(BAD_CAST "1.0");
3✔
274

275
  psRootNode = msOWSCommonExceptionReport(
3✔
276
      psNsOws, OWS_1_0_0, msOWSGetSchemasLocation(map), "1.0.0",
277
      msOWSGetLanguage(map, "exception"), exceptionCode, "service",
278
      errorString);
279

280
  xmlDocSetRootElement(psDoc, psRootNode);
3✔
281

282
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI,
3✔
283
           BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
284

285
  msIO_setHeader("Status", "200 OK");
3✔
286
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
3✔
287
  msIO_sendHeaders();
3✔
288

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

291
  msIO_printf("%s", buffer);
3✔
292

293
  /*free buffer and the document */
294
  free(errorString);
3✔
295
  xmlFree(buffer);
3✔
296
  xmlFreeDoc(psDoc);
3✔
297
  xmlFreeNs(psNsOws);
3✔
298

299
  /* clear error since we have already reported it */
300
  msResetErrorList();
3✔
301

302
  return MS_FAILURE;
3✔
303
}
304

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

320
  if (!request) {
1,892✔
321
    return status;
322
  }
323

324
  force_ows_mode = (ows_mode == OWS || ows_mode == WFS);
1,892✔
325

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

336
  compliance_mode = msOWSStrictCompliance(map);
1,892✔
337

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

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

424
  msOWSClearRequestObj(&ows_request);
1,886✔
425
  return status;
426
}
427

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

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

535
  return len;
536
}
537

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

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

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

577
  return MS_FALSE;
578
}
579

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

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

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

628
  if (!ip)
10,212✔
629
    return MS_FALSE; /* no endpoint ip */
630

631
  ip_list = msOWSLookupMetadata(metadata, namespaces, "allowed_ip_list");
×
632
  if (!ip_list)
×
UNCOV
633
    ip_list = msOWSLookupMetadata(metadata, "O", "allowed_ip_list");
×
634

UNCOV
635
  if (ip_list) {
×
636
    disabled = MS_TRUE;
UNCOV
637
    if (msOWSIpInMetadata(ip_list, ip))
×
638
      disabled = MS_FALSE;
639
  }
640

641
  ip_list = msOWSLookupMetadata(metadata, namespaces, "denied_ip_list");
×
642
  if (!ip_list)
×
UNCOV
643
    ip_list = msOWSLookupMetadata(metadata, "O", "denied_ip_list");
×
644

UNCOV
645
  if (ip_list && msOWSIpInMetadata(ip_list, ip))
×
646
    disabled = MS_TRUE;
647

648
  return disabled;
649
}
650

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

668
  if (request == NULL)
978✔
669
    return MS_FALSE;
670

671
  remote_ip = getenv("REMOTE_ADDR");
978✔
672

673
  /* First, we check in the layer metadata */
674
  if (layer && check_all_layers == MS_FALSE) {
978✔
675
    enable_request =
676
        msOWSLookupMetadata(&layer->metadata, namespaces, "enable_request");
151✔
677
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
151✔
678
      return MS_TRUE;
679
    if (disabled)
151✔
680
      return MS_FALSE;
681

682
    enable_request =
683
        msOWSLookupMetadata(&layer->metadata, "O", "enable_request");
151✔
684
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
151✔
685
      return MS_TRUE;
686
    if (disabled)
151✔
687
      return MS_FALSE;
688

689
    if (msOWSIpDisabled(&layer->metadata, namespaces, remote_ip))
151✔
690
      return MS_FALSE;
691
  }
692

693
  if (map && (check_all_layers == MS_FALSE || map->numlayers == 0)) {
978✔
694
    /* then we check in the map metadata */
695
    enable_request =
696
        msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
683✔
697
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
683✔
698
      return MS_TRUE;
699
    if (disabled)
335✔
700
      return MS_FALSE;
701

702
    enable_request =
703
        msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
331✔
704
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
331✔
705
      return MS_TRUE;
706
    if (disabled)
20✔
707
      return MS_FALSE;
708

709
    if (msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
20✔
710
      return MS_FALSE;
711
  }
712

713
  if (map && check_all_layers == MS_TRUE) {
315✔
714
    int i, globally_enabled = MS_FALSE;
715
    enable_request =
716
        msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
295✔
717
    globally_enabled =
718
        msOWSParseRequestMetadata(enable_request, request, &disabled);
295✔
719

720
    if (!globally_enabled && !disabled) {
295✔
721
      enable_request =
722
          msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
265✔
723
      globally_enabled =
724
          msOWSParseRequestMetadata(enable_request, request, &disabled);
265✔
725
    }
726

727
    if (globally_enabled &&
560✔
728
        msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
292✔
729
      globally_enabled = MS_FALSE;
730

731
    /* Check all layers */
732
    for (i = 0; i < map->numlayers; i++) {
298✔
733
      int result = MS_FALSE;
734
      layerObj *lp;
735
      lp = (GET_LAYER(map, i));
298✔
736

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

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

748
        enable_request =
749
            msOWSLookupMetadata(&lp->metadata, "O", "enable_request");
288✔
750
        result = msOWSParseRequestMetadata(enable_request, request, &disabled);
288✔
751
        if (!result && disabled)
288✔
UNCOV
752
          continue;
×
753
      }
754

755
      if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))
295✔
UNCOV
756
        continue;
×
757

758
      if (result || (!disabled && globally_enabled))
295✔
759
        return MS_TRUE;
760
    }
761

UNCOV
762
    if (!disabled && globally_enabled)
×
763
      return MS_TRUE;
764
  }
765

766
  return MS_FALSE;
767
}
768

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

789
  if (ows_request->numlayers > 0)
1,847✔
790
    msFree(ows_request->enabled_layers);
9✔
791

792
  ows_request->numlayers = 0;
1,847✔
793
  ows_request->enabled_layers = NULL;
1,847✔
794

795
  if (request == NULL || (map == NULL) || (map->numlayers <= 0))
1,847✔
796
    return;
9✔
797

798
  remote_ip = getenv("REMOTE_ADDR");
1,838✔
799

800
  enable_request =
801
      msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
1,838✔
802
  globally_enabled =
803
      msOWSParseRequestMetadata(enable_request, request, &disabled);
1,838✔
804

805
  if (!globally_enabled && !disabled) {
1,838✔
806
    enable_request =
807
        msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
1,601✔
808
    globally_enabled =
809
        msOWSParseRequestMetadata(enable_request, request, &disabled);
1,601✔
810
  }
811

812
  if (globally_enabled &&
3,426✔
813
      msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
1,815✔
814
    globally_enabled = MS_FALSE;
815

816
  if (map->numlayers) {
1,838✔
817
    int i, layers_size = map->numlayers; /* for most of cases, this will be
818
                                            relatively small */
819

820
    ows_request->enabled_layers =
1,838✔
821
        (int *)msSmallMalloc(sizeof(int) * layers_size);
1,838✔
822

823
    for (i = 0; i < map->numlayers; i++) {
9,639✔
824
      int result = MS_FALSE;
825
      layerObj *lp;
826
      lp = (GET_LAYER(map, i));
7,801✔
827

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

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

839
        enable_request =
840
            msOWSLookupMetadata(&lp->metadata, "O", "enable_request");
7,576✔
841
        result = msOWSParseRequestMetadata(enable_request, request, &disabled);
7,576✔
842
        if (!result && disabled)
7,576✔
843
          continue;
4✔
844
      }
845

846
      if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))
7,639✔
UNCOV
847
        continue;
×
848

849
      if (result || (!disabled && globally_enabled)) {
7,639✔
850
        ows_request->enabled_layers[ows_request->numlayers] = lp->index;
7,627✔
851
        ows_request->numlayers++;
7,627✔
852
      }
853
    }
854

855
    if (ows_request->numlayers == 0) {
1,838✔
856
      msFree(ows_request->enabled_layers);
15✔
857
      ows_request->enabled_layers = NULL;
15✔
858
    }
859
  }
860
}
861

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

875
  *disabled = MS_FALSE;
21,536✔
876

877
  if (metadata == NULL)
21,536✔
878
    return MS_FALSE;
879

880
  ptr = (char *)metadata;
881
  const size_t len = strlen(ptr);
3,365✔
882
  requestBuffer[0] = '\0';
3,365✔
883
  bufferPtr = requestBuffer;
884

885
  for (size_t i = 0; i <= len; ++i, ++ptr) {
15,494✔
886

887
    if (!wordFlag && isspace(*ptr))
12,532✔
UNCOV
888
      continue;
×
889

890
    wordFlag = MS_TRUE;
891

892
    if (*ptr == '!') {
12,532✔
893
      disableFlag = MS_TRUE;
894
      continue;
354✔
895
    } else if ((*ptr == ' ') ||
12,178✔
896
               (*ptr != '\0' && ptr[1] == '\0')) { /* end of word */
9,041✔
897
      if (ptr[1] == '\0' && *ptr != ' ') {
3,496✔
898
        *bufferPtr = *ptr;
3,321✔
899
        ++bufferPtr;
3,321✔
900
      }
901

902
      *bufferPtr = '\0';
3,496✔
903
      if (strcasecmp(request, requestBuffer) == 0) {
3,496✔
904
        *disabled = MS_TRUE; /* explicitly found, will stop the process in
403✔
905
                                msOWSRequestIsEnabled() */
906
        return (disableFlag ? MS_FALSE : MS_TRUE);
403✔
907
      } else {
908
        if (strcmp("*", requestBuffer) ==
3,093✔
909
            0) { /* check if we read the all flag */
910
          if (disableFlag)
2,862✔
911
            *disabled = MS_TRUE;
135✔
912
          allFlag = disableFlag ? MS_FALSE : MS_TRUE;
2,862✔
913
        }
914
        /* reset flags */
915
        wordFlag = MS_FALSE;
916
        disableFlag = MS_FALSE;
917
        bufferPtr = requestBuffer;
918
      }
919
    } else {
920
      *bufferPtr = *ptr;
8,682✔
921
      ++bufferPtr;
8,682✔
922
    }
923
  }
924

925
  return allFlag;
926
}
927

928
/*
929
** msOWSGetPrefixFromNamespace()
930
**
931
** Return the metadata name prefix from a character identifying the OWS
932
** namespace.
933
*/
934
static const char *msOWSGetPrefixFromNamespace(char chNamespace) {
274,822✔
935
  // Return should be a 3 character string, otherwise breaks assumption
936
  // in msOWSLookupMetadata()
937
  switch (chNamespace) {
274,822✔
938
  case 'O':
939
    return "ows";
940
  case 'A':
8,600✔
941
    return "oga"; /* oga_... (OGC Geospatial API) */
8,600✔
942
  case 'M':
52,536✔
943
    return "wms";
52,536✔
944
  case 'F':
27,981✔
945
    return "wfs";
27,981✔
946
  case 'C':
12,055✔
947
    return "wcs";
12,055✔
948
  case 'G':
101,485✔
949
    return "gml";
101,485✔
950
  case 'S':
1,404✔
951
    return "sos";
1,404✔
UNCOV
952
  default:
×
953
    /* We should never get here unless an invalid code (typo) is */
954
    /* present in the code, but since this happened before... */
UNCOV
955
    msSetError(MS_WMSERR, "Unsupported metadata namespace code (%c).",
×
956
               "msOWSGetPrefixFromNamespace()", chNamespace);
957
    assert(MS_FALSE);
UNCOV
958
    return NULL;
×
959
  }
960
}
961

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

976
  if (namespaces == NULL) {
197,192✔
UNCOV
977
    value = msLookupHashTable(metadata, (char *)name);
×
978
  } else {
979
    char buf[100] = "ows_";
197,192✔
980

981
    strlcpy(buf + 4, name, 96);
982

983
    while (value == NULL && *namespaces != '\0') {
466,776✔
984
      const char *prefix = msOWSGetPrefixFromNamespace(*namespaces);
269,584✔
985
      if (prefix == NULL)
269,584✔
UNCOV
986
        return NULL;
×
987
      assert(strlen(prefix) == 3);
988
      memcpy(buf, prefix, 3);
989
      value = msLookupHashTable(metadata, buf);
269,584✔
990
      namespaces++;
269,584✔
991
    }
992
  }
993

994
  return value;
995
}
996

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

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

1017
  if (name && !value) {
15,393✔
1018
    value = msOWSLookupMetadata(metadata, namespaces, name);
14,932✔
1019
  }
1020

1021
  return value;
15,393✔
1022
}
1023

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

1041
  if ((result = msOWSLookupMetadata(pri, namespaces, name)) == NULL) {
2,228✔
1042
    /* Try the secondary table */
1043
    result = msOWSLookupMetadata(sec, namespaces, name);
2,221✔
1044
  }
1045

1046
  return result;
2,228✔
1047
}
1048

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

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

1074
    nVersion = atoi(digits[0]) * 0x010000;
2,326✔
1075
    nVersion += atoi(digits[1]) * 0x0100;
2,326✔
1076
    if (numDigits > 2)
2,326✔
1077
      nVersion += atoi(digits[2]);
2,211✔
1078

1079
    msFreeCharArray(digits, numDigits);
2,326✔
1080

1081
    return nVersion;
2,326✔
1082
  }
1083

1084
  return OWS_VERSION_NOTSET;
1085
}
1086

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

1097
  if (pszBuffer)
591✔
1098
    snprintf(pszBuffer, OWS_VERSION_MAXLEN - 1, "%d.%d.%d",
591✔
1099
             (nVersion / 0x10000) % 0x100, (nVersion / 0x100) % 0x100,
591✔
1100
             nVersion % 0x100);
1101

1102
  return pszBuffer;
591✔
1103
}
1104

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

1123
  /* metadata value should already be in format "AUTHORITY:CODE" or "AUTO:..."
1124
   */
1125
  if (metadata &&
7,185✔
1126
      ((value = msOWSLookupMetadata(metadata, namespaces, "srs")) != NULL)) {
3,090✔
1127
    const char *space_ptr = strchr(value, ' ');
1128
    if (!bReturnOnlyFirstOne || space_ptr == NULL) {
2,469✔
1129
      *epsgCode = msStrdup(value);
1,265✔
1130
    } else {
1131
      /* caller requested only first projection code, copy up to first space */
1132
      *epsgCode = static_cast<char *>(
1,204✔
1133
          msSmallMalloc((space_ptr - value + 1) * sizeof(char)));
1,204✔
1134
      strlcpy(*epsgCode, value, space_ptr - value + 1);
1135
    }
1136
    return;
2,469✔
1137
  }
1138

1139
  if (proj && proj->numargs > 0) {
1,626✔
1140
    const char *arg = proj->args[0];
1,589✔
1141

1142
    if ((value = strstr(arg, "init=epsg:")) != NULL) {
1,589✔
1143
      /* Legacy PROJ 4 init= syntax, retained for backwards compatibility */
1144
      *epsgCode = static_cast<char *>(msSmallMalloc(
2,938✔
1145
          (strlen("EPSG:") + strlen(value + 10) + 1) * sizeof(char)));
1,469✔
1146
      snprintf(*epsgCode, strlen("EPSG:") + strlen(value + 10) + 1, "EPSG:%s",
1,469✔
1147
               value + 10);
1148
    } else if ((value = strstr(arg, "init=crs:")) != NULL) {
120✔
1149
      /* Legacy PROJ 4 init= syntax, retained for backwards compatibility */
1150
      *epsgCode = static_cast<char *>(msSmallMalloc(
×
1151
          (strlen("CRS:") + strlen(value + 9) + 1) * sizeof(char)));
×
UNCOV
1152
      snprintf(*epsgCode, strlen("CRS:") + strlen(value + 9) + 1, "CRS:%s",
×
1153
               value + 9);
1154
    } else if (strncasecmp(arg, "AUTO:", 5) == 0 ||
120✔
1155
               strncasecmp(arg, "AUTO2:", 6) == 0) {
120✔
UNCOV
1156
      *epsgCode = msStrdup(arg);
×
1157
    } else if (strncasecmp(arg, "init=", 5) == 0) {
120✔
1158
      /* Custom init= file path e.g. "init=./data/epsg2:42304" -
1159
      ** not a standard authority code, return NULL */
1160
      return;
1161
    } else {
1162
      /* Handle generic AUTHORITY:CODE pattern e.g. ESRI:54030, IAU_2015:30100
1163
       */
1164
      const char *sep = strchr(arg, ':');
1165
      if (sep != NULL) {
95✔
1166
        char auth[64];
1167
        size_t authlen = sep - arg;
14✔
1168
        if (authlen < sizeof(auth)) {
14✔
1169
          strlcpy(auth, arg, authlen + 1);
14✔
1170
          for (size_t j = 0; auth[j]; j++)
70✔
1171
            auth[j] = (char)toupper((unsigned char)auth[j]);
56✔
1172
          *epsgCode = static_cast<char *>(
14✔
1173
              msSmallMalloc((authlen + strlen(sep) + 1) * sizeof(char)));
14✔
1174
          snprintf(*epsgCode, authlen + strlen(sep) + 1, "%s:%s", auth,
14✔
1175
                   sep + 1);
1176
        }
1177
      }
1178
    }
1179
  }
1180
}
1181
/*
1182
** msOWSProjectToWGS84()
1183
**
1184
** Reprojects the extent to WGS84.
1185
**
1186
*/
1187
void msOWSProjectToWGS84(projectionObj *srcproj, rectObj *ext) {
315✔
1188
  if (srcproj->proj && !msProjIsGeographicCRS(srcproj)) {
315✔
1189
    projectionObj wgs84;
1190
    msInitProjection(&wgs84);
226✔
1191
    msProjectionInheritContextFrom(&wgs84, srcproj);
226✔
1192
    msLoadProjectionString(&wgs84, "+proj=longlat +ellps=WGS84 +datum=WGS84");
226✔
1193
    msProjectRect(srcproj, &wgs84, ext);
226✔
1194
    msFreeProjection(&wgs84);
226✔
1195
  }
1196
}
315✔
1197

1198
/* msOWSGetLanguage()
1199
**
1200
** returns the language via MAP/WEB/METADATA/ows_language
1201
**
1202
** Use value of "ows_language" metadata, if not set then
1203
** return "undefined" as a default
1204
*/
1205
const char *msOWSGetLanguage(mapObj *map, const char *context) {
145✔
1206
  const char *language;
1207

1208
  /* if this is an exception, MapServer always returns Exception
1209
     messages in en-US
1210
  */
1211
  if (strcmp(context, "exception") == 0) {
145✔
1212
    language = MS_ERROR_LANGUAGE;
1213
  }
1214
  /* if not, fetch language from mapfile metadata */
1215
  else {
UNCOV
1216
    language = msLookupHashTable(&(map->web.metadata), "ows_language");
×
1217

UNCOV
1218
    if (language == NULL) {
×
1219
      language = "undefined";
1220
    }
1221
  }
1222
  return language;
145✔
1223
}
1224

1225
/* msOWSGetSchemasLocation()
1226
**
1227
** schemas location is the root of the web tree where all WFS-related
1228
** schemas can be found on this server.  These URLs must exist in order
1229
** to validate xml.
1230
**
1231
** Use value of "ows_schemas_location" metadata, if not set then
1232
** return ".." as a default
1233
*/
1234
const char *msOWSGetSchemasLocation(mapObj *map) {
1,208✔
1235
  const char *schemas_location;
1236

1237
  schemas_location =
1238
      msLookupHashTable(&(map->web.metadata), "ows_schemas_location");
1,208✔
1239
  if (schemas_location == NULL)
1,208✔
1240
    schemas_location = OWS_DEFAULT_SCHEMAS_LOCATION;
1241

1242
  return schemas_location;
1,208✔
1243
}
1244

1245
/*
1246
** msOWSGetExpandedMetadataKey()
1247
*/
1248
char *msOWSGetExpandedMetadataKey(const char *namespaces,
2,603✔
1249
                                  const char *metadata_name) {
1250
  char *pszRet = msStringConcatenate(NULL, "");
2,603✔
1251
  for (int i = 0; namespaces[i] != '\0'; ++i) {
7,841✔
1252
    if (i > 0)
5,238✔
1253
      pszRet = msStringConcatenate(pszRet, " or ");
2,635✔
1254
    pszRet = msStringConcatenate(pszRet, "\"");
5,238✔
1255
    pszRet =
1256
        msStringConcatenate(pszRet, msOWSGetPrefixFromNamespace(namespaces[i]));
5,238✔
1257
    pszRet = msStringConcatenate(pszRet, "_");
5,238✔
1258
    pszRet = msStringConcatenate(pszRet, metadata_name);
5,238✔
1259
    pszRet = msStringConcatenate(pszRet, "\"");
5,238✔
1260
  }
1261
  return pszRet;
2,603✔
1262
}
1263

1264
/*
1265
** msOWSGetOnlineResource()
1266
**
1267
** Return the online resource for this service.  First try to lookup
1268
** specified metadata, and if not found then try to build the URL ourselves.
1269
**
1270
** Returns a newly allocated string that should be freed by the caller or
1271
** NULL in case of error.
1272
*/
1273
char *msOWSGetOnlineResource(mapObj *map, const char *namespaces,
769✔
1274
                             const char *metadata_name, cgiRequestObj *req) {
1275
  const char *value;
1276
  char *online_resource = NULL;
1277

1278
  /* We need this script's URL, including hostname. */
1279
  /* Default to use the value of the "onlineresource" metadata, and if not */
1280
  /* set then build it: "http://$(SERVER_NAME):$(SERVER_PORT)$(SCRIPT_NAME)?" */
1281
  /* (+append the map=... param if it was explicitly passed in QUERY_STRING) */
1282
  /*  */
1283
  if ((value = msOWSLookupMetadata(&(map->web.metadata), namespaces,
769✔
1284
                                   metadata_name))) {
1285
    online_resource = msOWSTerminateOnlineResource(value);
743✔
1286
  } else {
1287
    if ((online_resource = msBuildOnlineResource(map, req)) == NULL) {
26✔
1288
      char *pszExpandedMetadataKey =
1289
          msOWSGetExpandedMetadataKey(namespaces, metadata_name);
15✔
1290
      msSetError(MS_CGIERR, "Please set %s metadata.",
15✔
1291
                 "msOWSGetOnlineResource()", pszExpandedMetadataKey);
1292
      msFree(pszExpandedMetadataKey);
15✔
1293
      return NULL;
15✔
1294
    }
1295
  }
1296

1297
  return online_resource;
1298
}
1299

1300
/*
1301
** msOWSTerminateOnlineResource()
1302
**
1303
** Append trailing "?" or "&" to an onlineresource URL if it doesn't have
1304
** one already. The returned string is then ready to append GET parameters
1305
** to it.
1306
**
1307
** Returns a newly allocated string that should be freed by the caller or
1308
** NULL in case of error.
1309
*/
1310
char *msOWSTerminateOnlineResource(const char *src_url) {
743✔
1311
  char *online_resource = NULL;
1312
  size_t buffer_size = 0;
1313

1314
  if (src_url == NULL)
743✔
1315
    return NULL;
1316

1317
  buffer_size = strlen(src_url) + 2;
743✔
1318
  online_resource = (char *)malloc(buffer_size);
743✔
1319

1320
  if (online_resource == NULL) {
743✔
1321
    msSetError(MS_MEMERR, NULL, "msOWSTerminateOnlineResource()");
×
UNCOV
1322
    return NULL;
×
1323
  }
1324

1325
  strlcpy(online_resource, src_url, buffer_size);
1326

1327
  /* Append trailing '?' or '&' if missing. */
1328
  if (strchr(online_resource, '?') == NULL)
743✔
1329
    strlcat(online_resource, "?", buffer_size);
1330
  else {
1331
    char *c;
1332
    c = online_resource + strlen(online_resource) - 1;
648✔
1333
    if (*c != '?' && *c != '&')
648✔
1334
      strlcpy(c + 1, "&", buffer_size - strlen(online_resource));
6✔
1335
  }
1336

1337
  return online_resource;
1338
}
1339

1340
/************************************************************************/
1341
/*                         msUpdateGMLFieldMetadata                     */
1342
/*                                                                      */
1343
/*      Updates a fields GML metadata if it has not already             */
1344
/*      been set. Nullable is not implemented for all drivers           */
1345
/*      and can be set to 0 if unknown                                  */
1346
/************************************************************************/
1347
int msUpdateGMLFieldMetadata(layerObj *layer, const char *field_name,
2,882✔
1348
                             const char *gml_type, const char *gml_width,
1349
                             const char *gml_precision, const short nullable) {
1350

1351
  char md_item_name[256];
1352

1353
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_type", field_name);
1354
  if (msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
2,882✔
1355
    msInsertHashTable(&(layer->metadata), md_item_name, gml_type);
1,329✔
1356

1357
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_width", field_name);
1358
  if (strlen(gml_width) > 0 &&
4,088✔
1359
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
1,206✔
1360
    msInsertHashTable(&(layer->metadata), md_item_name, gml_width);
645✔
1361

1362
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_precision", field_name);
1363
  if (strlen(gml_precision) > 0 &&
3,011✔
1364
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
129✔
1365
    msInsertHashTable(&(layer->metadata), md_item_name, gml_precision);
68✔
1366

1367
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_nillable", field_name);
1368
  if (nullable > 0 &&
2,882✔
1369
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
×
UNCOV
1370
    msInsertHashTable(&(layer->metadata), md_item_name, "true");
×
1371

1372
  return MS_TRUE;
2,882✔
1373
}
1374

1375
void msOWSSetShapeCache(mapObj *map, const char *pszMetadataNamespaces) {
594✔
1376
  const char *pszFeaturesCacheCount = msOWSLookupMetadata(
594✔
1377
      &(map->web.metadata), pszMetadataNamespaces, "features_cache_count");
594✔
1378
  const char *pszFeaturesCacheSize = msOWSLookupMetadata(
594✔
1379
      &(map->web.metadata), pszMetadataNamespaces, "features_cache_size");
1380
  if (pszFeaturesCacheCount) {
594✔
1381
    map->query.cache_shapes = MS_TRUE;
4✔
1382
    map->query.max_cached_shape_count = atoi(pszFeaturesCacheCount);
4✔
1383
    if (map->debug >= MS_DEBUGLEVEL_V) {
4✔
UNCOV
1384
      msDebug("Caching up to %d shapes\n", map->query.max_cached_shape_count);
×
1385
    }
1386
  }
1387

1388
  if (pszFeaturesCacheSize) {
594✔
1389
    map->query.cache_shapes = MS_TRUE;
4✔
1390
    map->query.max_cached_shape_ram_amount = atoi(pszFeaturesCacheSize);
4✔
1391
    if (strstr(pszFeaturesCacheSize, "mb") ||
4✔
1392
        strstr(pszFeaturesCacheSize, "MB"))
UNCOV
1393
      map->query.max_cached_shape_ram_amount *= 1024 * 1024;
×
1394
    if (map->debug >= MS_DEBUGLEVEL_V) {
4✔
UNCOV
1395
      msDebug("Caching up to %d bytes of shapes\n",
×
1396
              map->query.max_cached_shape_ram_amount);
1397
    }
1398
  }
1399
}
594✔
1400

1401
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1402
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
1403

1404
/*
1405
** msRenameLayer()
1406
*/
UNCOV
1407
static int msRenameLayer(layerObj *lp, int count) {
×
1408
  char *newname;
1409
  const size_t nSize = strlen(lp->name) + 16;
×
1410
  newname = (char *)malloc(nSize);
×
1411
  if (!newname) {
×
1412
    msSetError(MS_MEMERR, NULL, "msRenameLayer()");
×
UNCOV
1413
    return MS_FAILURE;
×
1414
  }
1415
  snprintf(newname, nSize, "%s_%2.2d", lp->name, count);
1416
  free(lp->name);
×
UNCOV
1417
  lp->name = newname;
×
1418

UNCOV
1419
  return MS_SUCCESS;
×
1420
}
1421

1422
/*
1423
** msOWSMakeAllLayersUnique()
1424
*/
1425
int msOWSMakeAllLayersUnique(mapObj *map) {
1,487✔
1426
  std::map<std::string, int> namesToIdx;
1427
  std::map<std::string, int> namesToCount;
1428

1429
  /* Make sure all layers in the map file have valid and unique names */
1430
  for (int i = 0; i < map->numlayers; i++) {
8,768✔
1431
    layerObj *lp = GET_LAYER(map, i);
7,281✔
1432
    if (!lp->name)
7,281✔
UNCOV
1433
      continue;
×
1434
    const std::string name = msStringToLower(std::string(lp->name));
14,562✔
1435
    auto iterToIdx = namesToIdx.find(name);
1436
    if (iterToIdx == namesToIdx.end()) {
7,281✔
1437
      namesToIdx[name] = i;
7,281✔
1438
      namesToCount[name] = 1;
7,281✔
1439
    } else {
1440
      auto iterToCount = namesToCount.find(name);
1441
      assert(iterToCount != namesToCount.end());
1442
      if (iterToCount->second == 1) {
×
UNCOV
1443
        if (msRenameLayer(GET_LAYER(map, iterToIdx->second), 1) != MS_SUCCESS)
×
1444
          return MS_FAILURE;
1445
      }
1446
      ++iterToCount->second;
×
UNCOV
1447
      if (msRenameLayer(lp, iterToCount->second) != MS_SUCCESS)
×
1448
        return MS_FAILURE;
1449
    }
1450
  }
1451

1452
  return MS_SUCCESS;
1453
}
1454

1455
/*
1456
** msOWSNegotiateVersion()
1457
**
1458
** returns the most suitable version an OWS is to support given a client
1459
** version parameter.
1460
**
1461
** supported_versions must be ordered from highest to lowest
1462
**
1463
** Returns a version integer of the supported version
1464
**
1465
*/
1466

1467
int msOWSNegotiateVersion(int requested_version, const int supported_versions[],
106✔
1468
                          int num_supported_versions) {
1469
  int i;
1470

1471
  /* if version is not set return highest version */
1472
  if (!requested_version)
106✔
UNCOV
1473
    return supported_versions[0];
×
1474

1475
  /* if the requested version is lower than the lowest version return the lowest
1476
   * version  */
1477
  if (requested_version < supported_versions[num_supported_versions - 1])
106✔
1478
    return supported_versions[num_supported_versions - 1];
1479

1480
  /* return the first entry that's lower than or equal to the requested version
1481
   */
1482
  for (i = 0; i < num_supported_versions; i++) {
299✔
1483
    if (supported_versions[i] <= requested_version)
299✔
1484
      return supported_versions[i];
106✔
1485
  }
1486

1487
  return requested_version;
1488
}
1489

1490
/*
1491
** msOWSGetOnlineResource()
1492
**
1493
** Return the online resource for this service and add language parameter.
1494
**
1495
** Returns a newly allocated string that should be freed by the caller or
1496
** NULL in case of error.
1497
*/
1498
char *msOWSGetOnlineResource2(mapObj *map, const char *namespaces,
92✔
1499
                              const char *metadata_name, cgiRequestObj *req,
1500
                              const char *validated_language) {
1501
  char *online_resource =
1502
      msOWSGetOnlineResource(map, namespaces, metadata_name, req);
92✔
1503

1504
  if (online_resource && validated_language && validated_language[0]) {
92✔
1505
    /* online_resource is already terminated, so we can simply add language=...&
1506
     */
1507
    /* but first we need to make sure that online_resource has enough capacity
1508
     */
1509
    online_resource = (char *)msSmallRealloc(
20✔
1510
        online_resource,
1511
        strlen(online_resource) + strlen(validated_language) + 11);
20✔
1512
    strcat(online_resource, "language=");
1513
    strcat(online_resource, validated_language);
1514
    strcat(online_resource, "&");
1515
  }
1516

1517
  return online_resource;
92✔
1518
}
1519

1520
/* msOWSGetInspireSchemasLocation()
1521
**
1522
** schemas location is the root of the web tree where all Inspire-related
1523
** schemas can be found on this server.  These URLs must exist in order
1524
** to validate xml.
1525
**
1526
** Use value of "inspire_schemas_location" metadata
1527
*/
1528
const char *msOWSGetInspireSchemasLocation(mapObj *map) {
41✔
1529
  const char *schemas_location;
1530

1531
  schemas_location =
1532
      msLookupHashTable(&(map->web.metadata), "inspire_schemas_location");
41✔
1533
  if (schemas_location == NULL)
41✔
1534
    schemas_location = "http://inspire.ec.europa.eu/schemas";
1535

1536
  return schemas_location;
41✔
1537
}
1538

1539
/* msOWSGetLanguageList
1540
**
1541
** Returns the list of languages that this service supports
1542
**
1543
** Use value of "languages" metadata (comma-separated list), or NULL if not set
1544
**
1545
** Returns a malloced char** of length numitems which must be freed
1546
** by the caller, or NULL (with numitems = 0)
1547
*/
1548
char **msOWSGetLanguageList(mapObj *map, const char *namespaces,
1,448✔
1549
                            int *numitems) {
1550

1551
  const char *languages = NULL;
1552

1553
  languages =
1554
      msOWSLookupMetadata(&(map->web.metadata), namespaces, "languages");
1,448✔
1555
  if (languages && strlen(languages) > 0) {
1,448✔
1556
    return msStringSplit(languages, ',', numitems);
132✔
1557
  } else {
1558
    *numitems = 0;
1,316✔
1559
    return NULL;
1,316✔
1560
  }
1561
}
1562

1563
/* msOWSGetLanguageFromList
1564
**
1565
** Returns a language according to the language requested by the client
1566
**
1567
** If the requested language is in the languages metadata then use it,
1568
** otherwise ignore it and use the default language, which is the first entry in
1569
** the languages metadata list. Calling with a NULL requested_langauge
1570
** therefore returns this default language. If the language metadata list is
1571
** not defined then the language is set to NULL.
1572
**
1573
** Returns a malloced char* which must be freed by the caller, or NULL
1574
*/
1575
char *msOWSGetLanguageFromList(mapObj *map, const char *namespaces,
1,406✔
1576
                               const char *requested_language) {
1577
  int num_items = 0;
1,406✔
1578
  char **languages = msOWSGetLanguageList(map, namespaces, &num_items);
1,406✔
1579
  char *language = NULL;
1580

1581
  if (languages && num_items > 0) {
1,406✔
1582
    if (!requested_language ||
128✔
1583
        !msStringInArray(requested_language, languages, num_items)) {
16✔
1584
      language = msStrdup(languages[0]);
98✔
1585
    } else {
1586
      language = msStrdup(requested_language);
14✔
1587
    }
1588
  }
1589
  msFreeCharArray(languages, num_items);
1,406✔
1590

1591
  return language;
1,406✔
1592
}
1593

1594
/* msOWSLanguageNegotiation
1595
**
1596
** Returns a language according to the accepted languages requested by the
1597
*client
1598
**
1599
** Returns a malloced char* which must be freed by the caller, or NULL
1600
*/
1601
char *msOWSLanguageNegotiation(mapObj *map, const char *namespaces,
42✔
1602
                               char **accept_languages,
1603
                               int num_accept_languages) {
1604
  int num_languages = 0;
42✔
1605
  char **languages = NULL;
1606
  char *result_language = NULL;
1607

1608
  languages = msOWSGetLanguageList(map, namespaces, &num_languages);
42✔
1609

1610
  if (languages && num_languages > 0) {
42✔
1611
    int i;
1612
    for (i = 0; i < num_accept_languages; ++i) {
28✔
1613
      const char *accept_language = accept_languages[i];
20✔
1614

1615
      /* '*' means any language */
1616
      if (EQUAL(accept_language, "*")) {
20✔
UNCOV
1617
        result_language = msStrdup(languages[0]);
×
1618
        break;
1619
      } else if (msStringInArray(accept_language, languages, num_languages)) {
20✔
1620
        result_language = msStrdup(accept_language);
12✔
1621
        break;
1622
      }
1623
    }
1624

1625
    if (result_language == NULL) {
20✔
1626
      result_language = msStrdup(languages[0]);
8✔
1627
    }
1628
  }
1629

1630
  msFreeCharArray(languages, num_languages);
42✔
1631
  return result_language;
42✔
1632
}
1633

1634
/* msOWSPrintInspireCommonExtendedCapabilities
1635
**
1636
** Output INSPIRE common extended capabilities items to stream
1637
** The currently supported items are metadata and languages
1638
**
1639
** tag_name is the name (including ns prefix) of the tag to include the whole
1640
** extended capabilities block in
1641
**
1642
** service is currently included for future compatibility when differing
1643
** extended capabilities elements are included for different service types
1644
**
1645
** Returns a status code; MS_NOERR if all ok, action_if_not_found otherwise
1646
*/
1647
int msOWSPrintInspireCommonExtendedCapabilities(
41✔
1648
    FILE *stream, mapObj *map, const char *namespaces, int action_if_not_found,
1649
    const char *tag_name, const char *tag_ns, const char *validated_language,
1650
    const OWSServiceType service) {
1651

1652
  int metadataStatus = 0;
1653
  int languageStatus = 0;
1654

1655
  if (tag_ns)
41✔
1656
    msIO_fprintf(stream, "  <%s %s>\n", tag_name, tag_ns);
26✔
1657
  else
1658
    msIO_fprintf(stream, "  <%s>\n", tag_name);
15✔
1659

1660
  metadataStatus = msOWSPrintInspireCommonMetadata(
41✔
1661
      stream, map, namespaces, action_if_not_found, service);
1662
  languageStatus = msOWSPrintInspireCommonLanguages(
41✔
1663
      stream, map, namespaces, action_if_not_found, validated_language);
1664

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

1667
  return (metadataStatus != MS_NOERR) ? metadataStatus : languageStatus;
41✔
1668
}
1669

1670
/* msOWSPrintInspireCommonMetadata
1671
**
1672
** Output INSPIRE common metadata items to a stream
1673
**
1674
** Returns a status code; MS_NOERR if all OK, action_if_not_found otherwise
1675
*/
1676
int msOWSPrintInspireCommonMetadata(FILE *stream, mapObj *map,
41✔
1677
                                    const char *namespaces,
1678
                                    int action_if_not_found,
1679
                                    const OWSServiceType service) {
1680

1681
  int status = MS_NOERR;
1682
  const char *inspire_capabilities = NULL;
1683

1684
  inspire_capabilities = msOWSLookupMetadata(&(map->web.metadata), namespaces,
41✔
1685
                                             "inspire_capabilities");
1686

1687
  if (!inspire_capabilities) {
41✔
1688
    if (OWS_WARN == action_if_not_found) {
×
UNCOV
1689
      msIO_fprintf(
×
1690
          stream,
1691
          "<!-- WARNING: missing metadata entry for 'inspire_capabilities', "
1692
          "one of 'embed' and 'url' must be supplied. -->\n");
1693
    }
UNCOV
1694
    return action_if_not_found;
×
1695
  }
1696
  if (strcasecmp("url", inspire_capabilities) == 0) {
41✔
1697
    if (msOWSLookupMetadata(&(map->web.metadata), namespaces,
20✔
1698
                            "inspire_metadataurl_href") != NULL) {
1699
      msIO_fprintf(stream,
20✔
1700
                   "    <inspire_common:MetadataUrl "
1701
                   "xsi:type=\"inspire_common:resourceLocatorType\">\n");
1702
      msOWSPrintEncodeMetadata(
20✔
1703
          stream, &(map->web.metadata), namespaces, "inspire_metadataurl_href",
1704
          OWS_WARN, "      <inspire_common:URL>%s</inspire_common:URL>\n", "");
1705
      msOWSPrintEncodeMetadata(
20✔
1706
          stream, &(map->web.metadata), namespaces,
1707
          "inspire_metadataurl_format", OWS_WARN,
1708
          "      <inspire_common:MediaType>%s</inspire_common:MediaType>\n",
1709
          "");
1710
      msIO_fprintf(stream, "    </inspire_common:MetadataUrl>\n");
20✔
1711
    } else {
1712
      status = action_if_not_found;
UNCOV
1713
      if (OWS_WARN == action_if_not_found) {
×
1714
        char *pszExpandedMetadataKey =
1715
            msOWSGetExpandedMetadataKey(namespaces, "inspire_metadataurl_href");
×
UNCOV
1716
        msIO_fprintf(stream,
×
1717
                     "<!-- WARNING: Mandatory metadata %s was missing in this "
1718
                     "context. -->\n",
1719
                     pszExpandedMetadataKey);
UNCOV
1720
        msFree(pszExpandedMetadataKey);
×
1721
      }
1722
    }
1723
  } else if (strcasecmp("embed", inspire_capabilities) == 0) {
21✔
1724
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1725
                             "inspire_resourcelocator", OWS_WARN,
1726
                             "    <inspire_common:ResourceLocator>\n      "
1727
                             "<inspire_common:URL>%s</inspire_common:URL>\n    "
1728
                             "</inspire_common:ResourceLocator>\n",
1729
                             NULL);
1730
    msIO_fprintf(
21✔
1731
        stream,
1732
        "    "
1733
        "<inspire_common:ResourceType>service</inspire_common:ResourceType>\n");
1734
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1735
                             "inspire_temporal_reference", OWS_WARN,
1736
                             "    <inspire_common:TemporalReference>\n      "
1737
                             "<inspire_common:DateOfLastRevision>%s</"
1738
                             "inspire_common:DateOfLastRevision>\n    "
1739
                             "</inspire_common:TemporalReference>\n",
1740
                             "");
1741
    msIO_fprintf(stream, "    <inspire_common:Conformity>\n");
21✔
1742
    msIO_fprintf(stream, "      <inspire_common:Specification>\n");
21✔
1743
    msIO_fprintf(stream,
21✔
1744
                 "        <inspire_common:Title>-</inspire_common:Title>\n");
1745
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1746
                             "inspire_temporal_reference", OWS_NOERR,
1747
                             "        "
1748
                             "<inspire_common:DateOfLastRevision>%s</"
1749
                             "inspire_common:DateOfLastRevision>\n",
1750
                             "");
1751
    msIO_fprintf(stream, "      </inspire_common:Specification>\n");
21✔
1752
    msIO_fprintf(
21✔
1753
        stream,
1754
        "      <inspire_common:Degree>notEvaluated</inspire_common:Degree>\n");
1755
    msIO_fprintf(stream, "    </inspire_common:Conformity>\n");
21✔
1756
    msIO_fprintf(stream, "    <inspire_common:MetadataPointOfContact>\n");
21✔
1757
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1758
                             "inspire_mpoc_name", OWS_WARN,
1759
                             "      "
1760
                             "<inspire_common:OrganisationName>%s</"
1761
                             "inspire_common:OrganisationName>\n",
1762
                             "");
1763
    msOWSPrintEncodeMetadata(
21✔
1764
        stream, &(map->web.metadata), namespaces, "inspire_mpoc_email",
1765
        OWS_WARN,
1766
        "      <inspire_common:EmailAddress>%s</inspire_common:EmailAddress>\n",
1767
        "");
1768
    msIO_fprintf(stream, "    </inspire_common:MetadataPointOfContact>\n");
21✔
1769
    msOWSPrintEncodeMetadata(
21✔
1770
        stream, &(map->web.metadata), namespaces, "inspire_metadatadate",
1771
        OWS_WARN,
1772
        "      <inspire_common:MetadataDate>%s</inspire_common:MetadataDate>\n",
1773
        "");
1774
    if (service == OWS_WFS || service == OWS_WCS)
21✔
1775
      msIO_fprintf(stream, "    "
15✔
1776
                           "<inspire_common:SpatialDataServiceType>download</"
1777
                           "inspire_common:SpatialDataServiceType>\n");
1778
    else
1779
      msIO_fprintf(stream, "    "
6✔
1780
                           "<inspire_common:SpatialDataServiceType>view</"
1781
                           "inspire_common:SpatialDataServiceType>\n");
1782
    msOWSPrintEncodeMetadata(
21✔
1783
        stream, &(map->web.metadata), namespaces, "inspire_keyword", OWS_WARN,
1784
        "    <inspire_common:MandatoryKeyword>\n      "
1785
        "<inspire_common:KeywordValue>%s</inspire_common:KeywordValue>\n    "
1786
        "</inspire_common:MandatoryKeyword>\n",
1787
        "");
1788
  } else {
1789
    status = action_if_not_found;
1790
    if (OWS_WARN == action_if_not_found) {
×
UNCOV
1791
      msIO_fprintf(
×
1792
          stream,
1793
          "<!-- WARNING: invalid value '%s' for 'inspire_capabilities', only "
1794
          "'embed' and 'url' are supported. -->\n",
1795
          inspire_capabilities);
1796
    }
1797
  }
1798

1799
  return status;
1800
}
1801

1802
/* msOWSPrintInspireCommonLanguages
1803
**
1804
** Output INSPIRE supported languages block to stream
1805
**
1806
** Returns a status code; MS_NOERR if all OK; action_if_not_found otherwise
1807
*/
1808
int msOWSPrintInspireCommonLanguages(FILE *stream, mapObj *map,
41✔
1809
                                     const char *namespaces,
1810
                                     int action_if_not_found,
1811
                                     const char *validated_language) {
1812
  char *buffer =
1813
      NULL; /* temp variable for malloced strings that will need freeing */
1814
  int status = MS_NOERR;
1815

1816
  char *default_language = msOWSGetLanguageFromList(map, namespaces, NULL);
41✔
1817

1818
  if (validated_language && validated_language[0] && default_language) {
41✔
1819
    msIO_fprintf(stream, "    <inspire_common:SupportedLanguages>\n");
40✔
1820
    msIO_fprintf(
40✔
1821
        stream,
1822
        "      <inspire_common:DefaultLanguage><inspire_common:Language>%s"
1823
        "</inspire_common:Language></inspire_common:DefaultLanguage>\n",
1824
        buffer = msEncodeHTMLEntities(default_language));
40✔
1825
    msFree(buffer);
40✔
1826

1827
    /* append _exclude to our default_language*/
1828
    default_language = static_cast<char *>(msSmallRealloc(
40✔
1829
        default_language, strlen(default_language) + strlen("_exclude") + 1));
40✔
1830
    strcat(default_language, "_exclude");
1831

1832
    msOWSPrintEncodeMetadataList(
40✔
1833
        stream, &(map->web.metadata), namespaces, "languages", NULL, NULL,
1834
        "      <inspire_common:SupportedLanguage><inspire_common:Language>%s"
1835
        "</inspire_common:Language></inspire_common:SupportedLanguage>\n",
1836
        default_language);
1837
    msIO_fprintf(stream, "    </inspire_common:SupportedLanguages>\n");
40✔
1838
    msIO_fprintf(
40✔
1839
        stream,
1840
        "    <inspire_common:ResponseLanguage><inspire_common:Language>%s"
1841
        "</inspire_common:Language></inspire_common:ResponseLanguage>\n",
1842
        validated_language);
1843
  } else {
1844
    status = action_if_not_found;
1845
    if (OWS_WARN == action_if_not_found) {
1✔
1846
      char *pszExpandedMetadataKey =
1847
          msOWSGetExpandedMetadataKey(namespaces, "languages");
1✔
1848
      msIO_fprintf(stream,
1✔
1849
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1850
                   "context. -->\n",
1851
                   pszExpandedMetadataKey);
1852
      msFree(pszExpandedMetadataKey);
1✔
1853
    }
1854
  }
1855

1856
  msFree(default_language);
41✔
1857

1858
  return status;
41✔
1859
}
1860

1861
/*
1862
** msOWSPrintMetadata()
1863
**
1864
** Attempt to output a capability item.  If corresponding metadata is not
1865
** found then one of a number of predefined actions will be taken.
1866
** If a default value is provided and metadata is absent then the
1867
** default will be used.
1868
*/
1869

UNCOV
1870
int msOWSPrintMetadata(FILE *stream, hashTableObj *metadata,
×
1871
                       const char *namespaces, const char *name,
1872
                       int action_if_not_found, const char *format,
1873
                       const char *default_value) {
1874
  const char *value = NULL;
1875
  int status = MS_NOERR;
1876

1877
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)) != NULL) {
×
UNCOV
1878
    msIO_fprintf(stream, format, value);
×
1879
  } else {
UNCOV
1880
    if (action_if_not_found == OWS_WARN) {
×
1881
      char *pszExpandedMetadataKey =
1882
          msOWSGetExpandedMetadataKey(namespaces, name);
×
UNCOV
1883
      msIO_fprintf(stream,
×
1884
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1885
                   "context. -->\n",
1886
                   pszExpandedMetadataKey);
UNCOV
1887
      msFree(pszExpandedMetadataKey);
×
1888
      status = action_if_not_found;
1889
    }
1890

1891
    if (default_value)
×
UNCOV
1892
      msIO_fprintf(stream, format, default_value);
×
1893
  }
1894

UNCOV
1895
  return status;
×
1896
}
1897

1898
/*
1899
** msOWSPrintEncodeMetadata()
1900
**
1901
** Attempt to output a capability item.  If corresponding metadata is not
1902
** found then one of a number of predefined actions will be taken.
1903
** If a default value is provided and metadata is absent then the
1904
** default will be used.
1905
** Also encode the value with msEncodeHTMLEntities.
1906
*/
1907

1908
int msOWSPrintEncodeMetadata(FILE *stream, hashTableObj *metadata,
1,442✔
1909
                             const char *namespaces, const char *name,
1910
                             int action_if_not_found, const char *format,
1911
                             const char *default_value) {
1912
  return msOWSPrintEncodeMetadata2(stream, metadata, namespaces, name,
1,442✔
1913
                                   action_if_not_found, format, default_value,
1914
                                   NULL);
1,442✔
1915
}
1916

1917
/*
1918
** msOWSPrintEncodeMetadata2()
1919
**
1920
** Attempt to output a capability item in the requested language.
1921
** Fallback using no language parameter.
1922
*/
1923
int msOWSPrintEncodeMetadata2(FILE *stream, hashTableObj *metadata,
2,221✔
1924
                              const char *namespaces, const char *name,
1925
                              int action_if_not_found, const char *format,
1926
                              const char *default_value,
1927
                              const char *validated_language) {
1928
  const char *value;
1929
  char *pszEncodedValue = NULL;
1930
  int status = MS_NOERR;
1931

1932
  if ((value = msOWSLookupMetadataWithLanguage(metadata, namespaces, name,
2,221✔
1933
                                               validated_language))) {
1934
    pszEncodedValue = msEncodeHTMLEntities(value);
1,648✔
1935
    msIO_fprintf(stream, format, pszEncodedValue);
1,648✔
1936
    free(pszEncodedValue);
1,648✔
1937
  } else {
1938
    if (action_if_not_found == OWS_WARN) {
573✔
1939
      char *pszExpandedName = msStringConcatenate(NULL, name);
57✔
1940
      if (validated_language && validated_language[0]) {
57✔
UNCOV
1941
        pszExpandedName = msStringConcatenate(pszExpandedName, ".");
×
1942
        pszExpandedName =
UNCOV
1943
            msStringConcatenate(pszExpandedName, validated_language);
×
1944
      }
1945
      char *pszExpandedMetadataKey =
1946
          msOWSGetExpandedMetadataKey(namespaces, pszExpandedName);
57✔
1947
      msIO_fprintf(stream,
57✔
1948
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1949
                   "context. -->\n",
1950
                   pszExpandedMetadataKey);
1951
      msFree(pszExpandedName);
57✔
1952
      msFree(pszExpandedMetadataKey);
57✔
1953
      status = action_if_not_found;
1954
    }
1955

1956
    if (default_value) {
573✔
1957
      pszEncodedValue = msEncodeHTMLEntities(default_value);
218✔
1958
      msIO_fprintf(stream, format, default_value);
218✔
1959
      free(pszEncodedValue);
218✔
1960
    }
1961
  }
1962

1963
  return status;
2,221✔
1964
}
1965

1966
/*
1967
** msOWSGetEncodeMetadata()
1968
**
1969
** Equivalent to msOWSPrintEncodeMetadata. Returns en encoded value of the
1970
** metadata or the default value.
1971
** Caller should free the returned string.
1972
*/
1973
char *msOWSGetEncodeMetadata(hashTableObj *metadata, const char *namespaces,
402✔
1974
                             const char *name, const char *default_value) {
1975
  const char *value;
1976
  char *pszEncodedValue = NULL;
1977
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)))
402✔
1978
    pszEncodedValue = msEncodeHTMLEntities(value);
53✔
1979
  else if (default_value)
349✔
1980
    pszEncodedValue = msEncodeHTMLEntities(default_value);
290✔
1981

1982
  return pszEncodedValue;
402✔
1983
}
1984

1985
/*
1986
** msOWSPrintValidateMetadata()
1987
**
1988
** Attempt to output a capability item.  If corresponding metadata is not
1989
** found then one of a number of predefined actions will be taken.
1990
** If a default value is provided and metadata is absent then the
1991
** default will be used.
1992
** Also validate the value with msIsXMLTagValid.
1993
*/
1994

1995
int msOWSPrintValidateMetadata(FILE *stream, hashTableObj *metadata,
310✔
1996
                               const char *namespaces, const char *name,
1997
                               int action_if_not_found, const char *format,
1998
                               const char *default_value) {
1999
  const char *value;
2000
  int status = MS_NOERR;
2001

2002
  if ((value = msOWSLookupMetadata(metadata, namespaces, name))) {
310✔
2003
    if (msIsXMLTagValid(value) == MS_FALSE)
×
UNCOV
2004
      msIO_fprintf(stream,
×
2005
                   "<!-- WARNING: The value '%s' is not valid in a "
2006
                   "XML tag context. -->\n",
2007
                   value);
UNCOV
2008
    msIO_fprintf(stream, format, value);
×
2009
  } else {
2010
    if (action_if_not_found == OWS_WARN) {
310✔
2011
      char *pszExpandedMetadataKey =
2012
          msOWSGetExpandedMetadataKey(namespaces, name);
×
UNCOV
2013
      msIO_fprintf(stream,
×
2014
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
2015
                   "context. -->\n",
2016
                   pszExpandedMetadataKey);
UNCOV
2017
      msFree(pszExpandedMetadataKey);
×
2018
      status = action_if_not_found;
2019
    }
2020

2021
    if (default_value) {
310✔
2022
      if (msIsXMLTagValid(default_value) == MS_FALSE)
310✔
UNCOV
2023
        msIO_fprintf(stream,
×
2024
                     "<!-- WARNING: The value '%s' is not valid "
2025
                     "in a XML tag context. -->\n",
2026
                     default_value);
2027
      msIO_fprintf(stream, format, default_value);
310✔
2028
    }
2029
  }
2030

2031
  return status;
310✔
2032
}
2033

2034
/*
2035
** msOWSPrintGroupMetadata()
2036
**
2037
** Attempt to output a capability item.  If corresponding metadata is not
2038
** found then one of a number of predefined actions will be taken.
2039
** If a default value is provided and metadata is absent then the
2040
** default will be used.
2041
*/
UNCOV
2042
int msOWSPrintGroupMetadata(FILE *stream, mapObj *map, char *pszGroupName,
×
2043
                            const char *namespaces, const char *name,
2044
                            int action_if_not_found, const char *format,
2045
                            const char *default_value) {
UNCOV
2046
  return msOWSPrintGroupMetadata2(stream, map, pszGroupName, namespaces, name,
×
2047
                                  action_if_not_found, format, default_value,
UNCOV
2048
                                  NULL);
×
2049
}
2050

2051
/*
2052
** msOWSPrintGroupMetadata2()
2053
**
2054
** Attempt to output a capability item in the requested language.
2055
** Fallback using no language parameter.
2056
*/
UNCOV
2057
int msOWSPrintGroupMetadata2(FILE *stream, mapObj *map, char *pszGroupName,
×
2058
                             const char *namespaces, const char *name,
2059
                             int action_if_not_found, const char *format,
2060
                             const char *default_value,
2061
                             const char *validated_language) {
2062
  const char *value;
2063
  char *encoded;
2064
  int status = MS_NOERR;
2065
  int i;
2066

2067
  for (i = 0; i < map->numlayers; i++) {
×
2068
    if (GET_LAYER(map, i)->group &&
×
2069
        (strcmp(GET_LAYER(map, i)->group, pszGroupName) == 0)) {
×
UNCOV
2070
      if ((value = msOWSLookupMetadataWithLanguage(
×
2071
               &(GET_LAYER(map, i)->metadata), namespaces, name,
2072
               validated_language))) {
2073
        encoded = msEncodeHTMLEntities(value);
×
2074
        msIO_fprintf(stream, format, encoded);
×
2075
        msFree(encoded);
×
UNCOV
2076
        return status;
×
2077
      }
2078
    }
2079
  }
2080

UNCOV
2081
  if (action_if_not_found == OWS_WARN) {
×
2082
    char *pszExpandedMetadataKey =
2083
        msOWSGetExpandedMetadataKey(namespaces, name);
×
UNCOV
2084
    msIO_fprintf(stream,
×
2085
                 "<!-- WARNING: Mandatory metadata %s was missing in this "
2086
                 "context. -->\n",
2087
                 pszExpandedMetadataKey);
UNCOV
2088
    msFree(pszExpandedMetadataKey);
×
2089
    status = action_if_not_found;
2090
  }
2091

2092
  if (default_value) {
×
2093
    encoded = msEncodeHTMLEntities(default_value);
×
2094
    msIO_fprintf(stream, format, encoded);
×
UNCOV
2095
    msFree(encoded);
×
2096
  }
2097

2098
  return status;
2099
}
2100

2101
/* msOWSPrintURLType()
2102
**
2103
** Attempt to output a URL item in capabilities.  If corresponding metadata
2104
** is not found then one of a number of predefined actions will be taken.
2105
** Since it's a capability item, five metadata will be used to populate the
2106
** XML elements.
2107
**
2108
** The 'name' argument is the basename of the metadata items relating to this
2109
** URL type and the suffixes _type, _width, _height, _format and _href will
2110
** be appended to the name in the metadata search.
2111
** e.g. passing name=metadataurl will result in the following medata entries
2112
** being used:
2113
**    ows_metadataurl_type
2114
**    ows_metadataurl_format
2115
**    ows_metadataurl_href
2116
**    ... (width and height are unused for metadata)
2117
**
2118
** As for all the msOWSPrint*() functions, the namespace argument specifies
2119
** which prefix (ows_, wms_, wcs_, etc.) is used for the metadata names above.
2120
**
2121
** Then the final string will be built from
2122
** the tag_name and the five metadata. The template is:
2123
** <tag_name%type%width%height%format>%href</tag_name>
2124
**
2125
** For example the width format will usually be " width=\"%s\"".
2126
** An extern format will be "> <Format>%s</Format"
2127
**
2128
** Another template template may be used, but it needs to contains 5 %s,
2129
** otherwise leave it to NULL. If tag_format is used then you don't need the
2130
** tag_name and the tabspace.
2131
**
2132
** Note that all values will be HTML-encoded.
2133
**/
2134
int msOWSPrintURLType(FILE *stream, hashTableObj *metadata,
721✔
2135
                      const char *namespaces, const char *name,
2136
                      int action_if_not_found, const char *tag_format,
2137
                      const char *tag_name, const char *type_format,
2138
                      const char *width_format, const char *height_format,
2139
                      const char *urlfrmt_format, const char *href_format,
2140
                      int type_is_mandatory, int width_is_mandatory,
2141
                      int height_is_mandatory, int format_is_mandatory,
2142
                      int href_is_mandatory, const char *default_type,
2143
                      const char *default_width, const char *default_height,
2144
                      const char *default_urlfrmt, const char *default_href,
2145
                      const char *tabspace) {
2146
  const char *value;
2147
  char *metadata_name;
2148
  char *encoded;
2149
  int status = MS_NOERR;
2150
  char *type = NULL, *width = NULL, *height = NULL, *urlfrmt = NULL,
2151
       *href = NULL;
2152

2153
  const size_t buffer_size = strlen(name) + 10;
721✔
2154
  metadata_name = (char *)malloc(buffer_size);
721✔
2155

2156
  /* Get type */
2157
  if (type_format != NULL) {
721✔
2158
    snprintf(metadata_name, buffer_size, "%s_type", name);
2159
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
301✔
2160
    if (value != NULL) {
301✔
2161
      encoded = msEncodeHTMLEntities(value);
236✔
2162
      const size_t buffer_size_tmp = strlen(type_format) + strlen(encoded) + 1;
236✔
2163
      type = (char *)malloc(buffer_size_tmp);
236✔
2164
      snprintf(type, buffer_size_tmp, type_format, encoded);
2165
      msFree(encoded);
236✔
2166
    }
2167
  }
2168

2169
  /* Get width */
2170
  if (width_format != NULL) {
721✔
2171
    snprintf(metadata_name, buffer_size, "%s_width", name);
2172
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
175✔
2173
    if (value != NULL) {
175✔
2174
      encoded = msEncodeHTMLEntities(value);
19✔
2175
      const size_t buffer_size_tmp = strlen(width_format) + strlen(encoded) + 1;
19✔
2176
      width = (char *)malloc(buffer_size_tmp);
19✔
2177
      snprintf(width, buffer_size_tmp, width_format, encoded);
2178
      msFree(encoded);
19✔
2179
    }
2180
  }
2181

2182
  /* Get height */
2183
  if (height_format != NULL) {
721✔
2184
    snprintf(metadata_name, buffer_size, "%s_height", name);
2185
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
175✔
2186
    if (value != NULL) {
175✔
2187
      encoded = msEncodeHTMLEntities(value);
19✔
2188
      const size_t buffer_size_tmp =
19✔
2189
          strlen(height_format) + strlen(encoded) + 1;
19✔
2190
      height = (char *)malloc(buffer_size_tmp);
19✔
2191
      snprintf(height, buffer_size_tmp, height_format, encoded);
2192
      msFree(encoded);
19✔
2193
    }
2194
  }
2195

2196
  /* Get format */
2197
  if (urlfrmt_format != NULL) {
721✔
2198
    snprintf(metadata_name, buffer_size, "%s_format", name);
2199
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
692✔
2200
    if (value != NULL) {
692✔
2201
      encoded = msEncodeHTMLEntities(value);
249✔
2202
      const size_t buffer_size_tmp =
249✔
2203
          strlen(urlfrmt_format) + strlen(encoded) + 1;
249✔
2204
      urlfrmt = (char *)malloc(buffer_size_tmp);
249✔
2205
      snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, encoded);
2206
      msFree(encoded);
249✔
2207
    }
2208
  }
2209

2210
  /* Get href */
2211
  if (href_format != NULL) {
721✔
2212
    snprintf(metadata_name, buffer_size, "%s_href", name);
2213
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
721✔
2214
    if (value != NULL) {
721✔
2215
      encoded = msEncodeHTMLEntities(value);
273✔
2216
      const size_t buffer_size_tmp = strlen(href_format) + strlen(encoded) + 1;
273✔
2217
      href = (char *)malloc(buffer_size_tmp);
273✔
2218
      snprintf(href, buffer_size_tmp, href_format, encoded);
2219
      msFree(encoded);
273✔
2220
    }
2221
  }
2222

2223
  msFree(metadata_name);
721✔
2224

2225
  if (type || width || height || urlfrmt || href ||
721✔
2226
      (!metadata && (default_type || default_width || default_height ||
156✔
UNCOV
2227
                     default_urlfrmt || default_href))) {
×
2228
    if ((!type && type_is_mandatory) || (!width && width_is_mandatory) ||
429✔
2229
        (!height && height_is_mandatory) || (!urlfrmt && format_is_mandatory) ||
429✔
2230
        (!href && href_is_mandatory)) {
429✔
UNCOV
2231
      msIO_fprintf(stream,
×
2232
                   "<!-- WARNING: Some mandatory elements for '%s' are missing "
2233
                   "in this context. -->\n",
2234
                   tag_name);
UNCOV
2235
      if (action_if_not_found == OWS_WARN) {
×
2236
        char *pszExpandedMetadataKey =
2237
            msOWSGetExpandedMetadataKey(namespaces, name);
×
UNCOV
2238
        msIO_fprintf(stream,
×
2239
                     "<!-- WARNING: Mandatory metadata %s was missing in this "
2240
                     "context. -->\n",
2241
                     pszExpandedMetadataKey);
UNCOV
2242
        msFree(pszExpandedMetadataKey);
×
2243
        status = action_if_not_found;
2244
      }
2245
    } else {
2246
      if (!type && type_format && default_type) {
429✔
2247
        const size_t buffer_size_tmp =
13✔
2248
            strlen(type_format) + strlen(default_type) + 2;
13✔
2249
        type = (char *)malloc(buffer_size_tmp);
13✔
2250
        snprintf(type, buffer_size_tmp, type_format, default_type);
2251
      } else if (!type)
416✔
2252
        type = msStrdup("");
180✔
2253
      if (!width && width_format && default_width) {
429✔
2254
        const size_t buffer_size_tmp =
156✔
2255
            strlen(width_format) + strlen(default_width) + 2;
156✔
2256
        width = (char *)malloc(buffer_size_tmp);
156✔
2257
        snprintf(width, buffer_size_tmp, width_format, default_width);
2258
      } else if (!width)
273✔
2259
        width = msStrdup("");
254✔
2260
      if (!height && height_format && default_height) {
429✔
2261
        const size_t buffer_size_tmp =
156✔
2262
            strlen(height_format) + strlen(default_height) + 2;
156✔
2263
        height = (char *)malloc(buffer_size_tmp);
156✔
2264
        snprintf(height, buffer_size_tmp, height_format, default_height);
2265
      } else if (!height)
273✔
2266
        height = msStrdup("");
254✔
2267
      if (!urlfrmt && urlfrmt_format && default_urlfrmt) {
429✔
2268
        const size_t buffer_size_tmp =
156✔
2269
            strlen(urlfrmt_format) + strlen(default_urlfrmt) + 2;
156✔
2270
        urlfrmt = (char *)malloc(buffer_size_tmp);
156✔
2271
        snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, default_urlfrmt);
2272
      } else if (!urlfrmt)
273✔
2273
        urlfrmt = msStrdup("");
24✔
2274
      if (!href && href_format && default_href) {
429✔
2275
        const size_t buffer_size_tmp =
156✔
2276
            strlen(href_format) + strlen(default_href) + 2;
156✔
2277
        href = (char *)malloc(buffer_size_tmp);
156✔
2278
        snprintf(href, buffer_size_tmp, href_format, default_href);
2279
      } else if (!href)
273✔
UNCOV
2280
        href = msStrdup("");
×
2281

2282
      if (tag_format == NULL)
429✔
2283
        msIO_fprintf(stream, "%s<%s%s%s%s%s>%s</%s>\n", tabspace, tag_name,
405✔
2284
                     type, width, height, urlfrmt, href, tag_name);
2285
      else
2286
        msIO_fprintf(stream, tag_format, type, width, height, urlfrmt, href);
24✔
2287
    }
2288

2289
    msFree(type);
429✔
2290
    msFree(width);
429✔
2291
    msFree(height);
429✔
2292
    msFree(urlfrmt);
429✔
2293
    msFree(href);
429✔
2294
  } else {
429✔
2295
    if (action_if_not_found == OWS_WARN) {
292✔
2296
      char *pszExpandedMetadataKey =
2297
          msOWSGetExpandedMetadataKey(namespaces, name);
×
UNCOV
2298
      msIO_fprintf(stream,
×
2299
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
2300
                   "context. -->\n",
2301
                   pszExpandedMetadataKey);
UNCOV
2302
      msFree(pszExpandedMetadataKey);
×
2303
      status = action_if_not_found;
2304
    }
2305
  }
2306

2307
  return status;
721✔
2308
}
2309

2310
/* msOWSPrintParam()
2311
**
2312
** Same as printMetadata() but applied to mapfile parameters.
2313
**/
UNCOV
2314
int msOWSPrintParam(FILE *stream, const char *name, const char *value,
×
2315
                    int action_if_not_found, const char *format,
2316
                    const char *default_value) {
2317
  int status = MS_NOERR;
2318

2319
  if (value && strlen(value) > 0) {
×
UNCOV
2320
    msIO_fprintf(stream, format, value);
×
2321
  } else {
2322
    if (action_if_not_found == OWS_WARN) {
×
UNCOV
2323
      msIO_fprintf(stream,
×
2324
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2325
                   "in this context. -->\n",
2326
                   name);
2327
      status = action_if_not_found;
2328
    }
2329

2330
    if (default_value)
×
UNCOV
2331
      msIO_fprintf(stream, format, default_value);
×
2332
  }
2333

UNCOV
2334
  return status;
×
2335
}
2336

2337
/* msOWSPrintEncodeParam()
2338
**
2339
** Same as printEncodeMetadata() but applied to mapfile parameters.
2340
**/
2341
int msOWSPrintEncodeParam(FILE *stream, const char *name, const char *value,
318✔
2342
                          int action_if_not_found, const char *format,
2343
                          const char *default_value) {
2344
  int status = MS_NOERR;
2345
  char *encode;
2346

2347
  if (value && strlen(value) > 0) {
318✔
2348
    encode = msEncodeHTMLEntities(value);
318✔
2349
    msIO_fprintf(stream, format, encode);
318✔
2350
    msFree(encode);
318✔
2351
  } else {
2352
    if (action_if_not_found == OWS_WARN) {
×
UNCOV
2353
      msIO_fprintf(stream,
×
2354
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2355
                   "in this context. -->\n",
2356
                   name);
2357
      status = action_if_not_found;
2358
    }
2359

2360
    if (default_value) {
×
2361
      encode = msEncodeHTMLEntities(default_value);
×
2362
      msIO_fprintf(stream, format, encode);
×
UNCOV
2363
      msFree(encode);
×
2364
    }
2365
  }
2366

2367
  return status;
318✔
2368
}
2369

2370
/* msOWSPrintMetadataList()
2371
**
2372
** Prints comma-separated lists metadata.  (e.g. keywordList)
2373
** default_value serves 2 purposes if specified:
2374
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2375
**  (exclusion)
2376
** - will be printed if MetadataList is empty (fallback)
2377
**/
UNCOV
2378
int msOWSPrintMetadataList(FILE *stream, hashTableObj *metadata,
×
2379
                           const char *namespaces, const char *name,
2380
                           const char *startTag, const char *endTag,
2381
                           const char *itemFormat, const char *default_value) {
2382
  const char *value;
2383

UNCOV
2384
  value = msOWSLookupMetadata(metadata, namespaces, name);
×
2385

UNCOV
2386
  if (value == NULL) {
×
2387
    value = default_value;
2388
    default_value = NULL;
2389
  }
2390

UNCOV
2391
  if (value != NULL) {
×
2392
    char **keywords;
2393
    int numkeywords;
2394

2395
    keywords = msStringSplit(value, ',', &numkeywords);
×
UNCOV
2396
    if (keywords && numkeywords > 0) {
×
2397
      int kw;
2398
      if (startTag)
×
2399
        msIO_fprintf(stream, "%s", startTag);
×
2400
      for (kw = 0; kw < numkeywords; kw++) {
×
2401
        if (default_value != NULL &&
×
2402
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
×
2403
                0 &&
×
UNCOV
2404
            strncasecmp("_exclude", default_value + strlen(default_value) - 8,
×
2405
                        8) == 0)
UNCOV
2406
          continue;
×
2407

UNCOV
2408
        msIO_fprintf(stream, itemFormat, keywords[kw]);
×
2409
      }
2410
      if (endTag)
×
UNCOV
2411
        msIO_fprintf(stream, "%s", endTag);
×
2412
    }
UNCOV
2413
    msFreeCharArray(keywords, numkeywords);
×
2414
    return MS_TRUE;
2415
  }
2416
  return MS_FALSE;
2417
}
2418

2419
/* msOWSPrintEncodeMetadataList()
2420
**
2421
** Prints comma-separated lists metadata.  (e.g. keywordList)
2422
** This will print HTML encoded values.
2423
** default_value serves 2 purposes if specified:
2424
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2425
**  (exclusion)
2426
** - will be printed if MetadataList is empty (fallback)
2427
**/
2428
int msOWSPrintEncodeMetadataList(FILE *stream, hashTableObj *metadata,
520✔
2429
                                 const char *namespaces, const char *name,
2430
                                 const char *startTag, const char *endTag,
2431
                                 const char *itemFormat,
2432
                                 const char *default_value) {
2433
  const char *value;
2434
  char *encoded;
2435
  size_t default_value_len = 0;
2436

2437
  value = msOWSLookupMetadata(metadata, namespaces, name);
520✔
2438

2439
  if (value == NULL) {
520✔
2440
    value = default_value;
2441
    default_value = NULL;
2442
  }
2443
  if (default_value)
270✔
2444
    default_value_len = strlen(default_value);
47✔
2445

2446
  if (value != NULL) {
520✔
2447
    char **keywords;
2448
    int numkeywords;
2449

2450
    keywords = msStringSplit(value, ',', &numkeywords);
274✔
2451
    if (keywords && numkeywords > 0) {
274✔
2452
      int kw;
2453
      if (startTag)
274✔
2454
        msIO_fprintf(stream, "%s", startTag);
46✔
2455
      for (kw = 0; kw < numkeywords; kw++) {
808✔
2456
        if (default_value != NULL && default_value_len > 8 &&
534✔
2457
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
103✔
2458
                0 &&
80✔
2459
            strncasecmp("_exclude", default_value + default_value_len - 8, 8) ==
40✔
2460
                0)
2461
          continue;
40✔
2462

2463
        encoded = msEncodeHTMLEntities(keywords[kw]);
494✔
2464
        msIO_fprintf(stream, itemFormat, encoded);
494✔
2465
        msFree(encoded);
494✔
2466
      }
2467
      if (endTag)
274✔
2468
        msIO_fprintf(stream, "%s", endTag);
46✔
2469
    }
2470
    msFreeCharArray(keywords, numkeywords);
274✔
2471
    return MS_TRUE;
2472
  }
2473
  return MS_FALSE;
2474
}
2475

2476
/* msOWSPrintEncodeParamList()
2477
**
2478
** Same as msOWSPrintEncodeMetadataList() but applied to mapfile parameters.
2479
**/
2480
int msOWSPrintEncodeParamList(FILE *stream, const char *name, const char *value,
224✔
2481
                              int action_if_not_found, char delimiter,
2482
                              const char *startTag, const char *endTag,
2483
                              const char *format, const char *default_value) {
2484
  int status = MS_NOERR;
2485
  char *encoded;
2486
  char **items = NULL;
2487
  int numitems = 0, i;
224✔
2488

2489
  if (value && strlen(value) > 0)
224✔
2490
    items = msStringSplit(value, delimiter, &numitems);
166✔
2491
  else {
2492
    if (action_if_not_found == OWS_WARN) {
58✔
UNCOV
2493
      msIO_fprintf(stream,
×
2494
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2495
                   "in this context. -->\n",
2496
                   name);
2497
      status = action_if_not_found;
2498
    }
2499

2500
    if (default_value)
58✔
UNCOV
2501
      items = msStringSplit(default_value, delimiter, &numitems);
×
2502
  }
2503

2504
  if (items && numitems > 0) {
166✔
2505
    if (startTag)
166✔
UNCOV
2506
      msIO_fprintf(stream, "%s", startTag);
×
2507
    for (i = 0; i < numitems; i++) {
476✔
2508
      encoded = msEncodeHTMLEntities(items[i]);
310✔
2509
      msIO_fprintf(stream, format, encoded);
310✔
2510
      msFree(encoded);
310✔
2511
    }
2512
    if (endTag)
166✔
UNCOV
2513
      msIO_fprintf(stream, "%s", endTag);
×
2514
  }
2515
  msFreeCharArray(items, numitems);
224✔
2516

2517
  return status;
224✔
2518
}
2519

2520
/*
2521
** msOWSPrintEX_GeographicBoundingBox()
2522
**
2523
** Print a EX_GeographicBoundingBox tag for WMS1.3.0
2524
**
2525
*/
2526
void msOWSPrintEX_GeographicBoundingBox(FILE *stream, const char *tabspace,
119✔
2527
                                        rectObj *extent, projectionObj *srcproj)
2528

2529
{
2530
  const char *pszTag = "EX_GeographicBoundingBox"; /* The default for WMS */
2531
  rectObj ext;
2532

2533
  ext = *extent;
119✔
2534

2535
  /* always project to lat long */
2536
  msOWSProjectToWGS84(srcproj, &ext);
119✔
2537

2538
  msIO_fprintf(stream, "%s<%s>\n", tabspace, pszTag);
119✔
2539
  msIO_fprintf(stream, "%s    <westBoundLongitude>%.6f</westBoundLongitude>\n",
119✔
2540
               tabspace, ext.minx);
2541
  msIO_fprintf(stream, "%s    <eastBoundLongitude>%.6f</eastBoundLongitude>\n",
119✔
2542
               tabspace, ext.maxx);
2543
  msIO_fprintf(stream, "%s    <southBoundLatitude>%.6f</southBoundLatitude>\n",
119✔
2544
               tabspace, ext.miny);
2545
  msIO_fprintf(stream, "%s    <northBoundLatitude>%.6f</northBoundLatitude>\n",
119✔
2546
               tabspace, ext.maxy);
2547
  msIO_fprintf(stream, "%s</%s>\n", tabspace, pszTag);
119✔
2548

2549
  /* msIO_fprintf(stream, "%s<%s minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"
2550
     />\n", tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy); */
2551
}
119✔
2552

2553
/*
2554
** msOWSPrintLatLonBoundingBox()
2555
**
2556
** Print a LatLonBoundingBox tag for WMS, or LatLongBoundingBox for WFS
2557
** ... yes, the tag name differs between WMS and WFS, yuck!
2558
**
2559
*/
2560
void msOWSPrintLatLonBoundingBox(FILE *stream, const char *tabspace,
140✔
2561
                                 rectObj *extent, projectionObj *srcproj,
2562
                                 projectionObj *wfsproj,
2563
                                 OWSServiceType nService) {
2564
  const char *pszTag = "LatLonBoundingBox"; /* The default for WMS */
2565
  rectObj ext;
2566

2567
  ext = *extent;
140✔
2568

2569
  if (nService == OWS_WMS) { /* always project to lat long */
140✔
2570
    msOWSProjectToWGS84(srcproj, &ext);
114✔
2571
  } else if (nService == OWS_WFS) { /* called from wfs 1.0.0 only: project to
26✔
2572
                                       map srs, if set */
2573
    pszTag = "LatLongBoundingBox";
2574
    if (wfsproj) {
26✔
2575
      if (msProjectionsDiffer(srcproj, wfsproj) == MS_TRUE)
26✔
2576
        msProjectRect(srcproj, wfsproj, &ext);
17✔
2577
    }
2578
  }
2579

2580
  msIO_fprintf(
140✔
2581
      stream,
2582
      "%s<%s minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" maxy=\"%.6f\" />\n",
2583
      tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy);
2584
}
140✔
2585

2586
/*
2587
** Emit a bounding box if we can find projection information.
2588
** If <namespaces>_bbox_extended is not set, emit a single bounding box
2589
** using the layer's native SRS (ignoring any <namespaces>_srs metadata).
2590
**
2591
** If <namespaces>_bbox_extended is set to true, emit a bounding box
2592
** for every projection listed in the <namespaces>_srs list.
2593
** Check the map level metadata for both _bbox_extended and _srs,
2594
** if there is no such metadata at the layer level.
2595
** (These settings make more sense at the global/map level anyways)
2596
*/
2597
void msOWSPrintBoundingBox(FILE *stream, const char *tabspace, rectObj *extent,
233✔
2598
                           projectionObj *srcproj, hashTableObj *layer_meta,
2599
                           hashTableObj *map_meta, const char *namespaces,
2600
                           int wms_version) {
2601
  const char *value, *resx, *resy, *wms_bbox_extended;
2602
  char *encoded, *encoded_resx, *encoded_resy, *epsg_str;
2603
  char **epsgs;
2604
  int i, num_epsgs;
2605
  projectionObj proj;
2606
  rectObj ext;
2607

2608
  wms_bbox_extended =
2609
      msOWSLookupMetadata2(layer_meta, map_meta, namespaces, "bbox_extended");
233✔
2610
  if (wms_bbox_extended && strncasecmp(wms_bbox_extended, "true", 5) == 0) {
233✔
2611
    /* get a list of all projections from the metadata
2612
       try the layer metadata first, otherwise use the map's */
2613
    if (msOWSLookupMetadata(layer_meta, namespaces, "srs")) {
126✔
UNCOV
2614
      msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_FALSE, &epsg_str);
×
2615
    } else {
2616
      msOWSGetEPSGProj(srcproj, map_meta, namespaces, MS_FALSE, &epsg_str);
126✔
2617
    }
2618
    epsgs = msStringSplit(epsg_str, ' ', &num_epsgs);
126✔
2619
    msFree(epsg_str);
126✔
2620
  } else {
2621
    /* Look for EPSG code in PROJECTION block only.  "wms_srs" metadata cannot
2622
     * be used to establish the native projection of a layer for BoundingBox
2623
     * purposes.
2624
     */
2625
    epsgs = (char **)msSmallMalloc(sizeof(char *));
107✔
2626
    num_epsgs = 1;
107✔
2627
    msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_TRUE, &(epsgs[0]));
107✔
2628
  }
2629

2630
  for (i = 0; i < num_epsgs; i++) {
1,798✔
2631
    value = epsgs[i];
1,565✔
2632
    if (value && *value) {
1,565✔
2633
      memcpy(&ext, extent, sizeof(rectObj));
2634

2635
      /* reproject the extents for each SRS's bounding box */
2636
      msInitProjection(&proj);
1,553✔
2637
      msProjectionInheritContextFrom(&proj, srcproj);
1,553✔
2638
      if (msLoadProjectionStringEPSG(&proj, (char *)value) == 0) {
1,553✔
2639
        if (msProjectionsDiffer(srcproj, &proj) == MS_TRUE) {
1,535✔
2640
          msProjectRect(srcproj, &proj, &ext);
1,447✔
2641
        }
2642
        /*for wms 1.3.0 we need to make sure that we present the BBOX with
2643
          a reversed axes for some espg codes*/
2644
        if (wms_version >= OWS_1_3_0 && strncasecmp(value, "EPSG:", 5) == 0) {
1,535✔
2645
          msAxisNormalizePoints(&proj, 1, &(ext.minx), &(ext.miny));
781✔
2646
          msAxisNormalizePoints(&proj, 1, &(ext.maxx), &(ext.maxy));
781✔
2647
        }
2648
      }
2649

2650
      encoded = msEncodeHTMLEntities(value);
1,553✔
2651
      if (msProjIsGeographicCRS(&proj))
1,553✔
2652
        msIO_fprintf(stream,
588✔
2653
                     "%s<BoundingBox %s=\"%s\"\n"
2654
                     "%s            minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" "
2655
                     "maxy=\"%.6f\"",
2656
                     tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS",
2657
                     encoded, tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2658
      else
2659
        msIO_fprintf(
1,726✔
2660
            stream,
2661
            "%s<BoundingBox %s=\"%s\"\n"
2662
            "%s            minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"",
2663
            tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS", encoded,
2664
            tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2665

2666
      msFree(encoded);
1,553✔
2667
      msFreeProjection(&proj);
1,553✔
2668

2669
      if ((resx = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resx")) !=
1,553✔
2670
              NULL &&
1,555✔
2671
          (resy = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resy")) !=
2✔
2672
              NULL) {
2673
        encoded_resx = msEncodeHTMLEntities(resx);
2✔
2674
        encoded_resy = msEncodeHTMLEntities(resy);
2✔
2675
        msIO_fprintf(stream, "\n%s            resx=\"%s\" resy=\"%s\"",
2✔
2676
                     tabspace, encoded_resx, encoded_resy);
2677
        msFree(encoded_resx);
2✔
2678
        msFree(encoded_resy);
2✔
2679
      }
2680

2681
      msIO_fprintf(stream, " />\n");
1,553✔
2682
    }
2683
  }
2684
  msFreeCharArray(epsgs, num_epsgs);
233✔
2685
}
233✔
2686

2687
/*
2688
** Print the contact information
2689
*/
2690
void msOWSPrintContactInfo(FILE *stream, const char *tabspace, int nVersion,
62✔
2691
                           hashTableObj *metadata, const char *namespaces) {
2692
  /* contact information is a required element in 1.0.7 but the */
2693
  /* sub-elements such as ContactPersonPrimary, etc. are not! */
2694
  /* In 1.1.0, ContactInformation becomes optional. */
2695
  if (nVersion > OWS_1_0_0) {
62✔
2696
    msIO_fprintf(stream, "%s<ContactInformation>\n", tabspace);
60✔
2697

2698
    /* ContactPersonPrimary is optional, but when present then all its  */
2699
    /* sub-elements are mandatory */
2700

2701
    if (msOWSLookupMetadata(metadata, namespaces, "contactperson") ||
86✔
2702
        msOWSLookupMetadata(metadata, namespaces, "contactorganization")) {
26✔
2703
      msIO_fprintf(stream, "%s  <ContactPersonPrimary>\n", tabspace);
34✔
2704

2705
      msOWSPrintEncodeMetadata(
34✔
2706
          stream, metadata, namespaces, "contactperson", OWS_WARN,
2707
          "      <ContactPerson>%s</ContactPerson>\n", NULL);
2708
      msOWSPrintEncodeMetadata(
34✔
2709
          stream, metadata, namespaces, "contactorganization", OWS_WARN,
2710
          "      <ContactOrganization>%s</ContactOrganization>\n", NULL);
2711
      msIO_fprintf(stream, "%s  </ContactPersonPrimary>\n", tabspace);
34✔
2712
    }
2713

2714
    if (msOWSLookupMetadata(metadata, namespaces, "contactposition")) {
60✔
2715
      msOWSPrintEncodeMetadata(
34✔
2716
          stream, metadata, namespaces, "contactposition", OWS_NOERR,
2717
          "      <ContactPosition>%s</ContactPosition>\n", NULL);
2718
    }
2719

2720
    /* ContactAddress is optional, but when present then all its  */
2721
    /* sub-elements are mandatory */
2722
    if (msOWSLookupMetadata(metadata, namespaces, "addresstype") ||
86✔
2723
        msOWSLookupMetadata(metadata, namespaces, "address") ||
52✔
2724
        msOWSLookupMetadata(metadata, namespaces, "city") ||
52✔
2725
        msOWSLookupMetadata(metadata, namespaces, "stateorprovince") ||
52✔
2726
        msOWSLookupMetadata(metadata, namespaces, "postcode") ||
112✔
2727
        msOWSLookupMetadata(metadata, namespaces, "country")) {
26✔
2728
      msIO_fprintf(stream, "%s  <ContactAddress>\n", tabspace);
34✔
2729

2730
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "addresstype",
34✔
2731
                               OWS_WARN,
2732
                               "        <AddressType>%s</AddressType>\n", NULL);
2733
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "address",
34✔
2734
                               OWS_WARN, "        <Address>%s</Address>\n",
2735
                               NULL);
2736
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "city", OWS_WARN,
34✔
2737
                               "        <City>%s</City>\n", NULL);
2738
      msOWSPrintEncodeMetadata(
34✔
2739
          stream, metadata, namespaces, "stateorprovince", OWS_WARN,
2740
          "        <StateOrProvince>%s</StateOrProvince>\n", NULL);
2741
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "postcode",
34✔
2742
                               OWS_WARN, "        <PostCode>%s</PostCode>\n",
2743
                               NULL);
2744
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "country",
34✔
2745
                               OWS_WARN, "        <Country>%s</Country>\n",
2746
                               NULL);
2747
      msIO_fprintf(stream, "%s  </ContactAddress>\n", tabspace);
34✔
2748
    }
2749

2750
    if (msOWSLookupMetadata(metadata, namespaces, "contactvoicetelephone")) {
60✔
2751
      msOWSPrintEncodeMetadata(
34✔
2752
          stream, metadata, namespaces, "contactvoicetelephone", OWS_NOERR,
2753
          "      <ContactVoiceTelephone>%s</ContactVoiceTelephone>\n", NULL);
2754
    }
2755

2756
    if (msOWSLookupMetadata(metadata, namespaces,
60✔
2757
                            "contactfacsimiletelephone")) {
2758
      msOWSPrintEncodeMetadata(
28✔
2759
          stream, metadata, namespaces, "contactfacsimiletelephone", OWS_NOERR,
2760
          "      <ContactFacsimileTelephone>%s</ContactFacsimileTelephone>\n",
2761
          NULL);
2762
    }
2763

2764
    if (msOWSLookupMetadata(metadata, namespaces,
60✔
2765
                            "contactelectronicmailaddress")) {
2766
      msOWSPrintEncodeMetadata(
34✔
2767
          stream, metadata, namespaces, "contactelectronicmailaddress",
2768
          OWS_NOERR,
2769
          "  <ContactElectronicMailAddress>%s</ContactElectronicMailAddress>\n",
2770
          NULL);
2771
    }
2772
    msIO_fprintf(stream, "%s</ContactInformation>\n", tabspace);
60✔
2773
  }
2774
}
62✔
2775

2776
/*
2777
** msOWSGetLayerExtent()
2778
** Try to establish layer extent using the following order of priority:
2779
** 1. layer "*_extent" metadata
2780
** 2. layer->extent if valid
2781
** 3. map->extent if valid and the fallback_to_map_extent metadata item is set
2782
** 4. msLayerGetExtent() to read extent from the layer
2783
**
2784
** Returns MS_FAILURE if none of the above return a valid extent.
2785
**
2786
*/
2787
int msOWSGetLayerExtent(mapObj *map, layerObj *lp, const char *namespaces,
1,031✔
2788
                        rectObj *ext) {
2789
  const char *value;
2790
  if ((value = msOWSLookupMetadata(&(lp->metadata), namespaces, "extent")) !=
1,031✔
2791
      NULL) {
2792
    char **tokens;
2793
    int n;
2794
    tokens = msStringSplit(value, ' ', &n);
292✔
2795
    if (tokens == NULL || n != 4) {
292✔
UNCOV
2796
      msSetError(MS_WMSERR, "Wrong number of arguments for EXTENT metadata.",
×
2797
                 "msOWSGetLayerExtent()");
UNCOV
2798
      return MS_FAILURE;
×
2799
    }
2800
    ext->minx = atof(tokens[0]);
292✔
2801
    ext->miny = atof(tokens[1]);
292✔
2802
    ext->maxx = atof(tokens[2]);
292✔
2803
    ext->maxy = atof(tokens[3]);
292✔
2804

2805
    msFreeCharArray(tokens, n);
292✔
2806
    return MS_SUCCESS;
292✔
2807
  }
2808

2809
  if (MS_VALID_EXTENT(lp->extent)) {
739✔
2810
    *ext = lp->extent;
77✔
2811
    return MS_SUCCESS;
77✔
2812
  }
2813

2814
  // Check if we can use the MAP EXTENT rather than calculating
2815
  // a LAYER extent by querying its datasource using msLayerGetExtent.
2816
  // First check the LAYER metadata, then the MAP metadata
2817
  const char *pszUseMapExtent = msOWSLookupMetadata(&(lp->metadata), namespaces,
662✔
2818
                                                    "fallback_to_map_extent");
2819
  if (!pszUseMapExtent) {
662✔
2820
    pszUseMapExtent = msOWSLookupMetadata(&(map->web.metadata), namespaces,
661✔
2821
                                          "fallback_to_map_extent");
2822
  }
2823

2824
  if (pszUseMapExtent && CSLTestBoolean(pszUseMapExtent) &&
662✔
2825
      MS_VALID_EXTENT(map->extent)) {
2✔
2826

2827
    if (msProjectionsDiffer(&(lp->projection), &(map->projection)) == MS_TRUE) {
2✔
2828
      rectObj projectedExt = map->extent;
1✔
2829
      if (msProjectRect(&(map->projection), &(lp->projection), &projectedExt) ==
1✔
2830
          MS_SUCCESS) {
2831
        *ext = projectedExt;
1✔
2832
        return MS_SUCCESS;
1✔
2833
      }
2834
    } else {
2835
      *ext = map->extent;
1✔
2836
      return MS_SUCCESS;
1✔
2837
    }
2838
  }
2839

2840
  if (msLayerGetExtent(lp, ext) == MS_SUCCESS) {
660✔
2841
    return MS_SUCCESS;
2842
  }
2843

2844
  return MS_FAILURE;
2845
}
2846

2847
/**********************************************************************
2848
 *                          msOWSExecuteRequests()
2849
 *
2850
 * Execute a number of WFS/WMS HTTP requests in parallel, and then
2851
 * update layerObj information with the result of the requests.
2852
 **********************************************************************/
2853
int msOWSExecuteRequests(httpRequestObj *pasReqInfo, int numRequests,
44✔
2854
                         mapObj *map, int bCheckLocalCache) {
2855
  int nStatus, iReq;
2856

2857
  /* Execute requests */
2858
#if defined(USE_CURL)
2859
  nStatus = msHTTPExecuteRequests(pasReqInfo, numRequests, bCheckLocalCache);
44✔
2860
#else
2861
  msSetError(MS_WMSERR,
2862
             "msOWSExecuteRequests() called apparently without libcurl "
2863
             "configured, msHTTPExecuteRequests() not available.",
2864
             "msOWSExecuteRequests()");
2865
  return MS_FAILURE;
2866
#endif
2867

2868
  /* Scan list of layers and call the handler for each layer type to */
2869
  /* pass them the request results. */
2870
  for (iReq = 0; iReq < numRequests; iReq++) {
88✔
2871
    if (pasReqInfo[iReq].nLayerId >= 0 &&
44✔
2872
        pasReqInfo[iReq].nLayerId < map->numlayers) {
44✔
2873
      layerObj *lp;
2874

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

2877
      if (lp->connectiontype == MS_WFS)
44✔
2878
        msWFSUpdateRequestInfo(lp, &(pasReqInfo[iReq]));
3✔
2879
    }
2880
  }
2881

2882
  return nStatus;
44✔
2883
}
2884

2885
/**********************************************************************
2886
 *                          msOWSProcessException()
2887
 *
2888
 **********************************************************************/
2889
void msOWSProcessException(layerObj *lp, const char *pszFname, int nErrorCode,
×
2890
                           const char *pszFuncName) {
2891
  FILE *fp;
2892

2893
  if ((fp = fopen(pszFname, "r")) != NULL) {
×
2894
    char *pszBuf = NULL;
2895
    int nBufSize = 0;
2896
    char *pszStart, *pszEnd;
2897

2898
    fseek(fp, 0, SEEK_END);
×
UNCOV
2899
    nBufSize = ftell(fp);
×
2900
    if (nBufSize < 0) {
×
UNCOV
2901
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
×
UNCOV
2902
      fclose(fp);
×
2903
      return;
×
2904
    }
UNCOV
2905
    rewind(fp);
×
2906
    pszBuf = (char *)malloc((nBufSize + 1) * sizeof(char));
×
2907
    if (pszBuf == NULL) {
×
UNCOV
2908
      msSetError(MS_MEMERR, NULL, "msOWSProcessException()");
×
UNCOV
2909
      fclose(fp);
×
UNCOV
2910
      return;
×
2911
    }
2912

UNCOV
2913
    if ((int)fread(pszBuf, 1, nBufSize, fp) != nBufSize) {
×
UNCOV
2914
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
×
UNCOV
2915
      free(pszBuf);
×
UNCOV
2916
      fclose(fp);
×
UNCOV
2917
      return;
×
2918
    }
2919

2920
    pszBuf[nBufSize] = '\0';
×
2921

2922
    /* OK, got the data in the buffer.  Look for the <Message> tags */
UNCOV
2923
    if ((strstr(pszBuf, "<WFS_Exception>") && /* WFS style */
×
UNCOV
2924
         (pszStart = strstr(pszBuf, "<Message>")) &&
×
UNCOV
2925
         (pszEnd = strstr(pszStart, "</Message>"))) ||
×
2926
        (strstr(pszBuf, "<ServiceExceptionReport>") && /* WMS style */
×
2927
         (pszStart = strstr(pszBuf, "<ServiceException>")) &&
×
2928
         (pszEnd = strstr(pszStart, "</ServiceException>")))) {
UNCOV
2929
      pszStart = strchr(pszStart, '>') + 1;
×
2930
      *pszEnd = '\0';
×
2931
      msSetError(nErrorCode, "Got Remote Server Exception for layer %s: %s",
×
2932
                 pszFuncName, lp->name ? lp->name : "(null)", pszStart);
×
2933
    } else {
UNCOV
2934
      msSetError(
×
2935
          MS_WFSCONNERR,
2936
          "Unable to parse Remote Server Exception Message for layer %s.",
2937
          pszFuncName, lp->name ? lp->name : "(null)");
×
2938
    }
2939

UNCOV
2940
    free(pszBuf);
×
UNCOV
2941
    fclose(fp);
×
2942
  }
2943
}
2944

2945
/**********************************************************************
2946
 *                          msOWSBuildURLFilename()
2947
 *
2948
 * Build a unique filename for this URL to use in caching remote server
2949
 * requests.  Slashes and illegal characters will be turned into '_'
2950
 *
2951
 * Returns a newly allocated buffer that should be freed by the caller or
2952
 * NULL in case of error.
2953
 **********************************************************************/
2954
char *msOWSBuildURLFilename(const char *pszPath, const char *pszURL,
×
2955
                            const char *pszExt) {
2956
  char *pszBuf, *pszPtr;
2957
  int i;
2958
  size_t nBufLen = 0;
2959

UNCOV
2960
  nBufLen = strlen(pszURL) + strlen(pszExt) + 2;
×
UNCOV
2961
  if (pszPath)
×
UNCOV
2962
    nBufLen += (strlen(pszPath) + 1);
×
2963

UNCOV
2964
  pszBuf = (char *)malloc(nBufLen);
×
UNCOV
2965
  if (pszBuf == NULL) {
×
UNCOV
2966
    msSetError(MS_MEMERR, NULL, "msOWSBuildURLFilename()");
×
UNCOV
2967
    return NULL;
×
2968
  }
UNCOV
2969
  pszBuf[0] = '\0';
×
2970

UNCOV
2971
  if (pszPath) {
×
2972
#ifdef _WIN32
2973
    if (pszPath[strlen(pszPath) - 1] != '/' &&
2974
        pszPath[strlen(pszPath) - 1] != '\\')
2975
      snprintf(pszBuf, nBufLen, "%s\\", pszPath);
2976
    else
2977
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2978
#else
UNCOV
2979
    if (pszPath[strlen(pszPath) - 1] != '/')
×
2980
      snprintf(pszBuf, nBufLen, "%s/", pszPath);
2981
    else
2982
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2983
#endif
2984
  }
2985

UNCOV
2986
  pszPtr = pszBuf + strlen(pszBuf);
×
2987

UNCOV
2988
  for (i = 0; pszURL[i] != '\0'; i++) {
×
UNCOV
2989
    if (isalnum(pszURL[i]))
×
UNCOV
2990
      *pszPtr = pszURL[i];
×
2991
    else
UNCOV
2992
      *pszPtr = '_';
×
UNCOV
2993
    pszPtr++;
×
2994
  }
2995

2996
  strlcpy(pszPtr, pszExt, nBufLen);
2997

2998
  return pszBuf;
2999
}
3000

3001
/*
3002
** msOWSGetProjURN()
3003
**
3004
** Fetch an OGC URN for this layer or map.  Similar to msOWSGetEPSGProj()
3005
** but returns the result in the form "urn:ogc:def:crs:EPSG::27700".
3006
** The returned buffer is dynamically allocated, and must be freed by the
3007
** caller.
3008
*/
3009
char *msOWSGetProjURN(projectionObj *proj, hashTableObj *metadata,
470✔
3010
                      const char *namespaces, int bReturnOnlyFirstOne) {
3011
  char *result;
3012
  char **tokens;
3013
  int numtokens, i;
3014
  char *oldStyle = NULL;
470✔
3015

3016
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
470✔
3017

3018
  if (oldStyle == NULL || strncmp(oldStyle, "CRS:", 4) == 0 ||
470✔
3019
      strncmp(oldStyle, "AUTO:", 5) == 0 ||
464✔
3020
      strncmp(oldStyle, "AUTO2:", 6) == 0) {
464✔
3021
    msFree(oldStyle);
6✔
3022
    return NULL;
6✔
3023
  }
3024

3025
  result = msStrdup("");
464✔
3026

3027
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
464✔
3028
  msFree(oldStyle);
464✔
3029
  for (i = 0; tokens != NULL && i < numtokens; i++) {
996✔
3030
    char urn[100];
3031
    char *colon = strchr(tokens[i], ':');
532✔
3032

3033
    if (colon != NULL && strchr(colon + 1, ':') == NULL) {
532✔
3034
      *colon = 0;
532✔
3035
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:%s::%s", tokens[i],
532✔
3036
               colon + 1);
UNCOV
3037
    } else if (strcasecmp(tokens[i], "imageCRS") == 0)
×
3038
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:OGC::imageCRS");
UNCOV
3039
    else if (strncmp(tokens[i], "urn:ogc:def:crs:", 16) == 0) {
×
3040
      strlcpy(urn, tokens[i], sizeof(urn));
3041
    } else {
3042
      strlcpy(urn, "", sizeof(urn));
3043
    }
3044

3045
    if (strlen(urn) > 0) {
532✔
3046
      const size_t bufferSize = strlen(result) + strlen(urn) + 2;
532✔
3047
      result = (char *)msSmallRealloc(result, bufferSize);
532✔
3048

3049
      if (strlen(result) > 0)
532✔
3050
        strlcat(result, " ", bufferSize);
3051
      strlcat(result, urn, bufferSize);
3052
    } else {
UNCOV
3053
      msDebug("msOWSGetProjURN(): Failed to process SRS '%s', ignored.",
×
3054
              tokens[i]);
3055
    }
3056
  }
3057

3058
  msFreeCharArray(tokens, numtokens);
464✔
3059

3060
  if (strlen(result) == 0) {
464✔
UNCOV
3061
    msFree(result);
×
UNCOV
3062
    return NULL;
×
3063
  } else
3064
    return result;
3065
}
3066

3067
/*
3068
** msOWSGetProjURI()
3069
**
3070
** Fetch OGC CRS URIs for this layer or map. Similar to msOWSGetEPSGProj()
3071
** but returns URIs in the form:
3072
**
3073
**   http://www.opengis.net/def/crs/{AUTHORITY}/0/{CODE}
3074
**
3075
** The returned buffer is dynamically allocated and must be freed by the
3076
** caller.
3077
*/
3078

3079
char *msOWSGetProjURI(projectionObj *proj, hashTableObj *metadata,
248✔
3080
                      const char *namespaces, int bReturnOnlyFirstOne) {
3081
  char *result;
3082
  char **tokens;
3083
  int numtokens, i;
3084
  char *oldStyle = NULL;
248✔
3085

3086
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
248✔
3087
  if (oldStyle == NULL)
248✔
3088
    return NULL;
3089

3090
  result = msStrdup("");
246✔
3091
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
246✔
3092
  msFree(oldStyle);
246✔
3093

3094
  for (i = 0; tokens != NULL && i < numtokens; i++) {
528✔
3095
    char urn[256];
3096
    char *sep;
3097

3098
    urn[0] = '\0';
282✔
3099
    sep = strchr(tokens[i], ':');
282✔
3100

3101
    /* Special case for imageCRS */
3102
    if (strcasecmp(tokens[i], "imageCRS") == 0) {
282✔
3103
      snprintf(urn, sizeof(urn),
3104
               "http://www.opengis.net/def/crs/OGC/0/imageCRS");
3105
    }
3106
    /* Already a full CRS URI */
3107
    else if (strncmp(tokens[i], OGC_CRS_PREFIX, strlen(OGC_CRS_PREFIX)) == 0) {
282✔
3108
      strlcpy(urn, tokens[i], sizeof(urn));
3109
    }
3110
    /* Handle generic AUTHORITY:CODE pattern e.g. EPSG:4326, ESRI:54052 */
3111
    else if (sep != NULL) {
282✔
3112
      char auth[64];
3113
      const char *code;
3114
      size_t authlen;
3115

3116
      authlen = sep - tokens[i];
282✔
3117
      if (authlen >= sizeof(auth)) {
282✔
UNCOV
3118
        msDebug("msOWSGetProjURI(): Authority too long in SRS '%s', ignored.\n",
×
3119
                tokens[i]);
UNCOV
3120
        continue;
×
3121
      }
3122
      strlcpy(auth, tokens[i], authlen + 1);
282✔
3123
      code = sep + 1;
282✔
3124
      snprintf(urn, sizeof(urn), "%s%s/0/%s", OGC_CRS_PREFIX, auth, code);
3125
    }
3126

3127
    if (strlen(urn) > 0) {
282✔
3128
      result = (char *)msSmallRealloc(result, strlen(result) + strlen(urn) + 2);
282✔
3129
      if (strlen(result) > 0)
282✔
3130
        strcat(result, " ");
3131
      strcat(result, urn);
3132
    } else {
UNCOV
3133
      msDebug("msOWSGetProjURI(): Failed to process SRS '%s', ignored.\n",
×
3134
              tokens[i]);
3135
    }
3136
  }
3137

3138
  msFreeCharArray(tokens, numtokens);
246✔
3139

3140
  if (strlen(result) == 0) {
246✔
3141
    msFree(result);
×
UNCOV
3142
    return NULL;
×
3143
  }
3144

3145
  return result;
3146
}
3147
/*
3148
** msOWSGetDimensionInfo()
3149
**
3150
** Extract dimension information from a layer's metadata
3151
**
3152
** Before 4.9, only the time dimension was support. With the addition of
3153
** Web Map Context 1.1.0, we need to support every dimension types.
3154
** This function get the dimension information from special metadata in
3155
** the layer, but can also return default values for the time dimension.
3156
**
3157
*/
3158
void msOWSGetDimensionInfo(layerObj *layer, const char *pszDimension,
×
3159
                           const char **papszDimUserValue,
3160
                           const char **papszDimUnits,
3161
                           const char **papszDimDefault,
3162
                           const char **papszDimNearValue,
3163
                           const char **papszDimUnitSymbol,
3164
                           const char **papszDimMultiValue) {
3165
  char *pszDimensionItem;
3166
  size_t bufferSize = 0;
3167

3168
  if (pszDimension == NULL || layer == NULL)
×
3169
    return;
3170

3171
  bufferSize = strlen(pszDimension) + 50;
×
3172
  pszDimensionItem = (char *)malloc(bufferSize);
×
3173

3174
  /* units (mandatory in map context) */
3175
  if (papszDimUnits != NULL) {
×
3176
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_units", pszDimension);
UNCOV
3177
    *papszDimUnits =
×
3178
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3179
  }
3180
  /* unitSymbol (mandatory in map context) */
UNCOV
3181
  if (papszDimUnitSymbol != NULL) {
×
3182
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_unitsymbol",
3183
             pszDimension);
3184
    *papszDimUnitSymbol =
×
3185
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3186
  }
3187
  /* userValue (mandatory in map context) */
3188
  if (papszDimUserValue != NULL) {
×
3189
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_uservalue",
3190
             pszDimension);
3191
    *papszDimUserValue =
×
3192
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3193
  }
3194
  /* default */
3195
  if (papszDimDefault != NULL) {
×
3196
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_default",
3197
             pszDimension);
3198
    *papszDimDefault =
×
UNCOV
3199
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3200
  }
3201
  /* multipleValues */
UNCOV
3202
  if (papszDimMultiValue != NULL) {
×
3203
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_multiplevalues",
3204
             pszDimension);
UNCOV
3205
    *papszDimMultiValue =
×
UNCOV
3206
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3207
  }
3208
  /* nearestValue */
UNCOV
3209
  if (papszDimNearValue != NULL) {
×
3210
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_nearestvalue",
3211
             pszDimension);
UNCOV
3212
    *papszDimNearValue =
×
UNCOV
3213
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3214
  }
3215

3216
  /* Use default time value if necessary */
UNCOV
3217
  if (strcasecmp(pszDimension, "time") == 0) {
×
UNCOV
3218
    if (papszDimUserValue != NULL && *papszDimUserValue == NULL)
×
UNCOV
3219
      *papszDimUserValue =
×
UNCOV
3220
          msOWSLookupMetadata(&(layer->metadata), "MO", "time");
×
UNCOV
3221
    if (papszDimDefault != NULL && *papszDimDefault == NULL)
×
UNCOV
3222
      *papszDimDefault =
×
UNCOV
3223
          msOWSLookupMetadata(&(layer->metadata), "MO", "timedefault");
×
UNCOV
3224
    if (papszDimUnits != NULL && *papszDimUnits == NULL)
×
UNCOV
3225
      *papszDimUnits = "ISO8601";
×
UNCOV
3226
    if (papszDimUnitSymbol != NULL && *papszDimUnitSymbol == NULL)
×
UNCOV
3227
      *papszDimUnitSymbol = "t";
×
UNCOV
3228
    if (papszDimNearValue != NULL && *papszDimNearValue == NULL)
×
UNCOV
3229
      *papszDimNearValue = "0";
×
3230
  }
3231

UNCOV
3232
  free(pszDimensionItem);
×
3233

UNCOV
3234
  return;
×
3235
}
3236

3237
/**
3238
 * msOWSNegotiateUpdateSequence()
3239
 *
3240
 * returns the updateSequence value for an OWS
3241
 *
3242
 * @param requested_updatesequence the updatesequence passed by the client
3243
 * @param updatesequence the updatesequence set by the server
3244
 *
3245
 * @return result of comparison (-1, 0, 1)
3246
 * -1: lower / higher OR values not set by client or server
3247
 *  1: higher / lower
3248
 *  0: equal
3249
 */
3250

3251
int msOWSNegotiateUpdateSequence(const char *requested_updatesequence,
38✔
3252
                                 const char *updatesequence) {
3253
  int valtype1 = 1; /* default datatype for updatesequence passed by client */
3254
  int valtype2 = 1; /* default datatype for updatesequence set by server */
3255
  struct tm tm_requested_updatesequence, tm_updatesequence;
3256

3257
  /* if not specified by client, or set by server,
3258
     server responds with latest Capabilities XML */
3259
  if (!requested_updatesequence || !updatesequence)
38✔
3260
    return -1;
3261

3262
  /* test to see if server value is an integer (1), string (2) or timestamp (3)
3263
   */
3264
  if (msStringIsInteger(updatesequence) == MS_FAILURE)
38✔
3265
    valtype1 = 2;
3266

3267
  if (valtype1 == 2) { /* test if timestamp */
3268
    msTimeInit(&tm_updatesequence);
15✔
3269
    if (msParseTime(updatesequence, &tm_updatesequence) == MS_TRUE)
15✔
3270
      valtype1 = 3;
3271
    msResetErrorList();
15✔
3272
  }
3273

3274
  /* test to see if client value is an integer (1), string (2) or timestamp (3)
3275
   */
3276
  if (msStringIsInteger(requested_updatesequence) == MS_FAILURE)
38✔
3277
    valtype2 = 2;
3278

3279
  if (valtype2 == 2) { /* test if timestamp */
3280
    msTimeInit(&tm_requested_updatesequence);
15✔
3281
    if (msParseTime(requested_updatesequence, &tm_requested_updatesequence) ==
15✔
3282
        MS_TRUE)
3283
      valtype2 = 3;
3284
    msResetErrorList();
15✔
3285
  }
3286

3287
  /* if the datatypes do not match, do not compare, */
3288
  if (valtype1 != valtype2)
38✔
3289
    return -1;
3290

3291
  if (valtype1 == 1) { /* integer */
38✔
3292
    const int requested_updatesequence_i = atoi(requested_updatesequence);
3293
    const int updatesequence_i = atoi(updatesequence);
3294
    if (requested_updatesequence_i < updatesequence_i)
23✔
3295
      return -1;
3296

3297
    if (requested_updatesequence_i > updatesequence_i)
15✔
3298
      return 1;
3299

3300
    return 0;
8✔
3301
  }
3302

3303
  if (valtype1 == 2) /* string */
15✔
3304
    return strcasecmp(requested_updatesequence, updatesequence);
3✔
3305

3306
  assert(valtype1 == 3); /* timestamp */
3307
  /* compare timestamps */
3308
  return msDateCompare(&tm_requested_updatesequence, &tm_updatesequence) +
12✔
3309
         msTimeCompare(&tm_requested_updatesequence, &tm_updatesequence);
12✔
3310
}
3311

3312
/************************************************************************/
3313
/*                         msOwsIsOutputFormatValid                     */
3314
/*                                                                      */
3315
/*      Utility function to parse a comma separated list in a            */
3316
/*      metedata object and select and outputformat.                    */
3317
/************************************************************************/
3318
outputFormatObj *msOwsIsOutputFormatValid(mapObj *map, const char *format,
114✔
3319
                                          hashTableObj *metadata,
3320
                                          const char *namespaces,
3321
                                          const char *name) {
3322
  char **tokens = NULL;
3323
  int i, n;
3324
  outputFormatObj *psFormat = NULL;
3325
  const char *format_list = NULL;
3326

3327
  if (map && format && metadata && namespaces && name) {
114✔
3328
    msApplyDefaultOutputFormats(map);
114✔
3329
    format_list = msOWSLookupMetadata(metadata, namespaces, name);
114✔
3330
    n = 0;
114✔
3331
    if (format_list)
114✔
3332
      tokens = msStringSplit(format_list, ',', &n);
114✔
3333

3334
    if (tokens && n > 0) {
114✔
3335
      for (i = 0; i < n; i++) {
246✔
3336
        int iFormat = msGetOutputFormatIndex(map, tokens[i]);
246✔
3337
        const char *mimetype;
3338
        if (iFormat == -1)
246✔
3339
          continue;
28✔
3340

3341
        mimetype = map->outputformatlist[iFormat]->mimetype;
218✔
3342

3343
        msStringTrim(tokens[i]);
218✔
3344
        if (strcasecmp(tokens[i], format) == 0)
218✔
3345
          break;
3346
        if (mimetype && strcasecmp(mimetype, format) == 0)
106✔
3347
          break;
3348
      }
3349
      if (i < n)
114✔
3350
        psFormat = msSelectOutputFormat(map, format);
114✔
3351
    }
3352
    if (tokens)
114✔
3353
      msFreeCharArray(tokens, n);
114✔
3354
  }
3355

3356
  return psFormat;
114✔
3357
}
3358

3359
#endif /* defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined             \
3360
          (USE_WCS_SVR) || defined(USE_SOS_SVR) || defined(USE_WMS_LYR) ||     \
3361
          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