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

geographika / mapserver / 17823520225

18 Sep 2025 08:57AM UTC coverage: 41.442% (-0.02%) from 41.466%
17823520225

push

github

geographika
Use full path for CONFIG test

62112 of 149877 relevant lines covered (41.44%)

25354.57 hits per line

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

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

28
#include "mapserver.h"
29
#include "mapogcapi.h"
30
#include "mapserv-index.h"
31
#include "maptemplate.h"
32
#include "mapows.h"
33
#include "mapserv-config.h"
34

35
#include "cpl_conv.h"
36

37
#include "third-party/include_nlohmann_json.hpp"
38
#include "third-party/include_pantor_inja.hpp"
39

40
using namespace inja;
41
using json = nlohmann::json;
42

43
/*
44
** HTML Templates
45
*/
46
#define TEMPLATE_HTML_INDEX "landing.html"
47
#define TEMPLATE_HTML_MAP_INDEX "map.html"
48

49
static json processCGI(mapObj *map) {
×
50

51
  json response;
52

53
  // check if CGI modes have been disabled
54
  const char *enable_modes =
55
      msLookupHashTable(&(map->web.metadata), "ms_enable_modes");
×
56

57
  int disabled = MS_FALSE;
×
58
  msOWSParseRequestMetadata(enable_modes, "BROWSE", &disabled);
×
59

60
  if (disabled == MS_FALSE) {
×
61

62
    response["title"] = map->name;
×
63

64
    json serviceDocs =
65
        json::array({{{"href", "./?template=openlayers&mode=browse&layers=all"},
×
66
                      {"title", "OpenLayers Viewer"},
67
                      {"service", "CGI"},
68
                      {"type", "text/html"}}});
×
69
    response["service-doc"] = serviceDocs;
×
70
  }
71

72
  return response;
×
73
}
×
74

75
static json processOGCAPI(mapObj *map) {
×
76

77
  json response;
78

79
  const char *title = msOWSLookupMetadata(&(map->web.metadata), "AO", "title");
×
80
  if (!title) {
×
81
    title = map->name;
×
82
  }
83

84
  int status = msOWSRequestIsEnabled(map, NULL, "AO", "OGCAPI", MS_FALSE);
×
85
  if (status == MS_TRUE) {
×
86
    response["title"] = title;
×
87

88
    json serviceDescriptions = json::array({{{"href", "./ogcapi/?f=json"},
×
89
                                             {"title", "OGC API Root Service"},
90
                                             {"service", "OGC API"},
91
                                             {"type", "application/json"}}});
×
92
    json serviceDocs = json::array({{{"href", "./ogcapi/?f=html"},
×
93
                                     {"title", "OGC API Landing Page"},
94
                                     {"service", "OGC API"},
95
                                     {"type", "text/html"}}});
×
96
    response["service-desc"] = serviceDescriptions;
×
97
    response["service-doc"] = serviceDocs;
×
98
  }
99
  return response;
×
100
}
×
101

102
static json processWMS(mapObj *map, const char *onlineResource) {
×
103

104
  json response;
105

106
  const char *title = msOWSLookupMetadata(&(map->web.metadata), "MO", "title");
×
107
  if (!title) {
×
108
    title = map->name;
×
109
  }
110

111
  int globally_enabled = MS_FALSE;
112
  int disabled = MS_FALSE;
×
113

114
  const char *enable_request =
115
      msOWSLookupMetadata(&map->web.metadata, "OM", "enable_request");
×
116

117
  globally_enabled =
118
      msOWSParseRequestMetadata(enable_request, "GetCapabilities", &disabled);
×
119

120
  if (globally_enabled == MS_TRUE) {
×
121
    response["title"] = title;
×
122

123
    std::vector<std::string> versions = {"1.0.0", "1.1.0", "1.1.1", "1.3.0"};
×
124
    json serviceDescriptions = json::array();
×
125

126
    for (const auto &ver : versions) {
×
127
      serviceDescriptions.push_back(
×
128
          {{"href", std::string(onlineResource) + "version=" + ver +
×
129
                        "&request=GetCapabilities&service=WMS"},
130
           {"title", "WMS GetCapabilities URL (version " + ver + ")"},
×
131
           {"service", "WMS"},
132
           {"type", "text/xml"}});
133
    }
134
    response["service-desc"] = serviceDescriptions;
×
135
  }
×
136

137
  return response;
×
138
}
×
139

140
static json processWFS(mapObj *map) {
×
141

142
  json response;
143

144
  const char *title = msOWSLookupMetadata(&(map->web.metadata), "FO", "title");
×
145
  if (!title) {
×
146
    title = map->name;
×
147
  }
148

149
  int globally_enabled = MS_FALSE;
150
  int disabled = MS_FALSE;
×
151

152
  const char *enable_request =
153
      msOWSLookupMetadata(&map->web.metadata, "OF", "enable_request");
×
154

155
  globally_enabled =
156
      msOWSParseRequestMetadata(enable_request, "GetCapabilities", &disabled);
×
157

158
  if (globally_enabled == MS_TRUE) {
×
159

160
    response["title"] = title;
×
161

162
    std::vector<std::string> versions = {"1.0.0", "1.1.0", "2.0.0"};
×
163
    json serviceDescriptions = json::array();
×
164

165
    for (const auto &ver : versions) {
×
166
      serviceDescriptions.push_back(
×
167
          {{"href",
168
            "./?version=" + ver + "&request=GetCapabilities&service=WFS"},
×
169
           {"title", "WFS GetCapabilities URL (version " + ver + ")"},
×
170
           {"service", "WFS"},
171
           {"type", "text/xml"}});
172
    }
173
    response["service-desc"] = serviceDescriptions;
×
174
  }
×
175

176
  return response;
×
177
}
×
178

179
static json processWCS(mapObj *map) {
×
180

181
  json response;
182

183
  const char *title = msOWSLookupMetadata(&(map->web.metadata), "CO", "title");
×
184
  if (!title) {
×
185
    title = map->name;
×
186
  }
187

188
  int globally_enabled = MS_FALSE;
189
  int disabled = MS_FALSE;
×
190

191
  const char *enable_request =
192
      msOWSLookupMetadata(&map->web.metadata, "OC", "enable_request");
×
193

194
  globally_enabled =
195
      msOWSParseRequestMetadata(enable_request, "GetCapabilities", &disabled);
×
196

197
  if (globally_enabled == MS_TRUE) {
×
198
    response["title"] = title;
×
199

200
    std::vector<std::string> versions = {"1.0.0", "1.1.0", "2.0.0", "2.0.1"};
×
201
    json serviceDescriptions = json::array();
×
202

203
    for (auto &ver : versions) {
×
204
      serviceDescriptions.push_back(
×
205
          {{"href",
206
            "./?version=" + ver + "&request=GetCapabilities&service=WCS"},
×
207
           {"title", "WCS GetCapabilities URL (version " + ver + ")"},
×
208
           {"service", "WCS"},
209
           {"type", "text/xml"}});
210
    }
211
    response["service-desc"] = serviceDescriptions;
×
212
  }
×
213
  return response;
×
214
}
×
215

216
static json createMapDetails(mapObj *map, const char *onlineResource) {
×
217

218
  json links = json::array();
×
219
  json result;
220

221
  result = processCGI(map);
×
222
  if (!result.empty()) {
×
223
    links.push_back(result);
×
224
  }
225

226
#ifdef USE_OGCAPI_SVR
227
  result = processOGCAPI(map);
×
228
  if (!result.empty()) {
×
229
    links.push_back(result);
×
230
  }
231
#endif
232
#ifdef USE_WMS_SVR
233
  result = processWMS(map, onlineResource);
×
234
  if (!result.empty()) {
×
235
    links.push_back(result);
×
236
  }
237
#endif
238
#ifdef USE_WFS_SVR
239
  result = processWFS(map);
×
240
  if (!result.empty()) {
×
241
    links.push_back(result);
×
242
  }
243
#endif
244
#ifdef USE_WCS_SVR
245
  result = processWCS(map);
×
246
  if (!result.empty()) {
×
247
    links.push_back(result);
×
248
  }
249
#endif
250

251
  return {{"linkset", links}};
×
252
}
×
253

254
/**
255
 * Create a JSON object with a summary of the Mapfile
256
 **/
257
static json createMapSummary(mapObj *map, const char *key,
7✔
258
                             const char *onlineResource) {
259
  json mapJson;
260
  const char *value =
261
      msOWSLookupMetadata(&(map->web.metadata), "MCFGO", "title");
7✔
262

263
  mapJson["key"] = key;
14✔
264
  mapJson["has-error"] = 0;
7✔
265

266
  if (value) {
7✔
267
    mapJson["title"] = value;
8✔
268
  } else {
269
    mapJson["title"] = map->name;
9✔
270
  }
271

272
  mapJson["layer-count"] = map->numlayers;
7✔
273
  mapJson["name"] = map->name;
21✔
274

275
  mapJson["service-desc"] = json::array(
84✔
276
      {{{"href", std::string(onlineResource) + std::string(key) + "/?f=json"},
14✔
277
        {"title", key},
278
        {"type", "application/vnd.oai.openapi+json"}}});
279

280
  mapJson["service-doc"] = json::array(
84✔
281
      {{{"href", std::string(onlineResource) + std::string(key) + "/"},
14✔
282
        {"title", key},
283
        {"type", "text/html"}}});
284

285
  return mapJson;
7✔
286
}
210✔
287

288
/**
289
 * For invalid maps return a JSON object reporting the error
290
 **/
291
static json createMapError(const char *key) {
1✔
292
  json mapJson;
293
  mapJson["key"] = key;
2✔
294
  mapJson["has-error"] = true;
2✔
295
  mapJson["title"] = " <b>Error loading the map</b>";
2✔
296
  mapJson["layer-count"] = 0;
2✔
297
  mapJson["name"] = key;
2✔
298
  mapJson["service-desc"] =
1✔
299
      json::array({{{"href", "./" + std::string(key) + "/?f=json"},
13✔
300
                    {"title", key},
301
                    {"type", "application/vnd.oai.openapi+json"}}});
302

303
  mapJson["service-doc"] =
1✔
304
      json::array({{{"href", "./" + std::string(key) + "/"},
13✔
305
                    {"title", key},
306
                    {"type", "text/html"}}});
307

308
  return mapJson;
1✔
309
}
30✔
310

311
/**
312
 * Load the Map for the key, by getting its file path
313
 * from the CONFIG file
314
 */
315
static mapObj *getMapFromConfig(configObj *config, const char *key) {
8✔
316
  const char *mapfilePath = msLookupHashTable(&config->maps, key);
8✔
317

318
  if (!mapfilePath)
8✔
319
    return nullptr;
320

321
  char pathBuf[MS_MAXPATHLEN];
322
  mapfilePath = msConfigGetMap(config, key, pathBuf);
8✔
323

324
  return msLoadMap(mapfilePath, nullptr, config);
8✔
325
}
326

327
/**
328
 * Generate HTML by populating a template with the dynamic JSON
329
 */
330
static int createHTMLOutput(json response, const char *templateName) {
×
331
  std::string path;
332
  char fullpath[MS_MAXPATHLEN];
333

334
  path = getTemplateDirectory(NULL, "html_template_directory",
×
335
                              "MS_INDEX_TEMPLATE_DIRECTORY");
×
336
  if (path.empty()) {
×
337
    outputError(OGCAPI_CONFIG_ERROR, "Template directory not set.");
×
338
    return MS_FAILURE;
×
339
  }
340
  msBuildPath(fullpath, NULL, path.c_str());
×
341

342
  json j = {{"response", response}};
×
343
  outputTemplate(fullpath, templateName, j, OGCAPI_MIMETYPE_HTML);
×
344

345
  return MS_SUCCESS;
346
}
×
347

348
/**
349
 * Return an individual map landing page response
350
 */
351
int msOGCAPIDispatchMapIndexRequest(mapservObj *mapserv, configObj *config) {
×
352
#ifdef USE_OGCAPI_SVR
353
  if (!mapserv || !config)
×
354
    return -1; // Handle null pointers
355

356
  cgiRequestObj *request = mapserv->request;
×
357

358
  if (request->api_path_length != 1) {
×
359
    const char *pathInfo = getenv("PATH_INFO");
×
360
    msSetError(MS_OGCAPIERR, "Invalid PATH_INFO format: \"%s\"",
×
361
               "msOGCAPIDispatchMapIndexRequest()", pathInfo);
362
    return MS_FAILURE;
×
363
  }
364

365
  OGCAPIFormat format = msGetOutputFormat(request);
×
366
  const char *key = request->api_path[0];
×
367

368
  mapObj *map = getMapFromConfig(config, key);
×
369

370
  std::string onlineResource;
371

372
  if (char *res =
×
373
          msOWSGetOnlineResource(map, "MFCSGO", "onlineresource", request)) {
×
374
    onlineResource = res;
375
    free(res);
×
376
  } else {
377
    // use a relative URL if no onlineresource can be found or created
378
    onlineResource = "./?"; // fallback
379
  }
380

381
  json response = createMapDetails(map, onlineResource.c_str());
×
382

383
  // add in summary map details
384
  json summary = createMapSummary(map, key, onlineResource.c_str());
×
385
  response.update(summary);
×
386

387
  if (format == OGCAPIFormat::JSON) {
×
388
    outputJson(response, OGCAPI_MIMETYPE_JSON, {});
×
389
  } else if (format == OGCAPIFormat::HTML) {
×
390
    createHTMLOutput(response, TEMPLATE_HTML_MAP_INDEX);
×
391
  } else {
392
    outputError(OGCAPI_PARAM_ERROR, "Unsupported format requested.");
×
393
  }
394

395
  return MS_SUCCESS;
396

397
#else
398
  msSetError(MS_OGCAPIERR, "OGC API server support is not enabled.",
399
             "msOGCAPIDispatchMapIndexRequest()");
400
  return MS_FAILURE;
401
#endif
402
}
403

404
/**
405
 * Return the MapServer landing page response
406
 */
407
int msOGCAPIDispatchIndexRequest(mapservObj *mapserv, configObj *config) {
1✔
408
#ifdef USE_OGCAPI_SVR
409
  if (!mapserv || !config)
1✔
410
    return -1; // Handle null pointers
411

412
  cgiRequestObj *request = mapserv->request;
1✔
413
  OGCAPIFormat format;
414
  format = msGetOutputFormat(request);
1✔
415

416
  std::string onlineResource;
417

418
  if (char *res = msBuildOnlineResource(NULL, request)) {
1✔
419
    onlineResource = res;
420
    free(res);
×
421

422
    // remove trailing '?' if present
423
    if (!onlineResource.empty() && onlineResource.back() == '?') {
×
424
      onlineResource.pop_back();
425
    }
426
  } else {
427
    // use a relative URL if no onlineresource cannot be created
428
    onlineResource = "./"; // fallback
429
  }
430

431
  const char *key = NULL;
432
  json links = json::array();
1✔
433

434
  while ((key = msNextKeyFromHashTable(&config->maps, key)) != NULL) {
9✔
435
    if (mapObj *map = getMapFromConfig(config, key)) {
8✔
436
      links.push_back(createMapSummary(map, key, onlineResource.c_str()));
7✔
437
      msFreeMap(map);
7✔
438
    } else {
439
      // there was a problem loading the map
440
      links.push_back(createMapError(key));
2✔
441
    }
442
  }
443

444
  json response = {{"linkset", links}};
4✔
445

446
  if (format == OGCAPIFormat::JSON) {
1✔
447
    outputJson(response, OGCAPI_MIMETYPE_JSON, {});
2✔
448
  } else if (format == OGCAPIFormat::HTML) {
×
449
    createHTMLOutput(response, TEMPLATE_HTML_INDEX);
×
450
  } else {
451
    outputError(OGCAPI_PARAM_ERROR, "Unsupported format requested.");
×
452
  }
453

454
  return MS_SUCCESS;
455

456
#else
457
  msSetError(MS_OGCAPIERR, "OGC API server support is not enabled.",
458
             "msOGCAPIDispatchIndexRequest()");
459
  return MS_FAILURE;
460
#endif
461
}
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc