Coverage Report

Created: 2025-06-13 06:18

/src/MapServer/src/mapows.c
Line
Count
Source (jump to first uncovered line)
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 <assert.h>
46
47
/*
48
** msOWSInitRequestObj() initializes an owsRequestObj; i.e: sets
49
** all internal pointers to NULL.
50
*/
51
0
void msOWSInitRequestObj(owsRequestObj *ows_request) {
52
0
  ows_request->numlayers = 0;
53
0
  ows_request->numwmslayerargs = 0;
54
0
  ows_request->enabled_layers = NULL;
55
0
  ows_request->layerwmsfilterindex = NULL;
56
57
0
  ows_request->service = NULL;
58
0
  ows_request->version = NULL;
59
0
  ows_request->request = NULL;
60
0
  ows_request->document = NULL;
61
0
}
62
63
/*
64
** msOWSClearRequestObj() releases all resources associated with an
65
** owsRequestObj.
66
*/
67
0
void msOWSClearRequestObj(owsRequestObj *ows_request) {
68
0
  msFree(ows_request->enabled_layers);
69
0
  msFree(ows_request->layerwmsfilterindex);
70
0
  msFree(ows_request->service);
71
0
  msFree(ows_request->version);
72
0
  msFree(ows_request->request);
73
0
  if (ows_request->document) {
74
0
#if defined(USE_LIBXML2)
75
0
    xmlFreeDoc(ows_request->document);
76
0
    xmlCleanupParser();
77
#else
78
    CPLDestroyXMLNode(ows_request->document);
79
#endif
80
0
  }
81
0
}
82
83
#if defined(USE_LIBXML2) && LIBXML_VERSION < 20900
84
static int bExternalEntityAsked = MS_FALSE;
85
static xmlParserInputPtr dummyEntityLoader(const char *URL, const char *ID,
86
                                           xmlParserCtxtPtr context) {
87
  bExternalEntityAsked = MS_TRUE;
88
  return NULL;
89
}
90
#endif
91
92
/*
93
** msOWSPreParseRequest() parses a cgiRequestObj either with GET/KVP
94
** or with POST/XML. Only SERVICE, VERSION (or WMTVER) and REQUEST are
95
** being determined, all WxS (or SOS) specific parameters are parsed
96
** within the according handler.
97
** The results are saved within an owsRequestObj. If the request was
98
** transmitted with POST/XML, either the document (if compiled with
99
** libxml2) or the root CPLXMLNode is saved to the ows_request->document
100
** field.
101
** Returns MS_SUCCESS upon success, MS_FAILURE if severe errors occurred
102
** or MS_DONE, if the service could not be determined.
103
*/
104
static int msOWSPreParseRequest(cgiRequestObj *request,
105
0
                                owsRequestObj *ows_request) {
106
  /* decide if KVP or XML */
107
0
  if (request->type == MS_GET_REQUEST ||
108
0
      (request->type == MS_POST_REQUEST && request->contenttype &&
109
0
       strncmp(request->contenttype, "application/x-www-form-urlencoded",
110
0
               strlen("application/x-www-form-urlencoded")) == 0)) {
111
0
    int i;
112
    /* parse KVP parameters service, version and request */
113
0
    for (i = 0; i < request->NumParams; ++i) {
114
0
      if (ows_request->service == NULL &&
115
0
          EQUAL(request->ParamNames[i], "SERVICE")) {
116
0
        ows_request->service = msStrdup(request->ParamValues[i]);
117
0
      } else if (ows_request->version == NULL &&
118
0
                 (EQUAL(request->ParamNames[i], "VERSION") ||
119
0
                  EQUAL(request->ParamNames[i], "WMTVER"))) { /* for WMS */
120
0
        ows_request->version = msStrdup(request->ParamValues[i]);
121
0
      } else if (ows_request->request == NULL &&
122
0
                 EQUAL(request->ParamNames[i], "REQUEST")) {
123
0
        ows_request->request = msStrdup(request->ParamValues[i]);
124
0
      }
125
126
      /* stop if we have all necessary parameters */
127
0
      if (ows_request->service && ows_request->version &&
128
0
          ows_request->request) {
129
0
        break;
130
0
      }
131
0
    }
132
0
  } else if (request->type == MS_POST_REQUEST) {
133
0
#if defined(USE_LIBXML2)
134
0
    xmlNodePtr root = NULL;
135
#if LIBXML_VERSION < 20900
136
    xmlExternalEntityLoader oldExternalEntityLoader;
137
#endif
138
#else
139
    CPLXMLNode *temp;
140
#endif
141
0
    if (!request->postrequest || !strlen(request->postrequest)) {
142
0
      msSetError(MS_OWSERR, "POST request is empty.", "msOWSPreParseRequest()");
143
0
      return MS_FAILURE;
144
0
    }
145
0
#if defined(USE_LIBXML2)
146
#if LIBXML_VERSION < 20900
147
    oldExternalEntityLoader = xmlGetExternalEntityLoader();
148
    /* to avoid  XML External Entity vulnerability with libxml2 < 2.9 */
149
    xmlSetExternalEntityLoader(dummyEntityLoader);
150
    bExternalEntityAsked = MS_FALSE;
151
#endif
152
    /* parse to DOM-Structure with libxml2 and get the root element */
153
0
    ows_request->document =
154
0
        xmlParseMemory(request->postrequest, strlen(request->postrequest));
155
#if LIBXML_VERSION < 20900
156
    xmlSetExternalEntityLoader(oldExternalEntityLoader);
157
    if (bExternalEntityAsked) {
158
      msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",
159
                 "External entity fetch");
160
      return MS_FAILURE;
161
    }
162
#endif
163
0
    if (ows_request->document == NULL ||
164
0
        (root = xmlDocGetRootElement(ows_request->document)) == NULL) {
165
0
      const xmlError *error = xmlGetLastError();
166
0
      msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",
167
0
                 error->message);
168
0
      return MS_FAILURE;
169
0
    }
170
171
    /* Get service, version and request from root */
172
0
    xmlChar *serviceTmp = xmlGetProp(root, BAD_CAST "service");
173
0
    if (serviceTmp != NULL) {
174
0
      ows_request->service = msStrdup((char *)serviceTmp);
175
0
      xmlFree(serviceTmp);
176
0
    }
177
178
0
    xmlChar *versionTmp = xmlGetProp(root, BAD_CAST "version");
179
0
    if (versionTmp != NULL) {
180
0
      ows_request->version = msStrdup((char *)versionTmp);
181
0
      xmlFree(versionTmp);
182
0
    }
183
184
0
    ows_request->request = msStrdup((char *)root->name);
185
186
#else
187
    /* parse with CPLXML */
188
    ows_request->document = CPLParseXMLString(request->postrequest);
189
    if (ows_request->document == NULL) {
190
      msSetError(MS_OWSERR, "XML parsing error: %s", "msOWSPreParseRequest()",
191
                 CPLGetLastErrorMsg());
192
      return MS_FAILURE;
193
    }
194
195
    /* remove all namespaces */
196
    CPLStripXMLNamespace(ows_request->document, NULL, 1);
197
    for (temp = ows_request->document; temp != NULL; temp = temp->psNext) {
198
199
      if (temp->eType == CXT_Element) {
200
        const char *service, *version;
201
        ows_request->request = msStrdup(temp->pszValue);
202
203
        if ((service = CPLGetXMLValue(temp, "service", NULL)) != NULL) {
204
          ows_request->service = msStrdup(service);
205
        }
206
        if ((version = CPLGetXMLValue(temp, "version", NULL)) != NULL) {
207
          ows_request->version = msStrdup(version);
208
        }
209
        continue;
210
      }
211
    }
212
#endif /* defined(USE_LIBXML2) */
213
0
  } else {
214
0
    msSetError(MS_OWSERR, "Unknown request method. Use either GET or POST.",
215
0
               "msOWSPreParseRequest()");
216
0
    return MS_FAILURE;
217
0
  }
218
219
  /* certain WMS requests do not require the service parameter */
220
  /* see: http://trac.osgeo.org/mapserver/ticket/2531          */
221
0
  if (ows_request->service == NULL && ows_request->request != NULL) {
222
0
    if (EQUAL(ows_request->request, "GetMap") ||
223
0
        EQUAL(ows_request->request, "GetFeatureInfo")) {
224
0
      ows_request->service = msStrdup("WMS");
225
0
    } else { /* service could not be determined */
226
0
      return MS_DONE;
227
0
    }
228
0
  }
229
230
0
  return MS_SUCCESS;
231
0
}
232
233
/*
234
** msOWSDispatch() is the entry point for any OWS request (WMS, WFS, ...)
235
** - If this is a valid request then it is processed and MS_SUCCESS is returned
236
**   on success, or MS_FAILURE on failure.
237
** - If force_ows_mode is true then an exception will be produced if the
238
**   request is not recognized as a valid request.
239
** - If force_ows_mode is false and this does not appear to be a valid OWS
240
**   request then MS_DONE is returned and MapServer is expected to process
241
**   this as a regular MapServer (traditional CGI) request.
242
*/
243
0
int msOWSDispatch(mapObj *map, cgiRequestObj *request, int ows_mode) {
244
0
  int status = MS_DONE, force_ows_mode = 0;
245
0
  owsRequestObj ows_request;
246
247
0
  if (!request) {
248
0
    return status;
249
0
  }
250
251
0
  force_ows_mode = (ows_mode == OWS || ows_mode == WFS);
252
253
0
  msOWSInitRequestObj(&ows_request);
254
0
  switch (msOWSPreParseRequest(request, &ows_request)) {
255
0
  case MS_FAILURE: /* a severe error occurred */
256
0
    return MS_FAILURE;
257
0
  case MS_DONE:
258
    /* OWS Service could not be determined              */
259
    /* continue for now                                 */
260
0
    status = MS_DONE;
261
0
  }
262
263
0
  if (ows_request.service == NULL) {
264
0
#ifdef USE_LIBXML2
265
0
    if (ows_request.request && EQUAL(ows_request.request, "GetMetadata")) {
266
0
      status = msMetadataDispatch(map, request);
267
0
      msOWSClearRequestObj(&ows_request);
268
0
      return status;
269
0
    }
270
0
#endif
271
0
#ifdef USE_WFS_SVR
272
0
    if (msOWSLookupMetadata(&(map->web.metadata), "FO", "cite_wfs2") != NULL) {
273
0
      status = msWFSException(map, "service",
274
0
                              MS_OWS_ERROR_MISSING_PARAMETER_VALUE, NULL);
275
0
    } else
276
0
#endif
277
278
      /* exit if service is not set */
279
0
      if (force_ows_mode) {
280
0
        msSetError(MS_MISCERR,
281
0
                   "OWS Common exception: exceptionCode=MissingParameterValue, "
282
0
                   "locator=SERVICE, ExceptionText=SERVICE parameter missing.",
283
0
                   "msOWSDispatch()");
284
0
        status = MS_FAILURE;
285
0
      } else {
286
0
        status = MS_DONE;
287
0
      }
288
0
  } else if (EQUAL(ows_request.service, "WMS")) {
289
0
#ifdef USE_WMS_SVR
290
0
    status = msWMSDispatch(map, request, &ows_request, MS_FALSE);
291
#else
292
    msSetError(
293
        MS_WMSERR,
294
        "SERVICE=WMS requested, but WMS support not configured in MapServer.",
295
        "msOWSDispatch()");
296
#endif
297
0
  } else if (EQUAL(ows_request.service, "WFS")) {
298
0
#ifdef USE_WFS_SVR
299
0
    status = msWFSDispatch(map, request, &ows_request, (ows_mode == WFS));
300
#else
301
    msSetError(
302
        MS_WFSERR,
303
        "SERVICE=WFS requested, but WFS support not configured in MapServer.",
304
        "msOWSDispatch()");
305
#endif
306
0
  } else if (EQUAL(ows_request.service, "WCS")) {
307
0
#ifdef USE_WCS_SVR
308
0
    status = msWCSDispatch(map, request, &ows_request);
309
#else
310
    msSetError(
311
        MS_WCSERR,
312
        "SERVICE=WCS requested, but WCS support not configured in MapServer.",
313
        "msOWSDispatch()");
314
#endif
315
0
  } else if (EQUAL(ows_request.service, "SOS")) {
316
#ifdef USE_SOS_SVR
317
    status = msSOSDispatch(map, request, &ows_request);
318
#else
319
0
    msSetError(
320
0
        MS_SOSERR,
321
0
        "SERVICE=SOS requested, but SOS support not configured in MapServer.",
322
0
        "msOWSDispatch()");
323
0
#endif
324
0
  } else if (force_ows_mode) {
325
0
    msSetError(
326
0
        MS_MISCERR,
327
0
        "OWS Common exception: exceptionCode=InvalidParameterValue, "
328
0
        "locator=SERVICE, ExceptionText=SERVICE parameter value invalid.",
329
0
        "msOWSDispatch()");
330
0
    status = MS_FAILURE;
331
0
  }
332
333
0
  msOWSClearRequestObj(&ows_request);
334
0
  return status;
335
0
}
336
337
/*
338
** msOWSIpParse()
339
**
340
** Parse the IP address or range into a binary array.
341
** Supports ipv4 and ipv6 addresses
342
** Ranges can be specified using the CIDR notation (ie: 192.100.100.0/24)
343
**
344
** Returns the parsed of the IP (4 or 16).
345
*/
346
0
int msOWSIpParse(const char *ip, unsigned char *ip1, unsigned char *mask) {
347
0
  int len = 0, masklen, seps;
348
349
0
  if (msCountChars((char *)ip, '.') == 3) {
350
    /* ipv4 */
351
0
    unsigned char *val = ip1;
352
0
    len = 1;
353
0
    masklen = 32;
354
0
    *val = 0;
355
0
    while (*ip) {
356
0
      if (*ip >= '0' && *ip <= '9')
357
0
        (*val) = 10 * (*val) + (*ip - '0');
358
0
      else if (*ip == '.') {
359
0
        ++val;
360
0
        *val = 0;
361
0
        ++len;
362
0
      } else if (*ip == '/') {
363
0
        masklen = atoi(ip + 1);
364
0
        if (masklen > 32)
365
0
          masklen = 32;
366
0
        break;
367
0
      } else
368
0
        break;
369
0
      ++ip;
370
0
    }
371
0
    if (len != 4)
372
0
      return 0;
373
    /* write mask */
374
0
    if (mask) {
375
0
      memset(mask, 0, len);
376
0
      val = mask;
377
0
      while (masklen) {
378
0
        if (masklen >= 8) {
379
0
          *val = 0xff;
380
0
          masklen -= 8;
381
0
        } else {
382
0
          *val = -((unsigned char)pow(2, 8 - masklen));
383
0
          break;
384
0
        }
385
0
        ++val;
386
0
      }
387
0
    }
388
0
  } else if ((seps = msCountChars((char *)ip, ':')) > 1 && seps < 8) {
389
    /* ipv6 */
390
0
    unsigned short *val = (unsigned short *)ip1;
391
0
    len = 2;
392
0
    masklen = 128;
393
0
    *val = 0;
394
0
    while (*ip) {
395
0
      if (*ip >= '0' && *ip <= '9')
396
0
        (*val) = 16 * (*val) + (*ip - '0');
397
0
      else if (*ip >= 'a' && *ip <= 'f')
398
0
        (*val) = 16 * (*val) + (*ip - 'a' + 10);
399
0
      else if (*ip >= 'A' && *ip <= 'F')
400
0
        (*val) = 16 * (*val) + (*ip - 'A' + 10);
401
0
      else if (*ip == ':') {
402
0
        ++ip;
403
0
        ++val;
404
0
        len += 2;
405
0
        *val = 0;
406
0
        if (*ip == ':') {
407
          /* insert 0 values */
408
0
          while (seps <= 7) {
409
0
            ++val;
410
0
            len += 2;
411
0
            *val = 0;
412
0
            ++seps;
413
0
          }
414
0
        } else
415
0
          continue;
416
0
      } else if (*ip == '/') {
417
0
        masklen = atoi(ip + 1);
418
0
        if (masklen > 128)
419
0
          masklen = 128;
420
0
        break;
421
0
      } else
422
0
        break;
423
0
      ++ip;
424
0
    }
425
0
    if (len != 16)
426
0
      return 0;
427
    /* write mask */
428
0
    if (mask) {
429
0
      memset(mask, 0, len);
430
0
      val = (unsigned short *)mask;
431
0
      while (masklen) {
432
0
        if (masklen >= 16) {
433
0
          *val = 0xffff;
434
0
          masklen -= 16;
435
0
        } else {
436
0
          *val = -((unsigned short)pow(2, 16 - masklen));
437
0
          break;
438
0
        }
439
0
        ++val;
440
0
      }
441
0
    }
442
0
  }
443
444
0
  return len;
445
0
}
446
447
/*
448
** msOWSIpInList()
449
**
450
** Check if an ip is in a space separated list of IP addresses/ranges.
451
** Supports ipv4 and ipv6 addresses
452
** Ranges can be specified using the CIDR notation (ie: 192.100.100.0/24)
453
**
454
** Returns MS_TRUE if the IP is found.
455
*/
456
0
int msOWSIpInList(const char *ip_list, const char *ip) {
457
0
  int i, j, numips, iplen;
458
0
  unsigned char ip1[16];
459
0
  unsigned char ip2[16];
460
0
  unsigned char mask[16];
461
0
  char **ips;
462
463
  /* Parse input IP */
464
0
  iplen = msOWSIpParse(ip, (unsigned char *)&ip1, NULL);
465
0
  if (iplen != 4 && iplen != 16) /* ipv4 or ipv6 */
466
0
    return MS_FALSE;
467
468
0
  ips = msStringSplit(ip_list, ' ', &numips);
469
0
  if (ips) {
470
0
    for (i = 0; i < numips; i++) {
471
0
      if (msOWSIpParse(ips[i], (unsigned char *)&ip2, (unsigned char *)&mask) ==
472
0
          iplen) {
473
0
        for (j = 0; j < iplen; j++) {
474
0
          if ((ip1[j] & mask[j]) != (ip2[j] & mask[j]))
475
0
            break;
476
0
        }
477
0
        if (j == iplen) {
478
0
          msFreeCharArray(ips, numips);
479
0
          return MS_TRUE; /* match found */
480
0
        }
481
0
      }
482
0
    }
483
0
    msFreeCharArray(ips, numips);
484
0
  }
485
486
0
  return MS_FALSE;
487
0
}
488
489
/*
490
** msOWSIpDisabled()
491
**
492
** Check if an ip is in a list specified in the metadata section.
493
**
494
** Returns MS_TRUE if the IP is found.
495
*/
496
0
int msOWSIpInMetadata(const char *ip_list, const char *ip) {
497
0
  FILE *stream;
498
0
  char buffer[MS_BUFFER_LENGTH];
499
0
  int found = MS_FALSE;
500
501
0
  if (strncasecmp(ip_list, "file:", 5) == 0) {
502
0
    stream = fopen(ip_list + 5, "r");
503
0
    if (stream) {
504
0
      found = MS_FALSE;
505
0
      while (fgets(buffer, MS_BUFFER_LENGTH, stream)) {
506
0
        if (msOWSIpInList(buffer, ip)) {
507
0
          found = MS_TRUE;
508
0
          break;
509
0
        }
510
0
      }
511
0
      fclose(stream);
512
0
    }
513
0
  } else {
514
0
    if (msOWSIpInList(ip_list, ip))
515
0
      found = MS_TRUE;
516
0
  }
517
0
  return found;
518
0
}
519
520
/*
521
** msOWSIpDisabled()
522
**
523
** Check if the layers are enabled or disabled by IP list.
524
**
525
** 'namespaces' is a string with a letter for each namespace to lookup
526
** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_
527
** If namespaces is NULL then this function just does a regular metadata
528
** lookup.
529
**
530
** Returns the disabled flag.
531
*/
532
int msOWSIpDisabled(hashTableObj *metadata, const char *namespaces,
533
0
                    const char *ip) {
534
0
  const char *ip_list;
535
0
  int disabled = MS_FALSE;
536
537
0
  if (!ip)
538
0
    return MS_FALSE; /* no endpoint ip */
539
540
0
  ip_list = msOWSLookupMetadata(metadata, namespaces, "allowed_ip_list");
541
0
  if (!ip_list)
542
0
    ip_list = msOWSLookupMetadata(metadata, "O", "allowed_ip_list");
543
544
0
  if (ip_list) {
545
0
    disabled = MS_TRUE;
546
0
    if (msOWSIpInMetadata(ip_list, ip))
547
0
      disabled = MS_FALSE;
548
0
  }
549
550
0
  ip_list = msOWSLookupMetadata(metadata, namespaces, "denied_ip_list");
551
0
  if (!ip_list)
552
0
    ip_list = msOWSLookupMetadata(metadata, "O", "denied_ip_list");
553
554
0
  if (ip_list && msOWSIpInMetadata(ip_list, ip))
555
0
    disabled = MS_TRUE;
556
557
0
  return disabled;
558
0
}
559
560
/*
561
** msOWSRequestIsEnabled()
562
**
563
** Check if a layer is visible for a specific OWS request.
564
**
565
** 'namespaces' is a string with a letter for each namespace to lookup in
566
** the order they should be looked up. e.g. "MO" to lookup wms_ and ows_ If
567
** namespaces is NULL then this function just does a regular metadata
568
** lookup. If check_all_layers is set to MS_TRUE, the function will check
569
** all layers to see if the request is enable. (returns as soon as one is found)
570
*/
571
int msOWSRequestIsEnabled(mapObj *map, layerObj *layer, const char *namespaces,
572
0
                          const char *request, int check_all_layers) {
573
0
  int disabled = MS_FALSE; /* explicitly disabled flag */
574
0
  const char *enable_request;
575
0
  const char *remote_ip;
576
577
0
  if (request == NULL)
578
0
    return MS_FALSE;
579
580
0
  remote_ip = getenv("REMOTE_ADDR");
581
582
  /* First, we check in the layer metadata */
583
0
  if (layer && check_all_layers == MS_FALSE) {
584
0
    enable_request =
585
0
        msOWSLookupMetadata(&layer->metadata, namespaces, "enable_request");
586
0
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
587
0
      return MS_TRUE;
588
0
    if (disabled)
589
0
      return MS_FALSE;
590
591
0
    enable_request =
592
0
        msOWSLookupMetadata(&layer->metadata, "O", "enable_request");
593
0
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
594
0
      return MS_TRUE;
595
0
    if (disabled)
596
0
      return MS_FALSE;
597
598
0
    if (msOWSIpDisabled(&layer->metadata, namespaces, remote_ip))
599
0
      return MS_FALSE;
600
0
  }
601
602
0
  if (map && (check_all_layers == MS_FALSE || map->numlayers == 0)) {
603
    /* then we check in the map metadata */
604
0
    enable_request =
605
0
        msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
606
0
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
607
0
      return MS_TRUE;
608
0
    if (disabled)
609
0
      return MS_FALSE;
610
611
0
    enable_request =
612
0
        msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
613
0
    if (msOWSParseRequestMetadata(enable_request, request, &disabled))
614
0
      return MS_TRUE;
615
0
    if (disabled)
616
0
      return MS_FALSE;
617
618
0
    if (msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
619
0
      return MS_FALSE;
620
0
  }
621
622
0
  if (map && check_all_layers == MS_TRUE) {
623
0
    int i, globally_enabled = MS_FALSE;
624
0
    enable_request =
625
0
        msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
626
0
    globally_enabled =
627
0
        msOWSParseRequestMetadata(enable_request, request, &disabled);
628
629
0
    if (!globally_enabled && !disabled) {
630
0
      enable_request =
631
0
          msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
632
0
      globally_enabled =
633
0
          msOWSParseRequestMetadata(enable_request, request, &disabled);
634
0
    }
635
636
0
    if (globally_enabled &&
637
0
        msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
638
0
      globally_enabled = MS_FALSE;
639
640
    /* Check all layers */
641
0
    for (i = 0; i < map->numlayers; i++) {
642
0
      int result = MS_FALSE;
643
0
      layerObj *lp;
644
0
      lp = (GET_LAYER(map, i));
645
646
0
      enable_request =
647
0
          msOWSLookupMetadata(&lp->metadata, namespaces, "enable_request");
648
0
      result = msOWSParseRequestMetadata(enable_request, request, &disabled);
649
0
      if (!result && disabled)
650
0
        continue; /* if the request has been explicitly set to disabled,
651
                     continue */
652
653
0
      if (!result && !disabled) { /* if the request has not been found in the
654
                                     wms metadata, */
655
        /* check the ows namespace  */
656
657
0
        enable_request =
658
0
            msOWSLookupMetadata(&lp->metadata, "O", "enable_request");
659
0
        result = msOWSParseRequestMetadata(enable_request, request, &disabled);
660
0
        if (!result && disabled)
661
0
          continue;
662
0
      }
663
664
0
      if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))
665
0
        continue;
666
667
0
      if (result || (!disabled && globally_enabled))
668
0
        return MS_TRUE;
669
0
    }
670
671
0
    if (!disabled && globally_enabled)
672
0
      return MS_TRUE;
673
0
  }
674
675
0
  return MS_FALSE;
676
0
}
677
678
/*
679
** msOWSRequestLayersEnabled()
680
**
681
** Check if the layers are visible for a specific OWS request.
682
**
683
** 'namespaces' is a string with a letter for each namespace to lookup
684
** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_
685
** If namespaces is NULL then this function just does a regular metadata
686
** lookup.
687
**
688
** Generates an array of the layer ids enabled.
689
*/
690
void msOWSRequestLayersEnabled(mapObj *map, const char *namespaces,
691
                               const char *request,
692
0
                               owsRequestObj *ows_request) {
693
0
  int disabled = MS_FALSE; /* explicitly disabled flag */
694
0
  int globally_enabled = MS_FALSE;
695
0
  const char *enable_request;
696
0
  const char *remote_ip;
697
698
0
  if (ows_request->numlayers > 0)
699
0
    msFree(ows_request->enabled_layers);
700
701
0
  ows_request->numlayers = 0;
702
0
  ows_request->enabled_layers = NULL;
703
704
0
  if (request == NULL || (map == NULL) || (map->numlayers <= 0))
705
0
    return;
706
707
0
  remote_ip = getenv("REMOTE_ADDR");
708
709
0
  enable_request =
710
0
      msOWSLookupMetadata(&map->web.metadata, namespaces, "enable_request");
711
0
  globally_enabled =
712
0
      msOWSParseRequestMetadata(enable_request, request, &disabled);
713
714
0
  if (!globally_enabled && !disabled) {
715
0
    enable_request =
716
0
        msOWSLookupMetadata(&map->web.metadata, "O", "enable_request");
717
0
    globally_enabled =
718
0
        msOWSParseRequestMetadata(enable_request, request, &disabled);
719
0
  }
720
721
0
  if (globally_enabled &&
722
0
      msOWSIpDisabled(&map->web.metadata, namespaces, remote_ip))
723
0
    globally_enabled = MS_FALSE;
724
725
0
  if (map->numlayers) {
726
0
    int i, layers_size = map->numlayers; /* for most of cases, this will be
727
                                            relatively small */
728
729
0
    ows_request->enabled_layers =
730
0
        (int *)msSmallMalloc(sizeof(int) * layers_size);
731
732
0
    for (i = 0; i < map->numlayers; i++) {
733
0
      int result = MS_FALSE;
734
0
      layerObj *lp;
735
0
      lp = (GET_LAYER(map, i));
736
737
0
      enable_request =
738
0
          msOWSLookupMetadata(&lp->metadata, namespaces, "enable_request");
739
0
      result = msOWSParseRequestMetadata(enable_request, request, &disabled);
740
0
      if (!result && disabled)
741
0
        continue; /* if the request has been explicitly set to disabled,
742
                     continue */
743
744
0
      if (!result && !disabled) { /* if the request has not been found in the
745
                                     wms metadata, */
746
        /* check the ows namespace  */
747
748
0
        enable_request =
749
0
            msOWSLookupMetadata(&lp->metadata, "O", "enable_request");
750
0
        result = msOWSParseRequestMetadata(enable_request, request, &disabled);
751
0
        if (!result && disabled)
752
0
          continue;
753
0
      }
754
755
0
      if (msOWSIpDisabled(&lp->metadata, namespaces, remote_ip))
756
0
        continue;
757
758
0
      if (result || (!disabled && globally_enabled)) {
759
0
        ows_request->enabled_layers[ows_request->numlayers] = lp->index;
760
0
        ows_request->numlayers++;
761
0
      }
762
0
    }
763
764
0
    if (ows_request->numlayers == 0) {
765
0
      msFree(ows_request->enabled_layers);
766
0
      ows_request->enabled_layers = NULL;
767
0
    }
768
0
  }
769
0
}
770
771
/* msOWSParseRequestMetadata
772
 *
773
 * This function parse a enable_request metadata string and check if the
774
 * given request is present and enabled.
775
 */
776
int msOWSParseRequestMetadata(const char *metadata, const char *request,
777
0
                              int *disabled) {
778
0
  char requestBuffer[32];
779
0
  int wordFlag = MS_FALSE;
780
0
  int disableFlag = MS_FALSE;
781
0
  int allFlag = MS_FALSE;
782
0
  char *bufferPtr, *ptr = NULL;
783
784
0
  *disabled = MS_FALSE;
785
786
0
  if (metadata == NULL)
787
0
    return MS_FALSE;
788
789
0
  ptr = (char *)metadata;
790
0
  const size_t len = strlen(ptr);
791
0
  requestBuffer[0] = '\0';
792
0
  bufferPtr = requestBuffer;
793
794
0
  for (size_t i = 0; i <= len; ++i, ++ptr) {
795
796
0
    if (!wordFlag && isspace(*ptr))
797
0
      continue;
798
799
0
    wordFlag = MS_TRUE;
800
801
0
    if (*ptr == '!') {
802
0
      disableFlag = MS_TRUE;
803
0
      continue;
804
0
    } else if ((*ptr == ' ') ||
805
0
               (*ptr != '\0' && ptr[1] == '\0')) { /* end of word */
806
0
      if (ptr[1] == '\0' && *ptr != ' ') {
807
0
        *bufferPtr = *ptr;
808
0
        ++bufferPtr;
809
0
      }
810
811
0
      *bufferPtr = '\0';
812
0
      if (strcasecmp(request, requestBuffer) == 0) {
813
0
        *disabled = MS_TRUE; /* explicitly found, will stop the process in
814
                                msOWSRequestIsEnabled() */
815
0
        return (disableFlag ? MS_FALSE : MS_TRUE);
816
0
      } else {
817
0
        if (strcmp("*", requestBuffer) ==
818
0
            0) { /* check if we read the all flag */
819
0
          if (disableFlag)
820
0
            *disabled = MS_TRUE;
821
0
          allFlag = disableFlag ? MS_FALSE : MS_TRUE;
822
0
        }
823
        /* reset flags */
824
0
        wordFlag = MS_FALSE;
825
0
        disableFlag = MS_FALSE;
826
0
        bufferPtr = requestBuffer;
827
0
      }
828
0
    } else {
829
0
      *bufferPtr = *ptr;
830
0
      ++bufferPtr;
831
0
    }
832
0
  }
833
834
0
  return allFlag;
835
0
}
836
837
/*
838
** msOWSGetPrefixFromNamespace()
839
**
840
** Return the metadata name prefix from a character identifying the OWS
841
** namespace.
842
*/
843
0
static const char *msOWSGetPrefixFromNamespace(char chNamespace) {
844
  // Return should be a 3 character string, otherwise breaks assumption
845
  // in msOWSLookupMetadata()
846
0
  switch (chNamespace) {
847
0
  case 'O':
848
0
    return "ows";
849
0
  case 'A':
850
0
    return "oga"; /* oga_... (OGC Geospatial API) */
851
0
  case 'M':
852
0
    return "wms";
853
0
  case 'F':
854
0
    return "wfs";
855
0
  case 'C':
856
0
    return "wcs";
857
0
  case 'G':
858
0
    return "gml";
859
0
  case 'S':
860
0
    return "sos";
861
0
  default:
862
    /* We should never get here unless an invalid code (typo) is */
863
    /* present in the code, but since this happened before... */
864
0
    msSetError(MS_WMSERR, "Unsupported metadata namespace code (%c).",
865
0
               "msOWSGetPrefixFromNamespace()", chNamespace);
866
0
    assert(MS_FALSE);
867
0
    return NULL;
868
0
  }
869
0
}
870
871
/*
872
** msOWSLookupMetadata()
873
**
874
** Attempts to lookup a given metadata name in multiple OWS namespaces.
875
**
876
** 'namespaces' is a string with a letter for each namespace to lookup
877
** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_
878
** If namespaces is NULL then this function just does a regular metadata
879
** lookup.
880
*/
881
const char *msOWSLookupMetadata(hashTableObj *metadata, const char *namespaces,
882
0
                                const char *name) {
883
0
  const char *value = NULL;
884
885
0
  if (namespaces == NULL) {
886
0
    value = msLookupHashTable(metadata, (char *)name);
887
0
  } else {
888
0
    char buf[100] = "ows_";
889
890
0
    strlcpy(buf + 4, name, 96);
891
892
0
    while (value == NULL && *namespaces != '\0') {
893
0
      const char *prefix = msOWSGetPrefixFromNamespace(*namespaces);
894
0
      if (prefix == NULL)
895
0
        return NULL;
896
0
      assert(strlen(prefix) == 3);
897
0
      memcpy(buf, prefix, 3);
898
0
      value = msLookupHashTable(metadata, buf);
899
0
      namespaces++;
900
0
    }
901
0
  }
902
903
0
  return value;
904
0
}
905
906
/*
907
** msOWSLookupMetadataWithLanguage()
908
**
909
** Attempts to lookup a given metadata name in multiple OWS namespaces
910
** for a specific language.
911
*/
912
const char *msOWSLookupMetadataWithLanguage(hashTableObj *metadata,
913
                                            const char *namespaces,
914
                                            const char *name,
915
0
                                            const char *validated_language) {
916
0
  const char *value = NULL;
917
918
0
  if (name && validated_language && validated_language[0]) {
919
0
    size_t bufferSize = strlen(name) + strlen(validated_language) + 2;
920
0
    char *name2 = (char *)msSmallMalloc(bufferSize);
921
0
    snprintf(name2, bufferSize, "%s.%s", name, validated_language);
922
0
    value = msOWSLookupMetadata(metadata, namespaces, name2);
923
0
    free(name2);
924
0
  }
925
926
0
  if (name && !value) {
927
0
    value = msOWSLookupMetadata(metadata, namespaces, name);
928
0
  }
929
930
0
  return value;
931
0
}
932
933
/*
934
** msOWSLookupMetadata2()
935
**
936
** Attempts to lookup a given metadata name in multiple hashTables, and
937
** in multiple OWS namespaces within each. First searches the primary
938
** table and if no result is found, attempts the search using the
939
** secondary (fallback) table.
940
**
941
** 'namespaces' is a string with a letter for each namespace to lookup
942
** in the order they should be looked up. e.g. "MO" to lookup wms_ and ows_
943
** If namespaces is NULL then this function just does a regular metadata
944
** lookup.
945
*/
946
const char *msOWSLookupMetadata2(hashTableObj *pri, hashTableObj *sec,
947
0
                                 const char *namespaces, const char *name) {
948
0
  const char *result;
949
950
0
  if ((result = msOWSLookupMetadata(pri, namespaces, name)) == NULL) {
951
    /* Try the secondary table */
952
0
    result = msOWSLookupMetadata(sec, namespaces, name);
953
0
  }
954
955
0
  return result;
956
0
}
957
958
/* msOWSParseVersionString()
959
**
960
** Parse a version string in the format "a.b.c" or "a.b" and return an
961
** integer in the format 0x0a0b0c suitable for regular integer comparisons.
962
**
963
** Returns one of OWS_VERSION_NOTSET or OWS_VERSION_BADFORMAT if version
964
** could not be parsed.
965
*/
966
0
int msOWSParseVersionString(const char *pszVersion) {
967
0
  char **digits = NULL;
968
0
  int numDigits = 0;
969
970
0
  if (pszVersion) {
971
0
    int nVersion = 0;
972
0
    digits = msStringSplit(pszVersion, '.', &numDigits);
973
0
    if (digits == NULL || numDigits < 2 || numDigits > 3) {
974
0
      msSetError(MS_OWSERR,
975
0
                 "Invalid version (%s). Version must be in the "
976
0
                 "format 'x.y' or 'x.y.z'",
977
0
                 "msOWSParseVersionString()", pszVersion);
978
0
      if (digits)
979
0
        msFreeCharArray(digits, numDigits);
980
0
      return OWS_VERSION_BADFORMAT;
981
0
    }
982
983
0
    nVersion = atoi(digits[0]) * 0x010000;
984
0
    nVersion += atoi(digits[1]) * 0x0100;
985
0
    if (numDigits > 2)
986
0
      nVersion += atoi(digits[2]);
987
988
0
    msFreeCharArray(digits, numDigits);
989
990
0
    return nVersion;
991
0
  }
992
993
0
  return OWS_VERSION_NOTSET;
994
0
}
995
996
/* msOWSGetVersionString()
997
**
998
** Returns a OWS version string in the format a.b.c from the integer
999
** version value passed as argument (0x0a0b0c)
1000
**
1001
** Fills in the pszBuffer and returns a reference to it. Recommended buffer
1002
** size is OWS_VERSION_MAXLEN chars.
1003
*/
1004
0
const char *msOWSGetVersionString(int nVersion, char *pszBuffer) {
1005
1006
0
  if (pszBuffer)
1007
0
    snprintf(pszBuffer, OWS_VERSION_MAXLEN - 1, "%d.%d.%d",
1008
0
             (nVersion / 0x10000) % 0x100, (nVersion / 0x100) % 0x100,
1009
0
             nVersion % 0x100);
1010
1011
0
  return pszBuffer;
1012
0
}
1013
1014
/*
1015
** msOWSGetEPSGProj()
1016
**
1017
** Extract projection code for this layer/map.
1018
**
1019
** First look for a xxx_srs metadata. If not found then look for an EPSG
1020
** code in projectionObj, and if not found then return NULL.
1021
**
1022
** If bReturnOnlyFirstOne=TRUE and metadata contains multiple EPSG codes
1023
** then only the first one (which is assumed to be the layer's default
1024
** projection) is returned.
1025
*/
1026
void msOWSGetEPSGProj(projectionObj *proj, hashTableObj *metadata,
1027
                      const char *namespaces, int bReturnOnlyFirstOne,
1028
0
                      char **epsgCode) {
1029
0
  const char *value;
1030
0
  *epsgCode = NULL;
1031
1032
  /* metadata value should already be in format "EPSG:n" or "AUTO:..." */
1033
0
  if (metadata &&
1034
0
      ((value = msOWSLookupMetadata(metadata, namespaces, "srs")) != NULL)) {
1035
0
    const char *space_ptr;
1036
0
    if (!bReturnOnlyFirstOne || (space_ptr = strchr(value, ' ')) == NULL) {
1037
0
      *epsgCode = msStrdup(value);
1038
0
      return;
1039
0
    }
1040
1041
0
    *epsgCode = msSmallMalloc((space_ptr - value + 1) * sizeof(char));
1042
    /* caller requested only first projection code, copy up to the first space
1043
     * character*/
1044
0
    strlcpy(*epsgCode, value, space_ptr - value + 1);
1045
0
    return;
1046
0
  } else if (proj && proj->numargs > 0 &&
1047
0
             (value = strstr(proj->args[0], "init=epsg:")) != NULL) {
1048
0
    *epsgCode = msSmallMalloc((strlen("EPSG:") + strlen(value + 10) + 1) *
1049
0
                              sizeof(char));
1050
0
    sprintf(*epsgCode, "EPSG:%s", value + 10);
1051
0
    return;
1052
0
  } else if (proj && proj->numargs > 0 &&
1053
0
             (value = strstr(proj->args[0], "init=crs:")) != NULL) {
1054
0
    *epsgCode =
1055
0
        msSmallMalloc((strlen("CRS:") + strlen(value + 9) + 1) * sizeof(char));
1056
0
    sprintf(*epsgCode, "CRS:%s", value + 9);
1057
0
    return;
1058
0
  } else if (proj && proj->numargs > 0 &&
1059
0
             (strncasecmp(proj->args[0], "AUTO:", 5) == 0 ||
1060
0
              strncasecmp(proj->args[0], "AUTO2:", 6) == 0)) {
1061
0
    *epsgCode = msStrdup(proj->args[0]);
1062
0
    return;
1063
0
  }
1064
0
}
1065
1066
/*
1067
** msOWSProjectToWGS84()
1068
**
1069
** Reprojects the extent to WGS84.
1070
**
1071
*/
1072
0
void msOWSProjectToWGS84(projectionObj *srcproj, rectObj *ext) {
1073
0
  if (srcproj->proj && !msProjIsGeographicCRS(srcproj)) {
1074
0
    projectionObj wgs84;
1075
0
    msInitProjection(&wgs84);
1076
0
    msProjectionInheritContextFrom(&wgs84, srcproj);
1077
0
    msLoadProjectionString(&wgs84, "+proj=longlat +ellps=WGS84 +datum=WGS84");
1078
0
    msProjectRect(srcproj, &wgs84, ext);
1079
0
    msFreeProjection(&wgs84);
1080
0
  }
1081
0
}
1082
1083
/* msOWSGetLanguage()
1084
**
1085
** returns the language via MAP/WEB/METADATA/ows_language
1086
**
1087
** Use value of "ows_language" metadata, if not set then
1088
** return "undefined" as a default
1089
*/
1090
0
const char *msOWSGetLanguage(mapObj *map, const char *context) {
1091
0
  const char *language;
1092
1093
  /* if this is an exception, MapServer always returns Exception
1094
     messages in en-US
1095
  */
1096
0
  if (strcmp(context, "exception") == 0) {
1097
0
    language = MS_ERROR_LANGUAGE;
1098
0
  }
1099
  /* if not, fetch language from mapfile metadata */
1100
0
  else {
1101
0
    language = msLookupHashTable(&(map->web.metadata), "ows_language");
1102
1103
0
    if (language == NULL) {
1104
0
      language = "undefined";
1105
0
    }
1106
0
  }
1107
0
  return language;
1108
0
}
1109
1110
/* msOWSGetSchemasLocation()
1111
**
1112
** schemas location is the root of the web tree where all WFS-related
1113
** schemas can be found on this server.  These URLs must exist in order
1114
** to validate xml.
1115
**
1116
** Use value of "ows_schemas_location" metadata, if not set then
1117
** return ".." as a default
1118
*/
1119
0
const char *msOWSGetSchemasLocation(mapObj *map) {
1120
0
  const char *schemas_location;
1121
1122
0
  schemas_location =
1123
0
      msLookupHashTable(&(map->web.metadata), "ows_schemas_location");
1124
0
  if (schemas_location == NULL)
1125
0
    schemas_location = OWS_DEFAULT_SCHEMAS_LOCATION;
1126
1127
0
  return schemas_location;
1128
0
}
1129
1130
/*
1131
** msOWSGetExpandedMetadataKey()
1132
*/
1133
static char *msOWSGetExpandedMetadataKey(const char *namespaces,
1134
0
                                         const char *metadata_name) {
1135
0
  char *pszRet = msStringConcatenate(NULL, "");
1136
0
  for (int i = 0; namespaces[i] != '\0'; ++i) {
1137
0
    if (i > 0)
1138
0
      pszRet = msStringConcatenate(pszRet, " or ");
1139
0
    pszRet = msStringConcatenate(pszRet, "\"");
1140
0
    pszRet =
1141
0
        msStringConcatenate(pszRet, msOWSGetPrefixFromNamespace(namespaces[i]));
1142
0
    pszRet = msStringConcatenate(pszRet, "_");
1143
0
    pszRet = msStringConcatenate(pszRet, metadata_name);
1144
0
    pszRet = msStringConcatenate(pszRet, "\"");
1145
0
  }
1146
0
  return pszRet;
1147
0
}
1148
1149
/*
1150
** msOWSGetOnlineResource()
1151
**
1152
** Return the online resource for this service.  First try to lookup
1153
** specified metadata, and if not found then try to build the URL ourselves.
1154
**
1155
** Returns a newly allocated string that should be freed by the caller or
1156
** NULL in case of error.
1157
*/
1158
char *msOWSGetOnlineResource(mapObj *map, const char *namespaces,
1159
0
                             const char *metadata_name, cgiRequestObj *req) {
1160
0
  const char *value;
1161
0
  char *online_resource = NULL;
1162
1163
  /* We need this script's URL, including hostname. */
1164
  /* Default to use the value of the "onlineresource" metadata, and if not */
1165
  /* set then build it: "http://$(SERVER_NAME):$(SERVER_PORT)$(SCRIPT_NAME)?" */
1166
  /* (+append the map=... param if it was explicitly passed in QUERY_STRING) */
1167
  /*  */
1168
0
  if ((value = msOWSLookupMetadata(&(map->web.metadata), namespaces,
1169
0
                                   metadata_name))) {
1170
0
    online_resource = msOWSTerminateOnlineResource(value);
1171
0
  } else {
1172
0
    if ((online_resource = msBuildOnlineResource(map, req)) == NULL) {
1173
0
      char *pszExpandedMetadataKey =
1174
0
          msOWSGetExpandedMetadataKey(namespaces, metadata_name);
1175
0
      msSetError(MS_CGIERR, "Please set %s metadata.",
1176
0
                 "msOWSGetOnlineResource()", pszExpandedMetadataKey);
1177
0
      msFree(pszExpandedMetadataKey);
1178
0
      return NULL;
1179
0
    }
1180
0
  }
1181
1182
0
  return online_resource;
1183
0
}
1184
1185
/*
1186
** msOWSTerminateOnlineResource()
1187
**
1188
** Append trailing "?" or "&" to an onlineresource URL if it doesn't have
1189
** one already. The returned string is then ready to append GET parameters
1190
** to it.
1191
**
1192
** Returns a newly allocated string that should be freed by the caller or
1193
** NULL in case of error.
1194
*/
1195
0
char *msOWSTerminateOnlineResource(const char *src_url) {
1196
0
  char *online_resource = NULL;
1197
0
  size_t buffer_size = 0;
1198
1199
0
  if (src_url == NULL)
1200
0
    return NULL;
1201
1202
0
  buffer_size = strlen(src_url) + 2;
1203
0
  online_resource = (char *)malloc(buffer_size);
1204
1205
0
  if (online_resource == NULL) {
1206
0
    msSetError(MS_MEMERR, NULL, "msOWSTerminateOnlineResource()");
1207
0
    return NULL;
1208
0
  }
1209
1210
0
  strlcpy(online_resource, src_url, buffer_size);
1211
1212
  /* Append trailing '?' or '&' if missing. */
1213
0
  if (strchr(online_resource, '?') == NULL)
1214
0
    strlcat(online_resource, "?", buffer_size);
1215
0
  else {
1216
0
    char *c;
1217
0
    c = online_resource + strlen(online_resource) - 1;
1218
0
    if (*c != '?' && *c != '&')
1219
0
      strlcpy(c + 1, "&", buffer_size - strlen(online_resource));
1220
0
  }
1221
1222
0
  return online_resource;
1223
0
}
1224
1225
/************************************************************************/
1226
/*                         msUpdateGMLFieldMetadata                     */
1227
/*                                                                      */
1228
/*      Updates a fields GML metadata if it has not already             */
1229
/*      been set. Nullable is not implemented for all drivers           */
1230
/*      and can be set to 0 if unknown                                  */
1231
/************************************************************************/
1232
int msUpdateGMLFieldMetadata(layerObj *layer, const char *field_name,
1233
                             const char *gml_type, const char *gml_width,
1234
0
                             const char *gml_precision, const short nullable) {
1235
1236
0
  char md_item_name[256];
1237
1238
0
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_type", field_name);
1239
0
  if (msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
1240
0
    msInsertHashTable(&(layer->metadata), md_item_name, gml_type);
1241
1242
0
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_width", field_name);
1243
0
  if (strlen(gml_width) > 0 &&
1244
0
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
1245
0
    msInsertHashTable(&(layer->metadata), md_item_name, gml_width);
1246
1247
0
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_precision", field_name);
1248
0
  if (strlen(gml_precision) > 0 &&
1249
0
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
1250
0
    msInsertHashTable(&(layer->metadata), md_item_name, gml_precision);
1251
1252
0
  snprintf(md_item_name, sizeof(md_item_name), "gml_%s_nillable", field_name);
1253
0
  if (nullable > 0 &&
1254
0
      msLookupHashTable(&(layer->metadata), md_item_name) == NULL)
1255
0
    msInsertHashTable(&(layer->metadata), md_item_name, "true");
1256
1257
0
  return MS_TRUE;
1258
0
}
1259
1260
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1261
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
1262
1263
/*
1264
** msRenameLayer()
1265
*/
1266
0
static int msRenameLayer(layerObj *lp, int count) {
1267
0
  char *newname;
1268
0
  newname = (char *)malloc((strlen(lp->name) + 5) * sizeof(char));
1269
0
  if (!newname) {
1270
0
    msSetError(MS_MEMERR, NULL, "msRenameLayer()");
1271
0
    return MS_FAILURE;
1272
0
  }
1273
0
  sprintf(newname, "%s_%2.2d", lp->name, count);
1274
0
  free(lp->name);
1275
0
  lp->name = newname;
1276
1277
0
  return MS_SUCCESS;
1278
0
}
1279
1280
/*
1281
** msOWSMakeAllLayersUnique()
1282
*/
1283
0
int msOWSMakeAllLayersUnique(mapObj *map) {
1284
0
  int i, j;
1285
1286
  /* Make sure all layers in the map file have valid and unique names */
1287
0
  for (i = 0; i < map->numlayers; i++) {
1288
0
    int count = 1;
1289
0
    for (j = i + 1; j < map->numlayers; j++) {
1290
0
      if (GET_LAYER(map, i)->name == NULL || GET_LAYER(map, j)->name == NULL) {
1291
0
        continue;
1292
0
      }
1293
0
      if (strcasecmp(GET_LAYER(map, i)->name, GET_LAYER(map, j)->name) == 0 &&
1294
0
          msRenameLayer((GET_LAYER(map, j)), ++count) != MS_SUCCESS) {
1295
0
        return MS_FAILURE;
1296
0
      }
1297
0
    }
1298
1299
    /* Don't forget to rename the first layer if duplicates were found */
1300
0
    if (count > 1 && msRenameLayer((GET_LAYER(map, i)), 1) != MS_SUCCESS) {
1301
0
      return MS_FAILURE;
1302
0
    }
1303
0
  }
1304
0
  return MS_SUCCESS;
1305
0
}
1306
1307
/*
1308
** msOWSNegotiateVersion()
1309
**
1310
** returns the most suitable version an OWS is to support given a client
1311
** version parameter.
1312
**
1313
** supported_versions must be ordered from highest to lowest
1314
**
1315
** Returns a version integer of the supported version
1316
**
1317
*/
1318
1319
int msOWSNegotiateVersion(int requested_version, const int supported_versions[],
1320
0
                          int num_supported_versions) {
1321
0
  int i;
1322
1323
  /* if version is not set return highest version */
1324
0
  if (!requested_version)
1325
0
    return supported_versions[0];
1326
1327
  /* if the requested version is lower than the lowest version return the lowest
1328
   * version  */
1329
0
  if (requested_version < supported_versions[num_supported_versions - 1])
1330
0
    return supported_versions[num_supported_versions - 1];
1331
1332
  /* return the first entry that's lower than or equal to the requested version
1333
   */
1334
0
  for (i = 0; i < num_supported_versions; i++) {
1335
0
    if (supported_versions[i] <= requested_version)
1336
0
      return supported_versions[i];
1337
0
  }
1338
1339
0
  return requested_version;
1340
0
}
1341
1342
/*
1343
** msOWSGetOnlineResource()
1344
**
1345
** Return the online resource for this service and add language parameter.
1346
**
1347
** Returns a newly allocated string that should be freed by the caller or
1348
** NULL in case of error.
1349
*/
1350
char *msOWSGetOnlineResource2(mapObj *map, const char *namespaces,
1351
                              const char *metadata_name, cgiRequestObj *req,
1352
0
                              const char *validated_language) {
1353
0
  char *online_resource =
1354
0
      msOWSGetOnlineResource(map, namespaces, metadata_name, req);
1355
1356
0
  if (online_resource && validated_language && validated_language[0]) {
1357
    /* online_resource is already terminated, so we can simply add language=...&
1358
     */
1359
    /* but first we need to make sure that online_resource has enough capacity
1360
     */
1361
0
    online_resource = (char *)msSmallRealloc(
1362
0
        online_resource,
1363
0
        strlen(online_resource) + strlen(validated_language) + 11);
1364
0
    strcat(online_resource, "language=");
1365
0
    strcat(online_resource, validated_language);
1366
0
    strcat(online_resource, "&");
1367
0
  }
1368
1369
0
  return online_resource;
1370
0
}
1371
1372
/* msOWSGetInspireSchemasLocation()
1373
**
1374
** schemas location is the root of the web tree where all Inspire-related
1375
** schemas can be found on this server.  These URLs must exist in order
1376
** to validate xml.
1377
**
1378
** Use value of "inspire_schemas_location" metadata
1379
*/
1380
0
const char *msOWSGetInspireSchemasLocation(mapObj *map) {
1381
0
  const char *schemas_location;
1382
1383
0
  schemas_location =
1384
0
      msLookupHashTable(&(map->web.metadata), "inspire_schemas_location");
1385
0
  if (schemas_location == NULL)
1386
0
    schemas_location = "http://inspire.ec.europa.eu/schemas";
1387
1388
0
  return schemas_location;
1389
0
}
1390
1391
/* msOWSGetLanguageList
1392
**
1393
** Returns the list of languages that this service supports
1394
**
1395
** Use value of "languages" metadata (comma-separated list), or NULL if not set
1396
**
1397
** Returns a malloced char** of length numitems which must be freed
1398
** by the caller, or NULL (with numitems = 0)
1399
*/
1400
char **msOWSGetLanguageList(mapObj *map, const char *namespaces,
1401
0
                            int *numitems) {
1402
1403
0
  const char *languages = NULL;
1404
1405
0
  languages =
1406
0
      msOWSLookupMetadata(&(map->web.metadata), namespaces, "languages");
1407
0
  if (languages && strlen(languages) > 0) {
1408
0
    return msStringSplit(languages, ',', numitems);
1409
0
  } else {
1410
0
    *numitems = 0;
1411
0
    return NULL;
1412
0
  }
1413
0
}
1414
1415
/* msOWSGetLanguageFromList
1416
**
1417
** Returns a language according to the language requested by the client
1418
**
1419
** If the requested language is in the languages metadata then use it,
1420
** otherwise ignore it and use the default language, which is the first entry in
1421
** the languages metadata list. Calling with a NULL requested_langauge
1422
** therefore returns this default language. If the language metadata list is
1423
** not defined then the language is set to NULL.
1424
**
1425
** Returns a malloced char* which must be freed by the caller, or NULL
1426
*/
1427
char *msOWSGetLanguageFromList(mapObj *map, const char *namespaces,
1428
0
                               const char *requested_language) {
1429
0
  int num_items = 0;
1430
0
  char **languages = msOWSGetLanguageList(map, namespaces, &num_items);
1431
0
  char *language = NULL;
1432
1433
0
  if (languages && num_items > 0) {
1434
0
    if (!requested_language ||
1435
0
        !msStringInArray(requested_language, languages, num_items)) {
1436
0
      language = msStrdup(languages[0]);
1437
0
    } else {
1438
0
      language = msStrdup(requested_language);
1439
0
    }
1440
0
  }
1441
0
  msFreeCharArray(languages, num_items);
1442
1443
0
  return language;
1444
0
}
1445
1446
/* msOWSLanguageNegotiation
1447
**
1448
** Returns a language according to the accepted languages requested by the
1449
*client
1450
**
1451
** Returns a malloced char* which must be freed by the caller, or NULL
1452
*/
1453
char *msOWSLanguageNegotiation(mapObj *map, const char *namespaces,
1454
                               char **accept_languages,
1455
0
                               int num_accept_languages) {
1456
0
  int num_languages = 0;
1457
0
  char **languages = NULL;
1458
0
  char *result_language = NULL;
1459
1460
0
  languages = msOWSGetLanguageList(map, namespaces, &num_languages);
1461
1462
0
  if (languages && num_languages > 0) {
1463
0
    int i;
1464
0
    for (i = 0; i < num_accept_languages; ++i) {
1465
0
      const char *accept_language = accept_languages[i];
1466
1467
      /* '*' means any language */
1468
0
      if (EQUAL(accept_language, "*")) {
1469
0
        result_language = msStrdup(languages[0]);
1470
0
        break;
1471
0
      } else if (msStringInArray(accept_language, languages, num_languages)) {
1472
0
        result_language = msStrdup(accept_language);
1473
0
        break;
1474
0
      }
1475
0
    }
1476
1477
0
    if (result_language == NULL) {
1478
0
      result_language = msStrdup(languages[0]);
1479
0
    }
1480
0
  }
1481
1482
0
  msFreeCharArray(languages, num_languages);
1483
0
  return result_language;
1484
0
}
1485
1486
/* msOWSPrintInspireCommonExtendedCapabilities
1487
**
1488
** Output INSPIRE common extended capabilities items to stream
1489
** The currently supported items are metadata and languages
1490
**
1491
** tag_name is the name (including ns prefix) of the tag to include the whole
1492
** extended capabilities block in
1493
**
1494
** service is currently included for future compatibility when differing
1495
** extended capabilities elements are included for different service types
1496
**
1497
** Returns a status code; MS_NOERR if all ok, action_if_not_found otherwise
1498
*/
1499
int msOWSPrintInspireCommonExtendedCapabilities(
1500
    FILE *stream, mapObj *map, const char *namespaces, int action_if_not_found,
1501
    const char *tag_name, const char *tag_ns, const char *validated_language,
1502
0
    const OWSServiceType service) {
1503
1504
0
  int metadataStatus = 0;
1505
0
  int languageStatus = 0;
1506
1507
0
  if (tag_ns)
1508
0
    msIO_fprintf(stream, "  <%s %s>\n", tag_name, tag_ns);
1509
0
  else
1510
0
    msIO_fprintf(stream, "  <%s>\n", tag_name);
1511
1512
0
  metadataStatus = msOWSPrintInspireCommonMetadata(
1513
0
      stream, map, namespaces, action_if_not_found, service);
1514
0
  languageStatus = msOWSPrintInspireCommonLanguages(
1515
0
      stream, map, namespaces, action_if_not_found, validated_language);
1516
1517
0
  msIO_fprintf(stream, "  </%s>\n", tag_name);
1518
1519
0
  return (metadataStatus != MS_NOERR) ? metadataStatus : languageStatus;
1520
0
}
1521
1522
/* msOWSPrintInspireCommonMetadata
1523
**
1524
** Output INSPIRE common metadata items to a stream
1525
**
1526
** Returns a status code; MS_NOERR if all OK, action_if_not_found otherwise
1527
*/
1528
int msOWSPrintInspireCommonMetadata(FILE *stream, mapObj *map,
1529
                                    const char *namespaces,
1530
                                    int action_if_not_found,
1531
0
                                    const OWSServiceType service) {
1532
1533
0
  int status = MS_NOERR;
1534
0
  const char *inspire_capabilities = NULL;
1535
1536
0
  inspire_capabilities = msOWSLookupMetadata(&(map->web.metadata), namespaces,
1537
0
                                             "inspire_capabilities");
1538
1539
0
  if (!inspire_capabilities) {
1540
0
    if (OWS_WARN == action_if_not_found) {
1541
0
      msIO_fprintf(
1542
0
          stream,
1543
0
          "<!-- WARNING: missing metadata entry for 'inspire_capabilities', "
1544
0
          "one of 'embed' and 'url' must be supplied. -->\n");
1545
0
    }
1546
0
    return action_if_not_found;
1547
0
  }
1548
0
  if (strcasecmp("url", inspire_capabilities) == 0) {
1549
0
    if (msOWSLookupMetadata(&(map->web.metadata), namespaces,
1550
0
                            "inspire_metadataurl_href") != NULL) {
1551
0
      msIO_fprintf(stream,
1552
0
                   "    <inspire_common:MetadataUrl "
1553
0
                   "xsi:type=\"inspire_common:resourceLocatorType\">\n");
1554
0
      msOWSPrintEncodeMetadata(
1555
0
          stream, &(map->web.metadata), namespaces, "inspire_metadataurl_href",
1556
0
          OWS_WARN, "      <inspire_common:URL>%s</inspire_common:URL>\n", "");
1557
0
      msOWSPrintEncodeMetadata(
1558
0
          stream, &(map->web.metadata), namespaces,
1559
0
          "inspire_metadataurl_format", OWS_WARN,
1560
0
          "      <inspire_common:MediaType>%s</inspire_common:MediaType>\n",
1561
0
          "");
1562
0
      msIO_fprintf(stream, "    </inspire_common:MetadataUrl>\n");
1563
0
    } else {
1564
0
      status = action_if_not_found;
1565
0
      if (OWS_WARN == action_if_not_found) {
1566
0
        char *pszExpandedMetadataKey =
1567
0
            msOWSGetExpandedMetadataKey(namespaces, "inspire_metadataurl_href");
1568
0
        msIO_fprintf(stream,
1569
0
                     "<!-- WARNING: Mandatory metadata %s was missing in this "
1570
0
                     "context. -->\n",
1571
0
                     pszExpandedMetadataKey);
1572
0
        msFree(pszExpandedMetadataKey);
1573
0
      }
1574
0
    }
1575
0
  } else if (strcasecmp("embed", inspire_capabilities) == 0) {
1576
0
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
1577
0
                             "inspire_resourcelocator", OWS_WARN,
1578
0
                             "    <inspire_common:ResourceLocator>\n      "
1579
0
                             "<inspire_common:URL>%s</inspire_common:URL>\n    "
1580
0
                             "</inspire_common:ResourceLocator>\n",
1581
0
                             NULL);
1582
0
    msIO_fprintf(
1583
0
        stream,
1584
0
        "    "
1585
0
        "<inspire_common:ResourceType>service</inspire_common:ResourceType>\n");
1586
0
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
1587
0
                             "inspire_temporal_reference", OWS_WARN,
1588
0
                             "    <inspire_common:TemporalReference>\n      "
1589
0
                             "<inspire_common:DateOfLastRevision>%s</"
1590
0
                             "inspire_common:DateOfLastRevision>\n    "
1591
0
                             "</inspire_common:TemporalReference>\n",
1592
0
                             "");
1593
0
    msIO_fprintf(stream, "    <inspire_common:Conformity>\n");
1594
0
    msIO_fprintf(stream, "      <inspire_common:Specification>\n");
1595
0
    msIO_fprintf(stream,
1596
0
                 "        <inspire_common:Title>-</inspire_common:Title>\n");
1597
0
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
1598
0
                             "inspire_temporal_reference", OWS_NOERR,
1599
0
                             "        "
1600
0
                             "<inspire_common:DateOfLastRevision>%s</"
1601
0
                             "inspire_common:DateOfLastRevision>\n",
1602
0
                             "");
1603
0
    msIO_fprintf(stream, "      </inspire_common:Specification>\n");
1604
0
    msIO_fprintf(
1605
0
        stream,
1606
0
        "      <inspire_common:Degree>notEvaluated</inspire_common:Degree>\n");
1607
0
    msIO_fprintf(stream, "    </inspire_common:Conformity>\n");
1608
0
    msIO_fprintf(stream, "    <inspire_common:MetadataPointOfContact>\n");
1609
0
    msOWSPrintEncodeMetadata(stream, &(map->web.metadata), namespaces,
1610
0
                             "inspire_mpoc_name", OWS_WARN,
1611
0
                             "      "
1612
0
                             "<inspire_common:OrganisationName>%s</"
1613
0
                             "inspire_common:OrganisationName>\n",
1614
0
                             "");
1615
0
    msOWSPrintEncodeMetadata(
1616
0
        stream, &(map->web.metadata), namespaces, "inspire_mpoc_email",
1617
0
        OWS_WARN,
1618
0
        "      <inspire_common:EmailAddress>%s</inspire_common:EmailAddress>\n",
1619
0
        "");
1620
0
    msIO_fprintf(stream, "    </inspire_common:MetadataPointOfContact>\n");
1621
0
    msOWSPrintEncodeMetadata(
1622
0
        stream, &(map->web.metadata), namespaces, "inspire_metadatadate",
1623
0
        OWS_WARN,
1624
0
        "      <inspire_common:MetadataDate>%s</inspire_common:MetadataDate>\n",
1625
0
        "");
1626
0
    if (service == OWS_WFS || service == OWS_WCS)
1627
0
      msIO_fprintf(stream, "    "
1628
0
                           "<inspire_common:SpatialDataServiceType>download</"
1629
0
                           "inspire_common:SpatialDataServiceType>\n");
1630
0
    else
1631
0
      msIO_fprintf(stream, "    "
1632
0
                           "<inspire_common:SpatialDataServiceType>view</"
1633
0
                           "inspire_common:SpatialDataServiceType>\n");
1634
0
    msOWSPrintEncodeMetadata(
1635
0
        stream, &(map->web.metadata), namespaces, "inspire_keyword", OWS_WARN,
1636
0
        "    <inspire_common:MandatoryKeyword>\n      "
1637
0
        "<inspire_common:KeywordValue>%s</inspire_common:KeywordValue>\n    "
1638
0
        "</inspire_common:MandatoryKeyword>\n",
1639
0
        "");
1640
0
  } else {
1641
0
    status = action_if_not_found;
1642
0
    if (OWS_WARN == action_if_not_found) {
1643
0
      msIO_fprintf(
1644
0
          stream,
1645
0
          "<!-- WARNING: invalid value '%s' for 'inspire_capabilities', only "
1646
0
          "'embed' and 'url' are supported. -->\n",
1647
0
          inspire_capabilities);
1648
0
    }
1649
0
  }
1650
1651
0
  return status;
1652
0
}
1653
1654
/* msOWSPrintInspireCommonLanguages
1655
**
1656
** Output INSPIRE supported languages block to stream
1657
**
1658
** Returns a status code; MS_NOERR if all OK; action_if_not_found otherwise
1659
*/
1660
int msOWSPrintInspireCommonLanguages(FILE *stream, mapObj *map,
1661
                                     const char *namespaces,
1662
                                     int action_if_not_found,
1663
0
                                     const char *validated_language) {
1664
0
  char *buffer =
1665
0
      NULL; /* temp variable for malloced strings that will need freeing */
1666
0
  int status = MS_NOERR;
1667
1668
0
  char *default_language = msOWSGetLanguageFromList(map, namespaces, NULL);
1669
1670
0
  if (validated_language && validated_language[0] && default_language) {
1671
0
    msIO_fprintf(stream, "    <inspire_common:SupportedLanguages>\n");
1672
0
    msIO_fprintf(
1673
0
        stream,
1674
0
        "      <inspire_common:DefaultLanguage><inspire_common:Language>%s"
1675
0
        "</inspire_common:Language></inspire_common:DefaultLanguage>\n",
1676
0
        buffer = msEncodeHTMLEntities(default_language));
1677
0
    msFree(buffer);
1678
1679
    /* append _exclude to our default_language*/
1680
0
    default_language = msSmallRealloc(
1681
0
        default_language, strlen(default_language) + strlen("_exclude") + 1);
1682
0
    strcat(default_language, "_exclude");
1683
1684
0
    msOWSPrintEncodeMetadataList(
1685
0
        stream, &(map->web.metadata), namespaces, "languages", NULL, NULL,
1686
0
        "      <inspire_common:SupportedLanguage><inspire_common:Language>%s"
1687
0
        "</inspire_common:Language></inspire_common:SupportedLanguage>\n",
1688
0
        default_language);
1689
0
    msIO_fprintf(stream, "    </inspire_common:SupportedLanguages>\n");
1690
0
    msIO_fprintf(
1691
0
        stream,
1692
0
        "    <inspire_common:ResponseLanguage><inspire_common:Language>%s"
1693
0
        "</inspire_common:Language></inspire_common:ResponseLanguage>\n",
1694
0
        validated_language);
1695
0
  } else {
1696
0
    status = action_if_not_found;
1697
0
    if (OWS_WARN == action_if_not_found) {
1698
0
      char *pszExpandedMetadataKey =
1699
0
          msOWSGetExpandedMetadataKey(namespaces, "languages");
1700
0
      msIO_fprintf(stream,
1701
0
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1702
0
                   "context. -->\n",
1703
0
                   pszExpandedMetadataKey);
1704
0
      msFree(pszExpandedMetadataKey);
1705
0
    }
1706
0
  }
1707
1708
0
  msFree(default_language);
1709
1710
0
  return status;
1711
0
}
1712
1713
/*
1714
** msOWSPrintMetadata()
1715
**
1716
** Attempt to output a capability item.  If corresponding metadata is not
1717
** found then one of a number of predefined actions will be taken.
1718
** If a default value is provided and metadata is absent then the
1719
** default will be used.
1720
*/
1721
1722
int msOWSPrintMetadata(FILE *stream, hashTableObj *metadata,
1723
                       const char *namespaces, const char *name,
1724
                       int action_if_not_found, const char *format,
1725
0
                       const char *default_value) {
1726
0
  const char *value = NULL;
1727
0
  int status = MS_NOERR;
1728
1729
0
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)) != NULL) {
1730
0
    msIO_fprintf(stream, format, value);
1731
0
  } else {
1732
0
    if (action_if_not_found == OWS_WARN) {
1733
0
      char *pszExpandedMetadataKey =
1734
0
          msOWSGetExpandedMetadataKey(namespaces, name);
1735
0
      msIO_fprintf(stream,
1736
0
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1737
0
                   "context. -->\n",
1738
0
                   pszExpandedMetadataKey);
1739
0
      msFree(pszExpandedMetadataKey);
1740
0
      status = action_if_not_found;
1741
0
    }
1742
1743
0
    if (default_value)
1744
0
      msIO_fprintf(stream, format, default_value);
1745
0
  }
1746
1747
0
  return status;
1748
0
}
1749
1750
/*
1751
** msOWSPrintEncodeMetadata()
1752
**
1753
** Attempt to output a capability item.  If corresponding metadata is not
1754
** found then one of a number of predefined actions will be taken.
1755
** If a default value is provided and metadata is absent then the
1756
** default will be used.
1757
** Also encode the value with msEncodeHTMLEntities.
1758
*/
1759
1760
int msOWSPrintEncodeMetadata(FILE *stream, hashTableObj *metadata,
1761
                             const char *namespaces, const char *name,
1762
                             int action_if_not_found, const char *format,
1763
0
                             const char *default_value) {
1764
0
  return msOWSPrintEncodeMetadata2(stream, metadata, namespaces, name,
1765
0
                                   action_if_not_found, format, default_value,
1766
0
                                   NULL);
1767
0
}
1768
1769
/*
1770
** msOWSPrintEncodeMetadata2()
1771
**
1772
** Attempt to output a capability item in the requested language.
1773
** Fallback using no language parameter.
1774
*/
1775
int msOWSPrintEncodeMetadata2(FILE *stream, hashTableObj *metadata,
1776
                              const char *namespaces, const char *name,
1777
                              int action_if_not_found, const char *format,
1778
                              const char *default_value,
1779
0
                              const char *validated_language) {
1780
0
  const char *value;
1781
0
  char *pszEncodedValue = NULL;
1782
0
  int status = MS_NOERR;
1783
1784
0
  if ((value = msOWSLookupMetadataWithLanguage(metadata, namespaces, name,
1785
0
                                               validated_language))) {
1786
0
    pszEncodedValue = msEncodeHTMLEntities(value);
1787
0
    msIO_fprintf(stream, format, pszEncodedValue);
1788
0
    free(pszEncodedValue);
1789
0
  } else {
1790
0
    if (action_if_not_found == OWS_WARN) {
1791
0
      char *pszExpandedName = msStringConcatenate(NULL, name);
1792
0
      if (validated_language && validated_language[0]) {
1793
0
        pszExpandedName = msStringConcatenate(pszExpandedName, ".");
1794
0
        pszExpandedName =
1795
0
            msStringConcatenate(pszExpandedName, validated_language);
1796
0
      }
1797
0
      char *pszExpandedMetadataKey =
1798
0
          msOWSGetExpandedMetadataKey(namespaces, pszExpandedName);
1799
0
      msIO_fprintf(stream,
1800
0
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1801
0
                   "context. -->\n",
1802
0
                   pszExpandedMetadataKey);
1803
0
      msFree(pszExpandedName);
1804
0
      msFree(pszExpandedMetadataKey);
1805
0
      status = action_if_not_found;
1806
0
    }
1807
1808
0
    if (default_value) {
1809
0
      pszEncodedValue = msEncodeHTMLEntities(default_value);
1810
0
      msIO_fprintf(stream, format, default_value);
1811
0
      free(pszEncodedValue);
1812
0
    }
1813
0
  }
1814
1815
0
  return status;
1816
0
}
1817
1818
/*
1819
** msOWSGetEncodeMetadata()
1820
**
1821
** Equivalent to msOWSPrintEncodeMetadata. Returns en encoded value of the
1822
** metadata or the default value.
1823
** Caller should free the returned string.
1824
*/
1825
char *msOWSGetEncodeMetadata(hashTableObj *metadata, const char *namespaces,
1826
0
                             const char *name, const char *default_value) {
1827
0
  const char *value;
1828
0
  char *pszEncodedValue = NULL;
1829
0
  if ((value = msOWSLookupMetadata(metadata, namespaces, name)))
1830
0
    pszEncodedValue = msEncodeHTMLEntities(value);
1831
0
  else if (default_value)
1832
0
    pszEncodedValue = msEncodeHTMLEntities(default_value);
1833
1834
0
  return pszEncodedValue;
1835
0
}
1836
1837
/*
1838
** msOWSPrintValidateMetadata()
1839
**
1840
** Attempt to output a capability item.  If corresponding metadata is not
1841
** found then one of a number of predefined actions will be taken.
1842
** If a default value is provided and metadata is absent then the
1843
** default will be used.
1844
** Also validate the value with msIsXMLTagValid.
1845
*/
1846
1847
int msOWSPrintValidateMetadata(FILE *stream, hashTableObj *metadata,
1848
                               const char *namespaces, const char *name,
1849
                               int action_if_not_found, const char *format,
1850
0
                               const char *default_value) {
1851
0
  const char *value;
1852
0
  int status = MS_NOERR;
1853
1854
0
  if ((value = msOWSLookupMetadata(metadata, namespaces, name))) {
1855
0
    if (msIsXMLTagValid(value) == MS_FALSE)
1856
0
      msIO_fprintf(stream,
1857
0
                   "<!-- WARNING: The value '%s' is not valid in a "
1858
0
                   "XML tag context. -->\n",
1859
0
                   value);
1860
0
    msIO_fprintf(stream, format, value);
1861
0
  } else {
1862
0
    if (action_if_not_found == OWS_WARN) {
1863
0
      char *pszExpandedMetadataKey =
1864
0
          msOWSGetExpandedMetadataKey(namespaces, name);
1865
0
      msIO_fprintf(stream,
1866
0
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
1867
0
                   "context. -->\n",
1868
0
                   pszExpandedMetadataKey);
1869
0
      msFree(pszExpandedMetadataKey);
1870
0
      status = action_if_not_found;
1871
0
    }
1872
1873
0
    if (default_value) {
1874
0
      if (msIsXMLTagValid(default_value) == MS_FALSE)
1875
0
        msIO_fprintf(stream,
1876
0
                     "<!-- WARNING: The value '%s' is not valid "
1877
0
                     "in a XML tag context. -->\n",
1878
0
                     default_value);
1879
0
      msIO_fprintf(stream, format, default_value);
1880
0
    }
1881
0
  }
1882
1883
0
  return status;
1884
0
}
1885
1886
/*
1887
** msOWSPrintGroupMetadata()
1888
**
1889
** Attempt to output a capability item.  If corresponding metadata is not
1890
** found then one of a number of predefined actions will be taken.
1891
** If a default value is provided and metadata is absent then the
1892
** default will be used.
1893
*/
1894
int msOWSPrintGroupMetadata(FILE *stream, mapObj *map, char *pszGroupName,
1895
                            const char *namespaces, const char *name,
1896
                            int action_if_not_found, const char *format,
1897
0
                            const char *default_value) {
1898
0
  return msOWSPrintGroupMetadata2(stream, map, pszGroupName, namespaces, name,
1899
0
                                  action_if_not_found, format, default_value,
1900
0
                                  NULL);
1901
0
}
1902
1903
/*
1904
** msOWSPrintGroupMetadata2()
1905
**
1906
** Attempt to output a capability item in the requested language.
1907
** Fallback using no language parameter.
1908
*/
1909
int msOWSPrintGroupMetadata2(FILE *stream, mapObj *map, char *pszGroupName,
1910
                             const char *namespaces, const char *name,
1911
                             int action_if_not_found, const char *format,
1912
                             const char *default_value,
1913
0
                             const char *validated_language) {
1914
0
  const char *value;
1915
0
  char *encoded;
1916
0
  int status = MS_NOERR;
1917
0
  int i;
1918
1919
0
  for (i = 0; i < map->numlayers; i++) {
1920
0
    if (GET_LAYER(map, i)->group &&
1921
0
        (strcmp(GET_LAYER(map, i)->group, pszGroupName) == 0)) {
1922
0
      if ((value = msOWSLookupMetadataWithLanguage(
1923
0
               &(GET_LAYER(map, i)->metadata), namespaces, name,
1924
0
               validated_language))) {
1925
0
        encoded = msEncodeHTMLEntities(value);
1926
0
        msIO_fprintf(stream, format, encoded);
1927
0
        msFree(encoded);
1928
0
        return status;
1929
0
      }
1930
0
    }
1931
0
  }
1932
1933
0
  if (action_if_not_found == OWS_WARN) {
1934
0
    char *pszExpandedMetadataKey =
1935
0
        msOWSGetExpandedMetadataKey(namespaces, name);
1936
0
    msIO_fprintf(stream,
1937
0
                 "<!-- WARNING: Mandatory metadata %s was missing in this "
1938
0
                 "context. -->\n",
1939
0
                 pszExpandedMetadataKey);
1940
0
    msFree(pszExpandedMetadataKey);
1941
0
    status = action_if_not_found;
1942
0
  }
1943
1944
0
  if (default_value) {
1945
0
    encoded = msEncodeHTMLEntities(default_value);
1946
0
    msIO_fprintf(stream, format, encoded);
1947
0
    msFree(encoded);
1948
0
  }
1949
1950
0
  return status;
1951
0
}
1952
1953
/* msOWSPrintURLType()
1954
**
1955
** Attempt to output a URL item in capabilities.  If corresponding metadata
1956
** is not found then one of a number of predefined actions will be taken.
1957
** Since it's a capability item, five metadata will be used to populate the
1958
** XML elements.
1959
**
1960
** The 'name' argument is the basename of the metadata items relating to this
1961
** URL type and the suffixes _type, _width, _height, _format and _href will
1962
** be appended to the name in the metadata search.
1963
** e.g. passing name=metadataurl will result in the following medata entries
1964
** being used:
1965
**    ows_metadataurl_type
1966
**    ows_metadataurl_format
1967
**    ows_metadataurl_href
1968
**    ... (width and height are unused for metadata)
1969
**
1970
** As for all the msOWSPrint*() functions, the namespace argument specifies
1971
** which prefix (ows_, wms_, wcs_, etc.) is used for the metadata names above.
1972
**
1973
** Then the final string will be built from
1974
** the tag_name and the five metadata. The template is:
1975
** <tag_name%type%width%height%format>%href</tag_name>
1976
**
1977
** For example the width format will usually be " width=\"%s\"".
1978
** An extern format will be "> <Format>%s</Format"
1979
**
1980
** Another template template may be used, but it needs to contains 5 %s,
1981
** otherwise leave it to NULL. If tag_format is used then you don't need the
1982
** tag_name and the tabspace.
1983
**
1984
** Note that all values will be HTML-encoded.
1985
**/
1986
int msOWSPrintURLType(FILE *stream, hashTableObj *metadata,
1987
                      const char *namespaces, const char *name,
1988
                      int action_if_not_found, const char *tag_format,
1989
                      const char *tag_name, const char *type_format,
1990
                      const char *width_format, const char *height_format,
1991
                      const char *urlfrmt_format, const char *href_format,
1992
                      int type_is_mandatory, int width_is_mandatory,
1993
                      int height_is_mandatory, int format_is_mandatory,
1994
                      int href_is_mandatory, const char *default_type,
1995
                      const char *default_width, const char *default_height,
1996
                      const char *default_urlfrmt, const char *default_href,
1997
0
                      const char *tabspace) {
1998
0
  const char *value;
1999
0
  char *metadata_name;
2000
0
  char *encoded;
2001
0
  int status = MS_NOERR;
2002
0
  char *type = NULL, *width = NULL, *height = NULL, *urlfrmt = NULL,
2003
0
       *href = NULL;
2004
2005
0
  const size_t buffer_size = strlen(name) + 10;
2006
0
  metadata_name = (char *)malloc(buffer_size);
2007
2008
  /* Get type */
2009
0
  if (type_format != NULL) {
2010
0
    snprintf(metadata_name, buffer_size, "%s_type", name);
2011
0
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
2012
0
    if (value != NULL) {
2013
0
      encoded = msEncodeHTMLEntities(value);
2014
0
      const size_t buffer_size_tmp = strlen(type_format) + strlen(encoded) + 1;
2015
0
      type = (char *)malloc(buffer_size_tmp);
2016
0
      snprintf(type, buffer_size_tmp, type_format, encoded);
2017
0
      msFree(encoded);
2018
0
    }
2019
0
  }
2020
2021
  /* Get width */
2022
0
  if (width_format != NULL) {
2023
0
    snprintf(metadata_name, buffer_size, "%s_width", name);
2024
0
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
2025
0
    if (value != NULL) {
2026
0
      encoded = msEncodeHTMLEntities(value);
2027
0
      const size_t buffer_size_tmp = strlen(width_format) + strlen(encoded) + 1;
2028
0
      width = (char *)malloc(buffer_size_tmp);
2029
0
      snprintf(width, buffer_size_tmp, width_format, encoded);
2030
0
      msFree(encoded);
2031
0
    }
2032
0
  }
2033
2034
  /* Get height */
2035
0
  if (height_format != NULL) {
2036
0
    snprintf(metadata_name, buffer_size, "%s_height", name);
2037
0
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
2038
0
    if (value != NULL) {
2039
0
      encoded = msEncodeHTMLEntities(value);
2040
0
      const size_t buffer_size_tmp =
2041
0
          strlen(height_format) + strlen(encoded) + 1;
2042
0
      height = (char *)malloc(buffer_size_tmp);
2043
0
      snprintf(height, buffer_size_tmp, height_format, encoded);
2044
0
      msFree(encoded);
2045
0
    }
2046
0
  }
2047
2048
  /* Get format */
2049
0
  if (urlfrmt_format != NULL) {
2050
0
    snprintf(metadata_name, buffer_size, "%s_format", name);
2051
0
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
2052
0
    if (value != NULL) {
2053
0
      encoded = msEncodeHTMLEntities(value);
2054
0
      const size_t buffer_size_tmp =
2055
0
          strlen(urlfrmt_format) + strlen(encoded) + 1;
2056
0
      urlfrmt = (char *)malloc(buffer_size_tmp);
2057
0
      snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, encoded);
2058
0
      msFree(encoded);
2059
0
    }
2060
0
  }
2061
2062
  /* Get href */
2063
0
  if (href_format != NULL) {
2064
0
    snprintf(metadata_name, buffer_size, "%s_href", name);
2065
0
    value = msOWSLookupMetadata(metadata, namespaces, metadata_name);
2066
0
    if (value != NULL) {
2067
0
      encoded = msEncodeHTMLEntities(value);
2068
0
      const size_t buffer_size_tmp = strlen(href_format) + strlen(encoded) + 1;
2069
0
      href = (char *)malloc(buffer_size_tmp);
2070
0
      snprintf(href, buffer_size_tmp, href_format, encoded);
2071
0
      msFree(encoded);
2072
0
    }
2073
0
  }
2074
2075
0
  msFree(metadata_name);
2076
2077
0
  if (type || width || height || urlfrmt || href ||
2078
0
      (!metadata && (default_type || default_width || default_height ||
2079
0
                     default_urlfrmt || default_href))) {
2080
0
    if ((!type && type_is_mandatory) || (!width && width_is_mandatory) ||
2081
0
        (!height && height_is_mandatory) || (!urlfrmt && format_is_mandatory) ||
2082
0
        (!href && href_is_mandatory)) {
2083
0
      msIO_fprintf(stream,
2084
0
                   "<!-- WARNING: Some mandatory elements for '%s' are missing "
2085
0
                   "in this context. -->\n",
2086
0
                   tag_name);
2087
0
      if (action_if_not_found == OWS_WARN) {
2088
0
        char *pszExpandedMetadataKey =
2089
0
            msOWSGetExpandedMetadataKey(namespaces, name);
2090
0
        msIO_fprintf(stream,
2091
0
                     "<!-- WARNING: Mandatory metadata %s was missing in this "
2092
0
                     "context. -->\n",
2093
0
                     pszExpandedMetadataKey);
2094
0
        msFree(pszExpandedMetadataKey);
2095
0
        status = action_if_not_found;
2096
0
      }
2097
0
    } else {
2098
0
      if (!type && type_format && default_type) {
2099
0
        const size_t buffer_size_tmp =
2100
0
            strlen(type_format) + strlen(default_type) + 2;
2101
0
        type = (char *)malloc(buffer_size_tmp);
2102
0
        snprintf(type, buffer_size_tmp, type_format, default_type);
2103
0
      } else if (!type)
2104
0
        type = msStrdup("");
2105
0
      if (!width && width_format && default_width) {
2106
0
        const size_t buffer_size_tmp =
2107
0
            strlen(width_format) + strlen(default_width) + 2;
2108
0
        width = (char *)malloc(buffer_size_tmp);
2109
0
        snprintf(width, buffer_size_tmp, width_format, default_width);
2110
0
      } else if (!width)
2111
0
        width = msStrdup("");
2112
0
      if (!height && height_format && default_height) {
2113
0
        const size_t buffer_size_tmp =
2114
0
            strlen(height_format) + strlen(default_height) + 2;
2115
0
        height = (char *)malloc(buffer_size_tmp);
2116
0
        snprintf(height, buffer_size_tmp, height_format, default_height);
2117
0
      } else if (!height)
2118
0
        height = msStrdup("");
2119
0
      if (!urlfrmt && urlfrmt_format && default_urlfrmt) {
2120
0
        const size_t buffer_size_tmp =
2121
0
            strlen(urlfrmt_format) + strlen(default_urlfrmt) + 2;
2122
0
        urlfrmt = (char *)malloc(buffer_size_tmp);
2123
0
        snprintf(urlfrmt, buffer_size_tmp, urlfrmt_format, default_urlfrmt);
2124
0
      } else if (!urlfrmt)
2125
0
        urlfrmt = msStrdup("");
2126
0
      if (!href && href_format && default_href) {
2127
0
        const size_t buffer_size_tmp =
2128
0
            strlen(href_format) + strlen(default_href) + 2;
2129
0
        href = (char *)malloc(buffer_size_tmp);
2130
0
        snprintf(href, buffer_size_tmp, href_format, default_href);
2131
0
      } else if (!href)
2132
0
        href = msStrdup("");
2133
2134
0
      if (tag_format == NULL)
2135
0
        msIO_fprintf(stream, "%s<%s%s%s%s%s>%s</%s>\n", tabspace, tag_name,
2136
0
                     type, width, height, urlfrmt, href, tag_name);
2137
0
      else
2138
0
        msIO_fprintf(stream, tag_format, type, width, height, urlfrmt, href);
2139
0
    }
2140
2141
0
    msFree(type);
2142
0
    msFree(width);
2143
0
    msFree(height);
2144
0
    msFree(urlfrmt);
2145
0
    msFree(href);
2146
0
  } else {
2147
0
    if (action_if_not_found == OWS_WARN) {
2148
0
      char *pszExpandedMetadataKey =
2149
0
          msOWSGetExpandedMetadataKey(namespaces, name);
2150
0
      msIO_fprintf(stream,
2151
0
                   "<!-- WARNING: Mandatory metadata %s was missing in this "
2152
0
                   "context. -->\n",
2153
0
                   pszExpandedMetadataKey);
2154
0
      msFree(pszExpandedMetadataKey);
2155
0
      status = action_if_not_found;
2156
0
    }
2157
0
  }
2158
2159
0
  return status;
2160
0
}
2161
2162
/* msOWSPrintParam()
2163
**
2164
** Same as printMetadata() but applied to mapfile parameters.
2165
**/
2166
int msOWSPrintParam(FILE *stream, const char *name, const char *value,
2167
                    int action_if_not_found, const char *format,
2168
0
                    const char *default_value) {
2169
0
  int status = MS_NOERR;
2170
2171
0
  if (value && strlen(value) > 0) {
2172
0
    msIO_fprintf(stream, format, value);
2173
0
  } else {
2174
0
    if (action_if_not_found == OWS_WARN) {
2175
0
      msIO_fprintf(stream,
2176
0
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2177
0
                   "in this context. -->\n",
2178
0
                   name);
2179
0
      status = action_if_not_found;
2180
0
    }
2181
2182
0
    if (default_value)
2183
0
      msIO_fprintf(stream, format, default_value);
2184
0
  }
2185
2186
0
  return status;
2187
0
}
2188
2189
/* msOWSPrintEncodeParam()
2190
**
2191
** Same as printEncodeMetadata() but applied to mapfile parameters.
2192
**/
2193
int msOWSPrintEncodeParam(FILE *stream, const char *name, const char *value,
2194
                          int action_if_not_found, const char *format,
2195
0
                          const char *default_value) {
2196
0
  int status = MS_NOERR;
2197
0
  char *encode;
2198
2199
0
  if (value && strlen(value) > 0) {
2200
0
    encode = msEncodeHTMLEntities(value);
2201
0
    msIO_fprintf(stream, format, encode);
2202
0
    msFree(encode);
2203
0
  } else {
2204
0
    if (action_if_not_found == OWS_WARN) {
2205
0
      msIO_fprintf(stream,
2206
0
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2207
0
                   "in this context. -->\n",
2208
0
                   name);
2209
0
      status = action_if_not_found;
2210
0
    }
2211
2212
0
    if (default_value) {
2213
0
      encode = msEncodeHTMLEntities(default_value);
2214
0
      msIO_fprintf(stream, format, encode);
2215
0
      msFree(encode);
2216
0
    }
2217
0
  }
2218
2219
0
  return status;
2220
0
}
2221
2222
/* msOWSPrintMetadataList()
2223
**
2224
** Prints comma-separated lists metadata.  (e.g. keywordList)
2225
** default_value serves 2 purposes if specified:
2226
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2227
**  (exclusion)
2228
** - will be printed if MetadataList is empty (fallback)
2229
**/
2230
int msOWSPrintMetadataList(FILE *stream, hashTableObj *metadata,
2231
                           const char *namespaces, const char *name,
2232
                           const char *startTag, const char *endTag,
2233
0
                           const char *itemFormat, const char *default_value) {
2234
0
  const char *value;
2235
2236
0
  value = msOWSLookupMetadata(metadata, namespaces, name);
2237
2238
0
  if (value == NULL) {
2239
0
    value = default_value;
2240
0
    default_value = NULL;
2241
0
  }
2242
2243
0
  if (value != NULL) {
2244
0
    char **keywords;
2245
0
    int numkeywords;
2246
2247
0
    keywords = msStringSplit(value, ',', &numkeywords);
2248
0
    if (keywords && numkeywords > 0) {
2249
0
      int kw;
2250
0
      if (startTag)
2251
0
        msIO_fprintf(stream, "%s", startTag);
2252
0
      for (kw = 0; kw < numkeywords; kw++) {
2253
0
        if (default_value != NULL &&
2254
0
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
2255
0
                0 &&
2256
0
            strncasecmp("_exclude", default_value + strlen(default_value) - 8,
2257
0
                        8) == 0)
2258
0
          continue;
2259
2260
0
        msIO_fprintf(stream, itemFormat, keywords[kw]);
2261
0
      }
2262
0
      if (endTag)
2263
0
        msIO_fprintf(stream, "%s", endTag);
2264
0
    }
2265
0
    msFreeCharArray(keywords, numkeywords);
2266
0
    return MS_TRUE;
2267
0
  }
2268
0
  return MS_FALSE;
2269
0
}
2270
2271
/* msOWSPrintEncodeMetadataList()
2272
**
2273
** Prints comma-separated lists metadata.  (e.g. keywordList)
2274
** This will print HTML encoded values.
2275
** default_value serves 2 purposes if specified:
2276
** - won't be printed if part of MetadataList (default_value == key"_exclude")
2277
**  (exclusion)
2278
** - will be printed if MetadataList is empty (fallback)
2279
**/
2280
int msOWSPrintEncodeMetadataList(FILE *stream, hashTableObj *metadata,
2281
                                 const char *namespaces, const char *name,
2282
                                 const char *startTag, const char *endTag,
2283
                                 const char *itemFormat,
2284
0
                                 const char *default_value) {
2285
0
  const char *value;
2286
0
  char *encoded;
2287
0
  size_t default_value_len = 0;
2288
2289
0
  value = msOWSLookupMetadata(metadata, namespaces, name);
2290
2291
0
  if (value == NULL) {
2292
0
    value = default_value;
2293
0
    default_value = NULL;
2294
0
  }
2295
0
  if (default_value)
2296
0
    default_value_len = strlen(default_value);
2297
2298
0
  if (value != NULL) {
2299
0
    char **keywords;
2300
0
    int numkeywords;
2301
2302
0
    keywords = msStringSplit(value, ',', &numkeywords);
2303
0
    if (keywords && numkeywords > 0) {
2304
0
      int kw;
2305
0
      if (startTag)
2306
0
        msIO_fprintf(stream, "%s", startTag);
2307
0
      for (kw = 0; kw < numkeywords; kw++) {
2308
0
        if (default_value != NULL && default_value_len > 8 &&
2309
0
            strncasecmp(keywords[kw], default_value, strlen(keywords[kw])) ==
2310
0
                0 &&
2311
0
            strncasecmp("_exclude", default_value + default_value_len - 8, 8) ==
2312
0
                0)
2313
0
          continue;
2314
2315
0
        encoded = msEncodeHTMLEntities(keywords[kw]);
2316
0
        msIO_fprintf(stream, itemFormat, encoded);
2317
0
        msFree(encoded);
2318
0
      }
2319
0
      if (endTag)
2320
0
        msIO_fprintf(stream, "%s", endTag);
2321
0
    }
2322
0
    msFreeCharArray(keywords, numkeywords);
2323
0
    return MS_TRUE;
2324
0
  }
2325
0
  return MS_FALSE;
2326
0
}
2327
2328
/* msOWSPrintEncodeParamList()
2329
**
2330
** Same as msOWSPrintEncodeMetadataList() but applied to mapfile parameters.
2331
**/
2332
int msOWSPrintEncodeParamList(FILE *stream, const char *name, const char *value,
2333
                              int action_if_not_found, char delimiter,
2334
                              const char *startTag, const char *endTag,
2335
0
                              const char *format, const char *default_value) {
2336
0
  int status = MS_NOERR;
2337
0
  char *encoded;
2338
0
  char **items = NULL;
2339
0
  int numitems = 0, i;
2340
2341
0
  if (value && strlen(value) > 0)
2342
0
    items = msStringSplit(value, delimiter, &numitems);
2343
0
  else {
2344
0
    if (action_if_not_found == OWS_WARN) {
2345
0
      msIO_fprintf(stream,
2346
0
                   "<!-- WARNING: Mandatory mapfile parameter '%s' was missing "
2347
0
                   "in this context. -->\n",
2348
0
                   name);
2349
0
      status = action_if_not_found;
2350
0
    }
2351
2352
0
    if (default_value)
2353
0
      items = msStringSplit(default_value, delimiter, &numitems);
2354
0
  }
2355
2356
0
  if (items && numitems > 0) {
2357
0
    if (startTag)
2358
0
      msIO_fprintf(stream, "%s", startTag);
2359
0
    for (i = 0; i < numitems; i++) {
2360
0
      encoded = msEncodeHTMLEntities(items[i]);
2361
0
      msIO_fprintf(stream, format, encoded);
2362
0
      msFree(encoded);
2363
0
    }
2364
0
    if (endTag)
2365
0
      msIO_fprintf(stream, "%s", endTag);
2366
0
  }
2367
0
  msFreeCharArray(items, numitems);
2368
2369
0
  return status;
2370
0
}
2371
2372
/*
2373
** msOWSPrintEX_GeographicBoundingBox()
2374
**
2375
** Print a EX_GeographicBoundingBox tag for WMS1.3.0
2376
**
2377
*/
2378
void msOWSPrintEX_GeographicBoundingBox(FILE *stream, const char *tabspace,
2379
                                        rectObj *extent, projectionObj *srcproj)
2380
2381
0
{
2382
0
  const char *pszTag = "EX_GeographicBoundingBox"; /* The default for WMS */
2383
0
  rectObj ext;
2384
2385
0
  ext = *extent;
2386
2387
  /* always project to lat long */
2388
0
  msOWSProjectToWGS84(srcproj, &ext);
2389
2390
0
  msIO_fprintf(stream, "%s<%s>\n", tabspace, pszTag);
2391
0
  msIO_fprintf(stream, "%s    <westBoundLongitude>%.6f</westBoundLongitude>\n",
2392
0
               tabspace, ext.minx);
2393
0
  msIO_fprintf(stream, "%s    <eastBoundLongitude>%.6f</eastBoundLongitude>\n",
2394
0
               tabspace, ext.maxx);
2395
0
  msIO_fprintf(stream, "%s    <southBoundLatitude>%.6f</southBoundLatitude>\n",
2396
0
               tabspace, ext.miny);
2397
0
  msIO_fprintf(stream, "%s    <northBoundLatitude>%.6f</northBoundLatitude>\n",
2398
0
               tabspace, ext.maxy);
2399
0
  msIO_fprintf(stream, "%s</%s>\n", tabspace, pszTag);
2400
2401
  /* msIO_fprintf(stream, "%s<%s minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"
2402
     />\n", tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy); */
2403
0
}
2404
2405
/*
2406
** msOWSPrintLatLonBoundingBox()
2407
**
2408
** Print a LatLonBoundingBox tag for WMS, or LatLongBoundingBox for WFS
2409
** ... yes, the tag name differs between WMS and WFS, yuck!
2410
**
2411
*/
2412
void msOWSPrintLatLonBoundingBox(FILE *stream, const char *tabspace,
2413
                                 rectObj *extent, projectionObj *srcproj,
2414
                                 projectionObj *wfsproj,
2415
0
                                 OWSServiceType nService) {
2416
0
  const char *pszTag = "LatLonBoundingBox"; /* The default for WMS */
2417
0
  rectObj ext;
2418
2419
0
  ext = *extent;
2420
2421
0
  if (nService == OWS_WMS) { /* always project to lat long */
2422
0
    msOWSProjectToWGS84(srcproj, &ext);
2423
0
  } else if (nService == OWS_WFS) { /* called from wfs 1.0.0 only: project to
2424
                                       map srs, if set */
2425
0
    pszTag = "LatLongBoundingBox";
2426
0
    if (wfsproj) {
2427
0
      if (msProjectionsDiffer(srcproj, wfsproj) == MS_TRUE)
2428
0
        msProjectRect(srcproj, wfsproj, &ext);
2429
0
    }
2430
0
  }
2431
2432
0
  msIO_fprintf(
2433
0
      stream,
2434
0
      "%s<%s minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" maxy=\"%.6f\" />\n",
2435
0
      tabspace, pszTag, ext.minx, ext.miny, ext.maxx, ext.maxy);
2436
0
}
2437
2438
/*
2439
** Emit a bounding box if we can find projection information.
2440
** If <namespaces>_bbox_extended is not set, emit a single bounding box
2441
** using the layer's native SRS (ignoring any <namespaces>_srs metadata).
2442
**
2443
** If <namespaces>_bbox_extended is set to true, emit a bounding box
2444
** for every projection listed in the <namespaces>_srs list.
2445
** Check the map level metadata for both _bbox_extended and _srs,
2446
** if there is no such metadata at the layer level.
2447
** (These settings make more sense at the global/map level anyways)
2448
*/
2449
void msOWSPrintBoundingBox(FILE *stream, const char *tabspace, rectObj *extent,
2450
                           projectionObj *srcproj, hashTableObj *layer_meta,
2451
                           hashTableObj *map_meta, const char *namespaces,
2452
0
                           int wms_version) {
2453
0
  const char *value, *resx, *resy, *wms_bbox_extended;
2454
0
  char *encoded, *encoded_resx, *encoded_resy, *epsg_str;
2455
0
  char **epsgs;
2456
0
  int i, num_epsgs;
2457
0
  projectionObj proj;
2458
0
  rectObj ext;
2459
2460
0
  wms_bbox_extended =
2461
0
      msOWSLookupMetadata2(layer_meta, map_meta, namespaces, "bbox_extended");
2462
0
  if (wms_bbox_extended && strncasecmp(wms_bbox_extended, "true", 5) == 0) {
2463
    /* get a list of all projections from the metadata
2464
       try the layer metadata first, otherwise use the map's */
2465
0
    if (msOWSLookupMetadata(layer_meta, namespaces, "srs")) {
2466
0
      msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_FALSE, &epsg_str);
2467
0
    } else {
2468
0
      msOWSGetEPSGProj(srcproj, map_meta, namespaces, MS_FALSE, &epsg_str);
2469
0
    }
2470
0
    epsgs = msStringSplit(epsg_str, ' ', &num_epsgs);
2471
0
    msFree(epsg_str);
2472
0
  } else {
2473
    /* Look for EPSG code in PROJECTION block only.  "wms_srs" metadata cannot
2474
     * be used to establish the native projection of a layer for BoundingBox
2475
     * purposes.
2476
     */
2477
0
    epsgs = (char **)msSmallMalloc(sizeof(char *));
2478
0
    num_epsgs = 1;
2479
0
    msOWSGetEPSGProj(srcproj, layer_meta, namespaces, MS_TRUE, &(epsgs[0]));
2480
0
  }
2481
2482
0
  for (i = 0; i < num_epsgs; i++) {
2483
0
    value = epsgs[i];
2484
0
    if (value && *value) {
2485
0
      memcpy(&ext, extent, sizeof(rectObj));
2486
2487
      /* reproject the extents for each SRS's bounding box */
2488
0
      msInitProjection(&proj);
2489
0
      msProjectionInheritContextFrom(&proj, srcproj);
2490
0
      if (msLoadProjectionStringEPSG(&proj, (char *)value) == 0) {
2491
0
        if (msProjectionsDiffer(srcproj, &proj) == MS_TRUE) {
2492
0
          msProjectRect(srcproj, &proj, &ext);
2493
0
        }
2494
        /*for wms 1.3.0 we need to make sure that we present the BBOX with
2495
          a reversed axes for some espg codes*/
2496
0
        if (wms_version >= OWS_1_3_0 && strncasecmp(value, "EPSG:", 5) == 0) {
2497
0
          msAxisNormalizePoints(&proj, 1, &(ext.minx), &(ext.miny));
2498
0
          msAxisNormalizePoints(&proj, 1, &(ext.maxx), &(ext.maxy));
2499
0
        }
2500
0
      }
2501
2502
0
      encoded = msEncodeHTMLEntities(value);
2503
0
      if (msProjIsGeographicCRS(&proj))
2504
0
        msIO_fprintf(stream,
2505
0
                     "%s<BoundingBox %s=\"%s\"\n"
2506
0
                     "%s            minx=\"%.6f\" miny=\"%.6f\" maxx=\"%.6f\" "
2507
0
                     "maxy=\"%.6f\"",
2508
0
                     tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS",
2509
0
                     encoded, tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2510
0
      else
2511
0
        msIO_fprintf(
2512
0
            stream,
2513
0
            "%s<BoundingBox %s=\"%s\"\n"
2514
0
            "%s            minx=\"%g\" miny=\"%g\" maxx=\"%g\" maxy=\"%g\"",
2515
0
            tabspace, (wms_version >= OWS_1_3_0) ? "CRS" : "SRS", encoded,
2516
0
            tabspace, ext.minx, ext.miny, ext.maxx, ext.maxy);
2517
2518
0
      msFree(encoded);
2519
0
      msFreeProjection(&proj);
2520
2521
0
      if ((resx = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resx")) !=
2522
0
              NULL &&
2523
0
          (resy = msOWSLookupMetadata2(layer_meta, map_meta, "MFO", "resy")) !=
2524
0
              NULL) {
2525
0
        encoded_resx = msEncodeHTMLEntities(resx);
2526
0
        encoded_resy = msEncodeHTMLEntities(resy);
2527
0
        msIO_fprintf(stream, "\n%s            resx=\"%s\" resy=\"%s\"",
2528
0
                     tabspace, encoded_resx, encoded_resy);
2529
0
        msFree(encoded_resx);
2530
0
        msFree(encoded_resy);
2531
0
      }
2532
2533
0
      msIO_fprintf(stream, " />\n");
2534
0
    }
2535
0
  }
2536
0
  msFreeCharArray(epsgs, num_epsgs);
2537
0
}
2538
2539
/*
2540
** Print the contact information
2541
*/
2542
void msOWSPrintContactInfo(FILE *stream, const char *tabspace, int nVersion,
2543
0
                           hashTableObj *metadata, const char *namespaces) {
2544
  /* contact information is a required element in 1.0.7 but the */
2545
  /* sub-elements such as ContactPersonPrimary, etc. are not! */
2546
  /* In 1.1.0, ContactInformation becomes optional. */
2547
0
  if (nVersion > OWS_1_0_0) {
2548
0
    msIO_fprintf(stream, "%s<ContactInformation>\n", tabspace);
2549
2550
    /* ContactPersonPrimary is optional, but when present then all its  */
2551
    /* sub-elements are mandatory */
2552
2553
0
    if (msOWSLookupMetadata(metadata, namespaces, "contactperson") ||
2554
0
        msOWSLookupMetadata(metadata, namespaces, "contactorganization")) {
2555
0
      msIO_fprintf(stream, "%s  <ContactPersonPrimary>\n", tabspace);
2556
2557
0
      msOWSPrintEncodeMetadata(
2558
0
          stream, metadata, namespaces, "contactperson", OWS_WARN,
2559
0
          "      <ContactPerson>%s</ContactPerson>\n", NULL);
2560
0
      msOWSPrintEncodeMetadata(
2561
0
          stream, metadata, namespaces, "contactorganization", OWS_WARN,
2562
0
          "      <ContactOrganization>%s</ContactOrganization>\n", NULL);
2563
0
      msIO_fprintf(stream, "%s  </ContactPersonPrimary>\n", tabspace);
2564
0
    }
2565
2566
0
    if (msOWSLookupMetadata(metadata, namespaces, "contactposition")) {
2567
0
      msOWSPrintEncodeMetadata(
2568
0
          stream, metadata, namespaces, "contactposition", OWS_NOERR,
2569
0
          "      <ContactPosition>%s</ContactPosition>\n", NULL);
2570
0
    }
2571
2572
    /* ContactAddress is optional, but when present then all its  */
2573
    /* sub-elements are mandatory */
2574
0
    if (msOWSLookupMetadata(metadata, namespaces, "addresstype") ||
2575
0
        msOWSLookupMetadata(metadata, namespaces, "address") ||
2576
0
        msOWSLookupMetadata(metadata, namespaces, "city") ||
2577
0
        msOWSLookupMetadata(metadata, namespaces, "stateorprovince") ||
2578
0
        msOWSLookupMetadata(metadata, namespaces, "postcode") ||
2579
0
        msOWSLookupMetadata(metadata, namespaces, "country")) {
2580
0
      msIO_fprintf(stream, "%s  <ContactAddress>\n", tabspace);
2581
2582
0
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "addresstype",
2583
0
                               OWS_WARN,
2584
0
                               "        <AddressType>%s</AddressType>\n", NULL);
2585
0
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "address",
2586
0
                               OWS_WARN, "        <Address>%s</Address>\n",
2587
0
                               NULL);
2588
0
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "city", OWS_WARN,
2589
0
                               "        <City>%s</City>\n", NULL);
2590
0
      msOWSPrintEncodeMetadata(
2591
0
          stream, metadata, namespaces, "stateorprovince", OWS_WARN,
2592
0
          "        <StateOrProvince>%s</StateOrProvince>\n", NULL);
2593
0
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "postcode",
2594
0
                               OWS_WARN, "        <PostCode>%s</PostCode>\n",
2595
0
                               NULL);
2596
0
      msOWSPrintEncodeMetadata(stream, metadata, namespaces, "country",
2597
0
                               OWS_WARN, "        <Country>%s</Country>\n",
2598
0
                               NULL);
2599
0
      msIO_fprintf(stream, "%s  </ContactAddress>\n", tabspace);
2600
0
    }
2601
2602
0
    if (msOWSLookupMetadata(metadata, namespaces, "contactvoicetelephone")) {
2603
0
      msOWSPrintEncodeMetadata(
2604
0
          stream, metadata, namespaces, "contactvoicetelephone", OWS_NOERR,
2605
0
          "      <ContactVoiceTelephone>%s</ContactVoiceTelephone>\n", NULL);
2606
0
    }
2607
2608
0
    if (msOWSLookupMetadata(metadata, namespaces,
2609
0
                            "contactfacsimiletelephone")) {
2610
0
      msOWSPrintEncodeMetadata(
2611
0
          stream, metadata, namespaces, "contactfacsimiletelephone", OWS_NOERR,
2612
0
          "      <ContactFacsimileTelephone>%s</ContactFacsimileTelephone>\n",
2613
0
          NULL);
2614
0
    }
2615
2616
0
    if (msOWSLookupMetadata(metadata, namespaces,
2617
0
                            "contactelectronicmailaddress")) {
2618
0
      msOWSPrintEncodeMetadata(
2619
0
          stream, metadata, namespaces, "contactelectronicmailaddress",
2620
0
          OWS_NOERR,
2621
0
          "  <ContactElectronicMailAddress>%s</ContactElectronicMailAddress>\n",
2622
0
          NULL);
2623
0
    }
2624
0
    msIO_fprintf(stream, "%s</ContactInformation>\n", tabspace);
2625
0
  }
2626
0
}
2627
2628
/*
2629
** msOWSGetLayerExtent()
2630
**
2631
** Try to establish layer extent, first looking for "ows_extent" metadata, and
2632
** if not found then call msLayerGetExtent() which will lookup the
2633
** layer->extent member, and if not found will open layer to read extent.
2634
**
2635
*/
2636
int msOWSGetLayerExtent(mapObj *map, layerObj *lp, const char *namespaces,
2637
0
                        rectObj *ext) {
2638
0
  (void)map;
2639
0
  const char *value;
2640
2641
0
  if ((value = msOWSLookupMetadata(&(lp->metadata), namespaces, "extent")) !=
2642
0
      NULL) {
2643
0
    char **tokens;
2644
0
    int n;
2645
2646
0
    tokens = msStringSplit(value, ' ', &n);
2647
0
    if (tokens == NULL || n != 4) {
2648
0
      msSetError(MS_WMSERR, "Wrong number of arguments for EXTENT metadata.",
2649
0
                 "msOWSGetLayerExtent()");
2650
0
      return MS_FAILURE;
2651
0
    }
2652
0
    ext->minx = atof(tokens[0]);
2653
0
    ext->miny = atof(tokens[1]);
2654
0
    ext->maxx = atof(tokens[2]);
2655
0
    ext->maxy = atof(tokens[3]);
2656
2657
0
    msFreeCharArray(tokens, n);
2658
0
    return MS_SUCCESS;
2659
0
  } else {
2660
0
    return msLayerGetExtent(lp, ext);
2661
0
  }
2662
2663
0
  return MS_FAILURE;
2664
0
}
2665
2666
/**********************************************************************
2667
 *                          msOWSExecuteRequests()
2668
 *
2669
 * Execute a number of WFS/WMS HTTP requests in parallel, and then
2670
 * update layerObj information with the result of the requests.
2671
 **********************************************************************/
2672
int msOWSExecuteRequests(httpRequestObj *pasReqInfo, int numRequests,
2673
0
                         mapObj *map, int bCheckLocalCache) {
2674
0
  int nStatus, iReq;
2675
2676
  /* Execute requests */
2677
#if defined(USE_CURL)
2678
  nStatus = msHTTPExecuteRequests(pasReqInfo, numRequests, bCheckLocalCache);
2679
#else
2680
0
  msSetError(MS_WMSERR,
2681
0
             "msOWSExecuteRequests() called apparently without libcurl "
2682
0
             "configured, msHTTPExecuteRequests() not available.",
2683
0
             "msOWSExecuteRequests()");
2684
0
  return MS_FAILURE;
2685
0
#endif
2686
2687
  /* Scan list of layers and call the handler for each layer type to */
2688
  /* pass them the request results. */
2689
0
  for (iReq = 0; iReq < numRequests; iReq++) {
2690
0
    if (pasReqInfo[iReq].nLayerId >= 0 &&
2691
0
        pasReqInfo[iReq].nLayerId < map->numlayers) {
2692
0
      layerObj *lp;
2693
2694
0
      lp = GET_LAYER(map, pasReqInfo[iReq].nLayerId);
2695
2696
0
      if (lp->connectiontype == MS_WFS)
2697
0
        msWFSUpdateRequestInfo(lp, &(pasReqInfo[iReq]));
2698
0
    }
2699
0
  }
2700
2701
0
  return nStatus;
2702
0
}
2703
2704
/**********************************************************************
2705
 *                          msOWSProcessException()
2706
 *
2707
 **********************************************************************/
2708
void msOWSProcessException(layerObj *lp, const char *pszFname, int nErrorCode,
2709
0
                           const char *pszFuncName) {
2710
0
  FILE *fp;
2711
2712
0
  if ((fp = fopen(pszFname, "r")) != NULL) {
2713
0
    char *pszBuf = NULL;
2714
0
    int nBufSize = 0;
2715
0
    char *pszStart, *pszEnd;
2716
2717
0
    fseek(fp, 0, SEEK_END);
2718
0
    nBufSize = ftell(fp);
2719
0
    if (nBufSize < 0) {
2720
0
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
2721
0
      fclose(fp);
2722
0
      return;
2723
0
    }
2724
0
    rewind(fp);
2725
0
    pszBuf = (char *)malloc((nBufSize + 1) * sizeof(char));
2726
0
    if (pszBuf == NULL) {
2727
0
      msSetError(MS_MEMERR, NULL, "msOWSProcessException()");
2728
0
      fclose(fp);
2729
0
      return;
2730
0
    }
2731
2732
0
    if ((int)fread(pszBuf, 1, nBufSize, fp) != nBufSize) {
2733
0
      msSetError(MS_IOERR, NULL, "msOWSProcessException()");
2734
0
      free(pszBuf);
2735
0
      fclose(fp);
2736
0
      return;
2737
0
    }
2738
2739
0
    pszBuf[nBufSize] = '\0';
2740
2741
    /* OK, got the data in the buffer.  Look for the <Message> tags */
2742
0
    if ((strstr(pszBuf, "<WFS_Exception>") && /* WFS style */
2743
0
         (pszStart = strstr(pszBuf, "<Message>")) &&
2744
0
         (pszEnd = strstr(pszStart, "</Message>"))) ||
2745
0
        (strstr(pszBuf, "<ServiceExceptionReport>") && /* WMS style */
2746
0
         (pszStart = strstr(pszBuf, "<ServiceException>")) &&
2747
0
         (pszEnd = strstr(pszStart, "</ServiceException>")))) {
2748
0
      pszStart = strchr(pszStart, '>') + 1;
2749
0
      *pszEnd = '\0';
2750
0
      msSetError(nErrorCode, "Got Remote Server Exception for layer %s: %s",
2751
0
                 pszFuncName, lp->name ? lp->name : "(null)", pszStart);
2752
0
    } else {
2753
0
      msSetError(
2754
0
          MS_WFSCONNERR,
2755
0
          "Unable to parse Remote Server Exception Message for layer %s.",
2756
0
          pszFuncName, lp->name ? lp->name : "(null)");
2757
0
    }
2758
2759
0
    free(pszBuf);
2760
0
    fclose(fp);
2761
0
  }
2762
0
}
2763
2764
/**********************************************************************
2765
 *                          msOWSBuildURLFilename()
2766
 *
2767
 * Build a unique filename for this URL to use in caching remote server
2768
 * requests.  Slashes and illegal characters will be turned into '_'
2769
 *
2770
 * Returns a newly allocated buffer that should be freed by the caller or
2771
 * NULL in case of error.
2772
 **********************************************************************/
2773
char *msOWSBuildURLFilename(const char *pszPath, const char *pszURL,
2774
0
                            const char *pszExt) {
2775
0
  char *pszBuf, *pszPtr;
2776
0
  int i;
2777
0
  size_t nBufLen = 0;
2778
2779
0
  nBufLen = strlen(pszURL) + strlen(pszExt) + 2;
2780
0
  if (pszPath)
2781
0
    nBufLen += (strlen(pszPath) + 1);
2782
2783
0
  pszBuf = (char *)malloc(nBufLen);
2784
0
  if (pszBuf == NULL) {
2785
0
    msSetError(MS_MEMERR, NULL, "msOWSBuildURLFilename()");
2786
0
    return NULL;
2787
0
  }
2788
0
  pszBuf[0] = '\0';
2789
2790
0
  if (pszPath) {
2791
#ifdef _WIN32
2792
    if (pszPath[strlen(pszPath) - 1] != '/' &&
2793
        pszPath[strlen(pszPath) - 1] != '\\')
2794
      snprintf(pszBuf, nBufLen, "%s\\", pszPath);
2795
    else
2796
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2797
#else
2798
0
    if (pszPath[strlen(pszPath) - 1] != '/')
2799
0
      snprintf(pszBuf, nBufLen, "%s/", pszPath);
2800
0
    else
2801
0
      snprintf(pszBuf, nBufLen, "%s", pszPath);
2802
0
#endif
2803
0
  }
2804
2805
0
  pszPtr = pszBuf + strlen(pszBuf);
2806
2807
0
  for (i = 0; pszURL[i] != '\0'; i++) {
2808
0
    if (isalnum(pszURL[i]))
2809
0
      *pszPtr = pszURL[i];
2810
0
    else
2811
0
      *pszPtr = '_';
2812
0
    pszPtr++;
2813
0
  }
2814
2815
0
  strlcpy(pszPtr, pszExt, nBufLen);
2816
2817
0
  return pszBuf;
2818
0
}
2819
2820
/*
2821
** msOWSGetProjURN()
2822
**
2823
** Fetch an OGC URN for this layer or map.  Similar to msOWSGetEPSGProj()
2824
** but returns the result in the form "urn:ogc:def:crs:EPSG::27700".
2825
** The returned buffer is dynamically allocated, and must be freed by the
2826
** caller.
2827
*/
2828
char *msOWSGetProjURN(projectionObj *proj, hashTableObj *metadata,
2829
0
                      const char *namespaces, int bReturnOnlyFirstOne) {
2830
0
  char *result;
2831
0
  char **tokens;
2832
0
  int numtokens, i;
2833
0
  char *oldStyle = NULL;
2834
2835
0
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
2836
2837
0
  if (oldStyle == NULL || strncmp(oldStyle, "CRS:", 4) == 0 ||
2838
0
      strncmp(oldStyle, "AUTO:", 5) == 0 ||
2839
0
      strncmp(oldStyle, "AUTO2:", 6) == 0) {
2840
0
    msFree(oldStyle);
2841
0
    return NULL;
2842
0
  }
2843
2844
0
  result = msStrdup("");
2845
2846
0
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
2847
0
  msFree(oldStyle);
2848
0
  for (i = 0; tokens != NULL && i < numtokens; i++) {
2849
0
    char urn[100];
2850
0
    char *colon = strchr(tokens[i], ':');
2851
2852
0
    if (colon != NULL && strchr(colon + 1, ':') == NULL) {
2853
0
      *colon = 0;
2854
0
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:%s::%s", tokens[i],
2855
0
               colon + 1);
2856
0
    } else if (strcasecmp(tokens[i], "imageCRS") == 0)
2857
0
      snprintf(urn, sizeof(urn), "urn:ogc:def:crs:OGC::imageCRS");
2858
0
    else if (strncmp(tokens[i], "urn:ogc:def:crs:", 16) == 0) {
2859
0
      strlcpy(urn, tokens[i], sizeof(urn));
2860
0
    } else {
2861
0
      strlcpy(urn, "", sizeof(urn));
2862
0
    }
2863
2864
0
    if (strlen(urn) > 0) {
2865
0
      const size_t bufferSize = strlen(result) + strlen(urn) + 2;
2866
0
      result = (char *)msSmallRealloc(result, bufferSize);
2867
2868
0
      if (strlen(result) > 0)
2869
0
        strlcat(result, " ", bufferSize);
2870
0
      strlcat(result, urn, bufferSize);
2871
0
    } else {
2872
0
      msDebug("msOWSGetProjURN(): Failed to process SRS '%s', ignored.",
2873
0
              tokens[i]);
2874
0
    }
2875
0
  }
2876
2877
0
  msFreeCharArray(tokens, numtokens);
2878
2879
0
  if (strlen(result) == 0) {
2880
0
    msFree(result);
2881
0
    return NULL;
2882
0
  } else
2883
0
    return result;
2884
0
}
2885
2886
/*
2887
** msOWSGetProjURI()
2888
**
2889
** Fetch an OGC URI for this layer or map.  Similar to msOWSGetEPSGProj()
2890
** but returns the result in the form
2891
*"http://www.opengis.net/def/crs/EPSG/0/27700".
2892
** The returned buffer is dynamically allocated, and must be freed by the
2893
** caller.
2894
*/
2895
char *msOWSGetProjURI(projectionObj *proj, hashTableObj *metadata,
2896
0
                      const char *namespaces, int bReturnOnlyFirstOne) {
2897
0
  char *result;
2898
0
  char **tokens;
2899
0
  int numtokens, i;
2900
0
  char *oldStyle = NULL;
2901
2902
0
  msOWSGetEPSGProj(proj, metadata, namespaces, bReturnOnlyFirstOne, &oldStyle);
2903
2904
0
  if (oldStyle == NULL || !EQUALN(oldStyle, "EPSG:", 5)) {
2905
0
    msFree(oldStyle); // avoid leak
2906
0
    return NULL;
2907
0
  }
2908
2909
0
  result = msStrdup("");
2910
2911
0
  tokens = msStringSplit(oldStyle, ' ', &numtokens);
2912
0
  msFree(oldStyle);
2913
0
  for (i = 0; tokens != NULL && i < numtokens; i++) {
2914
0
    char urn[100];
2915
2916
0
    if (strncmp(tokens[i], "EPSG:", 5) == 0)
2917
0
      snprintf(urn, sizeof(urn), "http://www.opengis.net/def/crs/EPSG/0/%s",
2918
0
               tokens[i] + 5);
2919
0
    else if (strcasecmp(tokens[i], "imageCRS") == 0)
2920
0
      snprintf(urn, sizeof(urn),
2921
0
               "http://www.opengis.net/def/crs/OGC/0/imageCRS");
2922
0
    else if (strncmp(tokens[i], "http://www.opengis.net/def/crs/", 16) == 0)
2923
0
      snprintf(urn, sizeof(urn), "%s", tokens[i]);
2924
0
    else
2925
0
      strlcpy(urn, "", sizeof(urn));
2926
2927
0
    if (strlen(urn) > 0) {
2928
0
      result = (char *)msSmallRealloc(result, strlen(result) + strlen(urn) + 2);
2929
2930
0
      if (strlen(result) > 0)
2931
0
        strcat(result, " ");
2932
0
      strcat(result, urn);
2933
0
    } else {
2934
0
      msDebug("msOWSGetProjURI(): Failed to process SRS '%s', ignored.",
2935
0
              tokens[i]);
2936
0
    }
2937
0
  }
2938
2939
0
  msFreeCharArray(tokens, numtokens);
2940
2941
0
  if (strlen(result) == 0) {
2942
0
    msFree(result);
2943
0
    return NULL;
2944
0
  } else
2945
0
    return result;
2946
0
}
2947
2948
/*
2949
** msOWSGetDimensionInfo()
2950
**
2951
** Extract dimension information from a layer's metadata
2952
**
2953
** Before 4.9, only the time dimension was support. With the addition of
2954
** Web Map Context 1.1.0, we need to support every dimension types.
2955
** This function get the dimension information from special metadata in
2956
** the layer, but can also return default values for the time dimension.
2957
**
2958
*/
2959
void msOWSGetDimensionInfo(layerObj *layer, const char *pszDimension,
2960
                           const char **papszDimUserValue,
2961
                           const char **papszDimUnits,
2962
                           const char **papszDimDefault,
2963
                           const char **papszDimNearValue,
2964
                           const char **papszDimUnitSymbol,
2965
0
                           const char **papszDimMultiValue) {
2966
0
  char *pszDimensionItem;
2967
0
  size_t bufferSize = 0;
2968
2969
0
  if (pszDimension == NULL || layer == NULL)
2970
0
    return;
2971
2972
0
  bufferSize = strlen(pszDimension) + 50;
2973
0
  pszDimensionItem = (char *)malloc(bufferSize);
2974
2975
  /* units (mandatory in map context) */
2976
0
  if (papszDimUnits != NULL) {
2977
0
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_units", pszDimension);
2978
0
    *papszDimUnits =
2979
0
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
2980
0
  }
2981
  /* unitSymbol (mandatory in map context) */
2982
0
  if (papszDimUnitSymbol != NULL) {
2983
0
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_unitsymbol",
2984
0
             pszDimension);
2985
0
    *papszDimUnitSymbol =
2986
0
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
2987
0
  }
2988
  /* userValue (mandatory in map context) */
2989
0
  if (papszDimUserValue != NULL) {
2990
0
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_uservalue",
2991
0
             pszDimension);
2992
0
    *papszDimUserValue =
2993
0
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
2994
0
  }
2995
  /* default */
2996
0
  if (papszDimDefault != NULL) {
2997
0
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_default",
2998
0
             pszDimension);
2999
0
    *papszDimDefault =
3000
0
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
3001
0
  }
3002
  /* multipleValues */
3003
0
  if (papszDimMultiValue != NULL) {
3004
0
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_multiplevalues",
3005
0
             pszDimension);
3006
0
    *papszDimMultiValue =
3007
0
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
3008
0
  }
3009
  /* nearestValue */
3010
0
  if (papszDimNearValue != NULL) {
3011
0
    snprintf(pszDimensionItem, bufferSize, "dimension_%s_nearestvalue",
3012
0
             pszDimension);
3013
0
    *papszDimNearValue =
3014
0
        msOWSLookupMetadata(&(layer->metadata), "MO", pszDimensionItem);
3015
0
  }
3016
3017
  /* Use default time value if necessary */
3018
0
  if (strcasecmp(pszDimension, "time") == 0) {
3019
0
    if (papszDimUserValue != NULL && *papszDimUserValue == NULL)
3020
0
      *papszDimUserValue =
3021
0
          msOWSLookupMetadata(&(layer->metadata), "MO", "time");
3022
0
    if (papszDimDefault != NULL && *papszDimDefault == NULL)
3023
0
      *papszDimDefault =
3024
0
          msOWSLookupMetadata(&(layer->metadata), "MO", "timedefault");
3025
0
    if (papszDimUnits != NULL && *papszDimUnits == NULL)
3026
0
      *papszDimUnits = "ISO8601";
3027
0
    if (papszDimUnitSymbol != NULL && *papszDimUnitSymbol == NULL)
3028
0
      *papszDimUnitSymbol = "t";
3029
0
    if (papszDimNearValue != NULL && *papszDimNearValue == NULL)
3030
0
      *papszDimNearValue = "0";
3031
0
  }
3032
3033
0
  free(pszDimensionItem);
3034
3035
0
  return;
3036
0
}
3037
3038
/**
3039
 * msOWSNegotiateUpdateSequence()
3040
 *
3041
 * returns the updateSequence value for an OWS
3042
 *
3043
 * @param requested_updatesequence the updatesequence passed by the client
3044
 * @param updatesequence the updatesequence set by the server
3045
 *
3046
 * @return result of comparison (-1, 0, 1)
3047
 * -1: lower / higher OR values not set by client or server
3048
 *  1: higher / lower
3049
 *  0: equal
3050
 */
3051
3052
int msOWSNegotiateUpdateSequence(const char *requested_updatesequence,
3053
0
                                 const char *updatesequence) {
3054
0
  int valtype1 = 1; /* default datatype for updatesequence passed by client */
3055
0
  int valtype2 = 1; /* default datatype for updatesequence set by server */
3056
0
  struct tm tm_requested_updatesequence, tm_updatesequence;
3057
3058
  /* if not specified by client, or set by server,
3059
     server responds with latest Capabilities XML */
3060
0
  if (!requested_updatesequence || !updatesequence)
3061
0
    return -1;
3062
3063
  /* test to see if server value is an integer (1), string (2) or timestamp (3)
3064
   */
3065
0
  if (msStringIsInteger(updatesequence) == MS_FAILURE)
3066
0
    valtype1 = 2;
3067
3068
0
  if (valtype1 == 2) { /* test if timestamp */
3069
0
    msTimeInit(&tm_updatesequence);
3070
0
    if (msParseTime(updatesequence, &tm_updatesequence) == MS_TRUE)
3071
0
      valtype1 = 3;
3072
0
    msResetErrorList();
3073
0
  }
3074
3075
  /* test to see if client value is an integer (1), string (2) or timestamp (3)
3076
   */
3077
0
  if (msStringIsInteger(requested_updatesequence) == MS_FAILURE)
3078
0
    valtype2 = 2;
3079
3080
0
  if (valtype2 == 2) { /* test if timestamp */
3081
0
    msTimeInit(&tm_requested_updatesequence);
3082
0
    if (msParseTime(requested_updatesequence, &tm_requested_updatesequence) ==
3083
0
        MS_TRUE)
3084
0
      valtype2 = 3;
3085
0
    msResetErrorList();
3086
0
  }
3087
3088
  /* if the datatypes do not match, do not compare, */
3089
0
  if (valtype1 != valtype2)
3090
0
    return -1;
3091
3092
0
  if (valtype1 == 1) { /* integer */
3093
0
    const int requested_updatesequence_i = atoi(requested_updatesequence);
3094
0
    const int updatesequence_i = atoi(updatesequence);
3095
0
    if (requested_updatesequence_i < updatesequence_i)
3096
0
      return -1;
3097
3098
0
    if (requested_updatesequence_i > updatesequence_i)
3099
0
      return 1;
3100
3101
0
    return 0;
3102
0
  }
3103
3104
0
  if (valtype1 == 2) /* string */
3105
0
    return strcasecmp(requested_updatesequence, updatesequence);
3106
3107
0
  assert(valtype1 == 3); /* timestamp */
3108
  /* compare timestamps */
3109
0
  return msDateCompare(&tm_requested_updatesequence, &tm_updatesequence) +
3110
0
         msTimeCompare(&tm_requested_updatesequence, &tm_updatesequence);
3111
0
}
3112
3113
/************************************************************************/
3114
/*                         msOwsIsOutputFormatValid                     */
3115
/*                                                                      */
3116
/*      Utility function to parse a comma separated list in a            */
3117
/*      metedata object and select and outputformat.                    */
3118
/************************************************************************/
3119
outputFormatObj *msOwsIsOutputFormatValid(mapObj *map, const char *format,
3120
                                          hashTableObj *metadata,
3121
                                          const char *namespaces,
3122
0
                                          const char *name) {
3123
0
  char **tokens = NULL;
3124
0
  int i, n;
3125
0
  outputFormatObj *psFormat = NULL;
3126
0
  const char *format_list = NULL;
3127
3128
0
  if (map && format && metadata && namespaces && name) {
3129
0
    msApplyDefaultOutputFormats(map);
3130
0
    format_list = msOWSLookupMetadata(metadata, namespaces, name);
3131
0
    n = 0;
3132
0
    if (format_list)
3133
0
      tokens = msStringSplit(format_list, ',', &n);
3134
3135
0
    if (tokens && n > 0) {
3136
0
      for (i = 0; i < n; i++) {
3137
0
        int iFormat = msGetOutputFormatIndex(map, tokens[i]);
3138
0
        const char *mimetype;
3139
0
        if (iFormat == -1)
3140
0
          continue;
3141
3142
0
        mimetype = map->outputformatlist[iFormat]->mimetype;
3143
3144
0
        msStringTrim(tokens[i]);
3145
0
        if (strcasecmp(tokens[i], format) == 0)
3146
0
          break;
3147
0
        if (mimetype && strcasecmp(mimetype, format) == 0)
3148
0
          break;
3149
0
      }
3150
0
      if (i < n)
3151
0
        psFormat = msSelectOutputFormat(map, format);
3152
0
    }
3153
0
    if (tokens)
3154
0
      msFreeCharArray(tokens, n);
3155
0
  }
3156
3157
0
  return psFormat;
3158
0
}
3159
3160
#endif /* defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined             \
3161
          (USE_WCS_SVR) || defined(USE_SOS_SVR) || defined(USE_WMS_LYR) ||     \
3162
          defined(USE_WFS_LYR) */