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

geographika / mapserver / 17709373190

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

push

github

geographika
Add index templates

62086 of 149729 relevant lines covered (41.47%)

25036.08 hits per line

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

71.21
/src/mapows.c
1
/******************************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  OGC Web Services (WMS, WFS) support functions
6
 * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 ****************************************************************************/
29

30
#include "mapserver.h"
31
#include "maptime.h"
32
#include "maptemplate.h"
33
#include "mapows.h"
34

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

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

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

58
  ows_request->service = NULL;
1,818✔
59
  ows_request->version = NULL;
1,818✔
60
  ows_request->request = NULL;
1,818✔
61
  ows_request->document = NULL;
1,818✔
62
}
1,818✔
63

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

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

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

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

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

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

185
    ows_request->request = msStrdup((char *)root->name);
157✔
186

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

196
    /* remove all namespaces */
197
    CPLStripXMLNamespace(ows_request->document, NULL, 1);
198
    for (temp = ows_request->document; temp != NULL; temp = temp->psNext) {
199

200
      if (temp->eType == CXT_Element) {
201
        const char *service, *version;
202
        ows_request->request = msStrdup(temp->pszValue);
203

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

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

231
  return MS_SUCCESS;
232
}
233

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

241
  compliance_mode_str =
242
      msOWSLookupMetadata(&(map->web.metadata), "MO", "compliance_mode");
2,361✔
243
  return (compliance_mode_str != NULL) &&
2,361✔
244
         (strcasecmp(compliance_mode_str, "true") == 0);
9✔
245
}
246

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

257
  xmlDocPtr psDoc = NULL;
258
  xmlNodePtr psRootNode = NULL;
259
  xmlNsPtr psNsOws = NULL;
260
  xmlChar *buffer = NULL;
3✔
261

262
  psNsOws = xmlNewNs(NULL, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI,
3✔
263
                     BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
264

265
  errorString = msGetErrorString("\n");
3✔
266

267
  psDoc = xmlNewDoc(BAD_CAST "1.0");
3✔
268

269
  psRootNode = msOWSCommonExceptionReport(
3✔
270
      psNsOws, OWS_1_0_0, msOWSGetSchemasLocation(map), "1.0.0",
271
      msOWSGetLanguage(map, "exception"), exceptionCode, "service",
272
      errorString);
273

274
  xmlDocSetRootElement(psDoc, psRootNode);
3✔
275

276
  xmlNewNs(psRootNode, BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_URI,
3✔
277
           BAD_CAST MS_OWSCOMMON_OWS_NAMESPACE_PREFIX);
278

279
  msIO_setHeader("Status", "200 OK");
3✔
280
  msIO_setHeader("Content-Type", "text/xml; charset=UTF-8");
3✔
281
  msIO_sendHeaders();
3✔
282

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

285
  msIO_printf("%s", buffer);
3✔
286

287
  /*free buffer and the document */
288
  free(errorString);
3✔
289
  xmlFree(buffer);
3✔
290
  xmlFreeDoc(psDoc);
3✔
291
  xmlFreeNs(psNsOws);
3✔
292

293
  /* clear error since we have already reported it */
294
  msResetErrorList();
3✔
295

296
  return MS_FAILURE;
3✔
297
}
298

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

314
  if (!request) {
1,817✔
315
    return status;
316
  }
317

318
  force_ows_mode = (ows_mode == OWS || ows_mode == WFS);
1,817✔
319

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

330
  compliance_mode = msOWSStrictCompliance(map);
1,817✔
331

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

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

418
  msOWSClearRequestObj(&ows_request);
1,811✔
419
  return status;
1,811✔
420
}
421

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

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

529
  return len;
530
}
531

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

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

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

571
  return MS_FALSE;
572
}
573

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

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

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

622
  if (!ip)
9,692✔
623
    return MS_FALSE; /* no endpoint ip */
624

625
  ip_list = msOWSLookupMetadata(metadata, namespaces, "allowed_ip_list");
×
626
  if (!ip_list)
×
627
    ip_list = msOWSLookupMetadata(metadata, "O", "allowed_ip_list");
×
628

629
  if (ip_list) {
×
630
    disabled = MS_TRUE;
631
    if (msOWSIpInMetadata(ip_list, ip))
×
632
      disabled = MS_FALSE;
633
  }
634

635
  ip_list = msOWSLookupMetadata(metadata, namespaces, "denied_ip_list");
×
636
  if (!ip_list)
×
637
    ip_list = msOWSLookupMetadata(metadata, "O", "denied_ip_list");
×
638

639
  if (ip_list && msOWSIpInMetadata(ip_list, ip))
×
640
    disabled = MS_TRUE;
641

642
  return disabled;
643
}
644

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

662
  if (request == NULL)
688✔
663
    return MS_FALSE;
664

665
  remote_ip = getenv("REMOTE_ADDR");
688✔
666

667
  /* First, we check in the layer metadata */
668
  if (layer && check_all_layers == MS_FALSE) {
688✔
669
    enable_request =
670
        msOWSLookupMetadata(&layer->metadata, namespaces, "enable_request");
30✔
671
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
30✔
672
      return MS_TRUE;
673
    if (disabled)
30✔
674
      return MS_FALSE;
675

676
    enable_request =
677
        msOWSLookupMetadata(&layer->metadata, "O", "enable_request");
30✔
678
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
30✔
679
      return MS_TRUE;
680
    if (disabled)
30✔
681
      return MS_FALSE;
682

683
    if (msOWSIpDisabled(&layer->metadata, namespaces, remote_ip))
30✔
684
      return MS_FALSE;
685
  }
686

687
  if (map && (check_all_layers == MS_FALSE || map->numlayers == 0)) {
688✔
688
    /* then we check in the map metadata */
689
    enable_request =
690
        msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
409✔
691
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
409✔
692
      return MS_TRUE;
693
    if (disabled)
310✔
694
      return MS_FALSE;
695

696
    enable_request =
697
        msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
306✔
698
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
306✔
699
      return MS_TRUE;
700
    if (disabled)
11✔
701
      return MS_FALSE;
702

703
    if (msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
11✔
704
      return MS_FALSE;
705
  }
706

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

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

721
    if (globally_enabled &&
537✔
722
        msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
276✔
723
      globally_enabled = MS_FALSE;
724

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

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

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

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

749
      if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))
279✔
750
        continue;
×
751

752
      if (result || (!disabled && globally_enabled))
279✔
753
        return MS_TRUE;
754
    }
755

756
    if (!disabled && globally_enabled)
×
757
      return MS_TRUE;
758
  }
759

760
  return MS_FALSE;
761
}
762

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

783
  if (ows_request->numlayers > 0)
1,785✔
784
    msFree(ows_request->enabled_layers);
9✔
785

786
  ows_request->numlayers = 0;
1,785✔
787
  ows_request->enabled_layers = NULL;
1,785✔
788

789
  if (request == NULL || (map == NULL) || (map->numlayers <= 0))
1,785✔
790
    return;
9✔
791

792
  remote_ip = getenv("REMOTE_ADDR");
1,776✔
793

794
  enable_request =
795
      msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
1,776✔
796
  globally_enabled =
797
      msOWSParseRequestMetadata(enable_request, request, &disabled);
1,776✔
798

799
  if (!globally_enabled && !disabled) {
1,776✔
800
    enable_request =
801
        msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
1,555✔
802
    globally_enabled =
803
        msOWSParseRequestMetadata(enable_request, request, &disabled);
1,555✔
804
  }
805

806
  if (globally_enabled &&
3,318✔
807
      msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
1,753✔
808
    globally_enabled = MS_FALSE;
809

810
  if (map->numlayers) {
1,776✔
811
    int i, layers_size = map->numlayers; /* for most of cases, this will be
812
                                            relatively small */
813

814
    ows_request->enabled_layers =
1,776✔
815
        (int *)msSmallMalloc(sizeof(int) * layers_size);
1,776✔
816

817
    for (i = 0; i < map->numlayers; i++) {
9,281✔
818
      int result = MS_FALSE;
819
      layerObj *lp;
820
      lp = (GET_LAYER(map, i));
7,505✔
821

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

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

833
        enable_request =
834
            msOWSLookupMetadata(&lp->metadata, "O", "enable_request");
7,293✔
835
        result = msOWSParseRequestMetadata(enable_request, request, &disabled);
7,293✔
836
        if (!result && disabled)
7,293✔
837
          continue;
4✔
838
      }
839

840
      if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))
7,343✔
841
        continue;
×
842

843
      if (result || (!disabled && globally_enabled)) {
7,343✔
844
        ows_request->enabled_layers[ows_request->numlayers] = lp->index;
7,331✔
845
        ows_request->numlayers++;
7,331✔
846
      }
847
    }
848

849
    if (ows_request->numlayers == 0) {
1,776✔
850
      msFree(ows_request->enabled_layers);
15✔
851
      ows_request->enabled_layers = NULL;
15✔
852
    }
853
  }
854
}
855

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

869
  *disabled = MS_FALSE;
20,193✔
870

871
  if (metadata == NULL)
20,193✔
872
    return MS_FALSE;
873

874
  ptr = (char *)metadata;
875
  const size_t len = strlen(ptr);
2,877✔
876
  requestBuffer[0] = '\0';
2,877✔
877
  bufferPtr = requestBuffer;
878

879
  for (size_t i = 0; i <= len; ++i, ++ptr) {
13,238✔
880

881
    if (!wordFlag && isspace(*ptr))
10,517✔
882
      continue;
×
883

884
    wordFlag = MS_TRUE;
885

886
    if (*ptr == '!') {
10,517✔
887
      disableFlag = MS_TRUE;
888
      continue;
353✔
889
    } else if ((*ptr == ' ') ||
10,164✔
890
               (*ptr != '\0' && ptr[1] == '\0')) { /* end of word */
7,273✔
891
      if (ptr[1] == '\0' && *ptr != ' ') {
3,008✔
892
        *bufferPtr = *ptr;
2,838✔
893
        ++bufferPtr;
2,838✔
894
      }
895

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

919
  return allFlag;
920
}
921

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

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

970
  if (namespaces == NULL) {
173,233✔
971
    value = msLookupHashTable(metadata, (char *)name);
×
972
  } else {
973
    char buf[100] = "ows_";
173,233✔
974

975
    strlcpy(buf + 4, name, 96);
976

977
    while (value == NULL && *namespaces != '\0') {
399,746✔
978
      const char *prefix = msOWSGetPrefixFromNamespace(*namespaces);
226,513✔
979
      if (prefix == NULL)
226,513✔
980
        return NULL;
×
981
      assert(strlen(prefix) == 3);
982
      memcpy(buf, prefix, 3);
983
      value = msLookupHashTable(metadata, buf);
226,513✔
984
      namespaces++;
226,513✔
985
    }
986
  }
987

988
  return value;
989
}
990

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

1003
  if (name && validated_language && validated_language[0]) {
4,493✔
1004
    size_t bufferSize = strlen(name) + strlen(validated_language) + 2;
984✔
1005
    char *name2 = (char *)msSmallMalloc(bufferSize);
984✔
1006
    snprintf(name2, bufferSize, "%s.%s", name, validated_language);
1007
    value = msOWSLookupMetadata(metadata, namespaces, name2);
984✔
1008
    free(name2);
984✔
1009
  }
1010

1011
  if (name && !value) {
4,493✔
1012
    value = msOWSLookupMetadata(metadata, namespaces, name);
4,190✔
1013
  }
1014

1015
  return value;
4,493✔
1016
}
1017

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

1035
  if ((result = msOWSLookupMetadata(pri, namespaces, name)) == NULL) {
2,196✔
1036
    /* Try the secondary table */
1037
    result = msOWSLookupMetadata(sec, namespaces, name);
2,189✔
1038
  }
1039

1040
  return result;
2,196✔
1041
}
1042

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

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

1068
    nVersion = atoi(digits[0]) * 0x010000;
2,250✔
1069
    nVersion += atoi(digits[1]) * 0x0100;
2,250✔
1070
    if (numDigits > 2)
2,250✔
1071
      nVersion += atoi(digits[2]);
2,135✔
1072

1073
    msFreeCharArray(digits, numDigits);
2,250✔
1074

1075
    return nVersion;
2,250✔
1076
  }
1077

1078
  return OWS_VERSION_NOTSET;
1079
}
1080

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

1091
  if (pszBuffer)
578✔
1092
    snprintf(pszBuffer, OWS_VERSION_MAXLEN - 1, "%d.%d.%d",
578✔
1093
             (nVersion / 0x10000) % 0x100, (nVersion / 0x100) % 0x100,
578✔
1094
             nVersion % 0x100);
1095

1096
  return pszBuffer;
578✔
1097
}
1098

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

1117
  /* metadata value should already be in format "EPSG:n" or "AUTO:..." */
1118
  if (metadata &&
6,952✔
1119
      ((value = msOWSLookupMetadata(metadata, namespaces, "srs")) != NULL)) {
2,969✔
1120
    const char *space_ptr;
1121
    if (!bReturnOnlyFirstOne || (space_ptr = strchr(value, ' ')) == NULL) {
2,386✔
1122
      *epsgCode = msStrdup(value);
1,220✔
1123
      return;
1,220✔
1124
    }
1125

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

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

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

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

1188
    if (language == NULL) {
×
1189
      language = "undefined";
1190
    }
1191
  }
1192
  return language;
145✔
1193
}
1194

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

1207
  schemas_location =
1208
      msLookupHashTable(&(map->web.metadata), "ows_schemas_location");
1,180✔
1209
  if (schemas_location == NULL)
1,180✔
1210
    schemas_location = OWS_DEFAULT_SCHEMAS_LOCATION;
1211

1212
  return schemas_location;
1,180✔
1213
}
1214

1215
/*
1216
** msOWSGetExpandedMetadataKey()
1217
*/
1218
static char *msOWSGetExpandedMetadataKey(const char *namespaces,
57✔
1219
                                         const char *metadata_name) {
1220
  char *pszRet = msStringConcatenate(NULL, "");
57✔
1221
  for (int i = 0; namespaces[i] != '\0'; ++i) {
171✔
1222
    if (i > 0)
114✔
1223
      pszRet = msStringConcatenate(pszRet, " or ");
57✔
1224
    pszRet = msStringConcatenate(pszRet, "\"");
114✔
1225
    pszRet =
1226
        msStringConcatenate(pszRet, msOWSGetPrefixFromNamespace(namespaces[i]));
114✔
1227
    pszRet = msStringConcatenate(pszRet, "_");
114✔
1228
    pszRet = msStringConcatenate(pszRet, metadata_name);
114✔
1229
    pszRet = msStringConcatenate(pszRet, "\"");
114✔
1230
  }
1231
  return pszRet;
57✔
1232
}
1233

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

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

1267
  return online_resource;
1268
}
1269

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

1284
  if (src_url == NULL)
725✔
1285
    return NULL;
1286

1287
  buffer_size = strlen(src_url) + 2;
725✔
1288
  online_resource = (char *)malloc(buffer_size);
725✔
1289

1290
  if (online_resource == NULL) {
725✔
1291
    msSetError(MS_MEMERR, NULL, "msOWSTerminateOnlineResource()");
×
1292
    return NULL;
×
1293
  }
1294

1295
  strlcpy(online_resource, src_url, buffer_size);
1296

1297
  /* Append trailing '?' or '&' if missing. */
1298
  if (strchr(online_resource, '?') == NULL)
725✔
1299
    strlcat(online_resource, "?", buffer_size);
1300
  else {
1301
    char *c;
1302
    c = online_resource + strlen(online_resource) - 1;
630✔
1303
    if (*c != '?' && *c != '&')
630✔
1304
      strlcpy(c + 1, "&", buffer_size - strlen(online_resource));
6✔
1305
  }
1306

1307
  return online_resource;
1308
}
1309

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

1321
  char md_item_name[256];
1322

1323
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_type", field_name);
1324
  if (msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
2,848✔
1325
    msInsertHashTable(&(layer->metadata), md_item_name, gml_type);
1,311✔
1326

1327
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_width", field_name);
1328
  if (strlen(gml_width) > 0 &&
4,054✔
1329
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
1,206✔
1330
    msInsertHashTable(&(layer->metadata), md_item_name, gml_width);
645✔
1331

1332
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_precision", field_name);
1333
  if (strlen(gml_precision) > 0 &&
2,977✔
1334
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
129✔
1335
    msInsertHashTable(&(layer->metadata), md_item_name, gml_precision);
68✔
1336

1337
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_nillable", field_name);
1338
  if (nullable > 0 &&
2,848✔
1339
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
×
1340
    msInsertHashTable(&(layer->metadata), md_item_name, "true");
×
1341

1342
  return MS_TRUE;
2,848✔
1343
}
1344

1345
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1346
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
1347

1348
/*
1349
** msRenameLayer()
1350
*/
1351
static int msRenameLayer(layerObj *lp, int count) {
×
1352
  char *newname;
1353
  newname = (char *)malloc((strlen(lp->name) + 5) * sizeof(char));
×
1354
  if (!newname) {
×
1355
    msSetError(MS_MEMERR, NULL, "msRenameLayer()");
×
1356
    return MS_FAILURE;
×
1357
  }
1358
  sprintf(newname, "%s_%2.2d", lp->name, count);
1359
  free(lp->name);
×
1360
  lp->name = newname;
×
1361

1362
  return MS_SUCCESS;
×
1363
}
1364

1365
/*
1366
** msOWSMakeAllLayersUnique()
1367
*/
1368
int msOWSMakeAllLayersUnique(mapObj *map) {
1,438✔
1369
  int i, j;
1370

1371
  /* Make sure all layers in the map file have valid and unique names */
1372
  for (i = 0; i < map->numlayers; i++) {
8,436✔
1373
    int count = 1;
1374
    for (j = i + 1; j < map->numlayers; j++) {
43,367✔
1375
      if (GET_LAYER(map, i)->name == NULL || GET_LAYER(map, j)->name == NULL) {
36,369✔
1376
        continue;
×
1377
      }
1378
      if (strcasecmp(GET_LAYER(map, i)->name, GET_LAYER(map, j)->name) == 0 &&
36,369✔
1379
          msRenameLayer((GET_LAYER(map, j)), ++count) != MS_SUCCESS) {
×
1380
        return MS_FAILURE;
1381
      }
1382
    }
1383

1384
    /* Don't forget to rename the first layer if duplicates were found */
1385
    if (count > 1 && msRenameLayer((GET_LAYER(map, i)), 1) != MS_SUCCESS) {
6,998✔
1386
      return MS_FAILURE;
1387
    }
1388
  }
1389
  return MS_SUCCESS;
1390
}
1391

1392
/*
1393
** msOWSNegotiateVersion()
1394
**
1395
** returns the most suitable version an OWS is to support given a client
1396
** version parameter.
1397
**
1398
** supported_versions must be ordered from highest to lowest
1399
**
1400
** Returns a version integer of the supported version
1401
**
1402
*/
1403

1404
int msOWSNegotiateVersion(int requested_version, const int supported_versions[],
101✔
1405
                          int num_supported_versions) {
1406
  int i;
1407

1408
  /* if version is not set return highest version */
1409
  if (!requested_version)
101✔
1410
    return supported_versions[0];
×
1411

1412
  /* if the requested version is lower than the lowest version return the lowest
1413
   * version  */
1414
  if (requested_version < supported_versions[num_supported_versions - 1])
101✔
1415
    return supported_versions[num_supported_versions - 1];
1416

1417
  /* return the first entry that's lower than or equal to the requested version
1418
   */
1419
  for (i = 0; i < num_supported_versions; i++) {
282✔
1420
    if (supported_versions[i] <= requested_version)
282✔
1421
      return supported_versions[i];
101✔
1422
  }
1423

1424
  return requested_version;
1425
}
1426

1427
/*
1428
** msOWSGetOnlineResource()
1429
**
1430
** Return the online resource for this service and add language parameter.
1431
**
1432
** Returns a newly allocated string that should be freed by the caller or
1433
** NULL in case of error.
1434
*/
1435
char *msOWSGetOnlineResource2(mapObj *map, const char *namespaces,
88✔
1436
                              const char *metadata_name, cgiRequestObj *req,
1437
                              const char *validated_language) {
1438
  char *online_resource =
1439
      msOWSGetOnlineResource(map, namespaces, metadata_name, req);
88✔
1440

1441
  if (online_resource && validated_language && validated_language[0]) {
88✔
1442
    /* online_resource is already terminated, so we can simply add language=...&
1443
     */
1444
    /* but first we need to make sure that online_resource has enough capacity
1445
     */
1446
    online_resource = (char *)msSmallRealloc(
20✔
1447
        online_resource,
1448
        strlen(online_resource) + strlen(validated_language) + 11);
20✔
1449
    strcat(online_resource, "language=");
1450
    strcat(online_resource, validated_language);
1451
    strcat(online_resource, "&");
1452
  }
1453

1454
  return online_resource;
88✔
1455
}
1456

1457
/* msOWSGetInspireSchemasLocation()
1458
**
1459
** schemas location is the root of the web tree where all Inspire-related
1460
** schemas can be found on this server.  These URLs must exist in order
1461
** to validate xml.
1462
**
1463
** Use value of "inspire_schemas_location" metadata
1464
*/
1465
const char *msOWSGetInspireSchemasLocation(mapObj *map) {
41✔
1466
  const char *schemas_location;
1467

1468
  schemas_location =
1469
      msLookupHashTable(&(map->web.metadata), "inspire_schemas_location");
41✔
1470
  if (schemas_location == NULL)
41✔
1471
    schemas_location = "http://inspire.ec.europa.eu/schemas";
1472

1473
  return schemas_location;
41✔
1474
}
1475

1476
/* msOWSGetLanguageList
1477
**
1478
** Returns the list of languages that this service supports
1479
**
1480
** Use value of "languages" metadata (comma-separated list), or NULL if not set
1481
**
1482
** Returns a malloced char** of length numitems which must be freed
1483
** by the caller, or NULL (with numitems = 0)
1484
*/
1485
char **msOWSGetLanguageList(mapObj *map, const char *namespaces,
1,400✔
1486
                            int *numitems) {
1487

1488
  const char *languages = NULL;
1489

1490
  languages =
1491
      msOWSLookupMetadata(&(map->web.metadata), namespaces, "languages");
1,400✔
1492
  if (languages && strlen(languages) > 0) {
1,400✔
1493
    return msStringSplit(languages, ',', numitems);
132✔
1494
  } else {
1495
    *numitems = 0;
1,268✔
1496
    return NULL;
1,268✔
1497
  }
1498
}
1499

1500
/* msOWSGetLanguageFromList
1501
**
1502
** Returns a language according to the language requested by the client
1503
**
1504
** If the requested language is in the languages metadata then use it,
1505
** otherwise ignore it and use the default language, which is the first entry in
1506
** the languages metadata list. Calling with a NULL requested_langauge
1507
** therefore returns this default language. If the language metadata list is
1508
** not defined then the language is set to NULL.
1509
**
1510
** Returns a malloced char* which must be freed by the caller, or NULL
1511
*/
1512
char *msOWSGetLanguageFromList(mapObj *map, const char *namespaces,
1,359✔
1513
                               const char *requested_language) {
1514
  int num_items = 0;
1,359✔
1515
  char **languages = msOWSGetLanguageList(map, namespaces, &num_items);
1,359✔
1516
  char *language = NULL;
1517

1518
  if (languages && num_items > 0) {
1,359✔
1519
    if (!requested_language ||
128✔
1520
        !msStringInArray(requested_language, languages, num_items)) {
16✔
1521
      language = msStrdup(languages[0]);
98✔
1522
    } else {
1523
      language = msStrdup(requested_language);
14✔
1524
    }
1525
  }
1526
  msFreeCharArray(languages, num_items);
1,359✔
1527

1528
  return language;
1,359✔
1529
}
1530

1531
/* msOWSLanguageNegotiation
1532
**
1533
** Returns a language according to the accepted languages requested by the
1534
*client
1535
**
1536
** Returns a malloced char* which must be freed by the caller, or NULL
1537
*/
1538
char *msOWSLanguageNegotiation(mapObj *map, const char *namespaces,
41✔
1539
                               char **accept_languages,
1540
                               int num_accept_languages) {
1541
  int num_languages = 0;
41✔
1542
  char **languages = NULL;
1543
  char *result_language = NULL;
1544

1545
  languages = msOWSGetLanguageList(map, namespaces, &num_languages);
41✔
1546

1547
  if (languages && num_languages > 0) {
41✔
1548
    int i;
1549
    for (i = 0; i < num_accept_languages; ++i) {
28✔
1550
      const char *accept_language = accept_languages[i];
20✔
1551

1552
      /* '*' means any language */
1553
      if (EQUAL(accept_language, "*")) {
20✔
1554
        result_language = msStrdup(languages[0]);
×
1555
        break;
×
1556
      } else if (msStringInArray(accept_language, languages, num_languages)) {
20✔
1557
        result_language = msStrdup(accept_language);
12✔
1558
        break;
12✔
1559
      }
1560
    }
1561

1562
    if (result_language == NULL) {
20✔
1563
      result_language = msStrdup(languages[0]);
8✔
1564
    }
1565
  }
1566

1567
  msFreeCharArray(languages, num_languages);
41✔
1568
  return result_language;
41✔
1569
}
1570

1571
/* msOWSPrintInspireCommonExtendedCapabilities
1572
**
1573
** Output INSPIRE common extended capabilities items to stream
1574
** The currently supported items are metadata and languages
1575
**
1576
** tag_name is the name (including ns prefix) of the tag to include the whole
1577
** extended capabilities block in
1578
**
1579
** service is currently included for future compatibility when differing
1580
** extended capabilities elements are included for different service types
1581
**
1582
** Returns a status code; MS_NOERR if all ok, action_if_not_found otherwise
1583
*/
1584
int msOWSPrintInspireCommonExtendedCapabilities(
41✔
1585
    FILE *stream, mapObj *map, const char *namespaces, int action_if_not_found,
1586
    const char *tag_name, const char *tag_ns, const char *validated_language,
1587
    const OWSServiceType service) {
1588

1589
  int metadataStatus = 0;
1590
  int languageStatus = 0;
1591

1592
  if (tag_ns)
41✔
1593
    msIO_fprintf(stream, "  <%s %s>\n", tag_name, tag_ns);
26✔
1594
  else
1595
    msIO_fprintf(stream, "  <%s>\n", tag_name);
15✔
1596

1597
  metadataStatus = msOWSPrintInspireCommonMetadata(
41✔
1598
      stream, map, namespaces, action_if_not_found, service);
1599
  languageStatus = msOWSPrintInspireCommonLanguages(
41✔
1600
      stream, map, namespaces, action_if_not_found, validated_language);
1601

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

1604
  return (metadataStatus != MS_NOERR) ? metadataStatus : languageStatus;
41✔
1605
}
1606

1607
/* msOWSPrintInspireCommonMetadata
1608
**
1609
** Output INSPIRE common metadata items to a stream
1610
**
1611
** Returns a status code; MS_NOERR if all OK, action_if_not_found otherwise
1612
*/
1613
int msOWSPrintInspireCommonMetadata(FILE *stream, mapObj *map,
41✔
1614
                                    const char *namespaces,
1615
                                    int action_if_not_found,
1616
                                    const OWSServiceType service) {
1617

1618
  int status = MS_NOERR;
1619
  const char *inspire_capabilities = NULL;
1620

1621
  inspire_capabilities = msOWSLookupMetadata(&(map->web.metadata), namespaces,
41✔
1622
                                             "inspire_capabilities");
1623

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

1736
  return status;
1737
}
1738

1739
/* msOWSPrintInspireCommonLanguages
1740
**
1741
** Output INSPIRE supported languages block to stream
1742
**
1743
** Returns a status code; MS_NOERR if all OK; action_if_not_found otherwise
1744
*/
1745
int msOWSPrintInspireCommonLanguages(FILE *stream, mapObj *map,
41✔
1746
                                     const char *namespaces,
1747
                                     int action_if_not_found,
1748
                                     const char *validated_language) {
1749
  char *buffer =
1750
      NULL; /* temp variable for malloced strings that will need freeing */
1751
  int status = MS_NOERR;
1752

1753
  char *default_language = msOWSGetLanguageFromList(map, namespaces, NULL);
41✔
1754

1755
  if (validated_language && validated_language[0] && default_language) {
41✔
1756
    msIO_fprintf(stream, "    <inspire_common:SupportedLanguages>\n");
40✔
1757
    msIO_fprintf(
40✔
1758
        stream,
1759
        "      <inspire_common:DefaultLanguage><inspire_common:Language>%s"
1760
        "</inspire_common:Language></inspire_common:DefaultLanguage>\n",
1761
        buffer = msEncodeHTMLEntities(default_language));
40✔
1762
    msFree(buffer);
40✔
1763

1764
    /* append _exclude to our default_language*/
1765
    default_language = msSmallRealloc(
40✔
1766
        default_language, strlen(default_language) + strlen("_exclude") + 1);
40✔
1767
    strcat(default_language, "_exclude");
1768

1769
    msOWSPrintEncodeMetadataList(
40✔
1770
        stream, &(map->web.metadata), namespaces, "languages", NULL, NULL,
1771
        "      <inspire_common:SupportedLanguage><inspire_common:Language>%s"
1772
        "</inspire_common:Language></inspire_common:SupportedLanguage>\n",
1773
        default_language);
1774
    msIO_fprintf(stream, "    </inspire_common:SupportedLanguages>\n");
40✔
1775
    msIO_fprintf(
40✔
1776
        stream,
1777
        "    <inspire_common:ResponseLanguage><inspire_common:Language>%s"
1778
        "</inspire_common:Language></inspire_common:ResponseLanguage>\n",
1779
        validated_language);
1780
  } else {
1781
    status = action_if_not_found;
1782
    if (OWS_WARN == action_if_not_found) {
1✔
1783
      char *pszExpandedMetadataKey =
1784
          msOWSGetExpandedMetadataKey(namespaces, "languages");
1✔
1785
      msIO_fprintf(stream,
1✔
1786
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1787
                   "context. -->\n",
1788
                   pszExpandedMetadataKey);
1789
      msFree(pszExpandedMetadataKey);
1✔
1790
    }
1791
  }
1792

1793
  msFree(default_language);
41✔
1794

1795
  return status;
41✔
1796
}
1797

1798
/*
1799
** msOWSPrintMetadata()
1800
**
1801
** Attempt to output a capability item.  If corresponding metadata is not
1802
** found then one of a number of predefined actions will be taken.
1803
** If a default value is provided and metadata is absent then the
1804
** default will be used.
1805
*/
1806

1807
int msOWSPrintMetadata(FILE *stream, hashTableObj *metadata,
×
1808
                       const char *namespaces, const char *name,
1809
                       int action_if_not_found, const char *format,
1810
                       const char *default_value) {
1811
  const char *value = NULL;
1812
  int status = MS_NOERR;
1813

1814
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)) != NULL) {
×
1815
    msIO_fprintf(stream, format, value);
×
1816
  } else {
1817
    if (action_if_not_found == OWS_WARN) {
×
1818
      char *pszExpandedMetadataKey =
1819
          msOWSGetExpandedMetadataKey(namespaces, name);
×
1820
      msIO_fprintf(stream,
×
1821
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1822
                   "context. -->\n",
1823
                   pszExpandedMetadataKey);
1824
      msFree(pszExpandedMetadataKey);
×
1825
      status = action_if_not_found;
1826
    }
1827

1828
    if (default_value)
×
1829
      msIO_fprintf(stream, format, default_value);
×
1830
  }
1831

1832
  return status;
×
1833
}
1834

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

1845
int msOWSPrintEncodeMetadata(FILE *stream, hashTableObj *metadata,
1,362✔
1846
                             const char *namespaces, const char *name,
1847
                             int action_if_not_found, const char *format,
1848
                             const char *default_value) {
1849
  return msOWSPrintEncodeMetadata2(stream, metadata, namespaces, name,
1,362✔
1850
                                   action_if_not_found, format, default_value,
1851
                                   NULL);
1852
}
1853

1854
/*
1855
** msOWSPrintEncodeMetadata2()
1856
**
1857
** Attempt to output a capability item in the requested language.
1858
** Fallback using no language parameter.
1859
*/
1860
int msOWSPrintEncodeMetadata2(FILE *stream, hashTableObj *metadata,
2,127✔
1861
                              const char *namespaces, const char *name,
1862
                              int action_if_not_found, const char *format,
1863
                              const char *default_value,
1864
                              const char *validated_language) {
1865
  const char *value;
1866
  char *pszEncodedValue = NULL;
1867
  int status = MS_NOERR;
1868

1869
  if ((value = msOWSLookupMetadataWithLanguage(metadata, namespaces, name,
2,127✔
1870
                                               validated_language))) {
1871
    pszEncodedValue = msEncodeHTMLEntities(value);
1,623✔
1872
    msIO_fprintf(stream, format, pszEncodedValue);
1,623✔
1873
    free(pszEncodedValue);
1,623✔
1874
  } else {
1875
    if (action_if_not_found == OWS_WARN) {
504✔
1876
      char *pszExpandedName = msStringConcatenate(NULL, name);
56✔
1877
      if (validated_language && validated_language[0]) {
56✔
1878
        pszExpandedName = msStringConcatenate(pszExpandedName, ".");
×
1879
        pszExpandedName =
1880
            msStringConcatenate(pszExpandedName, validated_language);
×
1881
      }
1882
      char *pszExpandedMetadataKey =
1883
          msOWSGetExpandedMetadataKey(namespaces, pszExpandedName);
56✔
1884
      msIO_fprintf(stream,
56✔
1885
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1886
                   "context. -->\n",
1887
                   pszExpandedMetadataKey);
1888
      msFree(pszExpandedName);
56✔
1889
      msFree(pszExpandedMetadataKey);
56✔
1890
      status = action_if_not_found;
1891
    }
1892

1893
    if (default_value) {
504✔
1894
      pszEncodedValue = msEncodeHTMLEntities(default_value);
206✔
1895
      msIO_fprintf(stream, format, default_value);
206✔
1896
      free(pszEncodedValue);
206✔
1897
    }
1898
  }
1899

1900
  return status;
2,127✔
1901
}
1902

1903
/*
1904
** msOWSGetEncodeMetadata()
1905
**
1906
** Equivalent to msOWSPrintEncodeMetadata. Returns en encoded value of the
1907
** metadata or the default value.
1908
** Caller should free the returned string.
1909
*/
1910
char *msOWSGetEncodeMetadata(hashTableObj *metadata, const char *namespaces,
384✔
1911
                             const char *name, const char *default_value) {
1912
  const char *value;
1913
  char *pszEncodedValue = NULL;
1914
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)))
384✔
1915
    pszEncodedValue = msEncodeHTMLEntities(value);
49✔
1916
  else if (default_value)
335✔
1917
    pszEncodedValue = msEncodeHTMLEntities(default_value);
277✔
1918

1919
  return pszEncodedValue;
384✔
1920
}
1921

1922
/*
1923
** msOWSPrintValidateMetadata()
1924
**
1925
** Attempt to output a capability item.  If corresponding metadata is not
1926
** found then one of a number of predefined actions will be taken.
1927
** If a default value is provided and metadata is absent then the
1928
** default will be used.
1929
** Also validate the value with msIsXMLTagValid.
1930
*/
1931

1932
int msOWSPrintValidateMetadata(FILE *stream, hashTableObj *metadata,
244✔
1933
                               const char *namespaces, const char *name,
1934
                               int action_if_not_found, const char *format,
1935
                               const char *default_value) {
1936
  const char *value;
1937
  int status = MS_NOERR;
1938

1939
  if ((value = msOWSLookupMetadata(metadata, namespaces, name))) {
244✔
1940
    if (msIsXMLTagValid(value) == MS_FALSE)
×
1941
      msIO_fprintf(stream,
×
1942
                   "<!-- WARNING: The value '%s' is not valid in a "
1943
                   "XML tag context. -->\n",
1944
                   value);
1945
    msIO_fprintf(stream, format, value);
×
1946
  } else {
1947
    if (action_if_not_found == OWS_WARN) {
244✔
1948
      char *pszExpandedMetadataKey =
1949
          msOWSGetExpandedMetadataKey(namespaces, name);
×
1950
      msIO_fprintf(stream,
×
1951
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1952
                   "context. -->\n",
1953
                   pszExpandedMetadataKey);
1954
      msFree(pszExpandedMetadataKey);
×
1955
      status = action_if_not_found;
1956
    }
1957

1958
    if (default_value) {
244✔
1959
      if (msIsXMLTagValid(default_value) == MS_FALSE)
244✔
1960
        msIO_fprintf(stream,
×
1961
                     "<!-- WARNING: The value '%s' is not valid "
1962
                     "in a XML tag context. -->\n",
1963
                     default_value);
1964
      msIO_fprintf(stream, format, default_value);
244✔
1965
    }
1966
  }
1967

1968
  return status;
244✔
1969
}
1970

1971
/*
1972
** msOWSPrintGroupMetadata()
1973
**
1974
** Attempt to output a capability item.  If corresponding metadata is not
1975
** found then one of a number of predefined actions will be taken.
1976
** If a default value is provided and metadata is absent then the
1977
** default will be used.
1978
*/
1979
int msOWSPrintGroupMetadata(FILE *stream, mapObj *map, char *pszGroupName,
×
1980
                            const char *namespaces, const char *name,
1981
                            int action_if_not_found, const char *format,
1982
                            const char *default_value) {
1983
  return msOWSPrintGroupMetadata2(stream, map, pszGroupName, namespaces, name,
×
1984
                                  action_if_not_found, format, default_value,
1985
                                  NULL);
1986
}
1987

1988
/*
1989
** msOWSPrintGroupMetadata2()
1990
**
1991
** Attempt to output a capability item in the requested language.
1992
** Fallback using no language parameter.
1993
*/
1994
int msOWSPrintGroupMetadata2(FILE *stream, mapObj *map, char *pszGroupName,
22✔
1995
                             const char *namespaces, const char *name,
1996
                             int action_if_not_found, const char *format,
1997
                             const char *default_value,
1998
                             const char *validated_language) {
1999
  const char *value;
2000
  char *encoded;
2001
  int status = MS_NOERR;
2002
  int i;
2003

2004
  for (i = 0; i < map->numlayers; i++) {
42✔
2005
    if (GET_LAYER(map, i)->group &&
42✔
2006
        (strcmp(GET_LAYER(map, i)->group, pszGroupName) == 0)) {
24✔
2007
      if ((value = msOWSLookupMetadataWithLanguage(
22✔
2008
               &(GET_LAYER(map, i)->metadata), namespaces, name,
2009
               validated_language))) {
2010
        encoded = msEncodeHTMLEntities(value);
22✔
2011
        msIO_fprintf(stream, format, encoded);
22✔
2012
        msFree(encoded);
22✔
2013
        return status;
22✔
2014
      }
2015
    }
2016
  }
2017

2018
  if (action_if_not_found == OWS_WARN) {
×
2019
    char *pszExpandedMetadataKey =
2020
        msOWSGetExpandedMetadataKey(namespaces, name);
×
2021
    msIO_fprintf(stream,
×
2022
                 "<!-- WARNING: Mandatory metadata %s was missing in this "
2023
                 "context. -->\n",
2024
                 pszExpandedMetadataKey);
2025
    msFree(pszExpandedMetadataKey);
×
2026
    status = action_if_not_found;
2027
  }
2028

2029
  if (default_value) {
×
2030
    encoded = msEncodeHTMLEntities(default_value);
×
2031
    msIO_fprintf(stream, format, encoded);
×
2032
    msFree(encoded);
×
2033
  }
2034

2035
  return status;
2036
}
2037

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

2090
  const size_t buffer_size = strlen(name) + 10;
707✔
2091
  metadata_name = (char *)malloc(buffer_size);
707✔
2092

2093
  /* Get type */
2094
  if (type_format != NULL) {
707✔
2095
    snprintf(metadata_name, buffer_size, "%s_type", name);
2096
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
293✔
2097
    if (value != NULL) {
293✔
2098
      encoded = msEncodeHTMLEntities(value);
231✔
2099
      const size_t buffer_size_tmp = strlen(type_format) + strlen(encoded) + 1;
231✔
2100
      type = (char *)malloc(buffer_size_tmp);
231✔
2101
      snprintf(type, buffer_size_tmp, type_format, encoded);
2102
      msFree(encoded);
231✔
2103
    }
2104
  }
2105

2106
  /* Get width */
2107
  if (width_format != NULL) {
707✔
2108
    snprintf(metadata_name, buffer_size, "%s_width", name);
2109
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
173✔
2110
    if (value != NULL) {
173✔
2111
      encoded = msEncodeHTMLEntities(value);
19✔
2112
      const size_t buffer_size_tmp = strlen(width_format) + strlen(encoded) + 1;
19✔
2113
      width = (char *)malloc(buffer_size_tmp);
19✔
2114
      snprintf(width, buffer_size_tmp, width_format, encoded);
2115
      msFree(encoded);
19✔
2116
    }
2117
  }
2118

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

2133
  /* Get format */
2134
  if (urlfrmt_format != NULL) {
707✔
2135
    snprintf(metadata_name, buffer_size, "%s_format", name);
2136
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
681✔
2137
    if (value != NULL) {
681✔
2138
      encoded = msEncodeHTMLEntities(value);
246✔
2139
      const size_t buffer_size_tmp =
246✔
2140
          strlen(urlfrmt_format) + strlen(encoded) + 1;
246✔
2141
      urlfrmt = (char *)malloc(buffer_size_tmp);
246✔
2142
      snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, encoded);
2143
      msFree(encoded);
246✔
2144
    }
2145
  }
2146

2147
  /* Get href */
2148
  if (href_format != NULL) {
707✔
2149
    snprintf(metadata_name, buffer_size, "%s_href", name);
2150
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
707✔
2151
    if (value != NULL) {
707✔
2152
      encoded = msEncodeHTMLEntities(value);
268✔
2153
      const size_t buffer_size_tmp = strlen(href_format) + strlen(encoded) + 1;
268✔
2154
      href = (char *)malloc(buffer_size_tmp);
268✔
2155
      snprintf(href, buffer_size_tmp, href_format, encoded);
2156
      msFree(encoded);
268✔
2157
    }
2158
  }
2159

2160
  msFree(metadata_name);
707✔
2161

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

2219
      if (tag_format == NULL)
422✔
2220
        msIO_fprintf(stream, "%s<%s%s%s%s%s>%s</%s>\n", tabspace, tag_name,
400✔
2221
                     type, width, height, urlfrmt, href, tag_name);
2222
      else
2223
        msIO_fprintf(stream, tag_format, type, width, height, urlfrmt, href);
22✔
2224
    }
2225

2226
    msFree(type);
422✔
2227
    msFree(width);
422✔
2228
    msFree(height);
422✔
2229
    msFree(urlfrmt);
422✔
2230
    msFree(href);
422✔
2231
  } else {
2232
    if (action_if_not_found == OWS_WARN) {
285✔
2233
      char *pszExpandedMetadataKey =
2234
          msOWSGetExpandedMetadataKey(namespaces, name);
×
2235
      msIO_fprintf(stream,
×
2236
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
2237
                   "context. -->\n",
2238
                   pszExpandedMetadataKey);
2239
      msFree(pszExpandedMetadataKey);
×
2240
      status = action_if_not_found;
2241
    }
2242
  }
2243

2244
  return status;
707✔
2245
}
2246

2247
/* msOWSPrintParam()
2248
**
2249
** Same as printMetadata() but applied to mapfile parameters.
2250
**/
2251
int msOWSPrintParam(FILE *stream, const char *name, const char *value,
×
2252
                    int action_if_not_found, const char *format,
2253
                    const char *default_value) {
2254
  int status = MS_NOERR;
2255

2256
  if (value && strlen(value) > 0) {
×
2257
    msIO_fprintf(stream, format, value);
×
2258
  } else {
2259
    if (action_if_not_found == OWS_WARN) {
×
2260
      msIO_fprintf(stream,
×
2261
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2262
                   "in this context. -->\n",
2263
                   name);
2264
      status = action_if_not_found;
2265
    }
2266

2267
    if (default_value)
×
2268
      msIO_fprintf(stream, format, default_value);
×
2269
  }
2270

2271
  return status;
×
2272
}
2273

2274
/* msOWSPrintEncodeParam()
2275
**
2276
** Same as printEncodeMetadata() but applied to mapfile parameters.
2277
**/
2278
int msOWSPrintEncodeParam(FILE *stream, const char *name, const char *value,
323✔
2279
                          int action_if_not_found, const char *format,
2280
                          const char *default_value) {
2281
  int status = MS_NOERR;
2282
  char *encode;
2283

2284
  if (value && strlen(value) > 0) {
323✔
2285
    encode = msEncodeHTMLEntities(value);
323✔
2286
    msIO_fprintf(stream, format, encode);
323✔
2287
    msFree(encode);
323✔
2288
  } else {
2289
    if (action_if_not_found == OWS_WARN) {
×
2290
      msIO_fprintf(stream,
×
2291
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2292
                   "in this context. -->\n",
2293
                   name);
2294
      status = action_if_not_found;
2295
    }
2296

2297
    if (default_value) {
×
2298
      encode = msEncodeHTMLEntities(default_value);
×
2299
      msIO_fprintf(stream, format, encode);
×
2300
      msFree(encode);
×
2301
    }
2302
  }
2303

2304
  return status;
323✔
2305
}
2306

2307
/* msOWSPrintMetadataList()
2308
**
2309
** Prints comma-separated lists metadata.  (e.g. keywordList)
2310
** default_value serves 2 purposes if specified:
2311
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2312
**  (exclusion)
2313
** - will be printed if MetadataList is empty (fallback)
2314
**/
2315
int msOWSPrintMetadataList(FILE *stream, hashTableObj *metadata,
×
2316
                           const char *namespaces, const char *name,
2317
                           const char *startTag, const char *endTag,
2318
                           const char *itemFormat, const char *default_value) {
2319
  const char *value;
2320

2321
  value = msOWSLookupMetadata(metadata, namespaces, name);
×
2322

2323
  if (value == NULL) {
×
2324
    value = default_value;
2325
    default_value = NULL;
2326
  }
2327

2328
  if (value != NULL) {
×
2329
    char **keywords;
2330
    int numkeywords;
2331

2332
    keywords = msStringSplit(value, ',', &numkeywords);
×
2333
    if (keywords && numkeywords > 0) {
×
2334
      int kw;
2335
      if (startTag)
×
2336
        msIO_fprintf(stream, "%s", startTag);
×
2337
      for (kw = 0; kw < numkeywords; kw++) {
×
2338
        if (default_value != NULL &&
×
2339
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
×
2340
                0 &&
×
2341
            strncasecmp("_exclude", default_value + strlen(default_value) - 8,
×
2342
                        8) == 0)
2343
          continue;
×
2344

2345
        msIO_fprintf(stream, itemFormat, keywords[kw]);
×
2346
      }
2347
      if (endTag)
×
2348
        msIO_fprintf(stream, "%s", endTag);
×
2349
    }
2350
    msFreeCharArray(keywords, numkeywords);
×
2351
    return MS_TRUE;
2352
  }
2353
  return MS_FALSE;
2354
}
2355

2356
/* msOWSPrintEncodeMetadataList()
2357
**
2358
** Prints comma-separated lists metadata.  (e.g. keywordList)
2359
** This will print HTML encoded values.
2360
** default_value serves 2 purposes if specified:
2361
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2362
**  (exclusion)
2363
** - will be printed if MetadataList is empty (fallback)
2364
**/
2365
int msOWSPrintEncodeMetadataList(FILE *stream, hashTableObj *metadata,
512✔
2366
                                 const char *namespaces, const char *name,
2367
                                 const char *startTag, const char *endTag,
2368
                                 const char *itemFormat,
2369
                                 const char *default_value) {
2370
  const char *value;
2371
  char *encoded;
2372
  size_t default_value_len = 0;
2373

2374
  value = msOWSLookupMetadata(metadata, namespaces, name);
512✔
2375

2376
  if (value == NULL) {
512✔
2377
    value = default_value;
2378
    default_value = NULL;
2379
  }
2380
  if (default_value)
270✔
2381
    default_value_len = strlen(default_value);
47✔
2382

2383
  if (value != NULL) {
512✔
2384
    char **keywords;
2385
    int numkeywords;
2386

2387
    keywords = msStringSplit(value, ',', &numkeywords);
273✔
2388
    if (keywords && numkeywords > 0) {
273✔
2389
      int kw;
2390
      if (startTag)
273✔
2391
        msIO_fprintf(stream, "%s", startTag);
45✔
2392
      for (kw = 0; kw < numkeywords; kw++) {
806✔
2393
        if (default_value != NULL && default_value_len > 8 &&
533✔
2394
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
103✔
2395
                0 &&
80✔
2396
            strncasecmp("_exclude", default_value + default_value_len - 8, 8) ==
40✔
2397
                0)
2398
          continue;
40✔
2399

2400
        encoded = msEncodeHTMLEntities(keywords[kw]);
493✔
2401
        msIO_fprintf(stream, itemFormat, encoded);
493✔
2402
        msFree(encoded);
493✔
2403
      }
2404
      if (endTag)
273✔
2405
        msIO_fprintf(stream, "%s", endTag);
45✔
2406
    }
2407
    msFreeCharArray(keywords, numkeywords);
273✔
2408
    return MS_TRUE;
2409
  }
2410
  return MS_FALSE;
2411
}
2412

2413
/* msOWSPrintEncodeParamList()
2414
**
2415
** Same as msOWSPrintEncodeMetadataList() but applied to mapfile parameters.
2416
**/
2417
int msOWSPrintEncodeParamList(FILE *stream, const char *name, const char *value,
220✔
2418
                              int action_if_not_found, char delimiter,
2419
                              const char *startTag, const char *endTag,
2420
                              const char *format, const char *default_value) {
2421
  int status = MS_NOERR;
2422
  char *encoded;
2423
  char **items = NULL;
2424
  int numitems = 0, i;
220✔
2425

2426
  if (value && strlen(value) > 0)
220✔
2427
    items = msStringSplit(value, delimiter, &numitems);
164✔
2428
  else {
2429
    if (action_if_not_found == OWS_WARN) {
56✔
2430
      msIO_fprintf(stream,
×
2431
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2432
                   "in this context. -->\n",
2433
                   name);
2434
      status = action_if_not_found;
2435
    }
2436

2437
    if (default_value)
56✔
2438
      items = msStringSplit(default_value, delimiter, &numitems);
×
2439
  }
2440

2441
  if (items && numitems > 0) {
164✔
2442
    if (startTag)
164✔
2443
      msIO_fprintf(stream, "%s", startTag);
×
2444
    for (i = 0; i < numitems; i++) {
472✔
2445
      encoded = msEncodeHTMLEntities(items[i]);
308✔
2446
      msIO_fprintf(stream, format, encoded);
308✔
2447
      msFree(encoded);
308✔
2448
    }
2449
    if (endTag)
164✔
2450
      msIO_fprintf(stream, "%s", endTag);
×
2451
  }
2452
  msFreeCharArray(items, numitems);
220✔
2453

2454
  return status;
220✔
2455
}
2456

2457
/*
2458
** msOWSPrintEX_GeographicBoundingBox()
2459
**
2460
** Print a EX_GeographicBoundingBox tag for WMS1.3.0
2461
**
2462
*/
2463
void msOWSPrintEX_GeographicBoundingBox(FILE *stream, const char *tabspace,
115✔
2464
                                        rectObj *extent, projectionObj *srcproj)
2465

2466
{
2467
  const char *pszTag = "EX_GeographicBoundingBox"; /* The default for WMS */
2468
  rectObj ext;
2469

2470
  ext = *extent;
115✔
2471

2472
  /* always project to lat long */
2473
  msOWSProjectToWGS84(srcproj, &ext);
115✔
2474

2475
  msIO_fprintf(stream, "%s<%s>\n", tabspace, pszTag);
115✔
2476
  msIO_fprintf(stream, "%s    <westBoundLongitude>%.6f</westBoundLongitude>\n",
115✔
2477
               tabspace, ext.minx);
2478
  msIO_fprintf(stream, "%s    <eastBoundLongitude>%.6f</eastBoundLongitude>\n",
115✔
2479
               tabspace, ext.maxx);
2480
  msIO_fprintf(stream, "%s    <southBoundLatitude>%.6f</southBoundLatitude>\n",
115✔
2481
               tabspace, ext.miny);
2482
  msIO_fprintf(stream, "%s    <northBoundLatitude>%.6f</northBoundLatitude>\n",
115✔
2483
               tabspace, ext.maxy);
2484
  msIO_fprintf(stream, "%s</%s>\n", tabspace, pszTag);
115✔
2485

2486
  /* msIO_fprintf(stream, "%s<%s minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"
2487
     />\n", tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy); */
2488
}
115✔
2489

2490
/*
2491
** msOWSPrintLatLonBoundingBox()
2492
**
2493
** Print a LatLonBoundingBox tag for WMS, or LatLongBoundingBox for WFS
2494
** ... yes, the tag name differs between WMS and WFS, yuck!
2495
**
2496
*/
2497
void msOWSPrintLatLonBoundingBox(FILE *stream, const char *tabspace,
139✔
2498
                                 rectObj *extent, projectionObj *srcproj,
2499
                                 projectionObj *wfsproj,
2500
                                 OWSServiceType nService) {
2501
  const char *pszTag = "LatLonBoundingBox"; /* The default for WMS */
2502
  rectObj ext;
2503

2504
  ext = *extent;
139✔
2505

2506
  if (nService == OWS_WMS) { /* always project to lat long */
139✔
2507
    msOWSProjectToWGS84(srcproj, &ext);
114✔
2508
  } else if (nService == OWS_WFS) { /* called from wfs 1.0.0 only: project to
25✔
2509
                                       map srs, if set */
2510
    pszTag = "LatLongBoundingBox";
2511
    if (wfsproj) {
25✔
2512
      if (msProjectionsDiffer(srcproj, wfsproj) == MS_TRUE)
25✔
2513
        msProjectRect(srcproj, wfsproj, &ext);
16✔
2514
    }
2515
  }
2516

2517
  msIO_fprintf(
139✔
2518
      stream,
2519
      "%s<%s minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" maxy=\"%.6f\" />\n",
2520
      tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy);
2521
}
139✔
2522

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

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

2567
  for (i = 0; i < num_epsgs; i++) {
1,790✔
2568
    value = epsgs[i];
1,561✔
2569
    if (value && *value) {
1,561✔
2570
      memcpy(&ext, extent, sizeof(rectObj));
2571

2572
      /* reproject the extents for each SRS's bounding box */
2573
      msInitProjection(&proj);
1,545✔
2574
      msProjectionInheritContextFrom(&proj, srcproj);
1,545✔
2575
      if (msLoadProjectionStringEPSG(&proj, (char *)value) == 0) {
1,545✔
2576
        if (msProjectionsDiffer(srcproj, &proj) == MS_TRUE) {
1,527✔
2577
          msProjectRect(srcproj, &proj, &ext);
1,439✔
2578
        }
2579
        /*for wms 1.3.0 we need to make sure that we present the BBOX with
2580
          a reversed axes for some espg codes*/
2581
        if (wms_version >= OWS_1_3_0 && strncasecmp(value, "EPSG:", 5) == 0) {
1,527✔
2582
          msAxisNormalizePoints(&proj, 1, &(ext.minx), &(ext.miny));
775✔
2583
          msAxisNormalizePoints(&proj, 1, &(ext.maxx), &(ext.maxy));
775✔
2584
        }
2585
      }
2586

2587
      encoded = msEncodeHTMLEntities(value);
1,545✔
2588
      if (msProjIsGeographicCRS(&proj))
1,545✔
2589
        msIO_fprintf(stream,
584✔
2590
                     "%s<BoundingBox %s=\"%s\"\n"
2591
                     "%s            minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" "
2592
                     "maxy=\"%.6f\"",
2593
                     tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS",
2594
                     encoded, tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2595
      else
2596
        msIO_fprintf(
1,720✔
2597
            stream,
2598
            "%s<BoundingBox %s=\"%s\"\n"
2599
            "%s            minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"",
2600
            tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS", encoded,
2601
            tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2602

2603
      msFree(encoded);
1,545✔
2604
      msFreeProjection(&proj);
1,545✔
2605

2606
      if ((resx = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resx")) !=
1,545✔
2607
              NULL &&
2✔
2608
          (resy = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resy")) !=
2✔
2609
              NULL) {
2610
        encoded_resx = msEncodeHTMLEntities(resx);
2✔
2611
        encoded_resy = msEncodeHTMLEntities(resy);
2✔
2612
        msIO_fprintf(stream, "\n%s            resx=\"%s\" resy=\"%s\"",
2✔
2613
                     tabspace, encoded_resx, encoded_resy);
2614
        msFree(encoded_resx);
2✔
2615
        msFree(encoded_resy);
2✔
2616
      }
2617

2618
      msIO_fprintf(stream, " />\n");
1,545✔
2619
    }
2620
  }
2621
  msFreeCharArray(epsgs, num_epsgs);
229✔
2622
}
229✔
2623

2624
/*
2625
** Print the contact information
2626
*/
2627
void msOWSPrintContactInfo(FILE *stream, const char *tabspace, int nVersion,
60✔
2628
                           hashTableObj *metadata, const char *namespaces) {
2629
  /* contact information is a required element in 1.0.7 but the */
2630
  /* sub-elements such as ContactPersonPrimary, etc. are not! */
2631
  /* In 1.1.0, ContactInformation becomes optional. */
2632
  if (nVersion > OWS_1_0_0) {
60✔
2633
    msIO_fprintf(stream, "%s<ContactInformation>\n", tabspace);
58✔
2634

2635
    /* ContactPersonPrimary is optional, but when present then all its  */
2636
    /* sub-elements are mandatory */
2637

2638
    if (msOWSLookupMetadata(metadata, namespaces, "contactperson") ||
82✔
2639
        msOWSLookupMetadata(metadata, namespaces, "contactorganization")) {
24✔
2640
      msIO_fprintf(stream, "%s  <ContactPersonPrimary>\n", tabspace);
34✔
2641

2642
      msOWSPrintEncodeMetadata(
34✔
2643
          stream, metadata, namespaces, "contactperson", OWS_WARN,
2644
          "      <ContactPerson>%s</ContactPerson>\n", NULL);
2645
      msOWSPrintEncodeMetadata(
34✔
2646
          stream, metadata, namespaces, "contactorganization", OWS_WARN,
2647
          "      <ContactOrganization>%s</ContactOrganization>\n", NULL);
2648
      msIO_fprintf(stream, "%s  </ContactPersonPrimary>\n", tabspace);
34✔
2649
    }
2650

2651
    if (msOWSLookupMetadata(metadata, namespaces, "contactposition")) {
58✔
2652
      msOWSPrintEncodeMetadata(
34✔
2653
          stream, metadata, namespaces, "contactposition", OWS_NOERR,
2654
          "      <ContactPosition>%s</ContactPosition>\n", NULL);
2655
    }
2656

2657
    /* ContactAddress is optional, but when present then all its  */
2658
    /* sub-elements are mandatory */
2659
    if (msOWSLookupMetadata(metadata, namespaces, "addresstype") ||
82✔
2660
        msOWSLookupMetadata(metadata, namespaces, "address") ||
48✔
2661
        msOWSLookupMetadata(metadata, namespaces, "city") ||
48✔
2662
        msOWSLookupMetadata(metadata, namespaces, "stateorprovince") ||
48✔
2663
        msOWSLookupMetadata(metadata, namespaces, "postcode") ||
48✔
2664
        msOWSLookupMetadata(metadata, namespaces, "country")) {
24✔
2665
      msIO_fprintf(stream, "%s  <ContactAddress>\n", tabspace);
34✔
2666

2667
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "addresstype",
34✔
2668
                               OWS_WARN,
2669
                               "        <AddressType>%s</AddressType>\n", NULL);
2670
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "address",
34✔
2671
                               OWS_WARN, "        <Address>%s</Address>\n",
2672
                               NULL);
2673
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "city", OWS_WARN,
34✔
2674
                               "        <City>%s</City>\n", NULL);
2675
      msOWSPrintEncodeMetadata(
34✔
2676
          stream, metadata, namespaces, "stateorprovince", OWS_WARN,
2677
          "        <StateOrProvince>%s</StateOrProvince>\n", NULL);
2678
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "postcode",
34✔
2679
                               OWS_WARN, "        <PostCode>%s</PostCode>\n",
2680
                               NULL);
2681
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "country",
34✔
2682
                               OWS_WARN, "        <Country>%s</Country>\n",
2683
                               NULL);
2684
      msIO_fprintf(stream, "%s  </ContactAddress>\n", tabspace);
34✔
2685
    }
2686

2687
    if (msOWSLookupMetadata(metadata, namespaces, "contactvoicetelephone")) {
58✔
2688
      msOWSPrintEncodeMetadata(
34✔
2689
          stream, metadata, namespaces, "contactvoicetelephone", OWS_NOERR,
2690
          "      <ContactVoiceTelephone>%s</ContactVoiceTelephone>\n", NULL);
2691
    }
2692

2693
    if (msOWSLookupMetadata(metadata, namespaces,
58✔
2694
                            "contactfacsimiletelephone")) {
2695
      msOWSPrintEncodeMetadata(
28✔
2696
          stream, metadata, namespaces, "contactfacsimiletelephone", OWS_NOERR,
2697
          "      <ContactFacsimileTelephone>%s</ContactFacsimileTelephone>\n",
2698
          NULL);
2699
    }
2700

2701
    if (msOWSLookupMetadata(metadata, namespaces,
58✔
2702
                            "contactelectronicmailaddress")) {
2703
      msOWSPrintEncodeMetadata(
34✔
2704
          stream, metadata, namespaces, "contactelectronicmailaddress",
2705
          OWS_NOERR,
2706
          "  <ContactElectronicMailAddress>%s</ContactElectronicMailAddress>\n",
2707
          NULL);
2708
    }
2709
    msIO_fprintf(stream, "%s</ContactInformation>\n", tabspace);
58✔
2710
  }
2711
}
60✔
2712

2713
/*
2714
** msOWSGetLayerExtent()
2715
**
2716
** Try to establish layer extent, first looking for "ows_extent" metadata, and
2717
** if not found then call msLayerGetExtent() which will lookup the
2718
** layer->extent member, and if not found will open layer to read extent.
2719
**
2720
*/
2721
int msOWSGetLayerExtent(mapObj *map, layerObj *lp, const char *namespaces,
926✔
2722
                        rectObj *ext) {
2723
  (void)map;
2724
  const char *value;
2725

2726
  if ((value = msOWSLookupMetadata(&(lp->metadata), namespaces, "extent")) !=
926✔
2727
      NULL) {
2728
    char **tokens;
2729
    int n;
2730

2731
    tokens = msStringSplit(value, ' ', &n);
291✔
2732
    if (tokens == NULL || n != 4) {
291✔
2733
      msSetError(MS_WMSERR, "Wrong number of arguments for EXTENT metadata.",
×
2734
                 "msOWSGetLayerExtent()");
2735
      return MS_FAILURE;
×
2736
    }
2737
    ext->minx = atof(tokens[0]);
291✔
2738
    ext->miny = atof(tokens[1]);
291✔
2739
    ext->maxx = atof(tokens[2]);
291✔
2740
    ext->maxy = atof(tokens[3]);
291✔
2741

2742
    msFreeCharArray(tokens, n);
291✔
2743
    return MS_SUCCESS;
291✔
2744
  } else {
2745
    return msLayerGetExtent(lp, ext);
635✔
2746
  }
2747

2748
  return MS_FAILURE;
2749
}
2750

2751
/**********************************************************************
2752
 *                          msOWSExecuteRequests()
2753
 *
2754
 * Execute a number of WFS/WMS HTTP requests in parallel, and then
2755
 * update layerObj information with the result of the requests.
2756
 **********************************************************************/
2757
int msOWSExecuteRequests(httpRequestObj *pasReqInfo, int numRequests,
42✔
2758
                         mapObj *map, int bCheckLocalCache) {
2759
  int nStatus, iReq;
2760

2761
  /* Execute requests */
2762
#if defined(USE_CURL)
2763
  nStatus = msHTTPExecuteRequests(pasReqInfo, numRequests, bCheckLocalCache);
42✔
2764
#else
2765
  msSetError(MS_WMSERR,
2766
             "msOWSExecuteRequests() called apparently without libcurl "
2767
             "configured, msHTTPExecuteRequests() not available.",
2768
             "msOWSExecuteRequests()");
2769
  return MS_FAILURE;
2770
#endif
2771

2772
  /* Scan list of layers and call the handler for each layer type to */
2773
  /* pass them the request results. */
2774
  for (iReq = 0; iReq < numRequests; iReq++) {
84✔
2775
    if (pasReqInfo[iReq].nLayerId >= 0 &&
42✔
2776
        pasReqInfo[iReq].nLayerId < map->numlayers) {
42✔
2777
      layerObj *lp;
2778

2779
      lp = GET_LAYER(map, pasReqInfo[iReq].nLayerId);
42✔
2780

2781
      if (lp->connectiontype == MS_WFS)
42✔
2782
        msWFSUpdateRequestInfo(lp, &(pasReqInfo[iReq]));
3✔
2783
    }
2784
  }
2785

2786
  return nStatus;
42✔
2787
}
2788

2789
/**********************************************************************
2790
 *                          msOWSProcessException()
2791
 *
2792
 **********************************************************************/
2793
void msOWSProcessException(layerObj *lp, const char *pszFname, int nErrorCode,
×
2794
                           const char *pszFuncName) {
2795
  FILE *fp;
2796

2797
  if ((fp = fopen(pszFname, "r")) != NULL) {
×
2798
    char *pszBuf = NULL;
2799
    int nBufSize = 0;
2800
    char *pszStart, *pszEnd;
2801

2802
    fseek(fp, 0, SEEK_END);
×
2803
    nBufSize = ftell(fp);
×
2804
    if (nBufSize < 0) {
×
2805
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
×
2806
      fclose(fp);
×
2807
      return;
×
2808
    }
2809
    rewind(fp);
×
2810
    pszBuf = (char *)malloc((nBufSize + 1) * sizeof(char));
×
2811
    if (pszBuf == NULL) {
×
2812
      msSetError(MS_MEMERR, NULL, "msOWSProcessException()");
×
2813
      fclose(fp);
×
2814
      return;
×
2815
    }
2816

2817
    if ((int)fread(pszBuf, 1, nBufSize, fp) != nBufSize) {
×
2818
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
×
2819
      free(pszBuf);
×
2820
      fclose(fp);
×
2821
      return;
×
2822
    }
2823

2824
    pszBuf[nBufSize] = '\0';
×
2825

2826
    /* OK, got the data in the buffer.  Look for the <Message> tags */
2827
    if ((strstr(pszBuf, "<WFS_Exception>") && /* WFS style */
×
2828
         (pszStart = strstr(pszBuf, "<Message>")) &&
×
2829
         (pszEnd = strstr(pszStart, "</Message>"))) ||
×
2830
        (strstr(pszBuf, "<ServiceExceptionReport>") && /* WMS style */
×
2831
         (pszStart = strstr(pszBuf, "<ServiceException>")) &&
×
2832
         (pszEnd = strstr(pszStart, "</ServiceException>")))) {
×
2833
      pszStart = strchr(pszStart, '>') + 1;
×
2834
      *pszEnd = '\0';
×
2835
      msSetError(nErrorCode, "Got Remote Server Exception for layer %s: %s",
×
2836
                 pszFuncName, lp->name ? lp->name : "(null)", pszStart);
×
2837
    } else {
2838
      msSetError(
×
2839
          MS_WFSCONNERR,
2840
          "Unable to parse Remote Server Exception Message for layer %s.",
2841
          pszFuncName, lp->name ? lp->name : "(null)");
×
2842
    }
2843

2844
    free(pszBuf);
×
2845
    fclose(fp);
×
2846
  }
2847
}
2848

2849
/**********************************************************************
2850
 *                          msOWSBuildURLFilename()
2851
 *
2852
 * Build a unique filename for this URL to use in caching remote server
2853
 * requests.  Slashes and illegal characters will be turned into '_'
2854
 *
2855
 * Returns a newly allocated buffer that should be freed by the caller or
2856
 * NULL in case of error.
2857
 **********************************************************************/
2858
char *msOWSBuildURLFilename(const char *pszPath, const char *pszURL,
×
2859
                            const char *pszExt) {
2860
  char *pszBuf, *pszPtr;
2861
  int i;
2862
  size_t nBufLen = 0;
2863

2864
  nBufLen = strlen(pszURL) + strlen(pszExt) + 2;
×
2865
  if (pszPath)
×
2866
    nBufLen += (strlen(pszPath) + 1);
×
2867

2868
  pszBuf = (char *)malloc(nBufLen);
×
2869
  if (pszBuf == NULL) {
×
2870
    msSetError(MS_MEMERR, NULL, "msOWSBuildURLFilename()");
×
2871
    return NULL;
×
2872
  }
2873
  pszBuf[0] = '\0';
×
2874

2875
  if (pszPath) {
×
2876
#ifdef _WIN32
2877
    if (pszPath[strlen(pszPath) - 1] != '/' &&
2878
        pszPath[strlen(pszPath) - 1] != '\\')
2879
      snprintf(pszBuf, nBufLen, "%s\\", pszPath);
2880
    else
2881
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2882
#else
2883
    if (pszPath[strlen(pszPath) - 1] != '/')
×
2884
      snprintf(pszBuf, nBufLen, "%s/", pszPath);
2885
    else
2886
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2887
#endif
2888
  }
2889

2890
  pszPtr = pszBuf + strlen(pszBuf);
×
2891

2892
  for (i = 0; pszURL[i] != '\0'; i++) {
×
2893
    if (isalnum(pszURL[i]))
×
2894
      *pszPtr = pszURL[i];
×
2895
    else
2896
      *pszPtr = '_';
×
2897
    pszPtr++;
×
2898
  }
2899

2900
  strlcpy(pszPtr, pszExt, nBufLen);
2901

2902
  return pszBuf;
2903
}
2904

2905
/*
2906
** msOWSGetProjURN()
2907
**
2908
** Fetch an OGC URN for this layer or map.  Similar to msOWSGetEPSGProj()
2909
** but returns the result in the form "urn:ogc:def:crs:EPSG::27700".
2910
** The returned buffer is dynamically allocated, and must be freed by the
2911
** caller.
2912
*/
2913
char *msOWSGetProjURN(projectionObj *proj, hashTableObj *metadata,
453✔
2914
                      const char *namespaces, int bReturnOnlyFirstOne) {
2915
  char *result;
2916
  char **tokens;
2917
  int numtokens, i;
2918
  char *oldStyle = NULL;
453✔
2919

2920
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
453✔
2921

2922
  if (oldStyle == NULL || strncmp(oldStyle, "CRS:", 4) == 0 ||
453✔
2923
      strncmp(oldStyle, "AUTO:", 5) == 0 ||
447✔
2924
      strncmp(oldStyle, "AUTO2:", 6) == 0) {
447✔
2925
    msFree(oldStyle);
6✔
2926
    return NULL;
6✔
2927
  }
2928

2929
  result = msStrdup("");
447✔
2930

2931
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
447✔
2932
  msFree(oldStyle);
447✔
2933
  for (i = 0; tokens != NULL && i < numtokens; i++) {
948✔
2934
    char urn[100];
2935
    char *colon = strchr(tokens[i], ':');
501✔
2936

2937
    if (colon != NULL && strchr(colon + 1, ':') == NULL) {
501✔
2938
      *colon = 0;
501✔
2939
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:%s::%s", tokens[i],
501✔
2940
               colon + 1);
2941
    } else if (strcasecmp(tokens[i], "imageCRS") == 0)
×
2942
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:OGC::imageCRS");
2943
    else if (strncmp(tokens[i], "urn:ogc:def:crs:", 16) == 0) {
×
2944
      strlcpy(urn, tokens[i], sizeof(urn));
2945
    } else {
2946
      strlcpy(urn, "", sizeof(urn));
2947
    }
2948

2949
    if (strlen(urn) > 0) {
501✔
2950
      const size_t bufferSize = strlen(result) + strlen(urn) + 2;
501✔
2951
      result = (char *)msSmallRealloc(result, bufferSize);
501✔
2952

2953
      if (strlen(result) > 0)
501✔
2954
        strlcat(result, " ", bufferSize);
2955
      strlcat(result, urn, bufferSize);
2956
    } else {
2957
      msDebug("msOWSGetProjURN(): Failed to process SRS '%s', ignored.",
×
2958
              tokens[i]);
2959
    }
2960
  }
2961

2962
  msFreeCharArray(tokens, numtokens);
447✔
2963

2964
  if (strlen(result) == 0) {
447✔
2965
    msFree(result);
×
2966
    return NULL;
×
2967
  } else
2968
    return result;
2969
}
2970

2971
/*
2972
** msOWSGetProjURI()
2973
**
2974
** Fetch an OGC URI for this layer or map.  Similar to msOWSGetEPSGProj()
2975
** but returns the result in the form
2976
*"http://www.opengis.net/def/crs/EPSG/0/27700".
2977
** The returned buffer is dynamically allocated, and must be freed by the
2978
** caller.
2979
*/
2980
char *msOWSGetProjURI(projectionObj *proj, hashTableObj *metadata,
242✔
2981
                      const char *namespaces, int bReturnOnlyFirstOne) {
2982
  char *result;
2983
  char **tokens;
2984
  int numtokens, i;
2985
  char *oldStyle = NULL;
242✔
2986

2987
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
242✔
2988

2989
  if (oldStyle == NULL || !EQUALN(oldStyle, "EPSG:", 5)) {
242✔
2990
    msFree(oldStyle); // avoid leak
2✔
2991
    return NULL;
2✔
2992
  }
2993

2994
  result = msStrdup("");
240✔
2995

2996
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
240✔
2997
  msFree(oldStyle);
240✔
2998
  for (i = 0; tokens != NULL && i < numtokens; i++) {
513✔
2999
    char urn[100];
3000

3001
    if (strncmp(tokens[i], "EPSG:", 5) == 0)
273✔
3002
      snprintf(urn, sizeof(urn), "http://www.opengis.net/def/crs/EPSG/0/%s",
273✔
3003
               tokens[i] + 5);
3004
    else if (strcasecmp(tokens[i], "imageCRS") == 0)
×
3005
      snprintf(urn, sizeof(urn),
3006
               "http://www.opengis.net/def/crs/OGC/0/imageCRS");
3007
    else if (strncmp(tokens[i], "http://www.opengis.net/def/crs/", 16) == 0)
×
3008
      snprintf(urn, sizeof(urn), "%s", tokens[i]);
3009
    else
3010
      strlcpy(urn, "", sizeof(urn));
3011

3012
    if (strlen(urn) > 0) {
273✔
3013
      result = (char *)msSmallRealloc(result, strlen(result) + strlen(urn) + 2);
273✔
3014

3015
      if (strlen(result) > 0)
273✔
3016
        strcat(result, " ");
3017
      strcat(result, urn);
3018
    } else {
3019
      msDebug("msOWSGetProjURI(): Failed to process SRS '%s', ignored.",
×
3020
              tokens[i]);
3021
    }
3022
  }
3023

3024
  msFreeCharArray(tokens, numtokens);
240✔
3025

3026
  if (strlen(result) == 0) {
240✔
3027
    msFree(result);
×
3028
    return NULL;
×
3029
  } else
3030
    return result;
3031
}
3032

3033
/*
3034
** msOWSGetDimensionInfo()
3035
**
3036
** Extract dimension information from a layer's metadata
3037
**
3038
** Before 4.9, only the time dimension was support. With the addition of
3039
** Web Map Context 1.1.0, we need to support every dimension types.
3040
** This function get the dimension information from special metadata in
3041
** the layer, but can also return default values for the time dimension.
3042
**
3043
*/
3044
void msOWSGetDimensionInfo(layerObj *layer, const char *pszDimension,
×
3045
                           const char **papszDimUserValue,
3046
                           const char **papszDimUnits,
3047
                           const char **papszDimDefault,
3048
                           const char **papszDimNearValue,
3049
                           const char **papszDimUnitSymbol,
3050
                           const char **papszDimMultiValue) {
3051
  char *pszDimensionItem;
3052
  size_t bufferSize = 0;
3053

3054
  if (pszDimension == NULL || layer == NULL)
×
3055
    return;
3056

3057
  bufferSize = strlen(pszDimension) + 50;
×
3058
  pszDimensionItem = (char *)malloc(bufferSize);
×
3059

3060
  /* units (mandatory in map context) */
3061
  if (papszDimUnits != NULL) {
×
3062
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_units", pszDimension);
3063
    *papszDimUnits =
×
3064
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3065
  }
3066
  /* unitSymbol (mandatory in map context) */
3067
  if (papszDimUnitSymbol != NULL) {
×
3068
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_unitsymbol",
3069
             pszDimension);
3070
    *papszDimUnitSymbol =
×
3071
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3072
  }
3073
  /* userValue (mandatory in map context) */
3074
  if (papszDimUserValue != NULL) {
×
3075
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_uservalue",
3076
             pszDimension);
3077
    *papszDimUserValue =
×
3078
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3079
  }
3080
  /* default */
3081
  if (papszDimDefault != NULL) {
×
3082
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_default",
3083
             pszDimension);
3084
    *papszDimDefault =
×
3085
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3086
  }
3087
  /* multipleValues */
3088
  if (papszDimMultiValue != NULL) {
×
3089
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_multiplevalues",
3090
             pszDimension);
3091
    *papszDimMultiValue =
×
3092
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3093
  }
3094
  /* nearestValue */
3095
  if (papszDimNearValue != NULL) {
×
3096
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_nearestvalue",
3097
             pszDimension);
3098
    *papszDimNearValue =
×
3099
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
×
3100
  }
3101

3102
  /* Use default time value if necessary */
3103
  if (strcasecmp(pszDimension, "time") == 0) {
×
3104
    if (papszDimUserValue != NULL && *papszDimUserValue == NULL)
×
3105
      *papszDimUserValue =
×
3106
          msOWSLookupMetadata(&(layer->metadata), "MO", "time");
×
3107
    if (papszDimDefault != NULL && *papszDimDefault == NULL)
×
3108
      *papszDimDefault =
×
3109
          msOWSLookupMetadata(&(layer->metadata), "MO", "timedefault");
×
3110
    if (papszDimUnits != NULL && *papszDimUnits == NULL)
×
3111
      *papszDimUnits = "ISO8601";
×
3112
    if (papszDimUnitSymbol != NULL && *papszDimUnitSymbol == NULL)
×
3113
      *papszDimUnitSymbol = "t";
×
3114
    if (papszDimNearValue != NULL && *papszDimNearValue == NULL)
×
3115
      *papszDimNearValue = "0";
×
3116
  }
3117

3118
  free(pszDimensionItem);
×
3119

3120
  return;
×
3121
}
3122

3123
/**
3124
 * msOWSNegotiateUpdateSequence()
3125
 *
3126
 * returns the updateSequence value for an OWS
3127
 *
3128
 * @param requested_updatesequence the updatesequence passed by the client
3129
 * @param updatesequence the updatesequence set by the server
3130
 *
3131
 * @return result of comparison (-1, 0, 1)
3132
 * -1: lower / higher OR values not set by client or server
3133
 *  1: higher / lower
3134
 *  0: equal
3135
 */
3136

3137
int msOWSNegotiateUpdateSequence(const char *requested_updatesequence,
38✔
3138
                                 const char *updatesequence) {
3139
  int valtype1 = 1; /* default datatype for updatesequence passed by client */
3140
  int valtype2 = 1; /* default datatype for updatesequence set by server */
3141
  struct tm tm_requested_updatesequence, tm_updatesequence;
3142

3143
  /* if not specified by client, or set by server,
3144
     server responds with latest Capabilities XML */
3145
  if (!requested_updatesequence || !updatesequence)
38✔
3146
    return -1;
3147

3148
  /* test to see if server value is an integer (1), string (2) or timestamp (3)
3149
   */
3150
  if (msStringIsInteger(updatesequence) == MS_FAILURE)
38✔
3151
    valtype1 = 2;
3152

3153
  if (valtype1 == 2) { /* test if timestamp */
3154
    msTimeInit(&tm_updatesequence);
15✔
3155
    if (msParseTime(updatesequence, &tm_updatesequence) == MS_TRUE)
15✔
3156
      valtype1 = 3;
3157
    msResetErrorList();
15✔
3158
  }
3159

3160
  /* test to see if client value is an integer (1), string (2) or timestamp (3)
3161
   */
3162
  if (msStringIsInteger(requested_updatesequence) == MS_FAILURE)
38✔
3163
    valtype2 = 2;
3164

3165
  if (valtype2 == 2) { /* test if timestamp */
3166
    msTimeInit(&tm_requested_updatesequence);
15✔
3167
    if (msParseTime(requested_updatesequence, &tm_requested_updatesequence) ==
15✔
3168
        MS_TRUE)
3169
      valtype2 = 3;
3170
    msResetErrorList();
15✔
3171
  }
3172

3173
  /* if the datatypes do not match, do not compare, */
3174
  if (valtype1 != valtype2)
38✔
3175
    return -1;
3176

3177
  if (valtype1 == 1) { /* integer */
38✔
3178
    const int requested_updatesequence_i = atoi(requested_updatesequence);
3179
    const int updatesequence_i = atoi(updatesequence);
3180
    if (requested_updatesequence_i < updatesequence_i)
23✔
3181
      return -1;
3182

3183
    if (requested_updatesequence_i > updatesequence_i)
15✔
3184
      return 1;
3185

3186
    return 0;
8✔
3187
  }
3188

3189
  if (valtype1 == 2) /* string */
15✔
3190
    return strcasecmp(requested_updatesequence, updatesequence);
3✔
3191

3192
  assert(valtype1 == 3); /* timestamp */
3193
  /* compare timestamps */
3194
  return msDateCompare(&tm_requested_updatesequence, &tm_updatesequence) +
24✔
3195
         msTimeCompare(&tm_requested_updatesequence, &tm_updatesequence);
12✔
3196
}
3197

3198
/************************************************************************/
3199
/*                         msOwsIsOutputFormatValid                     */
3200
/*                                                                      */
3201
/*      Utility function to parse a comma separated list in a            */
3202
/*      metedata object and select and outputformat.                    */
3203
/************************************************************************/
3204
outputFormatObj *msOwsIsOutputFormatValid(mapObj *map, const char *format,
106✔
3205
                                          hashTableObj *metadata,
3206
                                          const char *namespaces,
3207
                                          const char *name) {
3208
  char **tokens = NULL;
3209
  int i, n;
3210
  outputFormatObj *psFormat = NULL;
3211
  const char *format_list = NULL;
3212

3213
  if (map && format && metadata && namespaces && name) {
106✔
3214
    msApplyDefaultOutputFormats(map);
106✔
3215
    format_list = msOWSLookupMetadata(metadata, namespaces, name);
106✔
3216
    n = 0;
106✔
3217
    if (format_list)
106✔
3218
      tokens = msStringSplit(format_list, ',', &n);
106✔
3219

3220
    if (tokens && n > 0) {
106✔
3221
      for (i = 0; i < n; i++) {
238✔
3222
        int iFormat = msGetOutputFormatIndex(map, tokens[i]);
238✔
3223
        const char *mimetype;
3224
        if (iFormat == -1)
238✔
3225
          continue;
28✔
3226

3227
        mimetype = map->outputformatlist[iFormat]->mimetype;
210✔
3228

3229
        msStringTrim(tokens[i]);
210✔
3230
        if (strcasecmp(tokens[i], format) == 0)
210✔
3231
          break;
3232
        if (mimetype && strcasecmp(mimetype, format) == 0)
106✔
3233
          break;
3234
      }
3235
      if (i < n)
106✔
3236
        psFormat = msSelectOutputFormat(map, format);
106✔
3237
    }
3238
    if (tokens)
106✔
3239
      msFreeCharArray(tokens, n);
106✔
3240
  }
3241

3242
  return psFormat;
106✔
3243
}
3244

3245
#endif /* defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined             \
3246
          (USE_WCS_SVR) || defined(USE_SOS_SVR) || defined(USE_WMS_LYR) ||     \
3247
          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