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

MapServer / MapServer / 22996579289

12 Mar 2026 10:04AM UTC coverage: 42.335% (+0.003%) from 42.332%
22996579289

push

github

web-flow
Merge pull request #7458 from geographika/extent-result

Update OGC Features test result with new services

64435 of 152201 relevant lines covered (42.34%)

27109.66 hits per line

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

71.49
/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
/*
52
** msOWSInitRequestObj() initializes an owsRequestObj; i.e: sets
53
** all internal pointers to NULL.
54
*/
55
void msOWSInitRequestObj(owsRequestObj *ows_request) {
1,873✔
56
  ows_request->numlayers = 0;
1,873✔
57
  ows_request->numwmslayerargs = 0;
1,873✔
58
  ows_request->enabled_layers = NULL;
1,873✔
59
  ows_request->layerwmsfilterindex = NULL;
1,873✔
60

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

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

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

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

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

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

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

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

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

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

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

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

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

235
  return MS_SUCCESS;
236
}
237

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

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

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

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

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

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

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

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

278
  xmlDocSetRootElement(psDoc, psRootNode);
3✔
279

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

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

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

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

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

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

300
  return MS_FAILURE;
3✔
301
}
302

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

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

322
  force_ows_mode = (ows_mode == OWS || ows_mode == WFS);
1,872✔
323

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

334
  compliance_mode = msOWSStrictCompliance(map);
1,872✔
335

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

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

422
  msOWSClearRequestObj(&ows_request);
1,866✔
423
  return status;
424
}
425

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

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

533
  return len;
534
}
535

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

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

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

575
  return MS_FALSE;
576
}
577

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

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

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

626
  if (!ip)
10,154✔
627
    return MS_FALSE; /* no endpoint ip */
628

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

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

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

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

646
  return disabled;
647
}
648

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

666
  if (request == NULL)
963✔
667
    return MS_FALSE;
668

669
  remote_ip = getenv("REMOTE_ADDR");
963✔
670

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

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

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

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

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

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

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

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

725
    if (globally_enabled &&
551✔
726
        msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
283✔
727
      globally_enabled = MS_FALSE;
728

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

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

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

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

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

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

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

764
  return MS_FALSE;
765
}
766

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

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

790
  ows_request->numlayers = 0;
1,827✔
791
  ows_request->enabled_layers = NULL;
1,827✔
792

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

796
  remote_ip = getenv("REMOTE_ADDR");
1,818✔
797

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

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

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

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

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

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

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

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

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

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

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

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

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

873
  *disabled = MS_FALSE;
21,425✔
874

875
  if (metadata == NULL)
21,425✔
876
    return MS_FALSE;
877

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

883
  for (size_t i = 0; i <= len; ++i, ++ptr) {
15,389✔
884

885
    if (!wordFlag && isspace(*ptr))
12,462✔
886
      continue;
×
887

888
    wordFlag = MS_TRUE;
889

890
    if (*ptr == '!') {
12,462✔
891
      disableFlag = MS_TRUE;
892
      continue;
354✔
893
    } else if ((*ptr == ' ') ||
12,108✔
894
               (*ptr != '\0' && ptr[1] == '\0')) { /* end of word */
9,006✔
895
      if (ptr[1] == '\0' && *ptr != ' ') {
3,461✔
896
        *bufferPtr = *ptr;
3,286✔
897
        ++bufferPtr;
3,286✔
898
      }
899

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

923
  return allFlag;
924
}
925

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

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

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

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

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

992
  return value;
993
}
994

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

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

1015
  if (name && !value) {
15,282✔
1016
    value = msOWSLookupMetadata(metadata, namespaces, name);
14,821✔
1017
  }
1018

1019
  return value;
15,282✔
1020
}
1021

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

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

1044
  return result;
2,220✔
1045
}
1046

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

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

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

1077
    msFreeCharArray(digits, numDigits);
2,301✔
1078

1079
    return nVersion;
2,301✔
1080
  }
1081

1082
  return OWS_VERSION_NOTSET;
1083
}
1084

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

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

1100
  return pszBuffer;
583✔
1101
}
1102

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

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

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

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

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

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

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

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

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

1217
  return schemas_location;
1,199✔
1218
}
1219

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

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

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

1272
  return online_resource;
1273
}
1274

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

1289
  if (src_url == NULL)
735✔
1290
    return NULL;
1291

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

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

1300
  strlcpy(online_resource, src_url, buffer_size);
1301

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

1312
  return online_resource;
1313
}
1314

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

1326
  char md_item_name[256];
1327

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

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

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

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

1347
  return MS_TRUE;
2,882✔
1348
}
1349

1350
void msOWSSetShapeCache(mapObj *map, const char *pszMetadataNamespaces) {
589✔
1351
  const char *pszFeaturesCacheCount = msOWSLookupMetadata(
589✔
1352
      &(map->web.metadata), pszMetadataNamespaces, "features_cache_count");
589✔
1353
  const char *pszFeaturesCacheSize = msOWSLookupMetadata(
589✔
1354
      &(map->web.metadata), pszMetadataNamespaces, "features_cache_size");
1355
  if (pszFeaturesCacheCount) {
589✔
1356
    map->query.cache_shapes = MS_TRUE;
4✔
1357
    map->query.max_cached_shape_count = atoi(pszFeaturesCacheCount);
4✔
1358
    if (map->debug >= MS_DEBUGLEVEL_V) {
4✔
1359
      msDebug("Caching up to %d shapes\n", map->query.max_cached_shape_count);
×
1360
    }
1361
  }
1362

1363
  if (pszFeaturesCacheSize) {
589✔
1364
    map->query.cache_shapes = MS_TRUE;
4✔
1365
    map->query.max_cached_shape_ram_amount = atoi(pszFeaturesCacheSize);
4✔
1366
    if (strstr(pszFeaturesCacheSize, "mb") ||
4✔
1367
        strstr(pszFeaturesCacheSize, "MB"))
1368
      map->query.max_cached_shape_ram_amount *= 1024 * 1024;
×
1369
    if (map->debug >= MS_DEBUGLEVEL_V) {
4✔
1370
      msDebug("Caching up to %d bytes of shapes\n",
×
1371
              map->query.max_cached_shape_ram_amount);
1372
    }
1373
  }
1374
}
589✔
1375

1376
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1377
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
1378

1379
/*
1380
** msRenameLayer()
1381
*/
1382
static int msRenameLayer(layerObj *lp, int count) {
×
1383
  char *newname;
1384
  const size_t nSize = strlen(lp->name) + 16;
×
1385
  newname = (char *)malloc(nSize);
×
1386
  if (!newname) {
×
1387
    msSetError(MS_MEMERR, NULL, "msRenameLayer()");
×
1388
    return MS_FAILURE;
×
1389
  }
1390
  snprintf(newname, nSize, "%s_%2.2d", lp->name, count);
1391
  free(lp->name);
×
1392
  lp->name = newname;
×
1393

1394
  return MS_SUCCESS;
×
1395
}
1396

1397
/*
1398
** msOWSMakeAllLayersUnique()
1399
*/
1400
int msOWSMakeAllLayersUnique(mapObj *map) {
1,479✔
1401
  std::map<std::string, int> namesToIdx;
1402
  std::map<std::string, int> namesToCount;
1403

1404
  /* Make sure all layers in the map file have valid and unique names */
1405
  for (int i = 0; i < map->numlayers; i++) {
8,752✔
1406
    layerObj *lp = GET_LAYER(map, i);
7,273✔
1407
    if (!lp->name)
7,273✔
1408
      continue;
×
1409
    const std::string name = msStringToLower(std::string(lp->name));
14,546✔
1410
    auto iterToIdx = namesToIdx.find(name);
1411
    if (iterToIdx == namesToIdx.end()) {
7,273✔
1412
      namesToIdx[name] = i;
7,273✔
1413
      namesToCount[name] = 1;
7,273✔
1414
    } else {
1415
      auto iterToCount = namesToCount.find(name);
1416
      assert(iterToCount != namesToCount.end());
1417
      if (iterToCount->second == 1) {
×
1418
        if (msRenameLayer(GET_LAYER(map, iterToIdx->second), 1) != MS_SUCCESS)
×
1419
          return MS_FAILURE;
1420
      }
1421
      ++iterToCount->second;
×
1422
      if (msRenameLayer(lp, iterToCount->second) != MS_SUCCESS)
×
1423
        return MS_FAILURE;
1424
    }
1425
  }
1426

1427
  return MS_SUCCESS;
1428
}
1429

1430
/*
1431
** msOWSNegotiateVersion()
1432
**
1433
** returns the most suitable version an OWS is to support given a client
1434
** version parameter.
1435
**
1436
** supported_versions must be ordered from highest to lowest
1437
**
1438
** Returns a version integer of the supported version
1439
**
1440
*/
1441

1442
int msOWSNegotiateVersion(int requested_version, const int supported_versions[],
101✔
1443
                          int num_supported_versions) {
1444
  int i;
1445

1446
  /* if version is not set return highest version */
1447
  if (!requested_version)
101✔
1448
    return supported_versions[0];
×
1449

1450
  /* if the requested version is lower than the lowest version return the lowest
1451
   * version  */
1452
  if (requested_version < supported_versions[num_supported_versions - 1])
101✔
1453
    return supported_versions[num_supported_versions - 1];
1454

1455
  /* return the first entry that's lower than or equal to the requested version
1456
   */
1457
  for (i = 0; i < num_supported_versions; i++) {
282✔
1458
    if (supported_versions[i] <= requested_version)
282✔
1459
      return supported_versions[i];
101✔
1460
  }
1461

1462
  return requested_version;
1463
}
1464

1465
/*
1466
** msOWSGetOnlineResource()
1467
**
1468
** Return the online resource for this service and add language parameter.
1469
**
1470
** Returns a newly allocated string that should be freed by the caller or
1471
** NULL in case of error.
1472
*/
1473
char *msOWSGetOnlineResource2(mapObj *map, const char *namespaces,
91✔
1474
                              const char *metadata_name, cgiRequestObj *req,
1475
                              const char *validated_language) {
1476
  char *online_resource =
1477
      msOWSGetOnlineResource(map, namespaces, metadata_name, req);
91✔
1478

1479
  if (online_resource && validated_language && validated_language[0]) {
91✔
1480
    /* online_resource is already terminated, so we can simply add language=...&
1481
     */
1482
    /* but first we need to make sure that online_resource has enough capacity
1483
     */
1484
    online_resource = (char *)msSmallRealloc(
20✔
1485
        online_resource,
1486
        strlen(online_resource) + strlen(validated_language) + 11);
20✔
1487
    strcat(online_resource, "language=");
1488
    strcat(online_resource, validated_language);
1489
    strcat(online_resource, "&");
1490
  }
1491

1492
  return online_resource;
91✔
1493
}
1494

1495
/* msOWSGetInspireSchemasLocation()
1496
**
1497
** schemas location is the root of the web tree where all Inspire-related
1498
** schemas can be found on this server.  These URLs must exist in order
1499
** to validate xml.
1500
**
1501
** Use value of "inspire_schemas_location" metadata
1502
*/
1503
const char *msOWSGetInspireSchemasLocation(mapObj *map) {
41✔
1504
  const char *schemas_location;
1505

1506
  schemas_location =
1507
      msLookupHashTable(&(map->web.metadata), "inspire_schemas_location");
41✔
1508
  if (schemas_location == NULL)
41✔
1509
    schemas_location = "http://inspire.ec.europa.eu/schemas";
1510

1511
  return schemas_location;
41✔
1512
}
1513

1514
/* msOWSGetLanguageList
1515
**
1516
** Returns the list of languages that this service supports
1517
**
1518
** Use value of "languages" metadata (comma-separated list), or NULL if not set
1519
**
1520
** Returns a malloced char** of length numitems which must be freed
1521
** by the caller, or NULL (with numitems = 0)
1522
*/
1523
char **msOWSGetLanguageList(mapObj *map, const char *namespaces,
1,438✔
1524
                            int *numitems) {
1525

1526
  const char *languages = NULL;
1527

1528
  languages =
1529
      msOWSLookupMetadata(&(map->web.metadata), namespaces, "languages");
1,438✔
1530
  if (languages && strlen(languages) > 0) {
1,438✔
1531
    return msStringSplit(languages, ',', numitems);
132✔
1532
  } else {
1533
    *numitems = 0;
1,306✔
1534
    return NULL;
1,306✔
1535
  }
1536
}
1537

1538
/* msOWSGetLanguageFromList
1539
**
1540
** Returns a language according to the language requested by the client
1541
**
1542
** If the requested language is in the languages metadata then use it,
1543
** otherwise ignore it and use the default language, which is the first entry in
1544
** the languages metadata list. Calling with a NULL requested_langauge
1545
** therefore returns this default language. If the language metadata list is
1546
** not defined then the language is set to NULL.
1547
**
1548
** Returns a malloced char* which must be freed by the caller, or NULL
1549
*/
1550
char *msOWSGetLanguageFromList(mapObj *map, const char *namespaces,
1,397✔
1551
                               const char *requested_language) {
1552
  int num_items = 0;
1,397✔
1553
  char **languages = msOWSGetLanguageList(map, namespaces, &num_items);
1,397✔
1554
  char *language = NULL;
1555

1556
  if (languages && num_items > 0) {
1,397✔
1557
    if (!requested_language ||
128✔
1558
        !msStringInArray(requested_language, languages, num_items)) {
16✔
1559
      language = msStrdup(languages[0]);
98✔
1560
    } else {
1561
      language = msStrdup(requested_language);
14✔
1562
    }
1563
  }
1564
  msFreeCharArray(languages, num_items);
1,397✔
1565

1566
  return language;
1,397✔
1567
}
1568

1569
/* msOWSLanguageNegotiation
1570
**
1571
** Returns a language according to the accepted languages requested by the
1572
*client
1573
**
1574
** Returns a malloced char* which must be freed by the caller, or NULL
1575
*/
1576
char *msOWSLanguageNegotiation(mapObj *map, const char *namespaces,
41✔
1577
                               char **accept_languages,
1578
                               int num_accept_languages) {
1579
  int num_languages = 0;
41✔
1580
  char **languages = NULL;
1581
  char *result_language = NULL;
1582

1583
  languages = msOWSGetLanguageList(map, namespaces, &num_languages);
41✔
1584

1585
  if (languages && num_languages > 0) {
41✔
1586
    int i;
1587
    for (i = 0; i < num_accept_languages; ++i) {
28✔
1588
      const char *accept_language = accept_languages[i];
20✔
1589

1590
      /* '*' means any language */
1591
      if (EQUAL(accept_language, "*")) {
20✔
1592
        result_language = msStrdup(languages[0]);
×
1593
        break;
1594
      } else if (msStringInArray(accept_language, languages, num_languages)) {
20✔
1595
        result_language = msStrdup(accept_language);
12✔
1596
        break;
1597
      }
1598
    }
1599

1600
    if (result_language == NULL) {
20✔
1601
      result_language = msStrdup(languages[0]);
8✔
1602
    }
1603
  }
1604

1605
  msFreeCharArray(languages, num_languages);
41✔
1606
  return result_language;
41✔
1607
}
1608

1609
/* msOWSPrintInspireCommonExtendedCapabilities
1610
**
1611
** Output INSPIRE common extended capabilities items to stream
1612
** The currently supported items are metadata and languages
1613
**
1614
** tag_name is the name (including ns prefix) of the tag to include the whole
1615
** extended capabilities block in
1616
**
1617
** service is currently included for future compatibility when differing
1618
** extended capabilities elements are included for different service types
1619
**
1620
** Returns a status code; MS_NOERR if all ok, action_if_not_found otherwise
1621
*/
1622
int msOWSPrintInspireCommonExtendedCapabilities(
41✔
1623
    FILE *stream, mapObj *map, const char *namespaces, int action_if_not_found,
1624
    const char *tag_name, const char *tag_ns, const char *validated_language,
1625
    const OWSServiceType service) {
1626

1627
  int metadataStatus = 0;
1628
  int languageStatus = 0;
1629

1630
  if (tag_ns)
41✔
1631
    msIO_fprintf(stream, "  <%s %s>\n", tag_name, tag_ns);
26✔
1632
  else
1633
    msIO_fprintf(stream, "  <%s>\n", tag_name);
15✔
1634

1635
  metadataStatus = msOWSPrintInspireCommonMetadata(
41✔
1636
      stream, map, namespaces, action_if_not_found, service);
1637
  languageStatus = msOWSPrintInspireCommonLanguages(
41✔
1638
      stream, map, namespaces, action_if_not_found, validated_language);
1639

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

1642
  return (metadataStatus != MS_NOERR) ? metadataStatus : languageStatus;
41✔
1643
}
1644

1645
/* msOWSPrintInspireCommonMetadata
1646
**
1647
** Output INSPIRE common metadata items to a stream
1648
**
1649
** Returns a status code; MS_NOERR if all OK, action_if_not_found otherwise
1650
*/
1651
int msOWSPrintInspireCommonMetadata(FILE *stream, mapObj *map,
41✔
1652
                                    const char *namespaces,
1653
                                    int action_if_not_found,
1654
                                    const OWSServiceType service) {
1655

1656
  int status = MS_NOERR;
1657
  const char *inspire_capabilities = NULL;
1658

1659
  inspire_capabilities = msOWSLookupMetadata(&(map->web.metadata), namespaces,
41✔
1660
                                             "inspire_capabilities");
1661

1662
  if (!inspire_capabilities) {
41✔
1663
    if (OWS_WARN == action_if_not_found) {
×
1664
      msIO_fprintf(
×
1665
          stream,
1666
          "<!-- WARNING: missing metadata entry for 'inspire_capabilities', "
1667
          "one of 'embed' and 'url' must be supplied. -->\n");
1668
    }
1669
    return action_if_not_found;
×
1670
  }
1671
  if (strcasecmp("url", inspire_capabilities) == 0) {
41✔
1672
    if (msOWSLookupMetadata(&(map->web.metadata), namespaces,
20✔
1673
                            "inspire_metadataurl_href") != NULL) {
1674
      msIO_fprintf(stream,
20✔
1675
                   "    <inspire_common:MetadataUrl "
1676
                   "xsi:type=\"inspire_common:resourceLocatorType\">\n");
1677
      msOWSPrintEncodeMetadata(
20✔
1678
          stream, &(map->web.metadata), namespaces, "inspire_metadataurl_href",
1679
          OWS_WARN, "      <inspire_common:URL>%s</inspire_common:URL>\n", "");
1680
      msOWSPrintEncodeMetadata(
20✔
1681
          stream, &(map->web.metadata), namespaces,
1682
          "inspire_metadataurl_format", OWS_WARN,
1683
          "      <inspire_common:MediaType>%s</inspire_common:MediaType>\n",
1684
          "");
1685
      msIO_fprintf(stream, "    </inspire_common:MetadataUrl>\n");
20✔
1686
    } else {
1687
      status = action_if_not_found;
1688
      if (OWS_WARN == action_if_not_found) {
×
1689
        char *pszExpandedMetadataKey =
1690
            msOWSGetExpandedMetadataKey(namespaces, "inspire_metadataurl_href");
×
1691
        msIO_fprintf(stream,
×
1692
                     "<!-- WARNING: Mandatory metadata %s was missing in this "
1693
                     "context. -->\n",
1694
                     pszExpandedMetadataKey);
1695
        msFree(pszExpandedMetadataKey);
×
1696
      }
1697
    }
1698
  } else if (strcasecmp("embed", inspire_capabilities) == 0) {
21✔
1699
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1700
                             "inspire_resourcelocator", OWS_WARN,
1701
                             "    <inspire_common:ResourceLocator>\n      "
1702
                             "<inspire_common:URL>%s</inspire_common:URL>\n    "
1703
                             "</inspire_common:ResourceLocator>\n",
1704
                             NULL);
1705
    msIO_fprintf(
21✔
1706
        stream,
1707
        "    "
1708
        "<inspire_common:ResourceType>service</inspire_common:ResourceType>\n");
1709
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1710
                             "inspire_temporal_reference", OWS_WARN,
1711
                             "    <inspire_common:TemporalReference>\n      "
1712
                             "<inspire_common:DateOfLastRevision>%s</"
1713
                             "inspire_common:DateOfLastRevision>\n    "
1714
                             "</inspire_common:TemporalReference>\n",
1715
                             "");
1716
    msIO_fprintf(stream, "    <inspire_common:Conformity>\n");
21✔
1717
    msIO_fprintf(stream, "      <inspire_common:Specification>\n");
21✔
1718
    msIO_fprintf(stream,
21✔
1719
                 "        <inspire_common:Title>-</inspire_common:Title>\n");
1720
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1721
                             "inspire_temporal_reference", OWS_NOERR,
1722
                             "        "
1723
                             "<inspire_common:DateOfLastRevision>%s</"
1724
                             "inspire_common:DateOfLastRevision>\n",
1725
                             "");
1726
    msIO_fprintf(stream, "      </inspire_common:Specification>\n");
21✔
1727
    msIO_fprintf(
21✔
1728
        stream,
1729
        "      <inspire_common:Degree>notEvaluated</inspire_common:Degree>\n");
1730
    msIO_fprintf(stream, "    </inspire_common:Conformity>\n");
21✔
1731
    msIO_fprintf(stream, "    <inspire_common:MetadataPointOfContact>\n");
21✔
1732
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
21✔
1733
                             "inspire_mpoc_name", OWS_WARN,
1734
                             "      "
1735
                             "<inspire_common:OrganisationName>%s</"
1736
                             "inspire_common:OrganisationName>\n",
1737
                             "");
1738
    msOWSPrintEncodeMetadata(
21✔
1739
        stream, &(map->web.metadata), namespaces, "inspire_mpoc_email",
1740
        OWS_WARN,
1741
        "      <inspire_common:EmailAddress>%s</inspire_common:EmailAddress>\n",
1742
        "");
1743
    msIO_fprintf(stream, "    </inspire_common:MetadataPointOfContact>\n");
21✔
1744
    msOWSPrintEncodeMetadata(
21✔
1745
        stream, &(map->web.metadata), namespaces, "inspire_metadatadate",
1746
        OWS_WARN,
1747
        "      <inspire_common:MetadataDate>%s</inspire_common:MetadataDate>\n",
1748
        "");
1749
    if (service == OWS_WFS || service == OWS_WCS)
21✔
1750
      msIO_fprintf(stream, "    "
15✔
1751
                           "<inspire_common:SpatialDataServiceType>download</"
1752
                           "inspire_common:SpatialDataServiceType>\n");
1753
    else
1754
      msIO_fprintf(stream, "    "
6✔
1755
                           "<inspire_common:SpatialDataServiceType>view</"
1756
                           "inspire_common:SpatialDataServiceType>\n");
1757
    msOWSPrintEncodeMetadata(
21✔
1758
        stream, &(map->web.metadata), namespaces, "inspire_keyword", OWS_WARN,
1759
        "    <inspire_common:MandatoryKeyword>\n      "
1760
        "<inspire_common:KeywordValue>%s</inspire_common:KeywordValue>\n    "
1761
        "</inspire_common:MandatoryKeyword>\n",
1762
        "");
1763
  } else {
1764
    status = action_if_not_found;
1765
    if (OWS_WARN == action_if_not_found) {
×
1766
      msIO_fprintf(
×
1767
          stream,
1768
          "<!-- WARNING: invalid value '%s' for 'inspire_capabilities', only "
1769
          "'embed' and 'url' are supported. -->\n",
1770
          inspire_capabilities);
1771
    }
1772
  }
1773

1774
  return status;
1775
}
1776

1777
/* msOWSPrintInspireCommonLanguages
1778
**
1779
** Output INSPIRE supported languages block to stream
1780
**
1781
** Returns a status code; MS_NOERR if all OK; action_if_not_found otherwise
1782
*/
1783
int msOWSPrintInspireCommonLanguages(FILE *stream, mapObj *map,
41✔
1784
                                     const char *namespaces,
1785
                                     int action_if_not_found,
1786
                                     const char *validated_language) {
1787
  char *buffer =
1788
      NULL; /* temp variable for malloced strings that will need freeing */
1789
  int status = MS_NOERR;
1790

1791
  char *default_language = msOWSGetLanguageFromList(map, namespaces, NULL);
41✔
1792

1793
  if (validated_language && validated_language[0] && default_language) {
41✔
1794
    msIO_fprintf(stream, "    <inspire_common:SupportedLanguages>\n");
40✔
1795
    msIO_fprintf(
40✔
1796
        stream,
1797
        "      <inspire_common:DefaultLanguage><inspire_common:Language>%s"
1798
        "</inspire_common:Language></inspire_common:DefaultLanguage>\n",
1799
        buffer = msEncodeHTMLEntities(default_language));
40✔
1800
    msFree(buffer);
40✔
1801

1802
    /* append _exclude to our default_language*/
1803
    default_language = static_cast<char *>(msSmallRealloc(
40✔
1804
        default_language, strlen(default_language) + strlen("_exclude") + 1));
40✔
1805
    strcat(default_language, "_exclude");
1806

1807
    msOWSPrintEncodeMetadataList(
40✔
1808
        stream, &(map->web.metadata), namespaces, "languages", NULL, NULL,
1809
        "      <inspire_common:SupportedLanguage><inspire_common:Language>%s"
1810
        "</inspire_common:Language></inspire_common:SupportedLanguage>\n",
1811
        default_language);
1812
    msIO_fprintf(stream, "    </inspire_common:SupportedLanguages>\n");
40✔
1813
    msIO_fprintf(
40✔
1814
        stream,
1815
        "    <inspire_common:ResponseLanguage><inspire_common:Language>%s"
1816
        "</inspire_common:Language></inspire_common:ResponseLanguage>\n",
1817
        validated_language);
1818
  } else {
1819
    status = action_if_not_found;
1820
    if (OWS_WARN == action_if_not_found) {
1✔
1821
      char *pszExpandedMetadataKey =
1822
          msOWSGetExpandedMetadataKey(namespaces, "languages");
1✔
1823
      msIO_fprintf(stream,
1✔
1824
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1825
                   "context. -->\n",
1826
                   pszExpandedMetadataKey);
1827
      msFree(pszExpandedMetadataKey);
1✔
1828
    }
1829
  }
1830

1831
  msFree(default_language);
41✔
1832

1833
  return status;
41✔
1834
}
1835

1836
/*
1837
** msOWSPrintMetadata()
1838
**
1839
** Attempt to output a capability item.  If corresponding metadata is not
1840
** found then one of a number of predefined actions will be taken.
1841
** If a default value is provided and metadata is absent then the
1842
** default will be used.
1843
*/
1844

1845
int msOWSPrintMetadata(FILE *stream, hashTableObj *metadata,
×
1846
                       const char *namespaces, const char *name,
1847
                       int action_if_not_found, const char *format,
1848
                       const char *default_value) {
1849
  const char *value = NULL;
1850
  int status = MS_NOERR;
1851

1852
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)) != NULL) {
×
1853
    msIO_fprintf(stream, format, value);
×
1854
  } else {
1855
    if (action_if_not_found == OWS_WARN) {
×
1856
      char *pszExpandedMetadataKey =
1857
          msOWSGetExpandedMetadataKey(namespaces, name);
×
1858
      msIO_fprintf(stream,
×
1859
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1860
                   "context. -->\n",
1861
                   pszExpandedMetadataKey);
1862
      msFree(pszExpandedMetadataKey);
×
1863
      status = action_if_not_found;
1864
    }
1865

1866
    if (default_value)
×
1867
      msIO_fprintf(stream, format, default_value);
×
1868
  }
1869

1870
  return status;
×
1871
}
1872

1873
/*
1874
** msOWSPrintEncodeMetadata()
1875
**
1876
** Attempt to output a capability item.  If corresponding metadata is not
1877
** found then one of a number of predefined actions will be taken.
1878
** If a default value is provided and metadata is absent then the
1879
** default will be used.
1880
** Also encode the value with msEncodeHTMLEntities.
1881
*/
1882

1883
int msOWSPrintEncodeMetadata(FILE *stream, hashTableObj *metadata,
1,421✔
1884
                             const char *namespaces, const char *name,
1885
                             int action_if_not_found, const char *format,
1886
                             const char *default_value) {
1887
  return msOWSPrintEncodeMetadata2(stream, metadata, namespaces, name,
1,421✔
1888
                                   action_if_not_found, format, default_value,
1889
                                   NULL);
1,421✔
1890
}
1891

1892
/*
1893
** msOWSPrintEncodeMetadata2()
1894
**
1895
** Attempt to output a capability item in the requested language.
1896
** Fallback using no language parameter.
1897
*/
1898
int msOWSPrintEncodeMetadata2(FILE *stream, hashTableObj *metadata,
2,200✔
1899
                              const char *namespaces, const char *name,
1900
                              int action_if_not_found, const char *format,
1901
                              const char *default_value,
1902
                              const char *validated_language) {
1903
  const char *value;
1904
  char *pszEncodedValue = NULL;
1905
  int status = MS_NOERR;
1906

1907
  if ((value = msOWSLookupMetadataWithLanguage(metadata, namespaces, name,
2,200✔
1908
                                               validated_language))) {
1909
    pszEncodedValue = msEncodeHTMLEntities(value);
1,643✔
1910
    msIO_fprintf(stream, format, pszEncodedValue);
1,643✔
1911
    free(pszEncodedValue);
1,643✔
1912
  } else {
1913
    if (action_if_not_found == OWS_WARN) {
557✔
1914
      char *pszExpandedName = msStringConcatenate(NULL, name);
55✔
1915
      if (validated_language && validated_language[0]) {
55✔
1916
        pszExpandedName = msStringConcatenate(pszExpandedName, ".");
×
1917
        pszExpandedName =
1918
            msStringConcatenate(pszExpandedName, validated_language);
×
1919
      }
1920
      char *pszExpandedMetadataKey =
1921
          msOWSGetExpandedMetadataKey(namespaces, pszExpandedName);
55✔
1922
      msIO_fprintf(stream,
55✔
1923
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1924
                   "context. -->\n",
1925
                   pszExpandedMetadataKey);
1926
      msFree(pszExpandedName);
55✔
1927
      msFree(pszExpandedMetadataKey);
55✔
1928
      status = action_if_not_found;
1929
    }
1930

1931
    if (default_value) {
557✔
1932
      pszEncodedValue = msEncodeHTMLEntities(default_value);
211✔
1933
      msIO_fprintf(stream, format, default_value);
211✔
1934
      free(pszEncodedValue);
211✔
1935
    }
1936
  }
1937

1938
  return status;
2,200✔
1939
}
1940

1941
/*
1942
** msOWSGetEncodeMetadata()
1943
**
1944
** Equivalent to msOWSPrintEncodeMetadata. Returns en encoded value of the
1945
** metadata or the default value.
1946
** Caller should free the returned string.
1947
*/
1948
char *msOWSGetEncodeMetadata(hashTableObj *metadata, const char *namespaces,
385✔
1949
                             const char *name, const char *default_value) {
1950
  const char *value;
1951
  char *pszEncodedValue = NULL;
1952
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)))
385✔
1953
    pszEncodedValue = msEncodeHTMLEntities(value);
49✔
1954
  else if (default_value)
336✔
1955
    pszEncodedValue = msEncodeHTMLEntities(default_value);
278✔
1956

1957
  return pszEncodedValue;
385✔
1958
}
1959

1960
/*
1961
** msOWSPrintValidateMetadata()
1962
**
1963
** Attempt to output a capability item.  If corresponding metadata is not
1964
** found then one of a number of predefined actions will be taken.
1965
** If a default value is provided and metadata is absent then the
1966
** default will be used.
1967
** Also validate the value with msIsXMLTagValid.
1968
*/
1969

1970
int msOWSPrintValidateMetadata(FILE *stream, hashTableObj *metadata,
310✔
1971
                               const char *namespaces, const char *name,
1972
                               int action_if_not_found, const char *format,
1973
                               const char *default_value) {
1974
  const char *value;
1975
  int status = MS_NOERR;
1976

1977
  if ((value = msOWSLookupMetadata(metadata, namespaces, name))) {
310✔
1978
    if (msIsXMLTagValid(value) == MS_FALSE)
×
1979
      msIO_fprintf(stream,
×
1980
                   "<!-- WARNING: The value '%s' is not valid in a "
1981
                   "XML tag context. -->\n",
1982
                   value);
1983
    msIO_fprintf(stream, format, value);
×
1984
  } else {
1985
    if (action_if_not_found == OWS_WARN) {
310✔
1986
      char *pszExpandedMetadataKey =
1987
          msOWSGetExpandedMetadataKey(namespaces, name);
×
1988
      msIO_fprintf(stream,
×
1989
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1990
                   "context. -->\n",
1991
                   pszExpandedMetadataKey);
1992
      msFree(pszExpandedMetadataKey);
×
1993
      status = action_if_not_found;
1994
    }
1995

1996
    if (default_value) {
310✔
1997
      if (msIsXMLTagValid(default_value) == MS_FALSE)
310✔
1998
        msIO_fprintf(stream,
×
1999
                     "<!-- WARNING: The value '%s' is not valid "
2000
                     "in a XML tag context. -->\n",
2001
                     default_value);
2002
      msIO_fprintf(stream, format, default_value);
310✔
2003
    }
2004
  }
2005

2006
  return status;
310✔
2007
}
2008

2009
/*
2010
** msOWSPrintGroupMetadata()
2011
**
2012
** Attempt to output a capability item.  If corresponding metadata is not
2013
** found then one of a number of predefined actions will be taken.
2014
** If a default value is provided and metadata is absent then the
2015
** default will be used.
2016
*/
2017
int msOWSPrintGroupMetadata(FILE *stream, mapObj *map, char *pszGroupName,
×
2018
                            const char *namespaces, const char *name,
2019
                            int action_if_not_found, const char *format,
2020
                            const char *default_value) {
2021
  return msOWSPrintGroupMetadata2(stream, map, pszGroupName, namespaces, name,
×
2022
                                  action_if_not_found, format, default_value,
2023
                                  NULL);
×
2024
}
2025

2026
/*
2027
** msOWSPrintGroupMetadata2()
2028
**
2029
** Attempt to output a capability item in the requested language.
2030
** Fallback using no language parameter.
2031
*/
2032
int msOWSPrintGroupMetadata2(FILE *stream, mapObj *map, char *pszGroupName,
×
2033
                             const char *namespaces, const char *name,
2034
                             int action_if_not_found, const char *format,
2035
                             const char *default_value,
2036
                             const char *validated_language) {
2037
  const char *value;
2038
  char *encoded;
2039
  int status = MS_NOERR;
2040
  int i;
2041

2042
  for (i = 0; i < map->numlayers; i++) {
×
2043
    if (GET_LAYER(map, i)->group &&
×
2044
        (strcmp(GET_LAYER(map, i)->group, pszGroupName) == 0)) {
×
2045
      if ((value = msOWSLookupMetadataWithLanguage(
×
2046
               &(GET_LAYER(map, i)->metadata), namespaces, name,
2047
               validated_language))) {
2048
        encoded = msEncodeHTMLEntities(value);
×
2049
        msIO_fprintf(stream, format, encoded);
×
2050
        msFree(encoded);
×
2051
        return status;
×
2052
      }
2053
    }
2054
  }
2055

2056
  if (action_if_not_found == OWS_WARN) {
×
2057
    char *pszExpandedMetadataKey =
2058
        msOWSGetExpandedMetadataKey(namespaces, name);
×
2059
    msIO_fprintf(stream,
×
2060
                 "<!-- WARNING: Mandatory metadata %s was missing in this "
2061
                 "context. -->\n",
2062
                 pszExpandedMetadataKey);
2063
    msFree(pszExpandedMetadataKey);
×
2064
    status = action_if_not_found;
2065
  }
2066

2067
  if (default_value) {
×
2068
    encoded = msEncodeHTMLEntities(default_value);
×
2069
    msIO_fprintf(stream, format, encoded);
×
2070
    msFree(encoded);
×
2071
  }
2072

2073
  return status;
2074
}
2075

2076
/* msOWSPrintURLType()
2077
**
2078
** Attempt to output a URL item in capabilities.  If corresponding metadata
2079
** is not found then one of a number of predefined actions will be taken.
2080
** Since it's a capability item, five metadata will be used to populate the
2081
** XML elements.
2082
**
2083
** The 'name' argument is the basename of the metadata items relating to this
2084
** URL type and the suffixes _type, _width, _height, _format and _href will
2085
** be appended to the name in the metadata search.
2086
** e.g. passing name=metadataurl will result in the following medata entries
2087
** being used:
2088
**    ows_metadataurl_type
2089
**    ows_metadataurl_format
2090
**    ows_metadataurl_href
2091
**    ... (width and height are unused for metadata)
2092
**
2093
** As for all the msOWSPrint*() functions, the namespace argument specifies
2094
** which prefix (ows_, wms_, wcs_, etc.) is used for the metadata names above.
2095
**
2096
** Then the final string will be built from
2097
** the tag_name and the five metadata. The template is:
2098
** <tag_name%type%width%height%format>%href</tag_name>
2099
**
2100
** For example the width format will usually be " width=\"%s\"".
2101
** An extern format will be "> <Format>%s</Format"
2102
**
2103
** Another template template may be used, but it needs to contains 5 %s,
2104
** otherwise leave it to NULL. If tag_format is used then you don't need the
2105
** tag_name and the tabspace.
2106
**
2107
** Note that all values will be HTML-encoded.
2108
**/
2109
int msOWSPrintURLType(FILE *stream, hashTableObj *metadata,
717✔
2110
                      const char *namespaces, const char *name,
2111
                      int action_if_not_found, const char *tag_format,
2112
                      const char *tag_name, const char *type_format,
2113
                      const char *width_format, const char *height_format,
2114
                      const char *urlfrmt_format, const char *href_format,
2115
                      int type_is_mandatory, int width_is_mandatory,
2116
                      int height_is_mandatory, int format_is_mandatory,
2117
                      int href_is_mandatory, const char *default_type,
2118
                      const char *default_width, const char *default_height,
2119
                      const char *default_urlfrmt, const char *default_href,
2120
                      const char *tabspace) {
2121
  const char *value;
2122
  char *metadata_name;
2123
  char *encoded;
2124
  int status = MS_NOERR;
2125
  char *type = NULL, *width = NULL, *height = NULL, *urlfrmt = NULL,
2126
       *href = NULL;
2127

2128
  const size_t buffer_size = strlen(name) + 10;
717✔
2129
  metadata_name = (char *)malloc(buffer_size);
717✔
2130

2131
  /* Get type */
2132
  if (type_format != NULL) {
717✔
2133
    snprintf(metadata_name, buffer_size, "%s_type", name);
2134
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
297✔
2135
    if (value != NULL) {
297✔
2136
      encoded = msEncodeHTMLEntities(value);
233✔
2137
      const size_t buffer_size_tmp = strlen(type_format) + strlen(encoded) + 1;
233✔
2138
      type = (char *)malloc(buffer_size_tmp);
233✔
2139
      snprintf(type, buffer_size_tmp, type_format, encoded);
2140
      msFree(encoded);
233✔
2141
    }
2142
  }
2143

2144
  /* Get width */
2145
  if (width_format != NULL) {
717✔
2146
    snprintf(metadata_name, buffer_size, "%s_width", name);
2147
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
175✔
2148
    if (value != NULL) {
175✔
2149
      encoded = msEncodeHTMLEntities(value);
19✔
2150
      const size_t buffer_size_tmp = strlen(width_format) + strlen(encoded) + 1;
19✔
2151
      width = (char *)malloc(buffer_size_tmp);
19✔
2152
      snprintf(width, buffer_size_tmp, width_format, encoded);
2153
      msFree(encoded);
19✔
2154
    }
2155
  }
2156

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

2171
  /* Get format */
2172
  if (urlfrmt_format != NULL) {
717✔
2173
    snprintf(metadata_name, buffer_size, "%s_format", name);
2174
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
691✔
2175
    if (value != NULL) {
691✔
2176
      encoded = msEncodeHTMLEntities(value);
248✔
2177
      const size_t buffer_size_tmp =
248✔
2178
          strlen(urlfrmt_format) + strlen(encoded) + 1;
248✔
2179
      urlfrmt = (char *)malloc(buffer_size_tmp);
248✔
2180
      snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, encoded);
2181
      msFree(encoded);
248✔
2182
    }
2183
  }
2184

2185
  /* Get href */
2186
  if (href_format != NULL) {
717✔
2187
    snprintf(metadata_name, buffer_size, "%s_href", name);
2188
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
717✔
2189
    if (value != NULL) {
717✔
2190
      encoded = msEncodeHTMLEntities(value);
270✔
2191
      const size_t buffer_size_tmp = strlen(href_format) + strlen(encoded) + 1;
270✔
2192
      href = (char *)malloc(buffer_size_tmp);
270✔
2193
      snprintf(href, buffer_size_tmp, href_format, encoded);
2194
      msFree(encoded);
270✔
2195
    }
2196
  }
2197

2198
  msFree(metadata_name);
717✔
2199

2200
  if (type || width || height || urlfrmt || href ||
717✔
2201
      (!metadata && (default_type || default_width || default_height ||
156✔
2202
                     default_urlfrmt || default_href))) {
×
2203
    if ((!type && type_is_mandatory) || (!width && width_is_mandatory) ||
426✔
2204
        (!height && height_is_mandatory) || (!urlfrmt && format_is_mandatory) ||
426✔
2205
        (!href && href_is_mandatory)) {
426✔
2206
      msIO_fprintf(stream,
×
2207
                   "<!-- WARNING: Some mandatory elements for '%s' are missing "
2208
                   "in this context. -->\n",
2209
                   tag_name);
2210
      if (action_if_not_found == OWS_WARN) {
×
2211
        char *pszExpandedMetadataKey =
2212
            msOWSGetExpandedMetadataKey(namespaces, name);
×
2213
        msIO_fprintf(stream,
×
2214
                     "<!-- WARNING: Mandatory metadata %s was missing in this "
2215
                     "context. -->\n",
2216
                     pszExpandedMetadataKey);
2217
        msFree(pszExpandedMetadataKey);
×
2218
        status = action_if_not_found;
2219
      }
2220
    } else {
2221
      if (!type && type_format && default_type) {
426✔
2222
        const size_t buffer_size_tmp =
13✔
2223
            strlen(type_format) + strlen(default_type) + 2;
13✔
2224
        type = (char *)malloc(buffer_size_tmp);
13✔
2225
        snprintf(type, buffer_size_tmp, type_format, default_type);
2226
      } else if (!type)
413✔
2227
        type = msStrdup("");
180✔
2228
      if (!width && width_format && default_width) {
426✔
2229
        const size_t buffer_size_tmp =
156✔
2230
            strlen(width_format) + strlen(default_width) + 2;
156✔
2231
        width = (char *)malloc(buffer_size_tmp);
156✔
2232
        snprintf(width, buffer_size_tmp, width_format, default_width);
2233
      } else if (!width)
270✔
2234
        width = msStrdup("");
251✔
2235
      if (!height && height_format && default_height) {
426✔
2236
        const size_t buffer_size_tmp =
156✔
2237
            strlen(height_format) + strlen(default_height) + 2;
156✔
2238
        height = (char *)malloc(buffer_size_tmp);
156✔
2239
        snprintf(height, buffer_size_tmp, height_format, default_height);
2240
      } else if (!height)
270✔
2241
        height = msStrdup("");
251✔
2242
      if (!urlfrmt && urlfrmt_format && default_urlfrmt) {
426✔
2243
        const size_t buffer_size_tmp =
156✔
2244
            strlen(urlfrmt_format) + strlen(default_urlfrmt) + 2;
156✔
2245
        urlfrmt = (char *)malloc(buffer_size_tmp);
156✔
2246
        snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, default_urlfrmt);
2247
      } else if (!urlfrmt)
270✔
2248
        urlfrmt = msStrdup("");
22✔
2249
      if (!href && href_format && default_href) {
426✔
2250
        const size_t buffer_size_tmp =
156✔
2251
            strlen(href_format) + strlen(default_href) + 2;
156✔
2252
        href = (char *)malloc(buffer_size_tmp);
156✔
2253
        snprintf(href, buffer_size_tmp, href_format, default_href);
2254
      } else if (!href)
270✔
2255
        href = msStrdup("");
×
2256

2257
      if (tag_format == NULL)
426✔
2258
        msIO_fprintf(stream, "%s<%s%s%s%s%s>%s</%s>\n", tabspace, tag_name,
404✔
2259
                     type, width, height, urlfrmt, href, tag_name);
2260
      else
2261
        msIO_fprintf(stream, tag_format, type, width, height, urlfrmt, href);
22✔
2262
    }
2263

2264
    msFree(type);
426✔
2265
    msFree(width);
426✔
2266
    msFree(height);
426✔
2267
    msFree(urlfrmt);
426✔
2268
    msFree(href);
426✔
2269
  } else {
426✔
2270
    if (action_if_not_found == OWS_WARN) {
291✔
2271
      char *pszExpandedMetadataKey =
2272
          msOWSGetExpandedMetadataKey(namespaces, name);
×
2273
      msIO_fprintf(stream,
×
2274
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
2275
                   "context. -->\n",
2276
                   pszExpandedMetadataKey);
2277
      msFree(pszExpandedMetadataKey);
×
2278
      status = action_if_not_found;
2279
    }
2280
  }
2281

2282
  return status;
717✔
2283
}
2284

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

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

2305
    if (default_value)
×
2306
      msIO_fprintf(stream, format, default_value);
×
2307
  }
2308

2309
  return status;
×
2310
}
2311

2312
/* msOWSPrintEncodeParam()
2313
**
2314
** Same as printEncodeMetadata() but applied to mapfile parameters.
2315
**/
2316
int msOWSPrintEncodeParam(FILE *stream, const char *name, const char *value,
316✔
2317
                          int action_if_not_found, const char *format,
2318
                          const char *default_value) {
2319
  int status = MS_NOERR;
2320
  char *encode;
2321

2322
  if (value && strlen(value) > 0) {
316✔
2323
    encode = msEncodeHTMLEntities(value);
316✔
2324
    msIO_fprintf(stream, format, encode);
316✔
2325
    msFree(encode);
316✔
2326
  } else {
2327
    if (action_if_not_found == OWS_WARN) {
×
2328
      msIO_fprintf(stream,
×
2329
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2330
                   "in this context. -->\n",
2331
                   name);
2332
      status = action_if_not_found;
2333
    }
2334

2335
    if (default_value) {
×
2336
      encode = msEncodeHTMLEntities(default_value);
×
2337
      msIO_fprintf(stream, format, encode);
×
2338
      msFree(encode);
×
2339
    }
2340
  }
2341

2342
  return status;
316✔
2343
}
2344

2345
/* msOWSPrintMetadataList()
2346
**
2347
** Prints comma-separated lists metadata.  (e.g. keywordList)
2348
** default_value serves 2 purposes if specified:
2349
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2350
**  (exclusion)
2351
** - will be printed if MetadataList is empty (fallback)
2352
**/
2353
int msOWSPrintMetadataList(FILE *stream, hashTableObj *metadata,
×
2354
                           const char *namespaces, const char *name,
2355
                           const char *startTag, const char *endTag,
2356
                           const char *itemFormat, const char *default_value) {
2357
  const char *value;
2358

2359
  value = msOWSLookupMetadata(metadata, namespaces, name);
×
2360

2361
  if (value == NULL) {
×
2362
    value = default_value;
2363
    default_value = NULL;
2364
  }
2365

2366
  if (value != NULL) {
×
2367
    char **keywords;
2368
    int numkeywords;
2369

2370
    keywords = msStringSplit(value, ',', &numkeywords);
×
2371
    if (keywords && numkeywords > 0) {
×
2372
      int kw;
2373
      if (startTag)
×
2374
        msIO_fprintf(stream, "%s", startTag);
×
2375
      for (kw = 0; kw < numkeywords; kw++) {
×
2376
        if (default_value != NULL &&
×
2377
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
×
2378
                0 &&
×
2379
            strncasecmp("_exclude", default_value + strlen(default_value) - 8,
×
2380
                        8) == 0)
2381
          continue;
×
2382

2383
        msIO_fprintf(stream, itemFormat, keywords[kw]);
×
2384
      }
2385
      if (endTag)
×
2386
        msIO_fprintf(stream, "%s", endTag);
×
2387
    }
2388
    msFreeCharArray(keywords, numkeywords);
×
2389
    return MS_TRUE;
2390
  }
2391
  return MS_FALSE;
2392
}
2393

2394
/* msOWSPrintEncodeMetadataList()
2395
**
2396
** Prints comma-separated lists metadata.  (e.g. keywordList)
2397
** This will print HTML encoded values.
2398
** default_value serves 2 purposes if specified:
2399
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2400
**  (exclusion)
2401
** - will be printed if MetadataList is empty (fallback)
2402
**/
2403
int msOWSPrintEncodeMetadataList(FILE *stream, hashTableObj *metadata,
512✔
2404
                                 const char *namespaces, const char *name,
2405
                                 const char *startTag, const char *endTag,
2406
                                 const char *itemFormat,
2407
                                 const char *default_value) {
2408
  const char *value;
2409
  char *encoded;
2410
  size_t default_value_len = 0;
2411

2412
  value = msOWSLookupMetadata(metadata, namespaces, name);
512✔
2413

2414
  if (value == NULL) {
512✔
2415
    value = default_value;
2416
    default_value = NULL;
2417
  }
2418
  if (default_value)
270✔
2419
    default_value_len = strlen(default_value);
47✔
2420

2421
  if (value != NULL) {
512✔
2422
    char **keywords;
2423
    int numkeywords;
2424

2425
    keywords = msStringSplit(value, ',', &numkeywords);
273✔
2426
    if (keywords && numkeywords > 0) {
273✔
2427
      int kw;
2428
      if (startTag)
273✔
2429
        msIO_fprintf(stream, "%s", startTag);
45✔
2430
      for (kw = 0; kw < numkeywords; kw++) {
806✔
2431
        if (default_value != NULL && default_value_len > 8 &&
533✔
2432
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
103✔
2433
                0 &&
80✔
2434
            strncasecmp("_exclude", default_value + default_value_len - 8, 8) ==
40✔
2435
                0)
2436
          continue;
40✔
2437

2438
        encoded = msEncodeHTMLEntities(keywords[kw]);
493✔
2439
        msIO_fprintf(stream, itemFormat, encoded);
493✔
2440
        msFree(encoded);
493✔
2441
      }
2442
      if (endTag)
273✔
2443
        msIO_fprintf(stream, "%s", endTag);
45✔
2444
    }
2445
    msFreeCharArray(keywords, numkeywords);
273✔
2446
    return MS_TRUE;
2447
  }
2448
  return MS_FALSE;
2449
}
2450

2451
/* msOWSPrintEncodeParamList()
2452
**
2453
** Same as msOWSPrintEncodeMetadataList() but applied to mapfile parameters.
2454
**/
2455
int msOWSPrintEncodeParamList(FILE *stream, const char *name, const char *value,
224✔
2456
                              int action_if_not_found, char delimiter,
2457
                              const char *startTag, const char *endTag,
2458
                              const char *format, const char *default_value) {
2459
  int status = MS_NOERR;
2460
  char *encoded;
2461
  char **items = NULL;
2462
  int numitems = 0, i;
224✔
2463

2464
  if (value && strlen(value) > 0)
224✔
2465
    items = msStringSplit(value, delimiter, &numitems);
164✔
2466
  else {
2467
    if (action_if_not_found == OWS_WARN) {
60✔
2468
      msIO_fprintf(stream,
4✔
2469
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2470
                   "in this context. -->\n",
2471
                   name);
2472
      status = action_if_not_found;
2473
    }
2474

2475
    if (default_value)
60✔
2476
      items = msStringSplit(default_value, delimiter, &numitems);
2✔
2477
  }
2478

2479
  if (items && numitems > 0) {
166✔
2480
    if (startTag)
166✔
2481
      msIO_fprintf(stream, "%s", startTag);
×
2482
    for (i = 0; i < numitems; i++) {
476✔
2483
      encoded = msEncodeHTMLEntities(items[i]);
310✔
2484
      msIO_fprintf(stream, format, encoded);
310✔
2485
      msFree(encoded);
310✔
2486
    }
2487
    if (endTag)
166✔
2488
      msIO_fprintf(stream, "%s", endTag);
×
2489
  }
2490
  msFreeCharArray(items, numitems);
224✔
2491

2492
  return status;
224✔
2493
}
2494

2495
/*
2496
** msOWSPrintEX_GeographicBoundingBox()
2497
**
2498
** Print a EX_GeographicBoundingBox tag for WMS1.3.0
2499
**
2500
*/
2501
void msOWSPrintEX_GeographicBoundingBox(FILE *stream, const char *tabspace,
119✔
2502
                                        rectObj *extent, projectionObj *srcproj)
2503

2504
{
2505
  const char *pszTag = "EX_GeographicBoundingBox"; /* The default for WMS */
2506
  rectObj ext;
2507

2508
  ext = *extent;
119✔
2509

2510
  /* always project to lat long */
2511
  msOWSProjectToWGS84(srcproj, &ext);
119✔
2512

2513
  msIO_fprintf(stream, "%s<%s>\n", tabspace, pszTag);
119✔
2514
  msIO_fprintf(stream, "%s    <westBoundLongitude>%.6f</westBoundLongitude>\n",
119✔
2515
               tabspace, ext.minx);
2516
  msIO_fprintf(stream, "%s    <eastBoundLongitude>%.6f</eastBoundLongitude>\n",
119✔
2517
               tabspace, ext.maxx);
2518
  msIO_fprintf(stream, "%s    <southBoundLatitude>%.6f</southBoundLatitude>\n",
119✔
2519
               tabspace, ext.miny);
2520
  msIO_fprintf(stream, "%s    <northBoundLatitude>%.6f</northBoundLatitude>\n",
119✔
2521
               tabspace, ext.maxy);
2522
  msIO_fprintf(stream, "%s</%s>\n", tabspace, pszTag);
119✔
2523

2524
  /* msIO_fprintf(stream, "%s<%s minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"
2525
     />\n", tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy); */
2526
}
119✔
2527

2528
/*
2529
** msOWSPrintLatLonBoundingBox()
2530
**
2531
** Print a LatLonBoundingBox tag for WMS, or LatLongBoundingBox for WFS
2532
** ... yes, the tag name differs between WMS and WFS, yuck!
2533
**
2534
*/
2535
void msOWSPrintLatLonBoundingBox(FILE *stream, const char *tabspace,
139✔
2536
                                 rectObj *extent, projectionObj *srcproj,
2537
                                 projectionObj *wfsproj,
2538
                                 OWSServiceType nService) {
2539
  const char *pszTag = "LatLonBoundingBox"; /* The default for WMS */
2540
  rectObj ext;
2541

2542
  ext = *extent;
139✔
2543

2544
  if (nService == OWS_WMS) { /* always project to lat long */
139✔
2545
    msOWSProjectToWGS84(srcproj, &ext);
114✔
2546
  } else if (nService == OWS_WFS) { /* called from wfs 1.0.0 only: project to
25✔
2547
                                       map srs, if set */
2548
    pszTag = "LatLongBoundingBox";
2549
    if (wfsproj) {
25✔
2550
      if (msProjectionsDiffer(srcproj, wfsproj) == MS_TRUE)
25✔
2551
        msProjectRect(srcproj, wfsproj, &ext);
16✔
2552
    }
2553
  }
2554

2555
  msIO_fprintf(
139✔
2556
      stream,
2557
      "%s<%s minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" maxy=\"%.6f\" />\n",
2558
      tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy);
2559
}
139✔
2560

2561
/*
2562
** Emit a bounding box if we can find projection information.
2563
** If <namespaces>_bbox_extended is not set, emit a single bounding box
2564
** using the layer's native SRS (ignoring any <namespaces>_srs metadata).
2565
**
2566
** If <namespaces>_bbox_extended is set to true, emit a bounding box
2567
** for every projection listed in the <namespaces>_srs list.
2568
** Check the map level metadata for both _bbox_extended and _srs,
2569
** if there is no such metadata at the layer level.
2570
** (These settings make more sense at the global/map level anyways)
2571
*/
2572
void msOWSPrintBoundingBox(FILE *stream, const char *tabspace, rectObj *extent,
233✔
2573
                           projectionObj *srcproj, hashTableObj *layer_meta,
2574
                           hashTableObj *map_meta, const char *namespaces,
2575
                           int wms_version) {
2576
  const char *value, *resx, *resy, *wms_bbox_extended;
2577
  char *encoded, *encoded_resx, *encoded_resy, *epsg_str;
2578
  char **epsgs;
2579
  int i, num_epsgs;
2580
  projectionObj proj;
2581
  rectObj ext;
2582

2583
  wms_bbox_extended =
2584
      msOWSLookupMetadata2(layer_meta, map_meta, namespaces, "bbox_extended");
233✔
2585
  if (wms_bbox_extended && strncasecmp(wms_bbox_extended, "true", 5) == 0) {
233✔
2586
    /* get a list of all projections from the metadata
2587
       try the layer metadata first, otherwise use the map's */
2588
    if (msOWSLookupMetadata(layer_meta, namespaces, "srs")) {
126✔
2589
      msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_FALSE, &epsg_str);
×
2590
    } else {
2591
      msOWSGetEPSGProj(srcproj, map_meta, namespaces, MS_FALSE, &epsg_str);
126✔
2592
    }
2593
    epsgs = msStringSplit(epsg_str, ' ', &num_epsgs);
126✔
2594
    msFree(epsg_str);
126✔
2595
  } else {
2596
    /* Look for EPSG code in PROJECTION block only.  "wms_srs" metadata cannot
2597
     * be used to establish the native projection of a layer for BoundingBox
2598
     * purposes.
2599
     */
2600
    epsgs = (char **)msSmallMalloc(sizeof(char *));
107✔
2601
    num_epsgs = 1;
107✔
2602
    msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_TRUE, &(epsgs[0]));
107✔
2603
  }
2604

2605
  for (i = 0; i < num_epsgs; i++) {
1,798✔
2606
    value = epsgs[i];
1,565✔
2607
    if (value && *value) {
1,565✔
2608
      memcpy(&ext, extent, sizeof(rectObj));
2609

2610
      /* reproject the extents for each SRS's bounding box */
2611
      msInitProjection(&proj);
1,545✔
2612
      msProjectionInheritContextFrom(&proj, srcproj);
1,545✔
2613
      if (msLoadProjectionStringEPSG(&proj, (char *)value) == 0) {
1,545✔
2614
        if (msProjectionsDiffer(srcproj, &proj) == MS_TRUE) {
1,527✔
2615
          msProjectRect(srcproj, &proj, &ext);
1,439✔
2616
        }
2617
        /*for wms 1.3.0 we need to make sure that we present the BBOX with
2618
          a reversed axes for some espg codes*/
2619
        if (wms_version >= OWS_1_3_0 && strncasecmp(value, "EPSG:", 5) == 0) {
1,527✔
2620
          msAxisNormalizePoints(&proj, 1, &(ext.minx), &(ext.miny));
775✔
2621
          msAxisNormalizePoints(&proj, 1, &(ext.maxx), &(ext.maxy));
775✔
2622
        }
2623
      }
2624

2625
      encoded = msEncodeHTMLEntities(value);
1,545✔
2626
      if (msProjIsGeographicCRS(&proj))
1,545✔
2627
        msIO_fprintf(stream,
584✔
2628
                     "%s<BoundingBox %s=\"%s\"\n"
2629
                     "%s            minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" "
2630
                     "maxy=\"%.6f\"",
2631
                     tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS",
2632
                     encoded, tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2633
      else
2634
        msIO_fprintf(
1,720✔
2635
            stream,
2636
            "%s<BoundingBox %s=\"%s\"\n"
2637
            "%s            minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"",
2638
            tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS", encoded,
2639
            tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2640

2641
      msFree(encoded);
1,545✔
2642
      msFreeProjection(&proj);
1,545✔
2643

2644
      if ((resx = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resx")) !=
1,545✔
2645
              NULL &&
1,547✔
2646
          (resy = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resy")) !=
2✔
2647
              NULL) {
2648
        encoded_resx = msEncodeHTMLEntities(resx);
2✔
2649
        encoded_resy = msEncodeHTMLEntities(resy);
2✔
2650
        msIO_fprintf(stream, "\n%s            resx=\"%s\" resy=\"%s\"",
2✔
2651
                     tabspace, encoded_resx, encoded_resy);
2652
        msFree(encoded_resx);
2✔
2653
        msFree(encoded_resy);
2✔
2654
      }
2655

2656
      msIO_fprintf(stream, " />\n");
1,545✔
2657
    }
2658
  }
2659
  msFreeCharArray(epsgs, num_epsgs);
233✔
2660
}
233✔
2661

2662
/*
2663
** Print the contact information
2664
*/
2665
void msOWSPrintContactInfo(FILE *stream, const char *tabspace, int nVersion,
62✔
2666
                           hashTableObj *metadata, const char *namespaces) {
2667
  /* contact information is a required element in 1.0.7 but the */
2668
  /* sub-elements such as ContactPersonPrimary, etc. are not! */
2669
  /* In 1.1.0, ContactInformation becomes optional. */
2670
  if (nVersion > OWS_1_0_0) {
62✔
2671
    msIO_fprintf(stream, "%s<ContactInformation>\n", tabspace);
60✔
2672

2673
    /* ContactPersonPrimary is optional, but when present then all its  */
2674
    /* sub-elements are mandatory */
2675

2676
    if (msOWSLookupMetadata(metadata, namespaces, "contactperson") ||
86✔
2677
        msOWSLookupMetadata(metadata, namespaces, "contactorganization")) {
26✔
2678
      msIO_fprintf(stream, "%s  <ContactPersonPrimary>\n", tabspace);
34✔
2679

2680
      msOWSPrintEncodeMetadata(
34✔
2681
          stream, metadata, namespaces, "contactperson", OWS_WARN,
2682
          "      <ContactPerson>%s</ContactPerson>\n", NULL);
2683
      msOWSPrintEncodeMetadata(
34✔
2684
          stream, metadata, namespaces, "contactorganization", OWS_WARN,
2685
          "      <ContactOrganization>%s</ContactOrganization>\n", NULL);
2686
      msIO_fprintf(stream, "%s  </ContactPersonPrimary>\n", tabspace);
34✔
2687
    }
2688

2689
    if (msOWSLookupMetadata(metadata, namespaces, "contactposition")) {
60✔
2690
      msOWSPrintEncodeMetadata(
34✔
2691
          stream, metadata, namespaces, "contactposition", OWS_NOERR,
2692
          "      <ContactPosition>%s</ContactPosition>\n", NULL);
2693
    }
2694

2695
    /* ContactAddress is optional, but when present then all its  */
2696
    /* sub-elements are mandatory */
2697
    if (msOWSLookupMetadata(metadata, namespaces, "addresstype") ||
86✔
2698
        msOWSLookupMetadata(metadata, namespaces, "address") ||
52✔
2699
        msOWSLookupMetadata(metadata, namespaces, "city") ||
52✔
2700
        msOWSLookupMetadata(metadata, namespaces, "stateorprovince") ||
52✔
2701
        msOWSLookupMetadata(metadata, namespaces, "postcode") ||
112✔
2702
        msOWSLookupMetadata(metadata, namespaces, "country")) {
26✔
2703
      msIO_fprintf(stream, "%s  <ContactAddress>\n", tabspace);
34✔
2704

2705
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "addresstype",
34✔
2706
                               OWS_WARN,
2707
                               "        <AddressType>%s</AddressType>\n", NULL);
2708
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "address",
34✔
2709
                               OWS_WARN, "        <Address>%s</Address>\n",
2710
                               NULL);
2711
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "city", OWS_WARN,
34✔
2712
                               "        <City>%s</City>\n", NULL);
2713
      msOWSPrintEncodeMetadata(
34✔
2714
          stream, metadata, namespaces, "stateorprovince", OWS_WARN,
2715
          "        <StateOrProvince>%s</StateOrProvince>\n", NULL);
2716
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "postcode",
34✔
2717
                               OWS_WARN, "        <PostCode>%s</PostCode>\n",
2718
                               NULL);
2719
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "country",
34✔
2720
                               OWS_WARN, "        <Country>%s</Country>\n",
2721
                               NULL);
2722
      msIO_fprintf(stream, "%s  </ContactAddress>\n", tabspace);
34✔
2723
    }
2724

2725
    if (msOWSLookupMetadata(metadata, namespaces, "contactvoicetelephone")) {
60✔
2726
      msOWSPrintEncodeMetadata(
34✔
2727
          stream, metadata, namespaces, "contactvoicetelephone", OWS_NOERR,
2728
          "      <ContactVoiceTelephone>%s</ContactVoiceTelephone>\n", NULL);
2729
    }
2730

2731
    if (msOWSLookupMetadata(metadata, namespaces,
60✔
2732
                            "contactfacsimiletelephone")) {
2733
      msOWSPrintEncodeMetadata(
28✔
2734
          stream, metadata, namespaces, "contactfacsimiletelephone", OWS_NOERR,
2735
          "      <ContactFacsimileTelephone>%s</ContactFacsimileTelephone>\n",
2736
          NULL);
2737
    }
2738

2739
    if (msOWSLookupMetadata(metadata, namespaces,
60✔
2740
                            "contactelectronicmailaddress")) {
2741
      msOWSPrintEncodeMetadata(
34✔
2742
          stream, metadata, namespaces, "contactelectronicmailaddress",
2743
          OWS_NOERR,
2744
          "  <ContactElectronicMailAddress>%s</ContactElectronicMailAddress>\n",
2745
          NULL);
2746
    }
2747
    msIO_fprintf(stream, "%s</ContactInformation>\n", tabspace);
60✔
2748
  }
2749
}
62✔
2750

2751
/*
2752
** msOWSGetLayerExtent()
2753
** Try to establish layer extent using the following order of priority:
2754
** 1. layer "*_extent" metadata
2755
** 2. layer->extent if valid
2756
** 3. map->extent if valid and the fallback_to_map_extent metadata item is set
2757
** 4. msLayerGetExtent() to read extent from the layer
2758
**
2759
** Returns MS_FAILURE if none of the above return a valid extent.
2760
**
2761
*/
2762
int msOWSGetLayerExtent(mapObj *map, layerObj *lp, const char *namespaces,
1,023✔
2763
                        rectObj *ext) {
2764
  const char *value;
2765
  if ((value = msOWSLookupMetadata(&(lp->metadata), namespaces, "extent")) !=
1,023✔
2766
      NULL) {
2767
    char **tokens;
2768
    int n;
2769
    tokens = msStringSplit(value, ' ', &n);
292✔
2770
    if (tokens == NULL || n != 4) {
292✔
2771
      msSetError(MS_WMSERR, "Wrong number of arguments for EXTENT metadata.",
×
2772
                 "msOWSGetLayerExtent()");
2773
      return MS_FAILURE;
×
2774
    }
2775
    ext->minx = atof(tokens[0]);
292✔
2776
    ext->miny = atof(tokens[1]);
292✔
2777
    ext->maxx = atof(tokens[2]);
292✔
2778
    ext->maxy = atof(tokens[3]);
292✔
2779

2780
    msFreeCharArray(tokens, n);
292✔
2781
    return MS_SUCCESS;
292✔
2782
  }
2783

2784
  if (MS_VALID_EXTENT(lp->extent)) {
731✔
2785
    *ext = lp->extent;
77✔
2786
    return MS_SUCCESS;
77✔
2787
  }
2788

2789
  // Check if we can use the MAP EXTENT rather than calculating
2790
  // a LAYER extent by querying its datasource using msLayerGetExtent.
2791
  // First check the LAYER metadata, then the MAP metadata
2792
  const char *pszUseMapExtent = msOWSLookupMetadata(&(lp->metadata), namespaces,
654✔
2793
                                                    "fallback_to_map_extent");
2794
  if (!pszUseMapExtent) {
654✔
2795
    pszUseMapExtent = msOWSLookupMetadata(&(map->web.metadata), namespaces,
653✔
2796
                                          "fallback_to_map_extent");
2797
  }
2798

2799
  if (pszUseMapExtent && CSLTestBoolean(pszUseMapExtent) &&
654✔
2800
      MS_VALID_EXTENT(map->extent)) {
2✔
2801

2802
    if (msProjectionsDiffer(&(lp->projection), &(map->projection)) == MS_TRUE) {
2✔
2803
      rectObj projectedExt = map->extent;
1✔
2804
      if (msProjectRect(&(map->projection), &(lp->projection), &projectedExt) ==
1✔
2805
          MS_SUCCESS) {
2806
        *ext = projectedExt;
1✔
2807
        return MS_SUCCESS;
1✔
2808
      }
2809
    } else {
2810
      *ext = map->extent;
1✔
2811
      return MS_SUCCESS;
1✔
2812
    }
2813
  }
2814

2815
  if (msLayerGetExtent(lp, ext) == MS_SUCCESS) {
652✔
2816
    return MS_SUCCESS;
2817
  }
2818

2819
  return MS_FAILURE;
2820
}
2821

2822
/**********************************************************************
2823
 *                          msOWSExecuteRequests()
2824
 *
2825
 * Execute a number of WFS/WMS HTTP requests in parallel, and then
2826
 * update layerObj information with the result of the requests.
2827
 **********************************************************************/
2828
int msOWSExecuteRequests(httpRequestObj *pasReqInfo, int numRequests,
44✔
2829
                         mapObj *map, int bCheckLocalCache) {
2830
  int nStatus, iReq;
2831

2832
  /* Execute requests */
2833
#if defined(USE_CURL)
2834
  nStatus = msHTTPExecuteRequests(pasReqInfo, numRequests, bCheckLocalCache);
44✔
2835
#else
2836
  msSetError(MS_WMSERR,
2837
             "msOWSExecuteRequests() called apparently without libcurl "
2838
             "configured, msHTTPExecuteRequests() not available.",
2839
             "msOWSExecuteRequests()");
2840
  return MS_FAILURE;
2841
#endif
2842

2843
  /* Scan list of layers and call the handler for each layer type to */
2844
  /* pass them the request results. */
2845
  for (iReq = 0; iReq < numRequests; iReq++) {
88✔
2846
    if (pasReqInfo[iReq].nLayerId >= 0 &&
44✔
2847
        pasReqInfo[iReq].nLayerId < map->numlayers) {
44✔
2848
      layerObj *lp;
2849

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

2852
      if (lp->connectiontype == MS_WFS)
44✔
2853
        msWFSUpdateRequestInfo(lp, &(pasReqInfo[iReq]));
3✔
2854
    }
2855
  }
2856

2857
  return nStatus;
44✔
2858
}
2859

2860
/**********************************************************************
2861
 *                          msOWSProcessException()
2862
 *
2863
 **********************************************************************/
2864
void msOWSProcessException(layerObj *lp, const char *pszFname, int nErrorCode,
×
2865
                           const char *pszFuncName) {
2866
  FILE *fp;
2867

2868
  if ((fp = fopen(pszFname, "r")) != NULL) {
×
2869
    char *pszBuf = NULL;
2870
    int nBufSize = 0;
2871
    char *pszStart, *pszEnd;
2872

2873
    fseek(fp, 0, SEEK_END);
×
2874
    nBufSize = ftell(fp);
×
2875
    if (nBufSize < 0) {
×
2876
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
×
2877
      fclose(fp);
×
2878
      return;
×
2879
    }
2880
    rewind(fp);
×
2881
    pszBuf = (char *)malloc((nBufSize + 1) * sizeof(char));
×
2882
    if (pszBuf == NULL) {
×
2883
      msSetError(MS_MEMERR, NULL, "msOWSProcessException()");
×
2884
      fclose(fp);
×
2885
      return;
×
2886
    }
2887

2888
    if ((int)fread(pszBuf, 1, nBufSize, fp) != nBufSize) {
×
2889
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
×
2890
      free(pszBuf);
×
2891
      fclose(fp);
×
2892
      return;
×
2893
    }
2894

2895
    pszBuf[nBufSize] = '\0';
×
2896

2897
    /* OK, got the data in the buffer.  Look for the <Message> tags */
2898
    if ((strstr(pszBuf, "<WFS_Exception>") && /* WFS style */
×
2899
         (pszStart = strstr(pszBuf, "<Message>")) &&
×
2900
         (pszEnd = strstr(pszStart, "</Message>"))) ||
×
2901
        (strstr(pszBuf, "<ServiceExceptionReport>") && /* WMS style */
×
2902
         (pszStart = strstr(pszBuf, "<ServiceException>")) &&
×
2903
         (pszEnd = strstr(pszStart, "</ServiceException>")))) {
2904
      pszStart = strchr(pszStart, '>') + 1;
×
2905
      *pszEnd = '\0';
×
2906
      msSetError(nErrorCode, "Got Remote Server Exception for layer %s: %s",
×
2907
                 pszFuncName, lp->name ? lp->name : "(null)", pszStart);
×
2908
    } else {
2909
      msSetError(
×
2910
          MS_WFSCONNERR,
2911
          "Unable to parse Remote Server Exception Message for layer %s.",
2912
          pszFuncName, lp->name ? lp->name : "(null)");
×
2913
    }
2914

2915
    free(pszBuf);
×
2916
    fclose(fp);
×
2917
  }
2918
}
2919

2920
/**********************************************************************
2921
 *                          msOWSBuildURLFilename()
2922
 *
2923
 * Build a unique filename for this URL to use in caching remote server
2924
 * requests.  Slashes and illegal characters will be turned into '_'
2925
 *
2926
 * Returns a newly allocated buffer that should be freed by the caller or
2927
 * NULL in case of error.
2928
 **********************************************************************/
2929
char *msOWSBuildURLFilename(const char *pszPath, const char *pszURL,
×
2930
                            const char *pszExt) {
2931
  char *pszBuf, *pszPtr;
2932
  int i;
2933
  size_t nBufLen = 0;
2934

2935
  nBufLen = strlen(pszURL) + strlen(pszExt) + 2;
×
2936
  if (pszPath)
×
2937
    nBufLen += (strlen(pszPath) + 1);
×
2938

2939
  pszBuf = (char *)malloc(nBufLen);
×
2940
  if (pszBuf == NULL) {
×
2941
    msSetError(MS_MEMERR, NULL, "msOWSBuildURLFilename()");
×
2942
    return NULL;
×
2943
  }
2944
  pszBuf[0] = '\0';
×
2945

2946
  if (pszPath) {
×
2947
#ifdef _WIN32
2948
    if (pszPath[strlen(pszPath) - 1] != '/' &&
2949
        pszPath[strlen(pszPath) - 1] != '\\')
2950
      snprintf(pszBuf, nBufLen, "%s\\", pszPath);
2951
    else
2952
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2953
#else
2954
    if (pszPath[strlen(pszPath) - 1] != '/')
×
2955
      snprintf(pszBuf, nBufLen, "%s/", pszPath);
2956
    else
2957
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2958
#endif
2959
  }
2960

2961
  pszPtr = pszBuf + strlen(pszBuf);
×
2962

2963
  for (i = 0; pszURL[i] != '\0'; i++) {
×
2964
    if (isalnum(pszURL[i]))
×
2965
      *pszPtr = pszURL[i];
×
2966
    else
2967
      *pszPtr = '_';
×
2968
    pszPtr++;
×
2969
  }
2970

2971
  strlcpy(pszPtr, pszExt, nBufLen);
2972

2973
  return pszBuf;
2974
}
2975

2976
/*
2977
** msOWSGetProjURN()
2978
**
2979
** Fetch an OGC URN for this layer or map.  Similar to msOWSGetEPSGProj()
2980
** but returns the result in the form "urn:ogc:def:crs:EPSG::27700".
2981
** The returned buffer is dynamically allocated, and must be freed by the
2982
** caller.
2983
*/
2984
char *msOWSGetProjURN(projectionObj *proj, hashTableObj *metadata,
459✔
2985
                      const char *namespaces, int bReturnOnlyFirstOne) {
2986
  char *result;
2987
  char **tokens;
2988
  int numtokens, i;
2989
  char *oldStyle = NULL;
459✔
2990

2991
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
459✔
2992

2993
  if (oldStyle == NULL || strncmp(oldStyle, "CRS:", 4) == 0 ||
459✔
2994
      strncmp(oldStyle, "AUTO:", 5) == 0 ||
451✔
2995
      strncmp(oldStyle, "AUTO2:", 6) == 0) {
451✔
2996
    msFree(oldStyle);
8✔
2997
    return NULL;
8✔
2998
  }
2999

3000
  result = msStrdup("");
451✔
3001

3002
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
451✔
3003
  msFree(oldStyle);
451✔
3004
  for (i = 0; tokens != NULL && i < numtokens; i++) {
956✔
3005
    char urn[100];
3006
    char *colon = strchr(tokens[i], ':');
505✔
3007

3008
    if (colon != NULL && strchr(colon + 1, ':') == NULL) {
505✔
3009
      *colon = 0;
505✔
3010
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:%s::%s", tokens[i],
505✔
3011
               colon + 1);
3012
    } else if (strcasecmp(tokens[i], "imageCRS") == 0)
×
3013
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:OGC::imageCRS");
3014
    else if (strncmp(tokens[i], "urn:ogc:def:crs:", 16) == 0) {
×
3015
      strlcpy(urn, tokens[i], sizeof(urn));
3016
    } else {
3017
      strlcpy(urn, "", sizeof(urn));
3018
    }
3019

3020
    if (strlen(urn) > 0) {
505✔
3021
      const size_t bufferSize = strlen(result) + strlen(urn) + 2;
505✔
3022
      result = (char *)msSmallRealloc(result, bufferSize);
505✔
3023

3024
      if (strlen(result) > 0)
505✔
3025
        strlcat(result, " ", bufferSize);
3026
      strlcat(result, urn, bufferSize);
3027
    } else {
3028
      msDebug("msOWSGetProjURN(): Failed to process SRS '%s', ignored.",
×
3029
              tokens[i]);
3030
    }
3031
  }
3032

3033
  msFreeCharArray(tokens, numtokens);
451✔
3034

3035
  if (strlen(result) == 0) {
451✔
3036
    msFree(result);
×
3037
    return NULL;
×
3038
  } else
3039
    return result;
3040
}
3041

3042
/*
3043
** msOWSGetProjURI()
3044
**
3045
** Fetch an OGC URI for this layer or map.  Similar to msOWSGetEPSGProj()
3046
** but returns the result in the form
3047
*"http://www.opengis.net/def/crs/EPSG/0/27700".
3048
** The returned buffer is dynamically allocated, and must be freed by the
3049
** caller.
3050
*/
3051
char *msOWSGetProjURI(projectionObj *proj, hashTableObj *metadata,
243✔
3052
                      const char *namespaces, int bReturnOnlyFirstOne) {
3053
  char *result;
3054
  char **tokens;
3055
  int numtokens, i;
3056
  char *oldStyle = NULL;
243✔
3057

3058
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
243✔
3059

3060
  if (oldStyle == NULL || !EQUALN(oldStyle, "EPSG:", 5)) {
243✔
3061
    msFree(oldStyle); // avoid leak
2✔
3062
    return NULL;
2✔
3063
  }
3064

3065
  result = msStrdup("");
241✔
3066

3067
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
241✔
3068
  msFree(oldStyle);
241✔
3069
  for (i = 0; tokens != NULL && i < numtokens; i++) {
515✔
3070
    char urn[100];
3071

3072
    if (strncmp(tokens[i], "EPSG:", 5) == 0)
274✔
3073
      snprintf(urn, sizeof(urn), "http://www.opengis.net/def/crs/EPSG/0/%s",
274✔
3074
               tokens[i] + 5);
3075
    else if (strcasecmp(tokens[i], "imageCRS") == 0)
×
3076
      snprintf(urn, sizeof(urn),
3077
               "http://www.opengis.net/def/crs/OGC/0/imageCRS");
3078
    else if (strncmp(tokens[i], "http://www.opengis.net/def/crs/", 16) == 0)
×
3079
      snprintf(urn, sizeof(urn), "%s", tokens[i]);
3080
    else
3081
      strlcpy(urn, "", sizeof(urn));
3082

3083
    if (strlen(urn) > 0) {
274✔
3084
      result = (char *)msSmallRealloc(result, strlen(result) + strlen(urn) + 2);
274✔
3085

3086
      if (strlen(result) > 0)
274✔
3087
        strcat(result, " ");
3088
      strcat(result, urn);
3089
    } else {
3090
      msDebug("msOWSGetProjURI(): Failed to process SRS '%s', ignored.",
×
3091
              tokens[i]);
3092
    }
3093
  }
3094

3095
  msFreeCharArray(tokens, numtokens);
241✔
3096

3097
  if (strlen(result) == 0) {
241✔
3098
    msFree(result);
×
3099
    return NULL;
×
3100
  } else
3101
    return result;
3102
}
3103

3104
/*
3105
** msOWSGetDimensionInfo()
3106
**
3107
** Extract dimension information from a layer's metadata
3108
**
3109
** Before 4.9, only the time dimension was support. With the addition of
3110
** Web Map Context 1.1.0, we need to support every dimension types.
3111
** This function get the dimension information from special metadata in
3112
** the layer, but can also return default values for the time dimension.
3113
**
3114
*/
3115
void msOWSGetDimensionInfo(layerObj *layer, const char *pszDimension,
×
3116
                           const char **papszDimUserValue,
3117
                           const char **papszDimUnits,
3118
                           const char **papszDimDefault,
3119
                           const char **papszDimNearValue,
3120
                           const char **papszDimUnitSymbol,
3121
                           const char **papszDimMultiValue) {
3122
  char *pszDimensionItem;
3123
  size_t bufferSize = 0;
3124

3125
  if (pszDimension == NULL || layer == NULL)
×
3126
    return;
3127

3128
  bufferSize = strlen(pszDimension) + 50;
×
3129
  pszDimensionItem = (char *)malloc(bufferSize);
×
3130

3131
  /* units (mandatory in map context) */
3132
  if (papszDimUnits != NULL) {
×
3133
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_units", pszDimension);
3134
    *papszDimUnits =
×
3135
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3136
  }
3137
  /* unitSymbol (mandatory in map context) */
3138
  if (papszDimUnitSymbol != NULL) {
×
3139
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_unitsymbol",
3140
             pszDimension);
3141
    *papszDimUnitSymbol =
×
3142
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3143
  }
3144
  /* userValue (mandatory in map context) */
3145
  if (papszDimUserValue != NULL) {
×
3146
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_uservalue",
3147
             pszDimension);
3148
    *papszDimUserValue =
×
3149
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3150
  }
3151
  /* default */
3152
  if (papszDimDefault != NULL) {
×
3153
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_default",
3154
             pszDimension);
3155
    *papszDimDefault =
×
3156
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3157
  }
3158
  /* multipleValues */
3159
  if (papszDimMultiValue != NULL) {
×
3160
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_multiplevalues",
3161
             pszDimension);
3162
    *papszDimMultiValue =
×
3163
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3164
  }
3165
  /* nearestValue */
3166
  if (papszDimNearValue != NULL) {
×
3167
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_nearestvalue",
3168
             pszDimension);
3169
    *papszDimNearValue =
×
3170
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3171
  }
3172

3173
  /* Use default time value if necessary */
3174
  if (strcasecmp(pszDimension, "time") == 0) {
×
3175
    if (papszDimUserValue != NULL && *papszDimUserValue == NULL)
×
3176
      *papszDimUserValue =
×
3177
          msOWSLookupMetadata(&(layer->metadata), "MO", "time");
×
3178
    if (papszDimDefault != NULL && *papszDimDefault == NULL)
×
3179
      *papszDimDefault =
×
3180
          msOWSLookupMetadata(&(layer->metadata), "MO", "timedefault");
×
3181
    if (papszDimUnits != NULL && *papszDimUnits == NULL)
×
3182
      *papszDimUnits = "ISO8601";
×
3183
    if (papszDimUnitSymbol != NULL && *papszDimUnitSymbol == NULL)
×
3184
      *papszDimUnitSymbol = "t";
×
3185
    if (papszDimNearValue != NULL && *papszDimNearValue == NULL)
×
3186
      *papszDimNearValue = "0";
×
3187
  }
3188

3189
  free(pszDimensionItem);
×
3190

3191
  return;
×
3192
}
3193

3194
/**
3195
 * msOWSNegotiateUpdateSequence()
3196
 *
3197
 * returns the updateSequence value for an OWS
3198
 *
3199
 * @param requested_updatesequence the updatesequence passed by the client
3200
 * @param updatesequence the updatesequence set by the server
3201
 *
3202
 * @return result of comparison (-1, 0, 1)
3203
 * -1: lower / higher OR values not set by client or server
3204
 *  1: higher / lower
3205
 *  0: equal
3206
 */
3207

3208
int msOWSNegotiateUpdateSequence(const char *requested_updatesequence,
38✔
3209
                                 const char *updatesequence) {
3210
  int valtype1 = 1; /* default datatype for updatesequence passed by client */
3211
  int valtype2 = 1; /* default datatype for updatesequence set by server */
3212
  struct tm tm_requested_updatesequence, tm_updatesequence;
3213

3214
  /* if not specified by client, or set by server,
3215
     server responds with latest Capabilities XML */
3216
  if (!requested_updatesequence || !updatesequence)
38✔
3217
    return -1;
3218

3219
  /* test to see if server value is an integer (1), string (2) or timestamp (3)
3220
   */
3221
  if (msStringIsInteger(updatesequence) == MS_FAILURE)
38✔
3222
    valtype1 = 2;
3223

3224
  if (valtype1 == 2) { /* test if timestamp */
3225
    msTimeInit(&tm_updatesequence);
15✔
3226
    if (msParseTime(updatesequence, &tm_updatesequence) == MS_TRUE)
15✔
3227
      valtype1 = 3;
3228
    msResetErrorList();
15✔
3229
  }
3230

3231
  /* test to see if client value is an integer (1), string (2) or timestamp (3)
3232
   */
3233
  if (msStringIsInteger(requested_updatesequence) == MS_FAILURE)
38✔
3234
    valtype2 = 2;
3235

3236
  if (valtype2 == 2) { /* test if timestamp */
3237
    msTimeInit(&tm_requested_updatesequence);
15✔
3238
    if (msParseTime(requested_updatesequence, &tm_requested_updatesequence) ==
15✔
3239
        MS_TRUE)
3240
      valtype2 = 3;
3241
    msResetErrorList();
15✔
3242
  }
3243

3244
  /* if the datatypes do not match, do not compare, */
3245
  if (valtype1 != valtype2)
38✔
3246
    return -1;
3247

3248
  if (valtype1 == 1) { /* integer */
38✔
3249
    const int requested_updatesequence_i = atoi(requested_updatesequence);
3250
    const int updatesequence_i = atoi(updatesequence);
3251
    if (requested_updatesequence_i < updatesequence_i)
23✔
3252
      return -1;
3253

3254
    if (requested_updatesequence_i > updatesequence_i)
15✔
3255
      return 1;
3256

3257
    return 0;
8✔
3258
  }
3259

3260
  if (valtype1 == 2) /* string */
15✔
3261
    return strcasecmp(requested_updatesequence, updatesequence);
3✔
3262

3263
  assert(valtype1 == 3); /* timestamp */
3264
  /* compare timestamps */
3265
  return msDateCompare(&tm_requested_updatesequence, &tm_updatesequence) +
12✔
3266
         msTimeCompare(&tm_requested_updatesequence, &tm_updatesequence);
12✔
3267
}
3268

3269
/************************************************************************/
3270
/*                         msOwsIsOutputFormatValid                     */
3271
/*                                                                      */
3272
/*      Utility function to parse a comma separated list in a            */
3273
/*      metedata object and select and outputformat.                    */
3274
/************************************************************************/
3275
outputFormatObj *msOwsIsOutputFormatValid(mapObj *map, const char *format,
109✔
3276
                                          hashTableObj *metadata,
3277
                                          const char *namespaces,
3278
                                          const char *name) {
3279
  char **tokens = NULL;
3280
  int i, n;
3281
  outputFormatObj *psFormat = NULL;
3282
  const char *format_list = NULL;
3283

3284
  if (map && format && metadata && namespaces && name) {
109✔
3285
    msApplyDefaultOutputFormats(map);
109✔
3286
    format_list = msOWSLookupMetadata(metadata, namespaces, name);
109✔
3287
    n = 0;
109✔
3288
    if (format_list)
109✔
3289
      tokens = msStringSplit(format_list, ',', &n);
109✔
3290

3291
    if (tokens && n > 0) {
109✔
3292
      for (i = 0; i < n; i++) {
241✔
3293
        int iFormat = msGetOutputFormatIndex(map, tokens[i]);
241✔
3294
        const char *mimetype;
3295
        if (iFormat == -1)
241✔
3296
          continue;
28✔
3297

3298
        mimetype = map->outputformatlist[iFormat]->mimetype;
213✔
3299

3300
        msStringTrim(tokens[i]);
213✔
3301
        if (strcasecmp(tokens[i], format) == 0)
213✔
3302
          break;
3303
        if (mimetype && strcasecmp(mimetype, format) == 0)
106✔
3304
          break;
3305
      }
3306
      if (i < n)
109✔
3307
        psFormat = msSelectOutputFormat(map, format);
109✔
3308
    }
3309
    if (tokens)
109✔
3310
      msFreeCharArray(tokens, n);
109✔
3311
  }
3312

3313
  return psFormat;
109✔
3314
}
3315

3316
#endif /* defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined             \
3317
          (USE_WCS_SVR) || defined(USE_SOS_SVR) || defined(USE_WMS_LYR) ||     \
3318
          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