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

geographika / mapserver / 17709373190

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

push

github

geographika
Add index templates

62086 of 149729 relevant lines covered (41.47%)

25036.08 hits per line

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

36.72
/src/maptemplate.c
1
/******************************************************************************
2
 * $id: maptemplate.c 7725 2008-06-21 15:56:58Z sdlime $
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Various template processing functions.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2008 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
#define NEED_IGNORE_RET_VAL
31

32
#include "maptemplate.h"
33
#include "maphash.h"
34
#include "mapserver.h"
35
#include "maptile.h"
36
#include "mapows.h"
37

38
#include "cpl_conv.h"
39

40
#include <sys/types.h>
41
#include <sys/stat.h>
42
#include <time.h>
43

44
#include <assert.h>
45
#include <ctype.h>
46

47
static inline void IGUR_sizet(size_t ignored) {
48
  (void)ignored;
49
} /* Ignore GCC Unused Result */
50
static inline void IGUR_voidp(void *ignored) {
51
  (void)ignored;
52
} /* Ignore GCC Unused Result */
53

54
static const char *const olUrl = "//mapserver.org/lib/10.5.0/ol-mapserver.js";
55
static const char *const olCssUrl =
56
    "//mapserver.org/lib/10.5.0/ol-mapserver.css";
57

58
static const char *const olTemplate =
59
    "<!DOCTYPE html>\n"
60
    "<html lang=\"en\">\n"
61
    "<head>\n"
62
    "    <meta charset=\"UTF-8\">\n"
63
    "    <meta name=\"viewport\" content=\"width=device-width, "
64
    "initial-scale=1.0\">\n"
65
    "    <title>MapServer Simple Viewer</title>\n"
66
    "    <link rel=\"stylesheet\" "
67
    "href=\"[openlayers_css_url]\">\n"
68
    "    <link rel=\"shortcut icon\" type=\"image/x-icon\" "
69
    "href=\"//mapserver.org/_static/mapserver.ico\" />\n"
70
    "    <style>\n"
71
    "        #map {\n"
72
    "            position: absolute;\n"
73
    "            top: 0;\n"
74
    "            left: 0;\n"
75
    "            width: 100%;\n"
76
    "            height: 100%;\n"
77
    "        }\n"
78
    "    </style>\n"
79
    "</head>\n"
80
    "<body>\n"
81
    "    <div id=\"map\"></div>\n"
82
    "    <script "
83
    "src=\"[openlayers_js_url]\"></script>\n"
84
    "    <script>\n"
85
    "        [openlayers_layer]\n"
86
    "        const map = new ol.Map({\n"
87
    "            layers: [mslayer],\n"
88
    "            target: 'map',\n"
89
    "            view: new ol.View()\n"
90
    "        });\n"
91
    "        map.getView().fit([[minx], [miny], [maxx], [maxy]], { size: "
92
    "map.getSize() });\n"
93
    "    </script>\n"
94
    "</body>\n"
95
    "</html>";
96

97
static const char *const olLayerMapServerTag =
98
    "const mslayer = new ol.layer.Image({\n"
99
    "            extent: [[minx], [miny], [maxx], [maxy]],\n"
100
    "            source: new ol.source.Image({\n"
101
    "                loader: ol.source.mapserver.createLoader({\n"
102
    "                    url: '[mapserv_onlineresource]',\n"
103
    "                    params: {\n"
104
    "                        'layers': '[layers]'\n"
105
    "                    }\n"
106
    "                })\n"
107
    "            })\n"
108
    "        });";
109

110
static const char *const olLayerWMSTag =
111
    "if (!ol.proj.get('[openlayers_projection]')) {\n"
112
    "            ol.proj.addProjection(new ol.proj.Projection({ code : "
113
    "'[openlayers_projection]' }));\n"
114
    "        }\n"
115
    "        const mslayer = new ol.layer.Image({\n"
116
    "            source: new ol.source.Image({\n"
117
    "                loader: ol.source.wms.createLoader({\n"
118
    "                    url: '[mapserv_onlineresource]',\n"
119
    "                    params: {\n"
120
    "                        LAYERS: '[LAYERS]',\n"
121
    "                        VERSION: '[VERSION]',\n"
122
    "                        FORMAT: 'image/png'\n"
123
    "                    },\n"
124
    "                    projection: '[openlayers_projection]',\n"
125
    "                    ratio: 1\n"
126
    "                })\n"
127
    "            })\n"
128
    "        });";
129

130
static char *processLine(mapservObj *mapserv, const char *instr, FILE *stream,
131
                         int mode);
132

133
static int isValidTemplate(FILE *stream, const char *filename) {
60✔
134
  char buffer[MS_BUFFER_LENGTH];
135

136
  if (fgets(buffer, MS_BUFFER_LENGTH, stream) != NULL) {
60✔
137
    if (!strcasestr(buffer, MS_TEMPLATE_MAGIC_STRING)) {
60✔
138
      msSetError(
×
139
          MS_WEBERR,
140
          "Missing magic string, %s doesn't look like a MapServer template.",
141
          "isValidTemplate()", filename);
142
      return MS_FALSE;
×
143
    }
144
  }
145

146
  return MS_TRUE;
147
}
148

149
/*
150
 * Redirect to (only use in CGI)
151
 *
152
 */
153
int msRedirect(const char *url) {
4✔
154
  msIO_setHeader("Status", "302 Found");
4✔
155
  msIO_setHeader("Uri", "%s", url);
4✔
156
  msIO_setHeader("Location", "%s", url);
4✔
157
  msIO_setHeader("Content-Type", "text/html");
4✔
158
  msIO_sendHeaders();
4✔
159
  return MS_SUCCESS;
4✔
160
}
161

162
/*
163
** Sets the map extent under a variety of scenarios.
164
*/
165
int setExtent(mapservObj *mapserv) {
81✔
166
  double cellx, celly, cellsize;
167

168
  if (mapserv->Mode == TILE) {
81✔
169
    if (MS_SUCCESS != msTileSetExtent(mapserv)) {
5✔
170
      return MS_FAILURE;
171
    }
172
  }
173
  switch (mapserv->CoordSource) {
81✔
174
  case FROMUSERBOX: /* user passed in a map extent */
175
    break;
176
  case FROMIMGBOX: /* fully interactive web, most likely with java front end */
×
177
    cellx = MS_CELLSIZE(mapserv->ImgExt.minx, mapserv->ImgExt.maxx,
×
178
                        mapserv->ImgCols);
179
    celly = MS_CELLSIZE(mapserv->ImgExt.miny, mapserv->ImgExt.maxy,
×
180
                        mapserv->ImgRows);
181
    mapserv->map->extent.minx =
×
182
        MS_IMAGE2MAP_X(mapserv->ImgBox.minx, mapserv->ImgExt.minx, cellx);
×
183
    mapserv->map->extent.maxx =
×
184
        MS_IMAGE2MAP_X(mapserv->ImgBox.maxx, mapserv->ImgExt.minx, cellx);
×
185
    mapserv->map->extent.maxy =
×
186
        MS_IMAGE2MAP_Y(mapserv->ImgBox.miny, mapserv->ImgExt.maxy,
×
187
                       celly); /* y's are flip flopped because img/map
188
                                  coordinate systems are */
189
    mapserv->map->extent.miny =
×
190
        MS_IMAGE2MAP_Y(mapserv->ImgBox.maxy, mapserv->ImgExt.maxy, celly);
×
191
    break;
×
192
  case FROMIMGPNT:
×
193
    cellx = MS_CELLSIZE(mapserv->ImgExt.minx, mapserv->ImgExt.maxx,
×
194
                        mapserv->ImgCols);
195
    celly = MS_CELLSIZE(mapserv->ImgExt.miny, mapserv->ImgExt.maxy,
×
196
                        mapserv->ImgRows);
197
    mapserv->mappnt.x =
×
198
        MS_IMAGE2MAP_X(mapserv->ImgPnt.x, mapserv->ImgExt.minx, cellx);
×
199
    mapserv->mappnt.y =
×
200
        MS_IMAGE2MAP_Y(mapserv->ImgPnt.y, mapserv->ImgExt.maxy, celly);
×
201

202
    mapserv->map->extent.minx =
×
203
        mapserv->mappnt.x -
×
204
        .5 * ((mapserv->ImgExt.maxx - mapserv->ImgExt.minx) /
×
205
              mapserv->fZoom); /* create an extent around that point */
×
206
    mapserv->map->extent.miny =
×
207
        mapserv->mappnt.y -
×
208
        .5 * ((mapserv->ImgExt.maxy - mapserv->ImgExt.miny) / mapserv->fZoom);
×
209
    mapserv->map->extent.maxx =
×
210
        mapserv->mappnt.x +
×
211
        .5 * ((mapserv->ImgExt.maxx - mapserv->ImgExt.minx) / mapserv->fZoom);
212
    mapserv->map->extent.maxy =
×
213
        mapserv->mappnt.y +
×
214
        .5 * ((mapserv->ImgExt.maxy - mapserv->ImgExt.miny) / mapserv->fZoom);
215
    break;
×
216
  case FROMREFPNT:
×
217
    cellx = MS_CELLSIZE(mapserv->map->reference.extent.minx,
×
218
                        mapserv->map->reference.extent.maxx,
219
                        mapserv->map->reference.width);
220
    celly = MS_CELLSIZE(mapserv->map->reference.extent.miny,
×
221
                        mapserv->map->reference.extent.maxy,
222
                        mapserv->map->reference.height);
223
    mapserv->mappnt.x = MS_IMAGE2MAP_X(
×
224
        mapserv->RefPnt.x, mapserv->map->reference.extent.minx, cellx);
225
    mapserv->mappnt.y = MS_IMAGE2MAP_Y(
×
226
        mapserv->RefPnt.y, mapserv->map->reference.extent.maxy, celly);
227

228
    mapserv->map->extent.minx =
×
229
        mapserv->mappnt.x -
×
230
        .5 * (mapserv->ImgExt.maxx -
×
231
              mapserv->ImgExt.minx); /* create an extent around that point */
×
232
    mapserv->map->extent.miny =
×
233
        mapserv->mappnt.y - .5 * (mapserv->ImgExt.maxy - mapserv->ImgExt.miny);
×
234
    mapserv->map->extent.maxx =
×
235
        mapserv->mappnt.x + .5 * (mapserv->ImgExt.maxx - mapserv->ImgExt.minx);
×
236
    mapserv->map->extent.maxy =
×
237
        mapserv->mappnt.y + .5 * (mapserv->ImgExt.maxy - mapserv->ImgExt.miny);
×
238
    break;
×
239
  case FROMBUF:
×
240
    mapserv->map->extent.minx =
×
241
        mapserv->mappnt.x -
×
242
        mapserv
243
            ->Buffer; /* create an extent around that point, using the buffer */
×
244
    mapserv->map->extent.miny = mapserv->mappnt.y - mapserv->Buffer;
×
245
    mapserv->map->extent.maxx = mapserv->mappnt.x + mapserv->Buffer;
×
246
    mapserv->map->extent.maxy = mapserv->mappnt.y + mapserv->Buffer;
×
247
    break;
×
248
  case FROMSCALE:
×
249
    cellsize =
×
250
        (mapserv->ScaleDenom / mapserv->map->resolution) /
×
251
        msInchesPerUnit(mapserv->map->units,
×
252
                        0); /* user supplied a point and a scale denominator */
253
    mapserv->map->extent.minx =
×
254
        mapserv->mappnt.x - cellsize * (mapserv->map->width - 1) / 2.0;
×
255
    mapserv->map->extent.miny =
×
256
        mapserv->mappnt.y - cellsize * (mapserv->map->height - 1) / 2.0;
×
257
    mapserv->map->extent.maxx =
×
258
        mapserv->mappnt.x + cellsize * (mapserv->map->width - 1) / 2.0;
×
259
    mapserv->map->extent.maxy =
×
260
        mapserv->mappnt.y + cellsize * (mapserv->map->height - 1) / 2.0;
×
261
    break;
×
262
  default: /* use the default in the mapfile if it exists */
66✔
263
    if ((mapserv->map->extent.minx == mapserv->map->extent.maxx) &&
66✔
264
        (mapserv->map->extent.miny == mapserv->map->extent.maxy)) {
×
265
      msSetError(MS_WEBERR, "No way to generate map extent.", "mapserv()");
×
266
      return MS_FAILURE;
×
267
    }
268
  }
269

270
  mapserv->RawExt = mapserv->map->extent; /* save unaltered extent */
81✔
271

272
  return MS_SUCCESS;
81✔
273
}
274

275
int checkWebScale(mapservObj *mapserv) {
93✔
276
  int status;
277
  rectObj work_extent = mapserv->map->extent;
93✔
278

279
  mapserv->map->cellsize = msAdjustExtent(
93✔
280
      &(work_extent), mapserv->map->width,
281
      mapserv->map->height); /* we do this cause we need a scale */
282
  if ((status = msCalculateScale(work_extent, mapserv->map->units,
93✔
283
                                 mapserv->map->width, mapserv->map->height,
284
                                 mapserv->map->resolution,
285
                                 &mapserv->map->scaledenom)) != MS_SUCCESS)
93✔
286
    return status;
287

288
  if ((mapserv->map->scaledenom < mapserv->map->web.minscaledenom) &&
90✔
289
      (mapserv->map->web.minscaledenom > 0)) {
290
    if (mapserv->map->web.mintemplate) { /* use the template provided */
×
291
      if (TEMPLATE_TYPE(mapserv->map->web.mintemplate) == MS_FILE) {
×
292
        if ((status = msReturnPage(mapserv, mapserv->map->web.mintemplate,
×
293
                                   BROWSE, NULL)) != MS_SUCCESS)
294
          return status;
295
      } else {
296
        if ((status = msReturnURL(mapserv, mapserv->map->web.mintemplate,
×
297
                                  BROWSE)) != MS_SUCCESS)
298
          return status;
299
      }
300
    } else { /* force zoom = 1 (i.e. pan) */
301
      mapserv->fZoom = mapserv->Zoom = 1;
×
302
      mapserv->ZoomDirection = 0;
×
303
      mapserv->CoordSource = FROMSCALE;
×
304
      mapserv->ScaleDenom = mapserv->map->web.minscaledenom;
×
305
      mapserv->mappnt.x =
×
306
          (mapserv->map->extent.maxx + mapserv->map->extent.minx) /
×
307
          2; /* use center of bad extent */
308
      mapserv->mappnt.y =
×
309
          (mapserv->map->extent.maxy + mapserv->map->extent.miny) / 2;
×
310
      setExtent(mapserv);
×
311
      mapserv->map->cellsize = msAdjustExtent(
×
312
          &(mapserv->map->extent), mapserv->map->width, mapserv->map->height);
×
313
      if ((status = msCalculateScale(mapserv->map->extent, mapserv->map->units,
×
314
                                     mapserv->map->width, mapserv->map->height,
315
                                     mapserv->map->resolution,
316
                                     &mapserv->map->scaledenom)) != MS_SUCCESS)
×
317
        return status;
318
    }
319
  } else {
320
    if ((mapserv->map->scaledenom > mapserv->map->web.maxscaledenom) &&
90✔
321
        (mapserv->map->web.maxscaledenom > 0)) {
322
      if (mapserv->map->web.maxtemplate) { /* use the template provided */
×
323
        if (TEMPLATE_TYPE(mapserv->map->web.maxtemplate) == MS_FILE) {
×
324
          if ((status = msReturnPage(mapserv, mapserv->map->web.maxtemplate,
×
325
                                     BROWSE, NULL)) != MS_SUCCESS)
326
            return status;
327
        } else {
328
          if ((status = msReturnURL(mapserv, mapserv->map->web.maxtemplate,
×
329
                                    BROWSE)) != MS_SUCCESS)
330
            return status;
331
        }
332
      } else { /* force zoom = 1 (i.e. pan) */
333
        mapserv->fZoom = mapserv->Zoom = 1;
×
334
        mapserv->ZoomDirection = 0;
×
335
        mapserv->CoordSource = FROMSCALE;
×
336
        mapserv->ScaleDenom = mapserv->map->web.maxscaledenom;
×
337
        mapserv->mappnt.x =
×
338
            (mapserv->map->extent.maxx + mapserv->map->extent.minx) /
×
339
            2; /* use center of bad extent */
340
        mapserv->mappnt.y =
×
341
            (mapserv->map->extent.maxy + mapserv->map->extent.miny) / 2;
×
342
        setExtent(mapserv);
×
343
        mapserv->map->cellsize = msAdjustExtent(
×
344
            &(mapserv->map->extent), mapserv->map->width, mapserv->map->height);
×
345
        if ((status = msCalculateScale(
×
346
                 mapserv->map->extent, mapserv->map->units, mapserv->map->width,
×
347
                 mapserv->map->height, mapserv->map->resolution,
348
                 &mapserv->map->scaledenom)) != MS_SUCCESS)
×
349
          return status;
350
      }
351
    }
352
  }
353

354
  return MS_SUCCESS;
355
}
356

357
int msReturnTemplateQuery(mapservObj *mapserv, char *queryFormat,
107✔
358
                          char **papszBuffer) {
359
  imageObj *img = NULL;
360
  int i, status;
361

362
  outputFormatObj *outputFormat = NULL;
363
  mapObj *map = mapserv->map;
107✔
364

365
  if (!queryFormat) {
107✔
366
    msSetError(MS_WEBERR, "Return format/mime-type not specified.",
×
367
               "msReturnTemplateQuery()");
368
    return MS_FAILURE;
×
369
  }
370

371
  msApplyDefaultOutputFormats(map);
107✔
372

373
  i = msGetOutputFormatIndex(
107✔
374
      map, queryFormat); /* queryFormat can be a mime-type or name */
375
  if (i >= 0)
107✔
376
    outputFormat = map->outputformatlist[i];
104✔
377

378
  if (outputFormat) {
104✔
379
    if (MS_RENDERER_PLUGIN(outputFormat)) {
104✔
380
      msInitializeRendererVTable(outputFormat);
11✔
381
    }
382
    if (MS_RENDERER_OGR(outputFormat)) {
104✔
383
      checkWebScale(mapserv);
44✔
384

385
      status = msOGRWriteFromQuery(map, outputFormat, mapserv->sendheaders);
44✔
386

387
      return status;
44✔
388
    }
389

390
    if (!MS_RENDERER_TEMPLATE(outputFormat)) { /* got an image format, return
60✔
391
                                                  the query results that way */
392
      outputFormatObj *tempOutputFormat = map->outputformat; /* save format */
11✔
393

394
      checkWebScale(mapserv);
11✔
395

396
      map->outputformat =
11✔
397
          outputFormat; /* override what was given for IMAGETYPE */
398
      img = msDrawMap(map, MS_TRUE);
11✔
399
      if (!img)
11✔
400
        return MS_FAILURE;
401
      map->outputformat = tempOutputFormat; /* restore format */
11✔
402

403
      if (mapserv->sendheaders) {
11✔
404
        msIO_setHeader("Content-Type", "%s", MS_IMAGE_MIME_TYPE(outputFormat));
22✔
405
        msIO_sendHeaders();
11✔
406
      }
407
      status = msSaveImage(map, img, NULL);
11✔
408
      msFreeImage(img);
11✔
409

410
      return status;
11✔
411
    }
412
  }
413

414
  /*
415
  ** At this point we know we have a template of some sort, either the new style
416
  *that references a or the old
417
  ** style made up of external files slammed together. Either way we may have to
418
  *compute a query map and other
419
  ** images. We only create support images IF the querymap has status=MS_ON.
420
  */
421
  if (map->querymap.status) {
52✔
422
    checkWebScale(mapserv);
2✔
423
    if (msGenerateImages(mapserv, MS_TRUE, MS_TRUE) != MS_SUCCESS)
2✔
424
      return MS_FAILURE;
425
  }
426

427
  if (outputFormat) {
52✔
428
    const char *file = msGetOutputFormatOption(outputFormat, "FILE", NULL);
49✔
429
    if (!file) {
49✔
430
      msSetError(MS_WEBERR,
×
431
                 "Template driver requires \"FILE\" format option be set.",
432
                 "msReturnTemplateQuery()");
433
      return MS_FAILURE;
×
434
    }
435

436
    if (mapserv->sendheaders) {
49✔
437
      const char *attachment =
438
          msGetOutputFormatOption(outputFormat, "ATTACHMENT", NULL);
49✔
439
      if (attachment)
49✔
440
        msIO_setHeader("Content-disposition", "attachment; filename=%s",
×
441
                       attachment);
442
      msIO_setHeader("Content-Type", "%s", outputFormat->mimetype);
49✔
443
      msIO_sendHeaders();
49✔
444
    }
445
    if ((status = msReturnPage(mapserv, (char *)file, BROWSE, papszBuffer)) !=
49✔
446
        MS_SUCCESS)
447
      return status;
448
  } else {
449
    if ((status = msReturnNestedTemplateQuery(mapserv, queryFormat,
3✔
450
                                              papszBuffer)) != MS_SUCCESS)
451
      return status;
452
  }
453

454
  return MS_SUCCESS;
455
}
456

457
/*
458
** Is a particular layer or group on, that is was it requested explicitly by the
459
*user.
460
*/
461
int isOn(mapservObj *mapserv, char *name, char *group) {
1,493✔
462
  int i;
463

464
  for (i = 0; i < mapserv->NumLayers; i++) {
1,964✔
465
    if (name && strcmp(mapserv->Layers[i], name) == 0)
1,016✔
466
      return (MS_TRUE);
467
    if (group && strcmp(mapserv->Layers[i], group) == 0)
471✔
468
      return (MS_TRUE);
469
  }
470

471
  return (MS_FALSE);
472
}
473

474
/************************************************************************/
475
/*            int sortLayerByOrder(mapObj *map, char* pszOrder)         */
476
/*                                                                      */
477
/*      sorth the displaying in ascending or descending order.          */
478
/************************************************************************/
479
int sortLayerByOrder(mapObj *map, const char *pszOrder) {
×
480
  int *panCurrentOrder = NULL;
481

482
  if (!map) {
×
483
    msSetError(MS_WEBERR, "Invalid pointer.", "sortLayerByOrder()");
×
484
    return MS_FAILURE;
×
485
  }
486
  /* ==================================================================== */
487
  /*      The flag "ascending" is in fact not useful since the            */
488
  /*      default ordering is ascending.                                  */
489
  /* ==================================================================== */
490

491
  /* -------------------------------------------------------------------- */
492
  /*      the map->layerorder should be set at this point in the          */
493
  /*      sortLayerByMetadata.                                            */
494
  /* -------------------------------------------------------------------- */
495
  if (map->layerorder) {
×
496
    panCurrentOrder = (int *)msSmallMalloc(map->numlayers * sizeof(int));
×
497
    for (int i = 0; i < map->numlayers; i++)
×
498
      panCurrentOrder[i] = map->layerorder[i];
×
499

500
    if (strcasecmp(pszOrder, "DESCENDING") == 0) {
×
501
      for (int i = 0; i < map->numlayers; i++)
×
502
        map->layerorder[i] = panCurrentOrder[map->numlayers - 1 - i];
×
503
    }
504

505
    free(panCurrentOrder);
×
506
  }
507

508
  return MS_SUCCESS;
509
}
510

511
/*!
512
 * This function set the map->layerorder
513
 * index order by the metadata column name
514
 */
515
static int sortLayerByMetadata(mapObj *map, const char *pszMetadata) {
×
516
  int nLegendOrder1;
517
  int nLegendOrder2;
518
  int i, j;
519
  int tmp;
520

521
  if (!map) {
×
522
    msSetError(MS_WEBERR, "Invalid pointer.", "sortLayerByMetadata()");
×
523
    return MS_FAILURE;
×
524
  }
525

526
  /*
527
   * Initiate to default order (Reverse mapfile order)
528
   */
529
  if (map->layerorder) {
×
530
    int *pnLayerOrder;
531

532
    /* Backup the original layer order to be able to reverse it */
533
    pnLayerOrder = (int *)msSmallMalloc(map->numlayers * sizeof(int));
×
534
    for (i = 0; i < map->numlayers; i++)
×
535
      pnLayerOrder[i] = map->layerorder[i];
×
536

537
    /* Get a new layerorder array */
538
    free(map->layerorder);
×
539
    map->layerorder = (int *)msSmallMalloc(map->numlayers * sizeof(int));
×
540

541
    /* Reverse the layerorder array */
542
    for (i = 0; i < map->numlayers; i++)
×
543
      map->layerorder[i] = pnLayerOrder[map->numlayers - i - 1];
×
544

545
    free(pnLayerOrder);
×
546
  } else {
547
    map->layerorder = (int *)msSmallMalloc(map->numlayers * sizeof(int));
×
548

549
    for (i = 0; i < map->numlayers; i++)
×
550
      map->layerorder[i] = map->numlayers - i - 1;
×
551
  }
552

553
  if (!pszMetadata)
×
554
    return MS_SUCCESS;
555

556
  /*
557
   * Bubble sort algo (not very efficient)
558
   * should implement a kind of quick sort
559
   * algo instead
560
   */
561
  for (i = 0; i < map->numlayers - 1; i++) {
×
562
    for (j = 0; j < map->numlayers - 1 - i; j++) {
×
563
      const char *pszLegendOrder1 = msLookupHashTable(
×
564
          &(GET_LAYER(map, map->layerorder[j + 1])->metadata), pszMetadata);
×
565
      const char *pszLegendOrder2 = msLookupHashTable(
×
566
          &(GET_LAYER(map, map->layerorder[j])->metadata), pszMetadata);
×
567

568
      if (!pszLegendOrder1 || !pszLegendOrder2)
×
569
        continue;
×
570

571
      nLegendOrder1 = atoi(pszLegendOrder1);
572
      nLegendOrder2 = atoi(pszLegendOrder2);
573

574
      if (nLegendOrder1 < nLegendOrder2) { /* compare the two neighbors */
×
575
        tmp = map->layerorder[j];          /* swap a[j] and a[j+1]      */
×
576
        map->layerorder[j] = map->layerorder[j + 1];
×
577
        map->layerorder[j + 1] = tmp;
×
578
      }
579
    }
580
  }
581

582
  return MS_SUCCESS;
583
}
584

585
/*
586
** This function return a pointer
587
** at the beginning of the first occurrence
588
** of pszTag in pszInstr.
589
**
590
** Tag can be [TAG] or [TAG something]
591
*/
592
static const char *findTag(const char *pszInstr, const char *pszTag) {
5,968✔
593
  const char *pszStart = NULL;
594
  int done = MS_FALSE;
595

596
  if (!pszInstr || !pszTag) {
5,968✔
597
    msSetError(MS_WEBERR, "Invalid pointer.", "findTag()");
×
598
    return NULL;
×
599
  }
600

601
  const int length =
5,968✔
602
      strlen(pszTag) + 1; /* adding [ character to the beginning */
5,968✔
603
  char *pszTag1 = (char *)msSmallMalloc(length + 1);
5,968✔
604

605
  strcpy(pszTag1, "[");
606
  strcat(pszTag1, pszTag);
607

608
  const char *pszTemp = pszInstr;
609
  do {
610
    pszStart = strstr(pszTemp, pszTag1);
5,968✔
611

612
    if (pszStart == NULL)
5,968✔
613
      done = MS_TRUE; /* tag not found */
614
    else if ((*(pszStart + length) == ']' || *(pszStart + length) == ' '))
1,205✔
615
      done = MS_TRUE; /* valid tag */
616
    else
617
      pszTemp += length; /* skip ahead and start over */
×
618
  } while (!done);
5,968✔
619

620
  free(pszTag1);
5,968✔
621

622
  return pszStart;
5,968✔
623
}
624

625
/*
626
** This function return a pointer
627
** to the end of the tag in pszTag
628
**
629
** The end of a tag is the next
630
** non-quoted ']' character.
631
** Return NULL if not found.
632
*/
633

634
char *findTagEnd(const char *pszTag) {
866✔
635
  char *pszEnd = NULL, *pszTmp = (char *)pszTag;
636

637
  while (pszTmp != NULL) {
11,996✔
638
    if (*pszTmp == '"')
11,996✔
639
      pszTmp = strchr(pszTmp + 1, '"');
818✔
640
    if ((pszTmp == NULL) || (*pszTmp == ']')) {
11,996✔
641
      pszEnd = pszTmp;
642
      pszTmp = NULL;
643
    } else
644
      pszTmp++;
11,130✔
645
  }
646

647
  return pszEnd;
866✔
648
}
649

650
/*
651
** Return a hashtableobj from instr of all
652
** arguments. hashtable must be freed by caller.
653
*/
654
static int getTagArgs(const char *pszTag, const char *pszInstr,
518✔
655
                      hashTableObj **ppoHashTable) {
656
  const char *pszStart, *pszEnd;
657
  int nLength;
658
  char **papszArgs, **papszVarVal;
659
  int nArgs, nDummy;
660
  int i;
661

662
  if (!pszTag || !pszInstr) {
518✔
663
    msSetError(MS_WEBERR, "Invalid pointer.", "getTagArgs()");
×
664
    return MS_FAILURE;
×
665
  }
666

667
  /* set position to the beginning of tag */
668
  pszStart = findTag(pszInstr, pszTag);
518✔
669

670
  if (pszStart) {
518✔
671
    /* find ending position */
672
    pszEnd = findTagEnd(pszStart);
518✔
673

674
    if (pszEnd) {
518✔
675
      /* skip the tag name */
676
      pszStart = pszStart + strlen(pszTag) + 1;
518✔
677

678
      /* get length of all args */
679
      nLength = pszEnd - pszStart;
518✔
680

681
      if (nLength > 0) { /* is there arguments ? */
518✔
682
        char *pszArgs = (char *)msSmallMalloc(nLength + 1);
447✔
683
        strlcpy(pszArgs, pszStart, nLength + 1);
684

685
        if (!(*ppoHashTable))
447✔
686
          *ppoHashTable = msCreateHashTable();
447✔
687

688
        /* put all arguments separate by space in a hash table */
689
        papszArgs = msStringTokenize(pszArgs, " ", &nArgs, MS_TRUE);
447✔
690

691
        /* msReturnTemplateQuerycheck all argument if they have values */
692
        for (i = 0; i < nArgs; i++) {
1,360✔
693
          if (strlen(papszArgs[i]) == 0) {
913✔
694
            free(papszArgs[i]);
447✔
695
            continue;
447✔
696
          }
697

698
          if (strchr(papszArgs[i], '=')) {
466✔
699
            papszVarVal =
700
                msStringTokenize(papszArgs[i], "=", &nDummy, MS_FALSE);
466✔
701
            msInsertHashTable(*ppoHashTable, papszVarVal[0], papszVarVal[1]);
466✔
702
            free(papszVarVal[0]);
466✔
703
            free(papszVarVal[1]);
466✔
704
            free(papszVarVal);
466✔
705
          } else /* no value specified. set it to 1 */
706
            msInsertHashTable(*ppoHashTable, papszArgs[i], "1");
×
707

708
          free(papszArgs[i]);
466✔
709
        }
710
        free(papszArgs);
447✔
711
        free(pszArgs);
447✔
712
      }
713
    }
714
  }
715

716
  return MS_SUCCESS;
717
}
718

719
/*
720
** Return a substring from instr between [tag] and [/tag]
721
** char * returned must be freed by caller.
722
** pszNextInstr will be a pointer at the end of the
723
** first occurrence found.
724
*/
725
static int getInlineTag(const char *pszTag, const char *pszInstr,
169✔
726
                        char **pszResult) {
727
  const char *pszEnd = NULL;
728
  int nInst = 0;
729
  int nLength;
730

731
  *pszResult = NULL;
169✔
732

733
  if (!pszInstr || !pszTag) {
169✔
734
    msSetError(MS_WEBERR, "Invalid pointer.", "getInlineTag()");
×
735
    return MS_FAILURE;
×
736
  }
737

738
  char *pszEndTag = (char *)msSmallMalloc(strlen(pszTag) + 3);
169✔
739
  strcpy(pszEndTag, "[/");
740
  strcat(pszEndTag, pszTag);
741

742
  /* find start tag */
743
  const char *pszPatIn = findTag(pszInstr, pszTag);
169✔
744
  const char *pszPatOut = strstr(pszInstr, pszEndTag);
169✔
745

746
  const char *pszStart = pszPatIn;
747

748
  const char *pszTmp = pszInstr;
749

750
  if (pszPatIn) {
169✔
751
    do {
752
      if (pszPatIn && pszPatIn < pszPatOut) {
338✔
753
        nInst++;
169✔
754

755
        pszTmp = pszPatIn;
756
      }
757

758
      if (pszPatOut && ((pszPatIn == NULL) || pszPatOut < pszPatIn)) {
338✔
759
        pszEnd = pszPatOut;
760
        nInst--;
169✔
761

762
        pszTmp = pszPatOut;
763
      }
764

765
      pszPatIn = findTag(pszTmp + 1, pszTag);
338✔
766
      pszPatOut = strstr(pszTmp + 1, pszEndTag);
338✔
767

768
    } while (pszTmp != NULL && nInst > 0);
338✔
769
  }
770

771
  if (pszStart && pszEnd) {
169✔
772
    /* find end of start tag */
773
    pszStart = strchr(pszStart, ']');
169✔
774

775
    if (pszStart) {
169✔
776
      pszStart++;
169✔
777

778
      nLength = pszEnd - pszStart;
169✔
779

780
      if (nLength > 0) {
169✔
781
        *pszResult = (char *)msSmallMalloc(nLength + 1);
169✔
782

783
        /* copy string between start and end tag */
784
        strlcpy(*pszResult, pszStart, nLength + 1);
785

786
        (*pszResult)[nLength] = '\0';
169✔
787
      }
788
    } else {
789
      msSetError(MS_WEBERR, "Malformed [%s] tag.", "getInlineTag()", pszTag);
×
790
      return MS_FAILURE;
×
791
    }
792
  }
793

794
  msFree(pszEndTag);
169✔
795

796
  return MS_SUCCESS;
169✔
797
}
798

799
/*!
800
 * this function process all if tag in pszInstr.
801
 * this function return a modified pszInstr.
802
 * ht mus contain all variables needed by the function
803
 * to interpret if expression.
804
 *
805
 * If bLastPass is true then all tests for 'null' values will be
806
 * considered true if the corresponding value is not set.
807
 */
808
int processIfTag(char **pszInstr, hashTableObj *ht, int bLastPass) {
×
809
  /*   char *pszNextInstr = pszInstr; */
810
  const char *pszEnd = NULL;
811

812
  if (!*pszInstr) {
×
813
    msSetError(MS_WEBERR, "Invalid pointer.", "processIfTag()");
×
814
    return MS_FAILURE;
×
815
  }
816

817
  /* find the if start tag */
818

819
  const char *pszStart = findTag(*pszInstr, "if");
×
820

821
  while (pszStart) {
×
822
    const char *pszPatIn = findTag(pszStart, "if");
×
823
    const char *pszPatOut = strstr(pszStart, "[/if]");
×
824
    const char *pszTmp = pszPatIn;
825

826
    int nInst = 0;
827
    do {
828
      if (pszPatIn && pszPatIn < pszPatOut) {
×
829
        nInst++;
×
830
        pszTmp = pszPatIn;
831
      }
832

833
      if (pszPatOut && ((pszPatIn == NULL) || pszPatOut < pszPatIn)) {
×
834
        pszEnd = pszPatOut;
835
        nInst--;
×
836
        pszTmp = pszPatOut;
837
      }
838

839
      pszPatIn = findTag(pszTmp + 1, "if");
×
840
      pszPatOut = strstr(pszTmp + 1, "[/if]");
×
841

842
    } while (nInst > 0);
×
843

844
    /* get the then string (if expression is true) */
845
    char *pszThen = NULL;
×
846
    if (getInlineTag("if", pszStart, &pszThen) != MS_SUCCESS) {
×
847
      msSetError(MS_WEBERR, "Malformed then if tag.", "processIfTag()");
×
848
      return MS_FAILURE;
×
849
    }
850

851
    /* retrieve if tag args */
852
    hashTableObj *ifArgs = NULL;
×
853
    if (getTagArgs("if", pszStart, &ifArgs) != MS_SUCCESS) {
×
854
      msSetError(MS_WEBERR, "Malformed args if tag.", "processIfTag()");
×
855
      return MS_FAILURE;
×
856
    }
857

858
    const char *pszName = msLookupHashTable(ifArgs, "name");
×
859
    const char *pszValue = msLookupHashTable(ifArgs, "value");
×
860
    const char *pszOperator = msLookupHashTable(ifArgs, "oper");
×
861
    if (pszOperator == NULL) /* Default operator if not set is "eq" */
×
862
      pszOperator = "eq";
863

864
    int bEmpty = 0;
865

866
    if (pszName) {
×
867
      /* build the complete if tag ([if all_args]then string[/if]) */
868
      /* to replace if by then string if expression is true */
869
      /* or by a white space if not. */
870
      const int nLength = pszEnd - pszStart;
×
871
      char *pszIfTag = (char *)msSmallMalloc(nLength + 6);
×
872
      strlcpy(pszIfTag, pszStart, nLength + 1);
×
873
      pszIfTag[nLength] = '\0';
×
874
      strcat(pszIfTag, "[/if]");
875

876
      const char *pszHTValue = msLookupHashTable(ht, pszName);
×
877

878
      if (strcmp(pszOperator, "neq") == 0) {
×
879
        if (pszValue && pszHTValue && strcasecmp(pszValue, pszHTValue) != 0) {
×
880
          *pszInstr = msReplaceSubstring(*pszInstr, pszIfTag, pszThen);
×
881
        } else if (pszHTValue) {
×
882
          *pszInstr = msReplaceSubstring(*pszInstr, pszIfTag, "");
×
883
          bEmpty = 1;
884
        }
885
      } else if (strcmp(pszOperator, "eq") == 0) {
×
886
        if (pszValue && pszHTValue && strcasecmp(pszValue, pszHTValue) == 0) {
×
887
          *pszInstr = msReplaceSubstring(*pszInstr, pszIfTag, pszThen);
×
888
        } else if (pszHTValue) {
×
889
          *pszInstr = msReplaceSubstring(*pszInstr, pszIfTag, "");
×
890
          bEmpty = 1;
891
        }
892
      } else if (strcmp(pszOperator, "isnull") == 0) {
×
893
        if (pszHTValue != NULL) {
×
894
          /* We met a non-null value... condition is false */
895
          *pszInstr = msReplaceSubstring(*pszInstr, pszIfTag, "");
×
896
          bEmpty = 1;
897
        } else if (bLastPass) {
×
898
          /* On last pass, if value is still null then condition is true */
899
          *pszInstr = msReplaceSubstring(*pszInstr, pszIfTag, pszThen);
×
900
        }
901
      } else if (strcmp(pszOperator, "isset") == 0) {
×
902
        if (pszHTValue != NULL) {
×
903
          /* Found a non-null value... condition is true */
904
          *pszInstr = msReplaceSubstring(*pszInstr, pszIfTag, pszThen);
×
905
        } else if (bLastPass) {
×
906
          /* On last pass, if value still not set then condition is false */
907
          *pszInstr = msReplaceSubstring(*pszInstr, pszIfTag, "");
×
908
          bEmpty = 1;
909
        }
910
      } else {
911
        msSetError(MS_WEBERR, "Unsupported operator (%s) in if tag.",
×
912
                   "processIfTag()", pszOperator);
913
        free(pszIfTag);
×
914
        free(pszThen);
×
915
        msFreeHashTable(ifArgs);
×
916
        return MS_FAILURE;
×
917
      }
918

919
      free(pszIfTag);
×
920
    }
921

922
    free(pszThen);
×
923

924
    msFreeHashTable(ifArgs);
×
925

926
    /* find the if start tag */
927
    if (bEmpty)
×
928
      pszStart = findTag(pszStart, "if");
×
929
    else
930
      pszStart = findTag(pszStart + 1, "if");
×
931
  }
932

933
  return MS_SUCCESS;
934
}
935

936
/* Helper function to return the text before the supplied string2 in string1. */
937
static char *getPreTagText(const char *string1, const char *string2) {
169✔
938
  int n;
939
  char *result, *tmpstr;
940

941
  if ((tmpstr = strstr(string1, string2)) == NULL)
169✔
942
    return msStrdup(""); /* return an empty string */
×
943

944
  n = strlen(string1) - strlen(tmpstr);
169✔
945
  result = (char *)msSmallMalloc(n + 1);
169✔
946
  strlcpy(result, string1, n + 1);
947

948
  return result;
949
}
950

951
/* Helper function to return the text after the supplied string2 in string1. */
952
static char *getPostTagText(const char *string1, const char *string2) {
169✔
953
  char *tmpstr;
954

955
  if ((tmpstr = strstr(string1, string2)) == NULL)
169✔
956
    return msStrdup(""); /* return an empty string */
×
957

958
  tmpstr += strlen(string2); /* skip string2 */
169✔
959
  return msStrdup(tmpstr);
169✔
960
}
961

962
/*
963
** Function to process a [feature ...] tag. This tag can *only* be found within
964
** a [resultset ...][/resultset] block.
965
*/
966
static int processFeatureTag(mapservObj *mapserv, char **line,
70✔
967
                             layerObj *layer) {
968
  char *preTag, *postTag; /* text before and after the tag */
969

970
  const char *argValue;
971
  char *tag, *tagInstance;
972
  hashTableObj *tagArgs = NULL;
70✔
973

974
  int limit = -1;
975
  const char *trimLast = NULL;
976

977
  int i, j, status;
978

979
  if (!*line) {
70✔
980
    msSetError(MS_WEBERR, "Invalid line pointer.", "processFeatureTag()");
×
981
    return (MS_FAILURE);
×
982
  }
983

984
  const char *tagStart = findTag(*line, "feature");
70✔
985
  if (!tagStart)
70✔
986
    return (MS_SUCCESS); /* OK, just return; */
987

988
  /* check for any tag arguments */
989
  if (getTagArgs("feature", tagStart, &tagArgs) != MS_SUCCESS)
70✔
990
    return (MS_FAILURE);
991
  if (tagArgs) {
70✔
992
    argValue = msLookupHashTable(tagArgs, "limit");
4✔
993
    if (argValue)
4✔
994
      limit = atoi(argValue);
995

996
    argValue = msLookupHashTable(tagArgs, "trimlast");
4✔
997
    if (argValue)
998
      trimLast = argValue;
999
  }
1000

1001
  if (strstr(*line, "[/feature]") ==
70✔
1002
      NULL) { /* we know the closing tag must be here, if not throw an error */
1003
    msSetError(MS_WEBERR, "[feature] tag found without closing [/feature].",
×
1004
               "processFeatureTag()");
1005
    msFreeHashTable(tagArgs);
×
1006
    return (MS_FAILURE);
×
1007
  }
1008

1009
  if (getInlineTag("feature", *line, &tag) != MS_SUCCESS) {
70✔
1010
    msSetError(MS_WEBERR, "Malformed feature tag.", "processFeatureTag()");
×
1011
    msFreeHashTable(tagArgs);
×
1012
    return MS_FAILURE;
×
1013
  }
1014

1015
  preTag = getPreTagText(*line, "[feature");
70✔
1016
  postTag = getPostTagText(*line, "[/feature]");
70✔
1017

1018
  /* start rebuilding **line */
1019
  free(*line);
70✔
1020
  *line = preTag;
70✔
1021

1022
  /* we know the layer has query results or we wouldn't be in this code */
1023

1024
#if 0
1025
  status = msLayerOpen(layer); /* open the layer */
1026
  if(status != MS_SUCCESS) return status;
1027
  status = msLayerGetItems(layer); /* retrieve all the item names */
1028
  if(status != MS_SUCCESS) return status;
1029
#endif
1030

1031
  if (layer->numjoins > 0) { /* initialize necessary JOINs here */
70✔
1032
    for (j = 0; j < layer->numjoins; j++) {
×
1033
      status = msJoinConnect(layer, &(layer->joins[j]));
×
1034
      if (status != MS_SUCCESS) {
×
1035
        msFreeHashTable(tagArgs);
×
1036
        msFree(postTag);
×
1037
        msFree(tag);
×
1038
        return status;
×
1039
      }
1040
    }
1041
  }
1042

1043
  mapserv->LRN = 1; /* layer result counter */
70✔
1044
  mapserv->resultlayer = layer;
70✔
1045
  msInitShape(&(mapserv->resultshape));
70✔
1046

1047
  if (limit == -1) /* return all */
70✔
1048
    limit = layer->resultcache->numresults;
70✔
1049
  else
1050
    limit = MS_MIN(limit, layer->resultcache->numresults);
×
1051

1052
  for (i = 0; i < limit; i++) {
357✔
1053
    status = msLayerGetShape(layer, &(mapserv->resultshape),
287✔
1054
                             &(layer->resultcache->results[i]));
287✔
1055
    if (status != MS_SUCCESS) {
287✔
1056
      msFreeHashTable(tagArgs);
×
1057
      msFree(postTag);
×
1058
      msFree(tag);
×
1059
      return status;
×
1060
    }
1061

1062
    mapserv->resultshape.classindex =
287✔
1063
        msShapeGetClass(layer, layer->map, &mapserv->resultshape, NULL, -1);
287✔
1064

1065
    /* prepare any necessary JOINs here (one-to-one only) */
1066
    if (layer->numjoins > 0) {
287✔
1067
      for (j = 0; j < layer->numjoins; j++) {
×
1068
        if (layer->joins[j].type == MS_JOIN_ONE_TO_ONE) {
×
1069
          msJoinPrepare(&(layer->joins[j]), &(mapserv->resultshape));
×
1070
          msJoinNext(&(layer->joins[j])); /* fetch the first row */
×
1071
        }
1072
      }
1073
    }
1074

1075
    /*
1076
    ** if necessary trim a few characters off the end of the tag
1077
    */
1078
    if (trimLast && (i == limit - 1)) {
287✔
1079
      char *ptr;
1080
      if ((ptr = strrstr(tag, trimLast)) != NULL)
4✔
1081
        *ptr = '\0';
4✔
1082
    }
1083

1084
    /* process the tag */
1085
    tagInstance = processLine(mapserv, tag, NULL, QUERY); /* do substitutions */
287✔
1086
    *line = msStringConcatenate(*line, tagInstance);      /* grow the line */
287✔
1087

1088
    free(tagInstance);
287✔
1089
    msFreeShape(&(mapserv->resultshape)); /* init too */
287✔
1090

1091
    mapserv->RN++; /* increment counters */
287✔
1092
    mapserv->LRN++;
287✔
1093
  }
1094

1095
  /* msLayerClose(layer); */
1096
  mapserv->resultlayer = NULL; /* necessary? */
70✔
1097

1098
  *line = msStringConcatenate(*line, postTag);
70✔
1099

1100
  /*
1101
  ** clean up
1102
  */
1103
  free(postTag);
70✔
1104
  free(tag);
70✔
1105
  msFreeHashTable(tagArgs);
70✔
1106

1107
  return (MS_SUCCESS);
70✔
1108
}
1109

1110
/*
1111
** Function process a [include src="..."] tag.
1112
**
1113
** TODO's:
1114
**   - allow URLs
1115
*/
1116
static int processIncludeTag(mapservObj *mapserv, char **line, FILE *stream,
540✔
1117
                             int mode) {
1118
  char *tag, *tagEnd;
1119
  hashTableObj *tagArgs = NULL;
540✔
1120
  int tagOffset, tagLength;
1121

1122
  char *content = NULL, *processedContent = NULL;
1123
  const char *src = NULL;
1124

1125
  FILE *includeStream;
1126
  char buffer[MS_BUFFER_LENGTH], path[MS_MAXPATHLEN];
1127

1128
  if (!*line) {
540✔
1129
    msSetError(MS_WEBERR, "Invalid line pointer.", "processIncludeTag()");
×
1130
    return (MS_FAILURE);
×
1131
  }
1132

1133
  const char *tagStart = findTag(*line, "include");
540✔
1134

1135
  /* It is OK to have no include tags, just return. */
1136
  if (!tagStart)
540✔
1137
    return MS_SUCCESS;
1138

1139
  while (tagStart) {
5✔
1140
    tagOffset = tagStart - *line;
3✔
1141

1142
    /* check for any tag arguments */
1143
    if (getTagArgs("include", tagStart, &tagArgs) != MS_SUCCESS) {
3✔
1144
      return (MS_FAILURE);
1145
    }
1146
    if (tagArgs) {
3✔
1147
      src = msLookupHashTable(tagArgs, "src");
3✔
1148
    }
1149

1150
    if (!src)
3✔
1151
      return (MS_SUCCESS); /* don't process the tag, could be something else so
1152
                              return MS_SUCCESS */
1153

1154
    if ((includeStream = fopen(msBuildPath(path, mapserv->map->mappath, src),
3✔
1155
                               "r")) == NULL) {
1156
      msSetError(MS_IOERR, "%s", "processIncludeTag()", src);
1✔
1157
      msFreeHashTable(tagArgs);
1✔
1158
      return MS_FAILURE;
1✔
1159
    }
1160

1161
    if (isValidTemplate(includeStream, src) != MS_TRUE) {
2✔
1162
      msFreeHashTable(tagArgs);
×
1163
      fclose(includeStream);
×
1164
      return MS_FAILURE;
×
1165
    }
1166

1167
    while (fgets(buffer, MS_BUFFER_LENGTH, includeStream) != NULL)
10✔
1168
      content = msStringConcatenate(content, buffer);
8✔
1169

1170
    /* done with included file handle */
1171
    fclose(includeStream);
2✔
1172

1173
    /* find the end of the tag */
1174
    tagEnd = findTagEnd(tagStart);
2✔
1175
    tagEnd++;
2✔
1176

1177
    /* build the complete tag so we can do substitution */
1178
    tagLength = tagEnd - tagStart;
2✔
1179
    tag = (char *)msSmallMalloc(tagLength + 1);
2✔
1180
    strlcpy(tag, tagStart, tagLength + 1);
1181

1182
    /* process any other tags in the content */
1183
    processedContent = processLine(mapserv, content, stream, mode);
2✔
1184

1185
    /* do the replacement */
1186
    *line = msReplaceSubstring(*line, tag, processedContent);
2✔
1187

1188
    /* clean up */
1189
    free(tag);
2✔
1190
    tag = NULL;
1191
    msFreeHashTable(tagArgs);
2✔
1192
    tagArgs = NULL;
2✔
1193
    free(content);
2✔
1194
    free(processedContent);
2✔
1195

1196
    if ((*line)[tagOffset] != '\0')
2✔
1197
      tagStart = findTag(*line + tagOffset + 1, "include");
2✔
1198
    else
1199
      tagStart = NULL;
1200
  }
1201

1202
  return (MS_SUCCESS);
1203
}
1204

1205
/*
1206
** Function to process a [resultset ...] tag.
1207
*/
1208
static int processResultSetTag(mapservObj *mapserv, char **line, FILE *stream) {
131✔
1209
  char lineBuffer[MS_BUFFER_LENGTH];
1210
  int foundTagEnd;
1211

1212
  char *preTag, *postTag; /* text before and after the tag */
1213

1214
  char *tag;
1215
  hashTableObj *tagArgs = NULL;
131✔
1216

1217
  const char *layerName = NULL;
1218
  const char *nodata = NULL;
1219

1220
  layerObj *lp;
1221

1222
  if (!*line) {
131✔
1223
    msSetError(MS_WEBERR, "Invalid line pointer.", "processResultSetTag()");
×
1224
    return (MS_FAILURE);
×
1225
  }
1226

1227
  const char *tagStart = findTag(*line, "resultset");
131✔
1228
  if (!tagStart)
131✔
1229
    return (MS_SUCCESS); /* OK, just return; */
1230

1231
  while (tagStart) {
198✔
1232
    /* initialize the tag arguments */
1233
    layerName = NULL;
1234

1235
    /* check for any tag arguments */
1236
    if (getTagArgs("resultset", tagStart, &tagArgs) != MS_SUCCESS)
99✔
1237
      return (MS_FAILURE);
1238
    if (tagArgs) {
99✔
1239
      layerName = msLookupHashTable(tagArgs, "layer");
99✔
1240
      nodata = msLookupHashTable(tagArgs, "nodata");
99✔
1241
    }
1242

1243
    if (!layerName) {
99✔
1244
      msSetError(MS_WEBERR,
×
1245
                 "[resultset] tag missing required 'layer' argument.",
1246
                 "processResultSetTag()");
1247
      msFreeHashTable(tagArgs);
×
1248
      return (MS_FAILURE);
×
1249
    }
1250

1251
    const int layerIndex = msGetLayerIndex(mapserv->map, layerName);
99✔
1252
    if (layerIndex >= mapserv->map->numlayers || layerIndex < 0) {
99✔
1253
      msSetError(MS_MISCERR, "Layer named '%s' does not exist.",
×
1254
                 "processResultSetTag()", layerName);
1255
      msFreeHashTable(tagArgs);
×
1256
      return MS_FAILURE;
×
1257
    }
1258
    lp = GET_LAYER(mapserv->map, layerIndex);
99✔
1259

1260
    if (strstr(*line, "[/resultset]") == NULL) { /* read ahead */
99✔
1261
      if (!stream) {
7✔
1262
        msSetError(MS_WEBERR, "Invalid file pointer.", "processResultSetTag()");
×
1263
        msFreeHashTable(tagArgs);
×
1264
        return (MS_FAILURE);
×
1265
      }
1266

1267
      foundTagEnd = MS_FALSE;
1268
      while (!foundTagEnd) {
1269
        if (fgets(lineBuffer, MS_BUFFER_LENGTH, stream) != NULL) {
114✔
1270
          *line = msStringConcatenate(*line, lineBuffer);
114✔
1271
          if (strstr(*line, "[/resultset]") != NULL)
114✔
1272
            foundTagEnd = MS_TRUE;
1273
        } else
1274
          break; /* ran out of file */
1275
      }
1276
      if (foundTagEnd == MS_FALSE) {
7✔
1277
        msSetError(MS_WEBERR,
×
1278
                   "[resultset] tag found without closing [/resultset].",
1279
                   "processResultSetTag()");
1280
        msFreeHashTable(tagArgs);
×
1281
        return (MS_FAILURE);
×
1282
      }
1283
    }
1284

1285
    if (getInlineTag("resultset", *line, &tag) != MS_SUCCESS) {
99✔
1286
      msSetError(MS_WEBERR, "Malformed resultset tag.",
×
1287
                 "processResultSetTag()");
1288
      msFreeHashTable(tagArgs);
×
1289
      return MS_FAILURE;
×
1290
    }
1291

1292
    preTag = getPreTagText(
99✔
1293
        *line, "[resultset"); /* TODO: need to handle tags in these */
1294
    postTag = getPostTagText(*line, "[/resultset]");
99✔
1295

1296
    /* start rebuilding **line */
1297
    free(*line);
99✔
1298
    *line = preTag;
99✔
1299

1300
    /* process any includes within the resultset tag */
1301
    if (processIncludeTag(mapserv, &tag, stream, 0) != MS_SUCCESS) {
99✔
1302
      msFreeHashTable(tagArgs);
×
1303
      return (MS_FAILURE);
×
1304
    }
1305

1306
    if (lp->resultcache && lp->resultcache->numresults > 0) {
99✔
1307
      /* probably will need a while-loop here to handle multiple instances of
1308
       * [feature ...] tags */
1309
      if (processFeatureTag(mapserv, &tag, lp) != MS_SUCCESS) {
70✔
1310
        msFreeHashTable(tagArgs);
×
1311
        return (MS_FAILURE); /* TODO: how to handle */
×
1312
      }
1313
      *line = msStringConcatenate(*line, tag);
70✔
1314
    } else if (nodata) {
29✔
1315
      *line = msStringConcatenate(*line, nodata);
1✔
1316
    }
1317

1318
    *line = msStringConcatenate(*line, postTag);
99✔
1319

1320
    /* clean up */
1321
    free(postTag);
99✔
1322
    free(tag);
99✔
1323

1324
    tagStart = findTag(*line, "resultset");
99✔
1325
  }
1326
  msFreeHashTable(tagArgs);
99✔
1327

1328
  return (MS_SUCCESS);
99✔
1329
}
1330

1331
/*
1332
** Function to process an [item ...] tag: line contains the tag, shape holds the
1333
*attributes.
1334
*/
1335
enum ITEM_ESCAPING { ESCAPE_HTML, ESCAPE_URL, ESCAPE_JSON, ESCAPE_NONE };
1336

1337
static int processItemTag(layerObj *layer, char **line, shapeObj *shape) {
310✔
1338
  int i;
1339

1340
  char *tagEnd;
1341

1342
  if (!*line) {
310✔
1343
    msSetError(MS_WEBERR, "Invalid line pointer.", "processItemTag()");
×
1344
    return (MS_FAILURE);
×
1345
  }
1346

1347
  const char *tagStart = findTag(*line, "item");
310✔
1348

1349
  if (!tagStart)
310✔
1350
    return (MS_SUCCESS); /* OK, just return; */
1351

1352
  while (tagStart) {
553✔
1353
    const char *format = "$value"; /* initialize the tag arguments */
1354
    const char *nullFormat = "";
1355
    int precision = -1;
1356
    int padding = -1;
1357
    const char *name = NULL;
1358
    const char *pattern = NULL;
1359
    int uc = MS_FALSE;
1360
    int lc = MS_FALSE;
1361
    int commify = MS_FALSE;
1362
    int ignoremissing = MS_FALSE;
1363
    int escape = ESCAPE_HTML;
1364
    int skip = MS_FALSE;
1365

1366
    /* check for any tag arguments */
1367
    hashTableObj *tagArgs = NULL;
339✔
1368
    if (getTagArgs("item", tagStart, &tagArgs) != MS_SUCCESS)
339✔
1369
      return (MS_FAILURE);
×
1370
    if (tagArgs) {
339✔
1371
      const char *argValue = msLookupHashTable(tagArgs, "name");
339✔
1372
      if (argValue)
1373
        name = argValue;
1374

1375
      argValue = msLookupHashTable(tagArgs, "pattern");
339✔
1376
      if (argValue)
1377
        pattern = argValue;
1378

1379
      argValue = msLookupHashTable(tagArgs, "precision");
339✔
1380
      if (argValue)
339✔
1381
        precision = atoi(argValue);
1382

1383
      argValue = msLookupHashTable(tagArgs, "padding");
339✔
1384
      if (argValue)
339✔
1385
        padding = atoi(argValue);
1386

1387
      argValue = msLookupHashTable(tagArgs, "format");
339✔
1388
      if (argValue)
339✔
1389
        format = argValue;
1390

1391
      argValue = msLookupHashTable(tagArgs, "nullformat");
339✔
1392
      if (argValue)
339✔
1393
        nullFormat = argValue;
1394

1395
      argValue = msLookupHashTable(tagArgs, "uc");
339✔
1396
      if (argValue && strcasecmp(argValue, "true") == 0)
339✔
1397
        uc = MS_TRUE;
1398

1399
      argValue = msLookupHashTable(tagArgs, "lc");
339✔
1400
      if (argValue && strcasecmp(argValue, "true") == 0)
339✔
1401
        lc = MS_TRUE;
1402

1403
      argValue = msLookupHashTable(tagArgs, "commify");
339✔
1404
      if (argValue && strcasecmp(argValue, "true") == 0)
339✔
1405
        commify = MS_TRUE;
1406

1407
      argValue = msLookupHashTable(tagArgs, "ignoremissing");
339✔
1408
      if (argValue && strcasecmp(argValue, "true") == 0)
339✔
1409
        ignoremissing = MS_TRUE;
1410

1411
      argValue = msLookupHashTable(tagArgs, "escape");
339✔
1412
      if (argValue && strcasecmp(argValue, "url") == 0)
339✔
1413
        escape = ESCAPE_URL;
1414
      else if (argValue && strcasecmp(argValue, "none") == 0)
×
1415
        escape = ESCAPE_NONE;
1416
      else if (argValue && strcasecmp(argValue, "json") == 0)
×
1417
        escape = ESCAPE_JSON;
1418

1419
      /* TODO: deal with sub strings */
1420
    }
1421

1422
    if (!name) {
339✔
1423
      msSetError(MS_WEBERR, "Item tag contains no name attribute.",
×
1424
                 "processItemTag()");
1425
      return (MS_FAILURE);
×
1426
    }
1427

1428
    for (i = 0; i < layer->numitems; i++)
2,349✔
1429
      if (strcasecmp(name, layer->items[i]) == 0)
2,348✔
1430
        break;
1431

1432
    if (i == layer->numitems) {
339✔
1433
      if (ignoremissing == MS_TRUE) {
1✔
1434
        skip = MS_TRUE;
1435
      } else {
1436
        msSetError(MS_WEBERR, "Item name (%s) not found in layer item list.",
×
1437
                   "processItemTag()", name);
1438
        return (MS_FAILURE);
×
1439
      }
1440
    }
1441

1442
    /*
1443
    ** now we know which item so build the tagValue
1444
    */
1445

1446
    char *tagValue = NULL;
1447
    if (!skip && (shape->values[i] && strlen(shape->values[i]) > 0)) {
338✔
1448
      char *itemValue = NULL;
1449

1450
      /* set tag text depending on pattern (if necessary), nullFormat can
1451
       * contain $value (#3637) */
1452
      if (pattern && msEvalRegex(pattern, shape->values[i]) != MS_TRUE)
338✔
1453
        tagValue = msStrdup(nullFormat);
1✔
1454
      else
1455
        tagValue = msStrdup(format);
337✔
1456

1457
      if (precision != -1) {
338✔
1458
        char numberFormat[16];
1459

1460
        itemValue = (char *)msSmallMalloc(64); /* plenty big */
2✔
1461
        snprintf(numberFormat, sizeof(numberFormat), "%%.%dlf", precision);
1462
        snprintf(itemValue, 64, numberFormat, atof(shape->values[i]));
2✔
1463
      } else
1464
        itemValue = msStrdup(shape->values[i]);
336✔
1465

1466
      if (commify == MS_TRUE)
338✔
1467
        itemValue = msCommifyString(itemValue);
×
1468

1469
      /* apply other effects */
1470
      if (uc == MS_TRUE)
338✔
1471
        for (unsigned j = 0; j < strlen(itemValue); j++)
10✔
1472
          itemValue[j] = toupper(itemValue[j]);
9✔
1473
      if (lc == MS_TRUE)
338✔
1474
        for (unsigned j = 0; j < strlen(itemValue); j++)
5✔
1475
          itemValue[j] = tolower(itemValue[j]);
4✔
1476

1477
      tagValue = msReplaceSubstring(tagValue, "$value", itemValue);
338✔
1478
      msFree(itemValue);
338✔
1479

1480
      if (padding > 0 && padding < 1000) {
338✔
1481
        int paddedSize = strlen(tagValue) + padding + 1;
3✔
1482
        char *paddedValue = NULL;
1483
        paddedValue = (char *)msSmallMalloc(paddedSize);
3✔
1484
        snprintf(paddedValue, paddedSize, "%-*s", padding, tagValue);
1485
        msFree(tagValue);
3✔
1486
        tagValue = paddedValue;
1487
      }
1488

1489
      if (!tagValue) {
338✔
1490
        msSetError(MS_WEBERR, "Error applying item format.",
×
1491
                   "processItemTag()");
1492
        return (MS_FAILURE); /* todo leaking... */
×
1493
      }
1494
    } else {
1495
      tagValue = msStrdup(nullFormat); /* attribute value is NULL or empty */
1✔
1496
    }
1497

1498
    /* find the end of the tag */
1499
    tagEnd = findTagEnd(tagStart);
339✔
1500
    tagEnd++;
339✔
1501

1502
    /* build the complete tag so we can do substitution */
1503
    const int tagLength = tagEnd - tagStart;
339✔
1504
    char *tag = (char *)msSmallMalloc(tagLength + 1);
339✔
1505
    strlcpy(tag, tagStart, tagLength + 1);
1506

1507
    /* do the replacement */
1508
    switch (escape) {
339✔
1509
    case ESCAPE_HTML: {
339✔
1510
      char *encodedTagValue = msEncodeHTMLEntities(tagValue);
339✔
1511
      *line = msReplaceSubstring(*line, tag, encodedTagValue);
339✔
1512
      msFree(encodedTagValue);
339✔
1513
      break;
339✔
1514
    }
1515
    case ESCAPE_JSON: {
×
1516
      char *encodedTagValue = msEscapeJSonString(tagValue);
×
1517
      *line = msReplaceSubstring(*line, tag, encodedTagValue);
×
1518
      msFree(encodedTagValue);
×
1519
      break;
×
1520
    }
1521
    case ESCAPE_URL: {
×
1522
      char *encodedTagValue = msEncodeUrl(tagValue);
×
1523
      *line = msReplaceSubstring(*line, tag, encodedTagValue);
×
1524
      msFree(encodedTagValue);
×
1525
      break;
×
1526
    }
1527
    case ESCAPE_NONE:
×
1528
      *line = msReplaceSubstring(*line, tag, tagValue);
×
1529
      break;
×
1530
    default:
1531
      break;
1532
    }
1533

1534
    /* clean up */
1535
    free(tag);
339✔
1536
    msFreeHashTable(tagArgs);
339✔
1537
    msFree(tagValue);
339✔
1538

1539
    tagStart = findTag(*line, "item");
339✔
1540
  }
1541

1542
  return (MS_SUCCESS);
1543
}
1544

1545
/*
1546
** Function process any number of MapServer extent tags (e.g. shpext, mapext,
1547
*etc...).
1548
*/
1549
static int processExtentTag(mapservObj *mapserv, char **line, char *name,
2,384✔
1550
                            rectObj *extent, projectionObj *rectProj) {
1551
  rectObj tempExtent;
1552

1553
  char number[64]; /* holds a single number in the extent */
1554
  char numberFormat[16];
1555

1556
  if (!*line) {
2,384✔
1557
    msSetError(MS_WEBERR, "Invalid line pointer.", "processExtentTag()");
×
1558
    return (MS_FAILURE);
×
1559
  }
1560

1561
  const char *tagStart = findTag(*line, name); /* this supports any extent */
2,384✔
1562

1563
  /* It is OK to have no include tags, just return. */
1564
  if (!tagStart)
2,384✔
1565
    return MS_SUCCESS;
1566

1567
  while (tagStart) {
8✔
1568
    double xExpand = 0, yExpand = 0; /* set tag argument defaults */
1569
    int precision = -1;
1570
    const char *format = "$minx $miny $maxx $maxy";
1571
    const char *projectionString = NULL;
1572

1573
    /* hack to handle tags like 'mapext_esc' easily */
1574
    int escape;
1575
    if (strstr(name, "_esc"))
5✔
1576
      escape = ESCAPE_URL;
1577
    else
1578
      escape = ESCAPE_HTML;
1579

1580
    const int tagOffset = tagStart - *line;
5✔
1581

1582
    /* check for any tag arguments */
1583
    hashTableObj *tagArgs = NULL;
5✔
1584
    if (getTagArgs(name, tagStart, &tagArgs) != MS_SUCCESS)
5✔
1585
      return (MS_FAILURE);
×
1586
    if (tagArgs) {
5✔
1587
      const char *argValue = msLookupHashTable(tagArgs, "expand");
2✔
1588
      if (argValue) {
2✔
1589
        if (strchr(argValue, '%') != NULL) {
×
1590
          float f;
1591
          sscanf(argValue, "%f%%", &f);
×
1592
          xExpand = ((f / 100.0) * (extent->maxx - extent->minx)) / 2;
×
1593
          yExpand = ((f / 100.0) * (extent->maxy - extent->miny)) / 2;
×
1594
        } else {
1595
          xExpand = atof(argValue);
1596
          yExpand = xExpand;
1597
        }
1598
      }
1599

1600
      argValue = msLookupHashTable(tagArgs, "escape");
2✔
1601
      if (argValue && strcasecmp(argValue, "url") == 0)
2✔
1602
        escape = ESCAPE_URL;
1603
      else if (argValue && strcasecmp(argValue, "none") == 0)
×
1604
        escape = ESCAPE_NONE;
1605

1606
      argValue = msLookupHashTable(tagArgs, "format");
2✔
1607
      if (argValue)
2✔
1608
        format = argValue;
1609

1610
      argValue = msLookupHashTable(tagArgs, "precision");
2✔
1611
      if (argValue)
2✔
1612
        precision = atoi(argValue);
1613

1614
      argValue = msLookupHashTable(tagArgs, "proj");
2✔
1615
      if (argValue)
1616
        projectionString = argValue;
1617
    }
1618

1619
    tempExtent.minx = extent->minx - xExpand;
5✔
1620
    tempExtent.miny = extent->miny - yExpand;
5✔
1621
    tempExtent.maxx = extent->maxx + xExpand;
5✔
1622
    tempExtent.maxy = extent->maxy + yExpand;
5✔
1623

1624
    /* no big deal to convert from file to image coordinates, but what are the
1625
     * image parameters */
1626
    if (rectProj && projectionString &&
5✔
1627
        strcasecmp(projectionString, "image") == 0) {
×
1628
      precision = 0;
1629

1630
      /* if necessary, project the shape to match the map */
1631
      if (msProjectionsDiffer(rectProj, &(mapserv->map->projection)))
×
1632
        msProjectRect(rectProj, &mapserv->map->projection, &tempExtent);
×
1633

1634
      /* convert tempExtent to image coordinates based on the map extent and
1635
       * cellsize */
1636
      tempExtent.minx = MS_MAP2IMAGE_X(
×
1637
          tempExtent.minx, mapserv->map->extent.minx, mapserv->map->cellsize);
1638
      tempExtent.miny = MS_MAP2IMAGE_Y(
×
1639
          tempExtent.miny, mapserv->map->extent.maxy, mapserv->map->cellsize);
1640
      tempExtent.maxx = MS_MAP2IMAGE_X(
×
1641
          tempExtent.minx, mapserv->map->extent.minx, mapserv->map->cellsize);
1642
      tempExtent.maxy = MS_MAP2IMAGE_Y(
×
1643
          tempExtent.miny, mapserv->map->extent.maxy, mapserv->map->cellsize);
1644
    } else if (rectProj && projectionString) {
5✔
1645
      projectionObj projection;
1646
      msInitProjection(&projection);
×
1647
      msProjectionInheritContextFrom(&projection, &mapserv->map->projection);
×
1648

1649
      if (MS_SUCCESS != msLoadProjectionString(&projection, projectionString))
×
1650
        return MS_FAILURE;
×
1651

1652
      if (msProjectionsDiffer(rectProj, &projection))
×
1653
        msProjectRect(rectProj, &projection, &tempExtent);
×
1654
    }
1655

1656
    char *tagValue = msStrdup(format);
5✔
1657

1658
    if (precision != -1)
5✔
1659
      snprintf(numberFormat, sizeof(numberFormat), "%%.%dlf", precision);
1660
    else
1661
      snprintf(numberFormat, sizeof(numberFormat), "%%f");
1662

1663
    snprintf(number, sizeof(number), numberFormat, tempExtent.minx);
5✔
1664
    tagValue = msReplaceSubstring(tagValue, "$minx", number);
5✔
1665
    snprintf(number, sizeof(number), numberFormat, tempExtent.miny);
5✔
1666
    tagValue = msReplaceSubstring(tagValue, "$miny", number);
5✔
1667
    snprintf(number, sizeof(number), numberFormat, tempExtent.maxx);
5✔
1668
    tagValue = msReplaceSubstring(tagValue, "$maxx", number);
5✔
1669
    snprintf(number, sizeof(number), numberFormat, tempExtent.maxy);
5✔
1670
    tagValue = msReplaceSubstring(tagValue, "$maxy", number);
5✔
1671

1672
    /* find the end of the tag */
1673
    char *tagEnd = findTagEnd(tagStart);
5✔
1674
    tagEnd++;
5✔
1675

1676
    /* build the complete tag so we can do substitution */
1677
    const int tagLength = tagEnd - tagStart;
5✔
1678
    char *tag = (char *)msSmallMalloc(tagLength + 1);
5✔
1679
    strlcpy(tag, tagStart, tagLength + 1);
1680

1681
    /* do the replacement */
1682
    switch (escape) {
5✔
1683
    case ESCAPE_HTML: {
5✔
1684
      char *encodedTagValue = msEncodeHTMLEntities(tagValue);
5✔
1685
      *line = msReplaceSubstring(*line, tag, encodedTagValue);
5✔
1686
      msFree(encodedTagValue);
5✔
1687
      break;
5✔
1688
    }
1689
    case ESCAPE_URL: {
×
1690
      char *encodedTagValue = msEncodeUrl(tagValue);
×
1691
      *line = msReplaceSubstring(*line, tag, encodedTagValue);
×
1692
      msFree(encodedTagValue);
×
1693
      break;
×
1694
    }
1695
    case ESCAPE_NONE:
×
1696
      *line = msReplaceSubstring(*line, tag, tagValue);
×
1697
      break;
×
1698
    default:
1699
      break;
1700
    }
1701

1702
    /* clean up */
1703
    free(tag);
5✔
1704
    msFreeHashTable(tagArgs);
5✔
1705
    msFree(tagValue);
5✔
1706

1707
    if ((*line)[tagOffset] != '\0')
5✔
1708
      tagStart = findTag(*line + tagOffset + 1, name);
5✔
1709
    else
1710
      tagStart = NULL;
1711
  }
1712

1713
  return (MS_SUCCESS);
1714
}
1715

1716
// RFC 77 TODO: Need to validate these changes with Assefa...
1717
static int processShplabelTag(layerObj *layer, char **line,
310✔
1718
                              shapeObj *origshape) {
1719
  char *tagEnd;
1720
  shapeObj tShape;
1721
  int precision = 0;
1722
  int clip_to_map = MS_TRUE;
1723
  int use_label_settings = MS_FALSE;
1724
  int labelposvalid = MS_FALSE;
1725
  pointObj labelPos;
1726
  int status;
1727
  char number[64]; /* holds a single number in the extent */
1728
  char numberFormat[16];
1729
  shapeObj *shape = NULL;
1730

1731
  if (!*line) {
310✔
1732
    msSetError(MS_WEBERR, "Invalid line pointer.", "processShplabelTag()");
×
1733
    return (MS_FAILURE);
×
1734
  }
1735
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
310✔
1736
    return MS_FAILURE;
1737

1738
  const char *tagStart = findTag(*line, "shplabel");
310✔
1739

1740
  /* It is OK to have no shplabel tags, just return. */
1741
  if (!tagStart)
310✔
1742
    return MS_SUCCESS;
1743

1744
  if (!origshape ||
×
1745
      origshape->numlines <= 0) { /* I suppose we need to make sure the part has
×
1746
                                     vertices (need shape checker?) */
1747
    msSetError(MS_WEBERR, "Null or empty shape.", "processShplabelTag()");
×
1748
    return (MS_FAILURE);
×
1749
  }
1750

1751
  while (tagStart) {
×
1752
    if (shape)
×
1753
      msFreeShape(shape);
×
1754
    shape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
×
1755
    msInitShape(shape);
×
1756
    msCopyShape(origshape, shape);
×
1757

1758
    const char *projectionString = NULL;
1759
    const char *format = "$x,$y";
1760
    const int tagOffset = tagStart - *line;
×
1761

1762
    hashTableObj *tagArgs = NULL;
×
1763
    if (getTagArgs("shplabel", tagStart, &tagArgs) != MS_SUCCESS)
×
1764
      return (MS_FAILURE);
×
1765
    if (tagArgs) {
×
1766
      const char *argValue = msLookupHashTable(tagArgs, "format");
×
1767
      if (argValue)
×
1768
        format = argValue;
1769

1770
      argValue = msLookupHashTable(tagArgs, "precision");
×
1771
      if (argValue)
×
1772
        precision = atoi(argValue);
1773

1774
      argValue = msLookupHashTable(tagArgs, "proj");
×
1775
      if (argValue)
1776
        projectionString = argValue;
1777

1778
      argValue = msLookupHashTable(tagArgs, "clip_to_map");
×
1779
      if (argValue) {
×
1780
        if (strcasecmp(argValue, "false") == 0)
×
1781
          clip_to_map = MS_FALSE;
1782
      }
1783

1784
      argValue = msLookupHashTable(tagArgs, "use_label_settings");
×
1785
      if (argValue) {
×
1786
        if (strcasecmp(argValue, "true") == 0)
×
1787
          use_label_settings = MS_TRUE;
1788
      }
1789
    }
1790

1791
    labelPos.x = -1;
×
1792
    labelPos.y = -1;
×
1793
    msInitShape(&tShape);
×
1794

1795
    tShape.type = MS_SHAPE_LINE;
×
1796
    tShape.line = (lineObj *)msSmallMalloc(sizeof(lineObj));
×
1797
    tShape.numlines = 1;
×
1798
    tShape.line[0].point = NULL; /* initialize the line */
×
1799
    tShape.line[0].numpoints = 0;
×
1800

1801
    double cellsize;
1802
    if (layer->map->cellsize <= 0)
×
1803
      cellsize =
1804
          MS_MAX(MS_CELLSIZE(layer->map->extent.minx, layer->map->extent.maxx,
×
1805
                             layer->map->width),
1806
                 MS_CELLSIZE(layer->map->extent.miny, layer->map->extent.maxy,
1807
                             layer->map->height));
1808
    else
1809
      cellsize = layer->map->cellsize;
1810

1811
    if (shape->type == MS_SHAPE_POINT) {
×
1812
      labelposvalid = MS_FALSE;
1813
      if (shape->numlines > 0 && shape->line[0].numpoints > 0) {
×
1814
        labelposvalid = MS_TRUE;
1815
        labelPos = shape->line[0].point[0];
×
1816
        if (layer->transform == MS_TRUE) {
×
1817
          if (layer->project &&
×
1818
              msProjectionsDiffer(&(layer->projection),
×
1819
                                  &(layer->map->projection))) {
1820
            reprojectionObj *reprojector =
1821
                msLayerGetReprojectorToMap(layer, layer->map);
×
1822
            if (reprojector) {
×
1823
              msProjectShapeEx(reprojector, shape);
×
1824
            }
1825
          }
1826

1827
          labelPos = shape->line[0].point[0];
×
1828
          labelPos.x =
×
1829
              MS_MAP2IMAGE_X(labelPos.x, layer->map->extent.minx, cellsize);
×
1830
          labelPos.y =
×
1831
              MS_MAP2IMAGE_Y(labelPos.y, layer->map->extent.maxy, cellsize);
×
1832
        }
1833
      }
1834
    } else if (shape->type == MS_SHAPE_LINE) {
×
1835
      labelposvalid = MS_FALSE;
1836
      if (layer->transform == MS_TRUE) {
×
1837
        if (layer->project && msProjectionsDiffer(&(layer->projection),
×
1838
                                                  &(layer->map->projection))) {
1839
          reprojectionObj *reprojector =
1840
              msLayerGetReprojectorToMap(layer, layer->map);
×
1841
          if (reprojector) {
×
1842
            msProjectShapeEx(reprojector, shape);
×
1843
          }
1844
        }
1845

1846
        if (clip_to_map)
×
1847
          msClipPolylineRect(shape, layer->map->extent);
×
1848

1849
        msTransformShapeToPixelRound(shape, layer->map->extent, cellsize);
×
1850
      } else
1851
        msOffsetShapeRelativeTo(shape, layer);
×
1852

1853
      if (shape->numlines > 0) {
×
1854
        struct label_auto_result lar;
1855
        memset(&lar, 0, sizeof(struct label_auto_result));
1856
        if (MS_UNLIKELY(MS_FAILURE == msPolylineLabelPoint(layer->map, shape,
×
1857
                                                           NULL, NULL, &lar,
1858
                                                           0))) {
1859
          free(lar.angles);
×
1860
          free(lar.label_points);
×
1861
          return MS_FAILURE;
×
1862
        }
1863
        if (lar.num_label_points > 0) {
×
1864
          /* convert to geo */
1865
          labelPos.x = lar.label_points[0].x;
×
1866
          labelPos.y = lar.label_points[0].y;
×
1867
          labelposvalid = MS_TRUE;
1868
        }
1869
        free(lar.angles);
×
1870
        free(lar.label_points);
×
1871
      }
1872
    } else if (shape->type == MS_SHAPE_POLYGON) {
×
1873
      labelposvalid = MS_FALSE;
1874
      if (layer->transform == MS_TRUE) {
×
1875
        if (layer->project && msProjectionsDiffer(&(layer->projection),
×
1876
                                                  &(layer->map->projection))) {
1877
          reprojectionObj *reprojector =
1878
              msLayerGetReprojectorToMap(layer, layer->map);
×
1879
          if (reprojector) {
×
1880
            msProjectShapeEx(reprojector, shape);
×
1881
          }
1882
        }
1883

1884
        if (clip_to_map)
×
1885
          msClipPolygonRect(shape, layer->map->extent);
×
1886

1887
        msTransformShapeToPixelRound(shape, layer->map->extent, cellsize);
×
1888
      } else
1889
        msOffsetShapeRelativeTo(shape, layer);
×
1890

1891
      if (shape->numlines > 0) {
×
1892
        if (msPolygonLabelPoint(shape, &labelPos, -1) == MS_SUCCESS) {
×
1893
          if (labelPos.x == -1 && labelPos.y == -1)
×
1894
            labelposvalid = MS_FALSE;
1895
          else
1896
            labelposvalid = MS_TRUE;
1897
        }
1898
      }
1899
    }
1900

1901
    if (labelposvalid == MS_TRUE) {
×
1902
      pointObj p1 = {0}; // initialize
×
1903
      pointObj p2 = {0};
×
1904
      int label_offset_x, label_offset_y;
1905
      labelObj *label = NULL;
1906
      label_bounds lbounds;
1907
      lineObj lbounds_line;
1908
      pointObj lbounds_point[5];
1909
      double tmp;
1910

1911
      p1.x = labelPos.x;
×
1912
      p1.y = labelPos.y;
×
1913

1914
      p2.x = labelPos.x;
×
1915
      p2.y = labelPos.y;
×
1916
      if (use_label_settings == MS_TRUE) {
×
1917

1918
        /* RFC 77: classes (and shapes) can have more than 1 piece of
1919
         * annotation, here we only use the first (index=0) */
1920
        if (shape->classindex >= 0 &&
×
1921
                layer->class[shape->classindex] -> numlabels > 0) {
×
1922
          label = layer->class[shape->classindex]->labels[0];
×
1923
          if (msGetLabelStatus(layer->map, layer, shape, label) == MS_ON) {
×
1924
            char *annotext = msShapeGetLabelAnnotation(layer, shape, label);
×
1925
            if (annotext) {
×
1926
              textSymbolObj ts;
1927
              initTextSymbol(&ts);
×
1928
              msPopulateTextSymbolForLabelAndString(&ts, label, annotext,
×
1929
                                                    layer->scalefactor, 1.0, 0);
1930

1931
              label_offset_x = (int)(label->offsetx * layer->scalefactor);
×
1932
              label_offset_y = (int)(label->offsety * layer->scalefactor);
×
1933
              lbounds.poly = &lbounds_line;
×
1934
              lbounds_line.numpoints = 5;
×
1935
              lbounds_line.point = lbounds_point;
×
1936

1937
              p1 = get_metrics(&labelPos, label->position, ts.textpath,
×
1938
                               label_offset_x, label_offset_y,
1939
                               label->angle * MS_DEG_TO_RAD, 0, &lbounds);
×
1940

1941
              /* should we use the point returned from  get_metrics?. From few
1942
                test done, It seems to return the UL corner of the text. For now
1943
                use the bounds.minx/miny */
1944
              p1.x = lbounds.bbox.minx;
×
1945
              p1.y = lbounds.bbox.miny;
×
1946
              p2.x = lbounds.bbox.maxx;
×
1947
              p2.y = lbounds.bbox.maxy;
×
1948
              freeTextSymbol(&ts);
×
1949
            }
1950
          }
1951
        }
1952
      }
1953

1954
      /* y's are flipped because it is in image coordinate systems */
1955
      p1.x = MS_IMAGE2MAP_X(p1.x, layer->map->extent.minx, cellsize);
×
1956
      tmp = p1.y;
×
1957
      p1.y = MS_IMAGE2MAP_Y(p2.y, layer->map->extent.maxy, cellsize);
×
1958
      p2.x = MS_IMAGE2MAP_X(p2.x, layer->map->extent.minx, cellsize);
×
1959
      p2.y = MS_IMAGE2MAP_Y(tmp, layer->map->extent.maxy, cellsize);
×
1960
      if (layer->transform == MS_TRUE) {
×
1961
        if (layer->project && msProjectionsDiffer(&(layer->projection),
×
1962
                                                  &(layer->map->projection))) {
1963
          msProjectPoint(&layer->map->projection, &layer->projection, &p1);
×
1964
          msProjectPoint(&layer->map->projection, &layer->projection, &p2);
×
1965
        }
1966
      }
1967
      msAddPointToLine(&(tShape.line[0]), &p1);
×
1968
      msAddPointToLine(&(tShape.line[0]), &p2);
×
1969
    } else
1970
      tShape.numlines = 0;
×
1971

1972
    if (projectionString && strcasecmp(projectionString, "image") == 0) {
×
1973
      precision = 0;
1974

1975
      /* if necessary, project the shape to match the map */
1976
      if (msProjectionsDiffer(&(layer->projection),
×
1977
                              &(layer->map->projection))) {
×
1978
        reprojectionObj *reprojector =
1979
            msLayerGetReprojectorToMap(layer, layer->map);
×
1980
        if (reprojector) {
×
1981
          msProjectShapeEx(reprojector, &tShape);
×
1982
        }
1983
      }
1984

1985
      msClipPolylineRect(&tShape, layer->map->extent);
×
1986

1987
      msTransformShapeToPixelRound(&tShape, layer->map->extent,
×
1988
                                   layer->map->cellsize);
×
1989
    } else if (projectionString) {
1990
      projectionObj projection;
1991
      msInitProjection(&projection);
×
1992
      msProjectionInheritContextFrom(&projection, &layer->map->projection);
×
1993

1994
      status = msLoadProjectionString(&projection, projectionString);
×
1995
      if (status != MS_SUCCESS)
×
1996
        return MS_FAILURE;
×
1997

1998
      if (msProjectionsDiffer(&(layer->projection), &projection))
×
1999
        msProjectShape(&layer->projection, &projection, &tShape);
×
2000
    }
2001

2002
    /* do the replacement */
2003
    char *tagValue = msStrdup(format);
×
2004
    if (precision > 0)
×
2005
      snprintf(numberFormat, sizeof(numberFormat), "%%.%dlf", precision);
2006
    else
2007
      snprintf(numberFormat, sizeof(numberFormat), "%%f");
2008

2009
    if (tShape.numlines > 0) {
×
2010
      if (strcasestr(tagValue, "$x") != 0) {
×
2011
        snprintf(number, sizeof(number), numberFormat,
×
2012
                 tShape.line[0].point[0].x);
×
2013
        tagValue = msReplaceSubstring(tagValue, "$x", number);
×
2014
      }
2015
      if (strcasestr(tagValue, "$y") != 0) {
×
2016
        snprintf(number, sizeof(number), numberFormat,
×
2017
                 tShape.line[0].point[0].y);
×
2018
        tagValue = msReplaceSubstring(tagValue, "$y", number);
×
2019
      }
2020

2021
      if (strcasestr(tagValue, "$minx") != 0) {
×
2022
        snprintf(number, sizeof(number), numberFormat,
×
2023
                 tShape.line[0].point[0].x);
×
2024
        tagValue = msReplaceSubstring(tagValue, "$minx", number);
×
2025
      }
2026
      if (strcasestr(tagValue, "$miny") != 0) {
×
2027
        snprintf(number, sizeof(number), numberFormat,
×
2028
                 tShape.line[0].point[0].y);
×
2029
        tagValue = msReplaceSubstring(tagValue, "$miny", number);
×
2030
      }
2031
      if (strcasestr(tagValue, "$maxx") != 0) {
×
2032
        snprintf(number, sizeof(number), numberFormat,
×
2033
                 tShape.line[0].point[1].x);
×
2034
        tagValue = msReplaceSubstring(tagValue, "$maxx", number);
×
2035
      }
2036
      if (strcasestr(tagValue, "$maxy") != 0) {
×
2037
        snprintf(number, sizeof(number), numberFormat,
×
2038
                 tShape.line[0].point[1].y);
×
2039
        tagValue = msReplaceSubstring(tagValue, "$maxy", number);
×
2040
      }
2041
    }
2042

2043
    /* find the end of the tag */
2044
    tagEnd = findTagEnd(tagStart);
×
2045
    tagEnd++;
×
2046

2047
    /* build the complete tag so we can do substitution */
2048
    const int tagLength = tagEnd - tagStart;
×
2049
    char *tag = (char *)msSmallMalloc(tagLength + 1);
×
2050
    strlcpy(tag, tagStart, tagLength + 1);
2051

2052
    *line = msReplaceSubstring(*line, tag, tagValue);
×
2053

2054
    /* clean up */
2055
    msFreeShape(&tShape);
×
2056
    free(tag);
×
2057
    msFreeHashTable(tagArgs);
×
2058
    msFree(tagValue);
×
2059

2060
    if ((*line)[tagOffset] != '\0')
×
2061
      tagStart = findTag(*line + tagOffset + 1, "shplabel");
×
2062
    else
2063
      tagStart = NULL;
2064
  }
2065
  if (shape)
×
2066
    msFreeShape(shape);
×
2067

2068
  return (MS_SUCCESS);
2069
}
2070

2071
/*
2072
** Function to process a [date ...] tag
2073
*/
2074
static int processDateTag(char **line) {
441✔
2075
  struct tm *datetime;
2076
  time_t t;
2077
  int result;
2078
  char *tag = NULL, *tagEnd;
2079
  hashTableObj *tagArgs = NULL;
441✔
2080
  int tagOffset, tagLength;
2081
#define DATE_BUFLEN 1024
2082
  char datestr[DATE_BUFLEN];
2083
  const char *argValue = NULL;
2084
  const char *format, *tz; /* tag parameters */
2085

2086
  if (!*line) {
441✔
2087
    msSetError(MS_WEBERR, "Invalid line pointer.", "processDateTag()");
×
2088
    return (MS_FAILURE);
×
2089
  }
2090

2091
  const char *tagStart = findTag(*line, "date");
441✔
2092

2093
  /* It is OK to have no date tags, just return. */
2094
  if (!tagStart)
441✔
2095
    return MS_SUCCESS;
2096

2097
  while (tagStart) {
×
2098
    /* set tag params to defaults */
2099
    format = DEFAULT_DATE_FORMAT;
2100
    tz = "";
2101

2102
    tagOffset = tagStart - *line;
×
2103

2104
    /* check for any tag arguments */
2105
    if (getTagArgs("date", tagStart, &tagArgs) != MS_SUCCESS)
×
2106
      return (MS_FAILURE);
2107

2108
    if (tagArgs) {
×
2109
      argValue = msLookupHashTable(tagArgs, "format");
×
2110
      if (argValue)
×
2111
        format = argValue;
2112
      argValue = msLookupHashTable(tagArgs, "tz");
×
2113
      if (argValue)
×
2114
        tz = argValue;
2115
    }
2116

2117
    t = time(NULL);
×
2118
    if (strncasecmp(tz, "gmt", 4) == 0) {
×
2119
      datetime = gmtime(&t);
×
2120
    } else {
2121
      datetime = localtime(&t);
×
2122
    }
2123
    result = strftime(datestr, DATE_BUFLEN, format, datetime);
×
2124

2125
    /* Only do the replacement if the date was successfully written */
2126
    if (result > 0) {
×
2127
      /* find the end of the tag */
2128
      tagEnd = findTagEnd(tagStart);
×
2129
      tagEnd++;
×
2130

2131
      /* build the complete tag so we can do substitution */
2132
      tagLength = tagEnd - tagStart;
×
2133
      tag = (char *)msSmallMalloc(tagLength + 1);
×
2134
      strlcpy(tag, tagStart, tagLength + 1);
2135

2136
      /* do the replacement */
2137
      *line = msReplaceSubstring(*line, tag, datestr);
×
2138
    }
2139

2140
    /* clean up */
2141
    msFree(tag);
×
2142
    tag = NULL;
2143
    msFreeHashTable(tagArgs);
×
2144
    tagArgs = NULL;
×
2145

2146
    if ((*line)[tagOffset] != '\0')
×
2147
      tagStart = findTag(*line + tagOffset + 1, "date");
×
2148
    else
2149
      tagStart = NULL;
2150
  }
2151

2152
  return (MS_SUCCESS);
2153
}
2154

2155
/*
2156
** Function to process a [shpxy ...] tag: line contains the tag, shape holds the
2157
*coordinates.
2158
**
2159
** TODO's:
2160
**   - May need to change attribute names.
2161
**   - Need generalization routines (not here, but in mapprimative.c).
2162
**   - Try to avoid all the realloc calls.
2163
*/
2164
static int processShpxyTag(layerObj *layer, char **line, shapeObj *shape) {
310✔
2165
  int i, j, p;
2166
  int status;
2167

2168
  char *tagEnd;
2169

2170
  shapeObj tShape;
2171
  char point[128];
2172

2173
  if (!*line) {
310✔
2174
    msSetError(MS_WEBERR, "Invalid line pointer.", "processShpxyTag()");
×
2175
    return (MS_FAILURE);
×
2176
  }
2177
  if (msCheckParentPointer(layer->map, "map") == MS_FAILURE)
310✔
2178
    return MS_FAILURE;
2179

2180
  const char *tagStart = findTag(*line, "shpxy");
310✔
2181

2182
  /* It is OK to have no shpxy tags, just return. */
2183
  if (!tagStart)
310✔
2184
    return MS_SUCCESS;
2185

2186
  if (!shape ||
2✔
2187
      shape->numlines <= 0) { /* I suppose we need to make sure the part has
2✔
2188
                                 vertices (need shape checker?) */
2189
    msSetError(MS_WEBERR, "Null or empty shape.", "processShpxyTag()");
×
2190
    return (MS_FAILURE);
×
2191
  }
2192

2193
  while (tagStart) {
4✔
2194
#ifdef USE_GEOS
2195
    double buffer = 0;
2196
    int bufferUnits = -1;
2197
#endif
2198

2199
    /*
2200
    ** Pointers to static strings, naming convention is:
2201
    **   char 1/2 - x=x, y=y, c=coordinate, p=part, s=shape, ir=inner ring,
2202
    *or=outer ring
2203
    **   last char - h=header, f=footer, s=separator
2204
    */
2205
    const char *xh = "", *yh = "";
2206
    const char *xf = ",";
2207
    const char *yf = "";
2208
    const char *cs = " ";
2209
    const char *ph = "", *pf = "";
2210
    const char *ps = " ";
2211
    const char *sh = "", *sf = "";
2212
    const char *irh = "",
2213
               *irf = ""; /* inner ring: necessary for complex polygons */
2214
    const char *orh = "", *orf = ""; /* outer ring */
2215

2216
    int centroid = MS_FALSE;
2217
    int precision = 0;
2218
    double scale_x = 1.0;
2219
    double scale_y = 1.0;
2220

2221
    const char *projectionString = NULL;
2222

2223
    const int tagOffset = tagStart - *line;
2✔
2224

2225
    /* check for any tag arguments */
2226
    hashTableObj *tagArgs = NULL;
2✔
2227
    if (getTagArgs("shpxy", tagStart, &tagArgs) != MS_SUCCESS)
2✔
2228
      return (MS_FAILURE);
×
2229
    if (tagArgs) {
2✔
2230
      const char *argValue = msLookupHashTable(tagArgs, "xh");
×
2231
      if (argValue)
×
2232
        xh = argValue;
2233
      argValue = msLookupHashTable(tagArgs, "xf");
×
2234
      if (argValue)
×
2235
        xf = argValue;
2236

2237
      argValue = msLookupHashTable(tagArgs, "yh");
×
2238
      if (argValue)
×
2239
        yh = argValue;
2240
      argValue = msLookupHashTable(tagArgs, "yf");
×
2241
      if (argValue)
×
2242
        yf = argValue;
2243

2244
      argValue = msLookupHashTable(tagArgs, "cs");
×
2245
      if (argValue)
×
2246
        cs = argValue;
2247

2248
      argValue = msLookupHashTable(tagArgs, "irh");
×
2249
      if (argValue)
×
2250
        irh = argValue;
2251
      argValue = msLookupHashTable(tagArgs, "irf");
×
2252
      if (argValue)
×
2253
        irf = argValue;
2254

2255
      argValue = msLookupHashTable(tagArgs, "orh");
×
2256
      if (argValue)
×
2257
        orh = argValue;
2258
      argValue = msLookupHashTable(tagArgs, "orf");
×
2259
      if (argValue)
×
2260
        orf = argValue;
2261

2262
      argValue = msLookupHashTable(tagArgs, "ph");
×
2263
      if (argValue)
×
2264
        ph = argValue;
2265
      argValue = msLookupHashTable(tagArgs, "pf");
×
2266
      if (argValue)
×
2267
        pf = argValue;
2268
      argValue = msLookupHashTable(tagArgs, "ps");
×
2269
      if (argValue)
×
2270
        ps = argValue;
2271

2272
      argValue = msLookupHashTable(tagArgs, "sh");
×
2273
      if (argValue)
×
2274
        sh = argValue;
2275
      argValue = msLookupHashTable(tagArgs, "sf");
×
2276
      if (argValue)
×
2277
        sf = argValue;
2278

2279
#ifdef USE_GEOS
2280
      argValue = msLookupHashTable(tagArgs, "buffer");
×
2281
      if (argValue) {
×
2282
        buffer = atof(argValue);
2283
        if (strstr(argValue, "px"))
×
2284
          bufferUnits = MS_PIXELS; /* may support others at some point */
2285
      }
2286
#endif
2287

2288
      argValue = msLookupHashTable(tagArgs, "precision");
×
2289
      if (argValue)
×
2290
        precision = atoi(argValue);
2291

2292
      argValue = msLookupHashTable(tagArgs, "scale");
×
2293
      if (argValue) {
×
2294
        scale_x = atof(argValue);
2295
        scale_y = scale_x;
2296
      }
2297

2298
      argValue = msLookupHashTable(tagArgs, "scale_x");
×
2299
      if (argValue)
×
2300
        scale_x = atof(argValue);
2301

2302
      argValue = msLookupHashTable(tagArgs, "scale_y");
×
2303
      if (argValue)
×
2304
        scale_y = atof(argValue);
2305

2306
      argValue = msLookupHashTable(tagArgs, "centroid");
×
2307
      if (argValue)
×
2308
        if (strcasecmp(argValue, "true") == 0)
×
2309
          centroid = MS_TRUE;
2310

2311
      argValue = msLookupHashTable(tagArgs, "proj");
×
2312
      if (argValue)
2313
        projectionString = argValue;
2314
    }
2315

2316
    /* build the per point format strings (version 1 contains the coordinate
2317
     * separator, version 2 doesn't) */
2318
    const int pointFormatLength =
2✔
2319
        strlen(xh) + strlen(xf) + strlen(yh) + strlen(yf) + strlen(cs) + 12 + 1;
2✔
2320
    char *pointFormat1 = (char *)msSmallMalloc(pointFormatLength);
2✔
2321
    snprintf(pointFormat1, pointFormatLength, "%s%%.%dlf%s%s%%.%dlf%s%s", xh,
2322
             precision, xf, yh, precision, yf, cs);
2323
    char *pointFormat2 = (char *)msSmallMalloc(pointFormatLength);
2✔
2324
    snprintf(pointFormat2, pointFormatLength, "%s%%.%dlf%s%s%%.%dlf%s", xh,
2325
             precision, xf, yh, precision, yf);
2326

2327
    /* make a copy of the original shape or compute a centroid if necessary */
2328
    msInitShape(&tShape);
2✔
2329
    if (centroid == MS_TRUE) {
2✔
2330
      pointObj p;
2331

2332
      p.x = (shape->bounds.minx + shape->bounds.maxx) / 2;
×
2333
      p.y = (shape->bounds.miny + shape->bounds.maxy) / 2;
×
2334

2335
      tShape.type = MS_SHAPE_POINT;
×
2336
      tShape.line = (lineObj *)msSmallMalloc(sizeof(lineObj));
×
2337
      tShape.numlines = 1;
×
2338
      tShape.line[0].point = NULL; /* initialize the line */
×
2339
      tShape.line[0].numpoints = 0;
×
2340

2341
      msAddPointToLine(&(tShape.line[0]), &p);
×
2342
    }
2343

2344
#ifdef USE_GEOS
2345
    else if (buffer != 0 && bufferUnits != MS_PIXELS) {
2✔
2346
      shapeObj *bufferShape = NULL;
2347

2348
      bufferShape = msGEOSBuffer(shape, buffer);
×
2349
      if (!bufferShape) {
×
2350
        free(pointFormat1);
×
2351
        free(pointFormat2);
×
2352
        return (MS_FAILURE); /* buffer failed */
×
2353
      }
2354
      msCopyShape(bufferShape, &tShape);
×
2355
      msFreeShape(bufferShape);
×
2356
    }
2357
#endif
2358
    else {
2359
      status = msCopyShape(shape, &tShape);
2✔
2360
      if (status != 0) {
2✔
2361
        free(pointFormat1);
×
2362
        free(pointFormat2);
×
2363
        return (MS_FAILURE); /* copy failed */
×
2364
      }
2365
    }
2366

2367
    /* no big deal to convert from file to image coordinates, but what are the
2368
     * image parameters */
2369
    if (projectionString && strcasecmp(projectionString, "image") == 0) {
2✔
2370
      precision = 0;
2371

2372
      /* if necessary, project the shape to match the map */
2373
      if (msProjectionsDiffer(&(layer->projection),
×
2374
                              &(layer->map->projection))) {
×
2375
        reprojectionObj *reprojector =
2376
            msLayerGetReprojectorToMap(layer, layer->map);
×
2377
        if (reprojector) {
×
2378
          msProjectShapeEx(reprojector, &tShape);
×
2379
        }
2380
      }
2381

2382
      switch (tShape.type) {
×
2383
      case (MS_SHAPE_POINT):
2384
        /* no clipping necessary */
2385
        break;
2386
      case (MS_SHAPE_LINE):
×
2387
        msClipPolylineRect(&tShape, layer->map->extent);
×
2388
        break;
×
2389
      case (MS_SHAPE_POLYGON):
×
2390
        msClipPolygonRect(&tShape, layer->map->extent);
×
2391
        break;
×
2392
      default:
2393
        /* TO DO: need an error message here */
2394
        return (MS_FAILURE);
2395
        break;
2396
      }
2397
      msTransformShapeToPixelRound(&tShape, layer->map->extent,
×
2398
                                   layer->map->cellsize);
×
2399

2400
#ifdef USE_GEOS
2401
      if (buffer != 0 && bufferUnits == MS_PIXELS) {
×
2402
        shapeObj *bufferShape = NULL;
2403

2404
        bufferShape = msGEOSBuffer(&tShape, buffer);
×
2405
        if (!bufferShape) {
×
2406
          if (!msIsDegenerateShape(
×
2407
                  &tShape))      /* If shape is degenerate this is expected. */
2408
            return (MS_FAILURE); /* buffer failed */
2409
        } else {
2410
          msFreeShape(&tShape); /* avoid memory leak */
×
2411
          msCopyShape(bufferShape, &tShape);
×
2412
          msFreeShape(bufferShape);
×
2413
        }
2414
      }
2415
#endif
2416

2417
    } else if (projectionString) {
2418
      projectionObj projection;
2419
      msInitProjection(&projection);
×
2420
      msProjectionInheritContextFrom(&projection, &(layer->projection));
×
2421

2422
      status = msLoadProjectionString(&projection, projectionString);
×
2423
      if (status != MS_SUCCESS)
×
2424
        return MS_FAILURE;
×
2425

2426
      if (msProjectionsDiffer(&(layer->projection), &projection))
×
2427
        msProjectShape(&layer->projection, &projection, &tShape);
×
2428
    }
2429

2430
    /* TODO: add thinning support here */
2431

2432
    /*
2433
    ** build the coordinate string
2434
    */
2435

2436
    char *coords = NULL;
2437
    if (strlen(sh) > 0)
2✔
2438
      coords = msStringConcatenate(coords, sh);
×
2439

2440
    /* do we need to handle inner/outer rings */
2441
    if (tShape.type == MS_SHAPE_POLYGON && strlen(orh) > 0 && strlen(irh) > 0) {
2✔
2442
      int *outers;
2443
      int firstPart; /* to keep track of inserting part separators before each
2444
                        part after the first */
2445
      outers = msGetOuterList(&tShape);
×
2446
      firstPart = 1;
2447
      /* loop over rings looking for outers*/
2448
      for (i = 0; i < tShape.numlines; i++) {
×
2449
        int *inners;
2450
        if (outers[i]) {
×
2451
          /* this is an outer ring */
2452
          if ((!firstPart) && (strlen(ps) > 0))
×
2453
            coords = msStringConcatenate(coords, ps);
×
2454
          firstPart = 0;
2455
          if (strlen(ph) > 0)
×
2456
            coords = msStringConcatenate(coords, ph);
×
2457
          coords = msStringConcatenate(coords, orh);
×
2458
          for (p = 0; p < tShape.line[i].numpoints - 1; p++) {
×
2459
            snprintf(point, sizeof(point), pointFormat1,
×
2460
                     scale_x * tShape.line[i].point[p].x,
×
2461
                     scale_y * tShape.line[i].point[p].y);
×
2462
            coords = msStringConcatenate(coords, point);
×
2463
          }
2464
          snprintf(point, sizeof(point), pointFormat2,
×
2465
                   scale_x * tShape.line[i].point[p].x,
×
2466
                   scale_y * tShape.line[i].point[p].y);
×
2467
          coords = msStringConcatenate(coords, point);
×
2468
          coords = msStringConcatenate(coords, orf);
×
2469

2470
          inners = msGetInnerList(&tShape, i, outers);
×
2471
          /* loop over rings looking for inners to this outer */
2472
          for (j = 0; j < tShape.numlines; j++) {
×
2473
            if (inners[j]) {
×
2474
              /* j is an inner ring of i */
2475
              coords = msStringConcatenate(coords, irh);
×
2476
              for (p = 0; p < tShape.line[j].numpoints - 1; p++) {
×
2477
                snprintf(point, sizeof(point), pointFormat1,
×
2478
                         scale_x * tShape.line[j].point[p].x,
×
2479
                         scale_y * tShape.line[j].point[p].y);
×
2480
                coords = msStringConcatenate(coords, point);
×
2481
              }
2482
              snprintf(point, sizeof(point), pointFormat2,
×
2483
                       scale_x * tShape.line[j].point[p].x,
×
2484
                       scale_y * tShape.line[j].point[p].y);
×
2485
              coords = msStringConcatenate(coords, irf);
×
2486
            }
2487
          }
2488
          free(inners);
×
2489
          if (strlen(pf) > 0)
×
2490
            coords = msStringConcatenate(coords, pf);
×
2491
        }
2492
      } /* end of loop over outer rings */
2493
      free(outers);
×
2494
    } else { /* output without ring formatting */
2495

2496
      for (i = 0; i < tShape.numlines; i++) { /* e.g. part */
4✔
2497

2498
        /* skip degenerate parts, really should only happen with pixel output */
2499
        if ((tShape.type == MS_SHAPE_LINE && tShape.line[i].numpoints < 2) ||
2✔
2500
            (tShape.type == MS_SHAPE_POLYGON && tShape.line[i].numpoints < 3))
2✔
2501
          continue;
×
2502

2503
        if (strlen(ph) > 0)
2✔
2504
          coords = msStringConcatenate(coords, ph);
×
2505

2506
        for (p = 0; p < tShape.line[i].numpoints - 1; p++) {
3,258✔
2507
          snprintf(point, sizeof(point), pointFormat1,
3,256✔
2508
                   scale_x * tShape.line[i].point[p].x,
3,256✔
2509
                   scale_y * tShape.line[i].point[p].y);
3,256✔
2510
          coords = msStringConcatenate(coords, point);
3,256✔
2511
        }
2512
        snprintf(point, sizeof(point), pointFormat2,
2✔
2513
                 scale_x * tShape.line[i].point[p].x,
2✔
2514
                 scale_y * tShape.line[i].point[p].y);
2✔
2515
        coords = msStringConcatenate(coords, point);
2✔
2516

2517
        if (strlen(pf) > 0)
2✔
2518
          coords = msStringConcatenate(coords, pf);
×
2519

2520
        if ((i < tShape.numlines - 1) && (strlen(ps) > 0))
2✔
2521
          coords = msStringConcatenate(coords, ps);
×
2522
      }
2523
    }
2524
    if (strlen(sf) > 0)
2✔
2525
      coords = msStringConcatenate(coords, sf);
×
2526

2527
    msFreeShape(&tShape);
2✔
2528

2529
    /* find the end of the tag */
2530
    tagEnd = findTagEnd(tagStart);
2✔
2531
    tagEnd++;
2✔
2532

2533
    /* build the complete tag so we can do substitution */
2534
    const int tagLength = tagEnd - tagStart;
2✔
2535
    char *tag = (char *)msSmallMalloc(tagLength + 1);
2✔
2536
    strlcpy(tag, tagStart, tagLength + 1);
2537

2538
    /* do the replacement */
2539
    *line = msReplaceSubstring(*line, tag, coords);
2✔
2540

2541
    /* clean up */
2542
    free(tag);
2✔
2543
    msFreeHashTable(tagArgs);
2✔
2544
    free(pointFormat1);
2✔
2545
    free(pointFormat2);
2✔
2546
    free(coords);
2✔
2547

2548
    if ((*line)[tagOffset] != '\0')
2✔
2549
      tagStart = findTag(*line + tagOffset + 1, "shpxy");
2✔
2550
    else
2551
      tagStart = NULL;
2552
  }
2553

2554
  return (MS_SUCCESS);
2555
}
2556

2557
/*!
2558
 * this function process all metadata
2559
 * in pszInstr. ht mus contain all corresponding
2560
 * metadata value.
2561
 *
2562
 * this function return a modified pszInstr
2563
 */
2564
int processMetadata(char **pszInstr, hashTableObj *ht) {
×
2565
  /* char *pszNextInstr = pszInstr; */
2566
  char *pszEnd;
2567
  char *pszMetadataTag;
2568
  const char *pszHashName;
2569
  const char *pszHashValue;
2570
  int nLength, nOffset;
2571

2572
  hashTableObj *metadataArgs = NULL;
×
2573

2574
  if (!*pszInstr) {
×
2575
    msSetError(MS_WEBERR, "Invalid pointer.", "processMetadata()");
×
2576
    return MS_FAILURE;
×
2577
  }
2578

2579
  /* set position to the beginning of metadata tag */
2580
  const char *pszStart = findTag(*pszInstr, "metadata");
×
2581

2582
  while (pszStart) {
×
2583
    /* get metadata args */
2584
    if (getTagArgs("metadata", pszStart, &metadataArgs) != MS_SUCCESS)
×
2585
      return MS_FAILURE;
2586

2587
    pszHashName = msLookupHashTable(metadataArgs, "name");
×
2588
    pszHashValue = msLookupHashTable(ht, pszHashName);
×
2589

2590
    nOffset = pszStart - *pszInstr;
×
2591

2592
    if (pszHashName && pszHashValue) {
×
2593
      /* set position to the end of metadata start tag */
2594
      pszEnd = strchr(pszStart, ']');
×
2595
      pszEnd++;
×
2596

2597
      /* build the complete metadata tag ([metadata all_args]) */
2598
      /* to replace it by the corresponding value from ht */
2599
      nLength = pszEnd - pszStart;
×
2600
      pszMetadataTag = (char *)msSmallMalloc(nLength + 1);
×
2601
      strlcpy(pszMetadataTag, pszStart, nLength + 1);
2602

2603
      *pszInstr = msReplaceSubstring(*pszInstr, pszMetadataTag, pszHashValue);
×
2604

2605
      free(pszMetadataTag);
×
2606
      pszMetadataTag = NULL;
2607
    }
2608

2609
    msFreeHashTable(metadataArgs);
×
2610
    metadataArgs = NULL;
×
2611

2612
    /* set position to the beginning of the next metadata tag */
2613
    if ((*pszInstr)[nOffset] != '\0')
×
2614
      pszStart = findTag(*pszInstr + nOffset + 1, "metadata");
×
2615
    else
2616
      pszStart = NULL;
2617
  }
2618

2619
  return MS_SUCCESS;
2620
}
2621

2622
/*!
2623
 * this function process all icon tag
2624
 * from pszInstr.
2625
 *
2626
 * This func return a modified pszInstr.
2627
 */
2628
int processIcon(mapObj *map, int nIdxLayer, int nIdxClass, char **pszInstr,
×
2629
                char *pszPrefix) {
2630
  int nWidth, nHeight, nLen;
2631
  char szImgFname[1024], *pszImgTag;
2632
  char szPath[MS_MAXPATHLEN];
2633
  hashTableObj *myHashTable = NULL;
×
2634
  FILE *fIcon;
2635

2636
  if (!map || nIdxLayer > map->numlayers || nIdxLayer < 0) {
×
2637
    msSetError(MS_WEBERR, "Invalid pointer.", "processIcon()");
×
2638
    return MS_FAILURE;
×
2639
  }
2640

2641
  /* find the beginning of tag */
2642
  pszImgTag = strstr(*pszInstr, "[leg_icon");
×
2643

2644
  while (pszImgTag) {
×
2645
    int i;
2646
    char szStyleCode[512] = "";
×
2647
    classObj *thisClass = NULL;
2648

2649
    /* It's okay to have no classes... we'll generate an empty icon in this case
2650
     */
2651
    if (nIdxClass >= 0 && nIdxClass < GET_LAYER(map, nIdxLayer)->numclasses)
×
2652
      thisClass = GET_LAYER(map, nIdxLayer)->class[nIdxClass];
×
2653

2654
    if (getTagArgs("leg_icon", pszImgTag, &myHashTable) != MS_SUCCESS)
×
2655
      return MS_FAILURE;
×
2656

2657
    /* if no specified width or height, set them to map default */
2658
    if (!msLookupHashTable(myHashTable, "width") ||
×
2659
        !msLookupHashTable(myHashTable, "height")) {
×
2660
      nWidth = map->legend.keysizex;
×
2661
      nHeight = map->legend.keysizey;
×
2662
    } else {
2663
      nWidth = atoi(msLookupHashTable(myHashTable, "width"));
×
2664
      nHeight = atoi(msLookupHashTable(myHashTable, "height"));
×
2665
    }
2666

2667
    /* Create a unique and predictable filename to cache the legend icons.
2668
     * Include some key parameters from the first 2 styles
2669
     */
2670
    for (i = 0; i < 2 && thisClass && i < thisClass->numstyles; i++) {
×
2671
      styleObj *style;
2672
      char *pszSymbolNameHash = NULL;
2673
      style = thisClass->styles[i];
×
2674
      if (style->symbolname)
×
2675
        pszSymbolNameHash = msHashString(style->symbolname);
×
2676

2677
      snprintf(szStyleCode + strlen(szStyleCode), 255, "s%d_%x_%x_%d_%s_%g", i,
×
2678
               MS_COLOR_GETRGB(style->color),
×
2679
               MS_COLOR_GETRGB(style->outlinecolor), style->symbol,
×
2680
               pszSymbolNameHash ? pszSymbolNameHash : "", style->angle);
2681
      msFree(pszSymbolNameHash);
×
2682
    }
2683

2684
    snprintf(szImgFname, sizeof(szImgFname), "%s_%d_%d_%d_%d_%s.%s%c",
×
2685
             pszPrefix, nIdxLayer, nIdxClass, nWidth, nHeight, szStyleCode,
2686
             MS_IMAGE_EXTENSION(map->outputformat), '\0');
×
2687

2688
    char *pszFullImgFname = msStrdup(
×
2689
        msBuildPath3(szPath, map->mappath, map->web.imagepath, szImgFname));
×
2690

2691
    /* check if icon already exist in cache */
2692
    if ((fIcon = fopen(pszFullImgFname, "r")) != NULL) {
×
2693
      /* File already exists. No need to generate it again */
2694
      fclose(fIcon);
×
2695
    } else {
2696
      /* Create an image corresponding to the current class */
2697
      imageObj *img = NULL;
2698

2699
      if (thisClass == NULL) {
×
2700
        /* Nonexistent class.  Create an empty image */
2701
        img = msCreateLegendIcon(map, NULL, NULL, nWidth, nHeight, MS_TRUE);
×
2702
      } else {
2703
        img = msCreateLegendIcon(map, GET_LAYER(map, nIdxLayer), thisClass,
×
2704
                                 nWidth, nHeight, MS_TRUE);
2705
      }
2706

2707
      if (!img) {
×
2708
        if (myHashTable)
×
2709
          msFreeHashTable(myHashTable);
×
2710

2711
        msSetError(MS_IMGERR, "Error while creating image.", "processIcon()");
×
2712
        msFree(pszFullImgFname);
×
2713
        return MS_FAILURE;
×
2714
      }
2715

2716
      /* save it with a unique file name */
2717
      if (msSaveImage(map, img, pszFullImgFname) != MS_SUCCESS) {
×
2718
        if (myHashTable)
×
2719
          msFreeHashTable(myHashTable);
×
2720

2721
        msFreeImage(img);
×
2722

2723
        msSetError(MS_IOERR, "Error saving GD image to disk (%s).",
×
2724
                   "processIcon()", pszFullImgFname);
2725
        msFree(pszFullImgFname);
×
2726
        return MS_FAILURE;
×
2727
      }
2728

2729
      msFreeImage(img);
×
2730
    }
2731

2732
    msFree(pszFullImgFname);
×
2733

2734
    nLen = (strchr(pszImgTag, ']') + 1) - pszImgTag;
×
2735

2736
    if (nLen > 0) {
×
2737
      char *pszTag;
2738

2739
      /* rebuild image tag ([leg_class_img all_args]) */
2740
      /* to replace it by the image url */
2741
      pszTag = (char *)msSmallMalloc(nLen + 1);
×
2742
      strlcpy(pszTag, pszImgTag, nLen + 1);
2743

2744
      char *pszFullImgUrlFname = (char *)msSmallMalloc(
×
2745
          strlen(map->web.imageurl) + strlen(szImgFname) + 1);
×
2746
      strcpy(pszFullImgUrlFname, map->web.imageurl);
×
2747
      strcat(pszFullImgUrlFname, szImgFname);
2748

2749
      *pszInstr = msReplaceSubstring(*pszInstr, pszTag, pszFullImgUrlFname);
×
2750

2751
      msFree(pszFullImgUrlFname);
×
2752
      msFree(pszTag);
×
2753

2754
      /* find the beginning of tag */
2755
      pszImgTag = strstr(*pszInstr, "[leg_icon");
×
2756
    } else {
2757
      pszImgTag = NULL;
2758
    }
2759

2760
    if (myHashTable) {
×
2761
      msFreeHashTable(myHashTable);
×
2762
      myHashTable = NULL;
×
2763
    }
2764
  }
2765

2766
  return MS_SUCCESS;
2767
}
2768

2769
/*!
2770
 * Replace all tags from group template
2771
 * with correct value.
2772
 *
2773
 * this function return a buffer containing
2774
 * the template with correct values.
2775
 *
2776
 * buffer must be freed by caller.
2777
 */
2778
int generateGroupTemplate(char *pszGroupTemplate, mapObj *map,
×
2779
                          char *pszGroupName, hashTableObj *oGroupArgs,
2780
                          char **pszTemp, char *pszPrefix) {
2781
  hashTableObj *myHashTable;
2782
  char pszStatus[3];
2783
  char *pszClassImg;
2784
  const char *pszOptFlag = NULL;
2785
  int i, j;
2786
  int nOptFlag = 15;
2787
  int bShowGroup;
2788

2789
  *pszTemp = NULL;
×
2790

2791
  if (!pszGroupName || !pszGroupTemplate) {
×
2792
    msSetError(MS_WEBERR, "Invalid pointer.", "generateGroupTemplate()");
×
2793
    return MS_FAILURE;
×
2794
  }
2795

2796
  /*
2797
   * Get the opt_flag is any.
2798
   */
2799
  if (oGroupArgs)
×
2800
    pszOptFlag = msLookupHashTable(oGroupArgs, "opt_flag");
×
2801

2802
  if (pszOptFlag)
×
2803
    nOptFlag = atoi(pszOptFlag);
2804

2805
  /*
2806
   * Check all layers, if one in the group
2807
   * should be visible, print the group.
2808
   * (Check for opt_flag)
2809
   */
2810
  bShowGroup = 0;
2811
  for (j = 0; j < map->numlayers; j++) {
×
2812
    if (GET_LAYER(map, map->layerorder[j])->group &&
×
2813
        strcmp(GET_LAYER(map, map->layerorder[j])->group, pszGroupName) == 0) {
×
2814
      /* don't display layer is off. */
2815
      if ((nOptFlag & 2) == 0 &&
×
2816
          GET_LAYER(map, map->layerorder[j])->status == MS_OFF)
×
2817
        bShowGroup = 0;
2818
      else
2819
        bShowGroup = 1;
2820

2821
      /* don't display layer is query. */
2822
      if ((nOptFlag & 4) == 0 &&
×
2823
          GET_LAYER(map, map->layerorder[j])->type == MS_LAYER_QUERY)
×
2824
        bShowGroup = 0;
2825

2826
      /* don't display layer if out of scale. */
2827
      if ((nOptFlag & 1) == 0) {
×
2828
        if (map->scaledenom > 0) {
×
2829
          if ((GET_LAYER(map, map->layerorder[j])->maxscaledenom > 0) &&
×
2830
              (map->scaledenom >
2831
               GET_LAYER(map, map->layerorder[j])->maxscaledenom))
2832
            bShowGroup = 0;
2833
          if ((GET_LAYER(map, map->layerorder[j])->minscaledenom > 0) &&
×
2834
              (map->scaledenom <=
2835
               GET_LAYER(map, map->layerorder[j])->minscaledenom))
2836
            bShowGroup = 0;
2837
        }
2838
      }
2839

2840
      /* The group contains one visible layer */
2841
      /* Draw the group */
2842
      if (bShowGroup)
×
2843
        break;
2844
    }
2845
  }
2846

2847
  if (!bShowGroup)
×
2848
    return MS_SUCCESS;
2849

2850
  /*
2851
   * Work from a copy
2852
   */
2853
  *pszTemp = (char *)msSmallMalloc(strlen(pszGroupTemplate) + 1);
×
2854
  strcpy(*pszTemp, pszGroupTemplate);
2855

2856
  /*
2857
   * Change group tags
2858
   */
2859
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_group_name]", pszGroupName);
×
2860

2861
  /*
2862
   * Create a hash table that contain info
2863
   * on current layer
2864
   */
2865
  myHashTable = msCreateHashTable();
×
2866

2867
  /*
2868
   * Check for the first layer
2869
   * that belong to this group.
2870
   * Get his status and check for if.
2871
   */
2872
  for (j = 0; j < map->numlayers; j++) {
×
2873
    if (GET_LAYER(map, map->layerorder[j])->group &&
×
2874
        strcmp(GET_LAYER(map, map->layerorder[j])->group, pszGroupName) == 0) {
×
2875
      snprintf(pszStatus, sizeof(pszStatus), "%d",
×
2876
               GET_LAYER(map, map->layerorder[j])->status);
2877
      msInsertHashTable(myHashTable, "layer_status", pszStatus);
×
2878
      msInsertHashTable(
×
2879
          myHashTable, "layer_visible",
2880
          msLayerIsVisible(map, GET_LAYER(map, map->layerorder[j])) ? "1"
×
2881
                                                                    : "0");
2882
      msInsertHashTable(
×
2883
          myHashTable, "layer_queryable",
2884
          msIsLayerQueryable(GET_LAYER(map, map->layerorder[j])) ? "1" : "0");
×
2885
      msInsertHashTable(myHashTable, "group_name", pszGroupName);
×
2886

2887
      if (processIfTag(pszTemp, myHashTable, MS_FALSE) != MS_SUCCESS)
×
2888
        return MS_FAILURE;
2889

2890
      if (processIfTag(pszTemp, &(GET_LAYER(map, map->layerorder[j])->metadata),
×
2891
                       MS_FALSE) != MS_SUCCESS)
2892
        return MS_FAILURE;
2893

2894
      if (processMetadata(pszTemp,
×
2895
                          &GET_LAYER(map, map->layerorder[j])->metadata) !=
×
2896
          MS_SUCCESS)
2897
        return MS_FAILURE;
2898

2899
      break;
2900
    }
2901
  }
2902

2903
  msFreeHashTable(myHashTable);
×
2904

2905
  /*
2906
   * Process all metadata tags
2907
   * only web object is accessible
2908
   */
2909
  if (processMetadata(pszTemp, &(map->web.metadata)) != MS_SUCCESS)
×
2910
    return MS_FAILURE;
2911

2912
  /*
2913
   * check for if tag
2914
   */
2915
  if (processIfTag(pszTemp, &(map->web.metadata), MS_TRUE) != MS_SUCCESS)
×
2916
    return MS_FAILURE;
2917

2918
  /*
2919
   * Check if leg_icon tag exist
2920
   * if so display the first layer first class icon
2921
   */
2922
  pszClassImg = strstr(*pszTemp, "[leg_icon");
×
2923
  if (pszClassImg) {
×
2924
    /* find first layer of this group */
2925
    for (i = 0; i < map->numlayers; i++)
×
2926
      if (GET_LAYER(map, map->layerorder[i])->group &&
×
2927
          strcmp(GET_LAYER(map, map->layerorder[i])->group, pszGroupName) == 0)
×
2928
        processIcon(map, map->layerorder[i], 0, pszTemp, pszPrefix);
×
2929
  }
2930

2931
  return MS_SUCCESS;
2932
}
2933

2934
/*!
2935
 * Replace all tags from layer template
2936
 * with correct value.
2937
 *
2938
 * this function return a buffer containing
2939
 * the template with correct values.
2940
 *
2941
 * buffer must be freed by caller.
2942
 */
2943
int generateLayerTemplate(char *pszLayerTemplate, mapObj *map, int nIdxLayer,
×
2944
                          hashTableObj *oLayerArgs, char **pszTemp,
2945
                          char *pszPrefix) {
2946
  hashTableObj *myHashTable;
2947
  char szStatus[10];
2948
  char szType[10];
2949

2950
  int nOptFlag = 0;
2951
  const char *pszOptFlag = NULL;
2952
  char *pszClassImg;
2953

2954
  char
2955
      szTmpstr[128]; /* easily big enough for the couple of instances we need */
2956

2957
  *pszTemp = NULL;
×
2958

2959
  if (!pszLayerTemplate || !map || nIdxLayer > map->numlayers ||
×
2960
      nIdxLayer < 0) {
2961
    msSetError(MS_WEBERR, "Invalid pointer.", "generateLayerTemplate()");
×
2962
    return MS_FAILURE;
×
2963
  }
2964

2965
  if (oLayerArgs)
×
2966
    pszOptFlag = msLookupHashTable(oLayerArgs, "opt_flag");
×
2967

2968
  if (pszOptFlag)
×
2969
    nOptFlag = atoi(pszOptFlag);
2970

2971
  /* don't display deleted layers */
2972
  if (GET_LAYER(map, nIdxLayer)->status == MS_DELETE)
×
2973
    return MS_SUCCESS;
2974

2975
  /* don't display layer is off. */
2976
  /* check this if Opt flag is not set */
2977
  if ((nOptFlag & 2) == 0 && GET_LAYER(map, nIdxLayer)->status == MS_OFF)
×
2978
    return MS_SUCCESS;
2979

2980
  /* don't display layer is query. */
2981
  /* check this if Opt flag is not set */
2982
  if ((nOptFlag & 4) == 0 && GET_LAYER(map, nIdxLayer)->type == MS_LAYER_QUERY)
×
2983
    return MS_SUCCESS;
2984

2985
  /* don't display layer if out of scale. */
2986
  /* check this if Opt flag is not set             */
2987
  if ((nOptFlag & 1) == 0) {
×
2988
    if (map->scaledenom > 0) {
×
2989
      if ((GET_LAYER(map, nIdxLayer)->maxscaledenom > 0) &&
×
2990
          (map->scaledenom > GET_LAYER(map, nIdxLayer)->maxscaledenom))
2991
        return MS_SUCCESS;
2992
      if ((GET_LAYER(map, nIdxLayer)->minscaledenom > 0) &&
×
2993
          (map->scaledenom <= GET_LAYER(map, nIdxLayer)->minscaledenom))
2994
        return MS_SUCCESS;
2995
    }
2996
  }
2997

2998
  /*
2999
   * Work from a copy
3000
   */
3001
  *pszTemp = msStrdup(pszLayerTemplate);
×
3002

3003
  /*
3004
   * Change layer tags
3005
   */
3006
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_layer_name]",
×
3007
                                GET_LAYER(map, nIdxLayer)->name);
×
3008
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_layer_group]",
×
3009
                                GET_LAYER(map, nIdxLayer)->group);
×
3010

3011
  snprintf(szTmpstr, sizeof(szTmpstr), "%d", nIdxLayer);
3012
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_layer_index]", szTmpstr);
×
3013

3014
  snprintf(szTmpstr, sizeof(szTmpstr), "%g",
×
3015
           GET_LAYER(map, nIdxLayer)->minscaledenom);
×
3016
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_layer_minscale]", szTmpstr);
×
3017
  *pszTemp =
×
3018
      msReplaceSubstring(*pszTemp, "[leg_layer_minscaledenom]", szTmpstr);
×
3019
  snprintf(szTmpstr, sizeof(szTmpstr), "%g",
×
3020
           GET_LAYER(map, nIdxLayer)->maxscaledenom);
×
3021
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_layer_maxscale]", szTmpstr);
×
3022
  *pszTemp =
×
3023
      msReplaceSubstring(*pszTemp, "[leg_layer_maxscaledenom]", szTmpstr);
×
3024

3025
  /*
3026
   * Create a hash table that contain info
3027
   * on current layer
3028
   */
3029
  myHashTable = msCreateHashTable();
×
3030

3031
  /*
3032
   * for now, only status and type is required by template
3033
   */
3034
  snprintf(szStatus, sizeof(szStatus), "%d", GET_LAYER(map, nIdxLayer)->status);
×
3035
  msInsertHashTable(myHashTable, "layer_status", szStatus);
×
3036

3037
  snprintf(szType, sizeof(szType), "%d", GET_LAYER(map, nIdxLayer)->type);
×
3038
  msInsertHashTable(myHashTable, "layer_type", szType);
×
3039

3040
  msInsertHashTable(
×
3041
      myHashTable, "layer_name",
3042
      (GET_LAYER(map, nIdxLayer)->name) ? GET_LAYER(map, nIdxLayer)->name : "");
×
3043
  msInsertHashTable(myHashTable, "layer_group",
×
3044
                    (GET_LAYER(map, nIdxLayer)->group)
×
3045
                        ? GET_LAYER(map, nIdxLayer)->group
3046
                        : "");
3047
  msInsertHashTable(myHashTable, "layer_visible",
×
3048
                    msLayerIsVisible(map, GET_LAYER(map, nIdxLayer)) ? "1"
×
3049
                                                                     : "0");
3050
  msInsertHashTable(myHashTable, "layer_queryable",
×
3051
                    msIsLayerQueryable(GET_LAYER(map, nIdxLayer)) ? "1" : "0");
×
3052

3053
  if (processIfTag(pszTemp, myHashTable, MS_FALSE) != MS_SUCCESS)
×
3054
    return MS_FAILURE;
3055

3056
  if (processIfTag(pszTemp, &(GET_LAYER(map, nIdxLayer)->metadata), MS_FALSE) !=
×
3057
      MS_SUCCESS)
3058
    return MS_FAILURE;
3059

3060
  if (processIfTag(pszTemp, &(map->web.metadata), MS_TRUE) != MS_SUCCESS)
×
3061
    return MS_FAILURE;
3062

3063
  msFreeHashTable(myHashTable);
×
3064

3065
  /*
3066
   * Check if leg_icon tag exist
3067
   * if so display the first class icon
3068
   */
3069
  pszClassImg = strstr(*pszTemp, "[leg_icon");
×
3070
  if (pszClassImg) {
×
3071
    processIcon(map, nIdxLayer, 0, pszTemp, pszPrefix);
×
3072
  }
3073

3074
  /* process all metadata tags
3075
   * only current layer and web object
3076
   * metadata are accessible
3077
   */
3078
  if (processMetadata(pszTemp, &GET_LAYER(map, nIdxLayer)->metadata) !=
×
3079
      MS_SUCCESS)
3080
    return MS_FAILURE;
3081

3082
  if (processMetadata(pszTemp, &(map->web.metadata)) != MS_SUCCESS)
×
3083
    return MS_FAILURE;
3084

3085
  return MS_SUCCESS;
3086
}
3087

3088
/*!
3089
 * Replace all tags from class template
3090
 * with correct value.
3091
 *
3092
 * this function return a buffer containing
3093
 * the template with correct values.
3094
 *
3095
 * buffer must be freed by caller.
3096
 */
3097
int generateClassTemplate(char *pszClassTemplate, mapObj *map, int nIdxLayer,
×
3098
                          int nIdxClass, hashTableObj *oClassArgs,
3099
                          char **pszTemp, char *pszPrefix) {
3100
  hashTableObj *myHashTable;
3101
  char szStatus[10];
3102
  char szType[10];
3103

3104
  char *pszClassImg;
3105
  int nOptFlag = 0;
3106
  const char *pszOptFlag = NULL;
3107

3108
  char
3109
      szTmpstr[128]; /* easily big enough for the couple of instances we need */
3110

3111
  *pszTemp = NULL;
×
3112

3113
  if (!pszClassTemplate || !map || nIdxLayer > map->numlayers ||
×
3114
      nIdxLayer < 0 || nIdxClass > GET_LAYER(map, nIdxLayer)->numclasses ||
×
3115
      nIdxClass < 0) {
3116

3117
    msSetError(MS_WEBERR, "Invalid pointer.", "generateClassTemplate()");
×
3118
    return MS_FAILURE;
×
3119
  }
3120

3121
  if (oClassArgs)
×
3122
    pszOptFlag = msLookupHashTable(oClassArgs, "Opt_flag");
×
3123

3124
  if (pszOptFlag)
×
3125
    nOptFlag = atoi(pszOptFlag);
3126

3127
  /* don't display deleted layers */
3128
  if (GET_LAYER(map, nIdxLayer)->status == MS_DELETE)
×
3129
    return MS_SUCCESS;
3130

3131
  /* don't display class if layer is off. */
3132
  /* check this if Opt flag is not set */
3133
  if ((nOptFlag & 2) == 0 && GET_LAYER(map, nIdxLayer)->status == MS_OFF)
×
3134
    return MS_SUCCESS;
3135

3136
  /* don't display class if layer is query. */
3137
  /* check this if Opt flag is not set       */
3138
  if ((nOptFlag & 4) == 0 && GET_LAYER(map, nIdxLayer)->type == MS_LAYER_QUERY)
×
3139
    return MS_SUCCESS;
3140

3141
  /* don't display layer if out of scale. */
3142
  /* check this if Opt flag is not set */
3143
  if ((nOptFlag & 1) == 0) {
×
3144
    if (map->scaledenom > 0) {
×
3145
      if ((GET_LAYER(map, nIdxLayer)->maxscaledenom > 0) &&
×
3146
          (map->scaledenom > GET_LAYER(map, nIdxLayer)->maxscaledenom))
3147
        return MS_SUCCESS;
3148
      if ((GET_LAYER(map, nIdxLayer)->minscaledenom > 0) &&
×
3149
          (map->scaledenom <= GET_LAYER(map, nIdxLayer)->minscaledenom))
3150
        return MS_SUCCESS;
3151
    }
3152
  }
3153

3154
  /*
3155
   * Work from a copy
3156
   */
3157
  *pszTemp = (char *)msSmallMalloc(strlen(pszClassTemplate) + 1);
×
3158
  strcpy(*pszTemp, pszClassTemplate);
3159

3160
  /*
3161
   * Change class tags
3162
   */
3163
  *pszTemp =
×
3164
      msReplaceSubstring(*pszTemp, "[leg_class_name]",
×
3165
                         GET_LAYER(map, nIdxLayer)->class[nIdxClass] -> name);
×
3166
  *pszTemp =
×
3167
      msReplaceSubstring(*pszTemp, "[leg_class_title]",
×
3168
                         GET_LAYER(map, nIdxLayer)->class[nIdxClass] -> title);
×
3169
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_layer_name]",
×
3170
                                GET_LAYER(map, nIdxLayer)->name);
×
3171

3172
  snprintf(szTmpstr, sizeof(szTmpstr), "%d", nIdxClass);
3173
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_class_index]", szTmpstr);
×
3174

3175
  snprintf(szTmpstr, sizeof(szTmpstr), "%g",
×
3176
           GET_LAYER(map, nIdxLayer)->class[nIdxClass] -> minscaledenom);
×
3177
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_class_minscale]", szTmpstr);
×
3178
  *pszTemp =
×
3179
      msReplaceSubstring(*pszTemp, "[leg_class_minscaledenom]", szTmpstr);
×
3180
  snprintf(szTmpstr, sizeof(szTmpstr), "%g",
×
3181
           GET_LAYER(map, nIdxLayer)->class[nIdxClass] -> maxscaledenom);
×
3182
  *pszTemp = msReplaceSubstring(*pszTemp, "[leg_class_maxscale]", szTmpstr);
×
3183
  *pszTemp =
×
3184
      msReplaceSubstring(*pszTemp, "[leg_class_maxscaledenom]", szTmpstr);
×
3185

3186
  /*
3187
   * Create a hash table that contain info
3188
   * on current layer
3189
   */
3190
  myHashTable = msCreateHashTable();
×
3191

3192
  /*
3193
   * for now, only status, type, name and group are  required by template
3194
   */
3195
  snprintf(szStatus, sizeof(szStatus), "%d", GET_LAYER(map, nIdxLayer)->status);
×
3196
  msInsertHashTable(myHashTable, "layer_status", szStatus);
×
3197

3198
  snprintf(szType, sizeof(szType), "%d", GET_LAYER(map, nIdxLayer)->type);
×
3199
  msInsertHashTable(myHashTable, "layer_type", szType);
×
3200

3201
  msInsertHashTable(
×
3202
      myHashTable, "layer_name",
3203
      (GET_LAYER(map, nIdxLayer)->name) ? GET_LAYER(map, nIdxLayer)->name : "");
×
3204
  msInsertHashTable(myHashTable, "layer_group",
×
3205
                    (GET_LAYER(map, nIdxLayer)->group)
×
3206
                        ? GET_LAYER(map, nIdxLayer)->group
3207
                        : "");
3208
  msInsertHashTable(myHashTable, "layer_visible",
×
3209
                    msLayerIsVisible(map, GET_LAYER(map, nIdxLayer)) ? "1"
×
3210
                                                                     : "0");
3211
  msInsertHashTable(myHashTable, "layer_queryable",
×
3212
                    msIsLayerQueryable(GET_LAYER(map, nIdxLayer)) ? "1" : "0");
×
3213
  msInsertHashTable(myHashTable, "class_name",
×
3214
                    (GET_LAYER(map, nIdxLayer)->class[nIdxClass] -> name)
×
3215
                    ? GET_LAYER(map, nIdxLayer)->class[nIdxClass] -> name
3216
                    : "");
3217

3218
  if (processIfTag(pszTemp, myHashTable, MS_FALSE) != MS_SUCCESS)
×
3219
    return MS_FAILURE;
3220

3221
  if (processIfTag(pszTemp, &(GET_LAYER(map, nIdxLayer)->metadata), MS_FALSE) !=
×
3222
      MS_SUCCESS)
3223
    return MS_FAILURE;
3224

3225
  if (processIfTag(pszTemp, &(map->web.metadata), MS_TRUE) != MS_SUCCESS)
×
3226
    return MS_FAILURE;
3227

3228
  msFreeHashTable(myHashTable);
×
3229

3230
  /*
3231
   * Check if leg_icon tag exist
3232
   */
3233
  pszClassImg = strstr(*pszTemp, "[leg_icon");
×
3234
  if (pszClassImg) {
×
3235
    processIcon(map, nIdxLayer, nIdxClass, pszTemp, pszPrefix);
×
3236
  }
3237

3238
  /* process all metadata tags
3239
   * only current layer and web object
3240
   * metadata are accessible
3241
   */
3242
  if (processMetadata(pszTemp, &GET_LAYER(map, nIdxLayer)->metadata) !=
×
3243
      MS_SUCCESS)
3244
    return MS_FAILURE;
3245

3246
  if (processMetadata(pszTemp, &(map->web.metadata)) != MS_SUCCESS)
×
3247
    return MS_FAILURE;
3248

3249
  return MS_SUCCESS;
3250
}
3251

3252
char *generateLegendTemplate(mapservObj *mapserv) {
×
3253
  FILE *stream;
3254
  char *file = NULL;
3255
  int length;
3256
  char *pszResult = NULL;
3257
  char *legGroupHtml = NULL;
×
3258
  char *legLayerHtml = NULL;
×
3259
  char *legClassHtml = NULL;
×
3260
  char *legLayerHtmlCopy = NULL;
×
3261
  char *legClassHtmlCopy = NULL;
×
3262
  char *legGroupHtmlCopy = NULL;
×
3263

3264
  char *legHeaderHtml = NULL;
×
3265
  char *legFooterHtml = NULL;
×
3266

3267
  char *pszPrefix = NULL;
3268
  char *pszMapFname = NULL;
3269

3270
  struct stat tmpStat;
3271

3272
  const char *pszOrderMetadata = NULL;
3273
  const char *pszOrder = NULL;
3274

3275
  int i, j, k;
3276
  char **papszGroups = NULL;
3277
  int nGroupNames = 0;
×
3278

3279
  const char *pszOrderValue;
3280

3281
  hashTableObj *groupArgs = NULL;
×
3282
  hashTableObj *layerArgs = NULL;
×
3283
  hashTableObj *classArgs = NULL;
×
3284

3285
  ms_regex_t re; /* compiled regular expression to be matched */
3286

3287
  int *panCurrentDrawingOrder = NULL;
3288
  char szPath[MS_MAXPATHLEN];
3289

3290
  if (ms_regcomp(&re, MS_TEMPLATE_EXPR,
×
3291
                 MS_REG_EXTENDED | MS_REG_NOSUB | MS_REG_ICASE) != 0) {
3292
    msSetError(MS_IOERR, "Error regcomp.", "generateLegendTemplate()");
×
3293
    return NULL;
×
3294
  }
3295

3296
  if (ms_regexec(&re, mapserv->map->legend.template, 0, NULL, 0) !=
×
3297
      0) { /* no match */
3298
    msSetError(MS_IOERR, "Invalid template file name.",
×
3299
               "generateLegendTemplate()");
3300
    ms_regfree(&re);
×
3301
    return NULL;
×
3302
  }
3303
  ms_regfree(&re);
×
3304

3305
  /* -------------------------------------------------------------------- */
3306
  /*      Save the current drawing order. The drawing order is reset      */
3307
  /*      at the end of the function.                                     */
3308
  /* -------------------------------------------------------------------- */
3309
  if (mapserv->map->numlayers > 0) {
×
3310
    panCurrentDrawingOrder =
3311
        (int *)msSmallMalloc(sizeof(int) * mapserv->map->numlayers);
×
3312

3313
    for (i = 0; i < mapserv->map->numlayers; i++) {
×
3314
      if (mapserv->map->layerorder)
×
3315
        panCurrentDrawingOrder[i] = mapserv->map->layerorder[i];
×
3316
      else
3317
        panCurrentDrawingOrder[i] = i;
×
3318
    }
3319
  }
3320

3321
  /*
3322
   * build prefix filename
3323
   * for legend icon creation
3324
   */
3325
  for (i = 0; i < mapserv->request->NumParams;
×
3326
       i++) /* find the mapfile parameter first */
×
3327
    if (strcasecmp(mapserv->request->ParamNames[i], "map") == 0)
×
3328
      break;
3329

3330
  if (i == mapserv->request->NumParams) {
×
3331
    const char *ms_mapfile = CPLGetConfigOption("MS_MAPFILE", NULL);
×
3332
    if (ms_mapfile)
×
3333
      pszMapFname = msStringConcatenate(pszMapFname, ms_mapfile);
×
3334
  } else {
3335
    if (getenv(mapserv->request->ParamValues[i])) /* an environment references
×
3336
                                                     the actual file to use */
3337
      pszMapFname = msStringConcatenate(
×
3338
          pszMapFname, getenv(mapserv->request->ParamValues[i]));
×
3339
    else
3340
      pszMapFname =
3341
          msStringConcatenate(pszMapFname, mapserv->request->ParamValues[i]);
×
3342
  }
3343

3344
  if (pszMapFname) {
×
3345
    if (stat(pszMapFname, &tmpStat) != -1) {
×
3346
      int nLen;
3347

3348
      nLen = (mapserv->map->name ? strlen(mapserv->map->name) : 0) + 50;
×
3349
      pszPrefix = (char *)msSmallMalloc((nLen + 1) * sizeof(char));
×
3350
      snprintf(pszPrefix, nLen, "%s_%ld_%ld", mapserv->map->name,
×
3351
               (long)tmpStat.st_size, (long)tmpStat.st_mtime);
×
3352
      pszPrefix[nLen] = '\0';
×
3353
    }
3354

3355
    free(pszMapFname);
×
3356
    pszMapFname = NULL;
3357
  } else {
3358
    /* -------------------------------------------------------------------- */
3359
    /*      map file name may not be available when the template functions    */
3360
    /*      are called from mapscript. Use the time stamp as prefix.        */
3361
    /* -------------------------------------------------------------------- */
3362
    char pszTime[20];
3363

3364
    snprintf(pszTime, sizeof(pszTime), "%ld", (long)time(NULL));
×
3365
    pszPrefix = msStringConcatenate(pszPrefix, pszTime);
×
3366
  }
3367

3368
  /* open template */
3369
  if ((stream = fopen(msBuildPath(szPath, mapserv->map->mappath,
×
3370
                                  mapserv->map->legend.template),
×
3371
                      "r")) == NULL) {
3372
    msSetError(MS_IOERR, "Error while opening template file.",
×
3373
               "generateLegendTemplate()");
3374
    free(pszResult);
3375
    pszResult = NULL;
3376
    goto error;
×
3377
  }
3378

3379
  fseek(stream, 0, SEEK_END);
×
3380
  long lengthLong = ftell(stream);
×
3381
  rewind(stream);
×
3382
  if (lengthLong < 0 || lengthLong > INT_MAX - 1) {
×
3383
    msSetError(MS_IOERR, "Too large template file.",
×
3384
               "generateLegendTemplate()");
3385
    free(pszResult);
3386
    pszResult = NULL;
3387
    goto error;
×
3388
  }
3389
  length = (int)lengthLong;
×
3390

3391
  file = (char *)malloc(length + 1);
×
3392
  if (file == NULL) {
×
3393
    msSetError(MS_IOERR, "Cannot allocate memory for template file.",
×
3394
               "generateLegendTemplate()");
3395
    free(pszResult);
3396
    pszResult = NULL;
3397
    goto error;
×
3398
  }
3399

3400
  /*
3401
   * Read all the template file
3402
   */
3403
  IGUR_sizet(fread(file, length, 1, stream));
×
3404
  /* E. Rouault: the below issue is due to opening in "r" mode, which is a
3405
   * synonymous of "rt" on Windows. In that mode \r\n are turned into \n,
3406
   * consequently less bytes are written in the output buffer than requested.
3407
   * A potential fix might be to open in "rb" mode, but is the code ready
3408
   * to deal with Windows \r\n end of lines ? */
3409
  /* Disabled for now due to Windows issue, see ticket #3814
3410
     if( 1 != fread(file, length, 1, stream)) {
3411
       msSetError(MS_IOERR, "Error while reading template file.",
3412
     "generateLegendTemplate()"); free(file); fclose(stream); return NULL;
3413
     }
3414
  */
3415
  file[length] = '\0';
×
3416

3417
  if (msValidateContexts(mapserv->map) !=
×
3418
      MS_SUCCESS) { /* make sure there are no recursive REQUIRES or
3419
                       LABELREQUIRES expressions */
3420
    if (pszResult)
3421
      free(pszResult);
3422
    pszResult = NULL;
3423
    goto error;
×
3424
  }
3425

3426
  /*
3427
   * Separate header/footer, groups, layers and class
3428
   */
3429
  getInlineTag("leg_header_html", file, &legHeaderHtml);
×
3430
  getInlineTag("leg_footer_html", file, &legFooterHtml);
×
3431
  getInlineTag("leg_group_html", file, &legGroupHtml);
×
3432
  getInlineTag("leg_layer_html", file, &legLayerHtml);
×
3433
  getInlineTag("leg_class_html", file, &legClassHtml);
×
3434

3435
  /*
3436
   * Retrieve arguments of all three parts
3437
   */
3438
  if (legGroupHtml)
×
3439
    if (getTagArgs("leg_group_html", file, &groupArgs) != MS_SUCCESS) {
×
3440
      if (pszResult)
3441
        free(pszResult);
3442
      pszResult = NULL;
3443
      goto error;
×
3444
    }
3445

3446
  if (legLayerHtml)
×
3447
    if (getTagArgs("leg_layer_html", file, &layerArgs) != MS_SUCCESS) {
×
3448
      if (pszResult)
3449
        free(pszResult);
3450
      pszResult = NULL;
3451
      goto error;
×
3452
    }
3453

3454
  if (legClassHtml)
×
3455
    if (getTagArgs("leg_class_html", file, &classArgs) != MS_SUCCESS) {
×
3456
      if (pszResult)
3457
        free(pszResult);
3458
      pszResult = NULL;
3459
      goto error;
×
3460
    }
3461

3462
  mapserv->map->cellsize = msAdjustExtent(
×
3463
      &(mapserv->map->extent), mapserv->map->width, mapserv->map->height);
×
3464
  if (msCalculateScale(mapserv->map->extent, mapserv->map->units,
×
3465
                       mapserv->map->width, mapserv->map->height,
3466
                       mapserv->map->resolution,
3467
                       &mapserv->map->scaledenom) != MS_SUCCESS) {
×
3468
    if (pszResult)
3469
      free(pszResult);
3470
    pszResult = NULL;
3471
    goto error;
×
3472
  }
3473

3474
  /* start with the header if present */
3475
  if (legHeaderHtml)
×
3476
    pszResult = msStringConcatenate(pszResult, legHeaderHtml);
×
3477

3478
  /********************************************************************/
3479

3480
  /*
3481
   * order layers if order_metadata args is set
3482
   * If not, keep default order
3483
   */
3484
  pszOrderMetadata = msLookupHashTable(layerArgs, "order_metadata");
×
3485

3486
  if (sortLayerByMetadata(mapserv->map, pszOrderMetadata) != MS_SUCCESS) {
×
3487
    if (pszResult)
×
3488
      free(pszResult);
×
3489
    pszResult = NULL;
3490
    goto error;
×
3491
  }
3492

3493
  /* -------------------------------------------------------------------- */
3494
  /*      if the order tag is set to ascending or descending, the         */
3495
  /*      current order will be changed to correspond to that.            */
3496
  /* -------------------------------------------------------------------- */
3497
  pszOrder = msLookupHashTable(layerArgs, "order");
×
3498
  if (pszOrder && ((strcasecmp(pszOrder, "ASCENDING") == 0) ||
×
3499
                   (strcasecmp(pszOrder, "DESCENDING") == 0))) {
×
3500
    if (sortLayerByOrder(mapserv->map, pszOrder) != MS_SUCCESS) {
×
3501
      if (pszResult)
×
3502
        free(pszResult);
×
3503
      pszResult = NULL;
3504
      goto error;
×
3505
    }
3506
  }
3507

3508
  if (legGroupHtml) {
×
3509
    /* retrieve group names */
3510
    papszGroups = msGetAllGroupNames(mapserv->map, &nGroupNames);
×
3511

3512
    for (i = 0; i < nGroupNames; i++) {
×
3513
      /* process group tags */
3514
      if (generateGroupTemplate(legGroupHtml, mapserv->map, papszGroups[i],
×
3515
                                groupArgs, &legGroupHtmlCopy,
3516
                                pszPrefix) != MS_SUCCESS) {
3517
        if (pszResult)
×
3518
          free(pszResult);
×
3519
        pszResult = NULL;
3520
        goto error;
×
3521
      }
3522

3523
      /* concatenate it to final result */
3524
      pszResult = msStringConcatenate(pszResult, legGroupHtmlCopy);
×
3525

3526
      /*
3527
               if(!pszResult)
3528
               {
3529
                  if(pszResult)
3530
                    free(pszResult);
3531
                  pszResult=NULL;
3532
                  goto error;
3533
               }
3534
      */
3535

3536
      if (legGroupHtmlCopy) {
×
3537
        free(legGroupHtmlCopy);
×
3538
        legGroupHtmlCopy = NULL;
×
3539
      }
3540

3541
      /* for all layers in group */
3542
      if (legLayerHtml) {
×
3543
        for (j = 0; j < mapserv->map->numlayers; j++) {
×
3544
          /*
3545
           * if order_metadata is set and the order
3546
           * value is less than 0, don't display it
3547
           */
3548
          pszOrderMetadata = msLookupHashTable(layerArgs, "order_metadata");
×
3549
          if (pszOrderMetadata) {
×
3550
            pszOrderValue = msLookupHashTable(
×
3551
                &(GET_LAYER(mapserv->map, mapserv->map->layerorder[j])
×
3552
                      ->metadata),
3553
                pszOrderMetadata);
3554
            if (pszOrderValue) {
×
3555
              const int nLegendOrder = atoi(pszOrderValue);
3556
              if (nLegendOrder < 0)
×
3557
                continue;
×
3558
            }
3559
          }
3560
          if (mapserv->hittest &&
×
3561
              mapserv->hittest->layerhits[mapserv->map->layerorder[j]].status ==
×
3562
                  0) {
3563
            continue;
×
3564
          }
3565

3566
          if (GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->group &&
×
3567
              strcmp(
×
3568
                  GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->group,
3569
                  papszGroups[i]) == 0) {
3570
            /* process all layer tags */
3571
            if (generateLayerTemplate(
×
3572
                    legLayerHtml, mapserv->map, mapserv->map->layerorder[j],
3573
                    layerArgs, &legLayerHtmlCopy, pszPrefix) != MS_SUCCESS) {
3574
              if (pszResult)
×
3575
                free(pszResult);
×
3576
              pszResult = NULL;
3577
              goto error;
×
3578
            }
3579

3580
            /* concatenate to final result */
3581
            pszResult = msStringConcatenate(pszResult, legLayerHtmlCopy);
×
3582

3583
            if (legLayerHtmlCopy) {
×
3584
              free(legLayerHtmlCopy);
×
3585
              legLayerHtmlCopy = NULL;
×
3586
            }
3587

3588
            /* for all classes in layer */
3589
            if (legClassHtml) {
×
3590
              for (k = 0;
3591
                   k < GET_LAYER(mapserv->map, mapserv->map->layerorder[j])
×
3592
                           ->numclasses;
×
3593
                   k++) {
×
3594
                /* process all class tags */
3595
                if (!GET_LAYER(mapserv->map, mapserv->map->layerorder[j])
×
3596
                         ->class[k] -> name)
×
3597
                  continue;
×
3598
                if (mapserv->hittest &&
×
3599
                    mapserv->hittest->layerhits[mapserv->map->layerorder[j]]
×
3600
                            .classhits[k]
×
3601
                            .status == 0) {
×
3602
                  continue;
×
3603
                }
3604

3605
                if (generateClassTemplate(legClassHtml, mapserv->map,
×
3606
                                          mapserv->map->layerorder[j], k,
3607
                                          classArgs, &legClassHtmlCopy,
3608
                                          pszPrefix) != MS_SUCCESS) {
3609
                  if (pszResult)
×
3610
                    free(pszResult);
×
3611
                  pszResult = NULL;
3612
                  goto error;
×
3613
                }
3614

3615
                /* concatenate to final result */
3616
                pszResult = msStringConcatenate(pszResult, legClassHtmlCopy);
×
3617

3618
                if (legClassHtmlCopy) {
×
3619
                  free(legClassHtmlCopy);
×
3620
                  legClassHtmlCopy = NULL;
×
3621
                }
3622
              }
3623
            }
3624
          }
3625
        }
3626
      } else if (legClassHtml) { /* no layer template specified but class and
×
3627
                                    group template */
3628
        for (j = 0; j < mapserv->map->numlayers; j++) {
×
3629
          /*
3630
           * if order_metadata is set and the order
3631
           * value is less than 0, don't display it
3632
           */
3633
          pszOrderMetadata = msLookupHashTable(layerArgs, "order_metadata");
×
3634
          if (pszOrderMetadata) {
×
3635
            pszOrderValue = msLookupHashTable(
×
3636
                &(GET_LAYER(mapserv->map, mapserv->map->layerorder[j])
×
3637
                      ->metadata),
3638
                pszOrderMetadata);
3639
            if (pszOrderValue) {
×
3640
              const int nLegendOrder = atoi(pszOrderValue);
3641
              if (nLegendOrder < 0)
×
3642
                continue;
×
3643
            }
3644
          }
3645
          if (mapserv->hittest &&
×
3646
              mapserv->hittest->layerhits[mapserv->map->layerorder[j]].status ==
×
3647
                  0) {
3648
            continue;
×
3649
          }
3650

3651
          if (GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->group &&
×
3652
              strcmp(
×
3653
                  GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->group,
3654
                  papszGroups[i]) == 0) {
3655
            /* for all classes in layer */
3656

3657
            for (k = 0; k < GET_LAYER(mapserv->map, mapserv->map->layerorder[j])
×
3658
                                ->numclasses;
×
3659
                 k++) {
×
3660
              /* process all class tags */
3661
              if (!GET_LAYER(mapserv->map, mapserv->map->layerorder[j])
×
3662
                       ->class[k] -> name)
×
3663
                continue;
×
3664
              if (mapserv->hittest &&
×
3665
                  mapserv->hittest->layerhits[mapserv->map->layerorder[j]]
×
3666
                          .classhits[k]
×
3667
                          .status == 0) {
×
3668
                continue;
×
3669
              }
3670

3671
              if (generateClassTemplate(legClassHtml, mapserv->map,
×
3672
                                        mapserv->map->layerorder[j], k,
3673
                                        classArgs, &legClassHtmlCopy,
3674
                                        pszPrefix) != MS_SUCCESS) {
3675
                if (pszResult)
×
3676
                  free(pszResult);
×
3677
                pszResult = NULL;
3678
                goto error;
×
3679
              }
3680

3681
              /* concatenate to final result */
3682
              pszResult = msStringConcatenate(pszResult, legClassHtmlCopy);
×
3683

3684
              if (legClassHtmlCopy) {
×
3685
                free(legClassHtmlCopy);
×
3686
                legClassHtmlCopy = NULL;
×
3687
              }
3688
            }
3689
          }
3690
        }
3691
      }
3692
    }
3693
  } else {
3694
    /* if no group template specified */
3695
    if (legLayerHtml) {
×
3696
      for (j = 0; j < mapserv->map->numlayers; j++) {
×
3697
        /*
3698
         * if order_metadata is set and the order
3699
         * value is less than 0, don't display it
3700
         */
3701
        pszOrderMetadata = msLookupHashTable(layerArgs, "order_metadata");
×
3702
        if (pszOrderMetadata) {
×
3703
          pszOrderValue = msLookupHashTable(
×
3704
              &(GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->metadata),
×
3705
              pszOrderMetadata);
3706
          if (pszOrderValue) {
×
3707
            const int nLegendOrder = atoi(pszOrderValue);
3708
            if (nLegendOrder < 0)
×
3709
              continue;
×
3710
          }
3711
        }
3712
        if (mapserv->hittest &&
×
3713
            mapserv->hittest->layerhits[mapserv->map->layerorder[j]].status ==
×
3714
                0) {
3715
          continue;
×
3716
        }
3717

3718
        /* process a layer tags */
3719
        if (generateLayerTemplate(legLayerHtml, mapserv->map,
×
3720
                                  mapserv->map->layerorder[j], layerArgs,
×
3721
                                  &legLayerHtmlCopy, pszPrefix) != MS_SUCCESS) {
3722
          if (pszResult)
×
3723
            free(pszResult);
×
3724
          pszResult = NULL;
3725
          goto error;
×
3726
        }
3727

3728
        /* concatenate to final result */
3729
        pszResult = msStringConcatenate(pszResult, legLayerHtmlCopy);
×
3730

3731
        if (legLayerHtmlCopy) {
×
3732
          free(legLayerHtmlCopy);
×
3733
          legLayerHtmlCopy = NULL;
×
3734
        }
3735

3736
        /* for all classes in layer */
3737
        if (legClassHtml) {
×
3738
          for (k = 0;
3739
               k <
×
3740
               GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->numclasses;
×
3741
               k++) {
×
3742
            /* process all class tags */
3743
            if (!GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->class[k]
×
3744
                -> name)
×
3745
              continue;
×
3746
            if (mapserv->hittest &&
×
3747
                mapserv->hittest->layerhits[mapserv->map->layerorder[j]]
×
3748
                        .classhits[k]
×
3749
                        .status == 0) {
×
3750
              continue;
×
3751
            }
3752

3753
            if (generateClassTemplate(
×
3754
                    legClassHtml, mapserv->map, mapserv->map->layerorder[j], k,
3755
                    classArgs, &legClassHtmlCopy, pszPrefix) != MS_SUCCESS) {
3756
              if (pszResult)
×
3757
                free(pszResult);
×
3758
              pszResult = NULL;
3759
              goto error;
×
3760
            }
3761

3762
            /* concatenate to final result */
3763
            pszResult = msStringConcatenate(pszResult, legClassHtmlCopy);
×
3764

3765
            if (legClassHtmlCopy) {
×
3766
              free(legClassHtmlCopy);
×
3767
              legClassHtmlCopy = NULL;
×
3768
            }
3769
          }
3770
        }
3771
      }
3772
    } else { /* if no group and layer template specified */
3773
      if (legClassHtml) {
×
3774
        for (j = 0; j < mapserv->map->numlayers; j++) {
×
3775
          /*
3776
           * if order_metadata is set and the order
3777
           * value is less than 0, don't display it
3778
           */
3779
          pszOrderMetadata = msLookupHashTable(layerArgs, "order_metadata");
×
3780
          if (pszOrderMetadata) {
×
3781
            pszOrderValue = msLookupHashTable(
×
3782
                &(GET_LAYER(mapserv->map, mapserv->map->layerorder[j])
×
3783
                      ->metadata),
3784
                pszOrderMetadata);
3785
            if (pszOrderValue) {
×
3786
              const int nLegendOrder = atoi(pszOrderValue);
3787
              if (nLegendOrder < 0)
×
3788
                continue;
×
3789
            }
3790
          }
3791
          if (mapserv->hittest &&
×
3792
              mapserv->hittest->layerhits[mapserv->map->layerorder[j]].status ==
×
3793
                  0) {
3794
            continue;
×
3795
          }
3796

3797
          for (k = 0;
3798
               k <
×
3799
               GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->numclasses;
×
3800
               k++) {
×
3801
            if (!GET_LAYER(mapserv->map, mapserv->map->layerorder[j])->class[k]
×
3802
                -> name)
×
3803
              continue;
×
3804
            if (mapserv->hittest &&
×
3805
                mapserv->hittest->layerhits[mapserv->map->layerorder[j]]
×
3806
                        .classhits[k]
×
3807
                        .status == 0) {
×
3808
              continue;
×
3809
            }
3810

3811
            if (generateClassTemplate(
×
3812
                    legClassHtml, mapserv->map, mapserv->map->layerorder[j], k,
3813
                    classArgs, &legClassHtmlCopy, pszPrefix) != MS_SUCCESS) {
3814
              if (pszResult)
×
3815
                free(pszResult);
×
3816
              pszResult = NULL;
3817
              goto error;
×
3818
            }
3819

3820
            pszResult = msStringConcatenate(pszResult, legClassHtmlCopy);
×
3821

3822
            if (legClassHtmlCopy) {
×
3823
              free(legClassHtmlCopy);
×
3824
              legClassHtmlCopy = NULL;
×
3825
            }
3826
          }
3827
        }
3828
      }
3829
    }
3830
  }
3831

3832
  /* finish with the footer if present */
3833
  if (legFooterHtml)
×
3834
    pszResult = msStringConcatenate(pszResult, legFooterHtml);
×
3835

3836
  /*
3837
   * if we reach this point, that mean no error was generated.
3838
   * So check if template is null and initialize it to <space>.
3839
   */
3840
  if (pszResult == NULL) {
×
3841
    pszResult = msStringConcatenate(pszResult, " ");
×
3842
  }
3843

3844
  /********************************************************************/
3845

3846
error:
×
3847

3848
  if (papszGroups) {
×
3849
    for (i = 0; i < nGroupNames; i++)
×
3850
      msFree(papszGroups[i]);
×
3851

3852
    msFree(papszGroups);
×
3853
  }
3854

3855
  msFreeHashTable(groupArgs);
×
3856
  msFreeHashTable(layerArgs);
×
3857
  msFreeHashTable(classArgs);
×
3858

3859
  msFree(file);
×
3860

3861
  msFree(legGroupHtmlCopy);
×
3862
  msFree(legLayerHtmlCopy);
×
3863
  msFree(legClassHtmlCopy);
×
3864

3865
  msFree(legHeaderHtml);
×
3866
  msFree(legFooterHtml);
×
3867

3868
  msFree(legGroupHtml);
×
3869
  msFree(legLayerHtml);
×
3870
  msFree(legClassHtml);
×
3871
  msFree(pszPrefix);
×
3872

3873
  if (stream)
×
3874
    fclose(stream);
×
3875

3876
  /* -------------------------------------------------------------------- */
3877
  /*      Reset the layerdrawing order.                                   */
3878
  /* -------------------------------------------------------------------- */
3879
  if (panCurrentDrawingOrder && mapserv->map->layerorder) {
×
3880
    for (i = 0; i < mapserv->map->numlayers; i++)
×
3881
      mapserv->map->layerorder[i] = panCurrentDrawingOrder[i];
×
3882

3883
    free(panCurrentDrawingOrder);
×
3884
  }
3885

3886
  return pszResult;
3887
}
3888

3889
char *processOneToManyJoin(mapservObj *mapserv, joinObj *join) {
×
3890
  int records = MS_FALSE;
3891
  FILE *stream = NULL;
3892
  char *outbuf;
3893
  char line[MS_BUFFER_LENGTH], *tmpline;
3894
  char szPath[MS_MAXPATHLEN];
3895

3896
  if ((outbuf = msStrdup("")) == NULL)
×
3897
    return (NULL); /* empty at first */
3898

3899
  msJoinPrepare(join, &(mapserv->resultshape)); /* execute the join */
×
3900
  while (msJoinNext(join) == MS_SUCCESS) {
×
3901
    /* First time through, deal with the header (if necessary) and open the main
3902
     * template. We only */
3903
    /* want to do this if there are joined records. */
3904
    if (records == MS_FALSE) {
×
3905
      if (join->header != NULL) {
×
3906
        /* coverity[dead_error_line] */
3907
        if (stream)
×
3908
          fclose(stream);
×
3909
        if ((stream =
×
3910
                 fopen(msBuildPath(szPath, mapserv->map->mappath, join->header),
×
3911
                       "r")) == NULL) {
3912
          msSetError(MS_IOERR, "Error while opening join header file %s.",
×
3913
                     "processOneToManyJoin()", join->header);
3914
          msFree(outbuf);
×
3915
          return (NULL);
×
3916
        }
3917

3918
        if (isValidTemplate(stream, join->header) != MS_TRUE) {
×
3919
          fclose(stream);
×
3920
          msFree(outbuf);
×
3921
          return NULL;
×
3922
        }
3923

3924
        /* echo file to the output buffer, no substitutions */
3925
        while (fgets(line, MS_BUFFER_LENGTH, stream) != NULL)
×
3926
          outbuf = msStringConcatenate(outbuf, line);
×
3927

3928
        fclose(stream);
×
3929
        stream = NULL;
3930
      }
3931

3932
      if ((stream =
×
3933
               fopen(msBuildPath(szPath, mapserv->map->mappath, join->template),
×
3934
                     "r")) == NULL) {
3935
        msSetError(MS_IOERR, "Error while opening join template file %s.",
×
3936
                   "processOneToManyJoin()", join->template);
3937
        msFree(outbuf);
×
3938
        return (NULL);
×
3939
      }
3940

3941
      if (isValidTemplate(stream, join->template) != MS_TRUE) {
×
3942
        fclose(stream);
×
3943
        msFree(outbuf);
×
3944
        return NULL;
×
3945
      }
3946

3947
      records = MS_TRUE;
3948
    }
3949

3950
    while (fgets(line, MS_BUFFER_LENGTH, stream) !=
×
3951
           NULL) { /* now on to the end of the template */
3952
      if (strchr(line, '[') != NULL) {
×
3953
        tmpline =
3954
            processLine(mapserv, line, NULL,
×
3955
                        QUERY); /* no multiline tags are allowed in a join */
3956
        if (!tmpline) {
×
3957
          msFree(outbuf);
×
3958
          fclose(stream);
×
3959
          return NULL;
×
3960
        }
3961
        outbuf = msStringConcatenate(outbuf, tmpline);
×
3962
        free(tmpline);
×
3963
      } else /* no subs, just echo */
3964
        outbuf = msStringConcatenate(outbuf, line);
×
3965
    }
3966

3967
    rewind(stream);
×
3968
    IGUR_voidp(
3969
        fgets(line, MS_BUFFER_LENGTH,
3970
              stream)); /* skip the first line since it's the magic string */
3971
  }                     /* next record */
3972

3973
  if (records == MS_TRUE && join->footer) {
×
3974
    if (stream)
×
3975
      fclose(stream);
×
3976
    if ((stream =
×
3977
             fopen(msBuildPath(szPath, mapserv->map->mappath, join->footer),
×
3978
                   "r")) == NULL) {
3979
      msSetError(MS_IOERR, "Error while opening join footer file %s.",
×
3980
                 "processOneToManyJoin()", join->footer);
3981
      msFree(outbuf);
×
3982
      return (NULL);
×
3983
    }
3984

3985
    if (isValidTemplate(stream, join->footer) != MS_TRUE) {
×
3986
      msFree(outbuf);
×
3987
      fclose(stream);
×
3988
      return NULL;
×
3989
    }
3990

3991
    /* echo file to the output buffer, no substitutions */
3992
    while (fgets(line, MS_BUFFER_LENGTH, stream) != NULL)
×
3993
      outbuf = msStringConcatenate(outbuf, line);
×
3994

3995
    fclose(stream);
×
3996
    stream = NULL;
3997
  }
3998

3999
  /* clear any data associated with the join */
4000
  msFreeCharArray(join->values, join->numitems);
×
4001
  join->values = NULL;
×
4002

4003
  if (stream)
×
4004
    fclose(stream);
×
4005

4006
  return (outbuf);
4007
}
4008

4009
/*
4010
** Process a single line in the template. A few tags (e.g.
4011
*[resultset]...[/resultset]) can be multi-line so
4012
** we pass the filehandle to look ahead if necessary.
4013
*/
4014
static char *processLine(mapservObj *mapserv, const char *instr, FILE *stream,
441✔
4015
                         int mode) {
4016
  int i, j;
4017
#define PROCESSLINE_BUFLEN 5120
4018
  char repstr[PROCESSLINE_BUFLEN], substr[PROCESSLINE_BUFLEN],
4019
      *outstr; /* repstr = replace string, substr = sub string */
4020
  struct hashObj *tp = NULL;
4021
  char *encodedstr;
4022

4023
  rectObj llextent;
4024
  pointObj llpoint;
4025

4026
  outstr = msStrdup(instr); /* work from a copy */
441✔
4027

4028
  if (strstr(outstr, "[version]"))
441✔
4029
    outstr = msReplaceSubstring(outstr, "[version]", msGetVersion());
×
4030

4031
  snprintf(repstr, PROCESSLINE_BUFLEN, "%s%s%s.%s", mapserv->map->web.imageurl,
882✔
4032
           mapserv->map->name, mapserv->Id,
441✔
4033
           MS_IMAGE_EXTENSION(mapserv->map->outputformat));
441✔
4034
  outstr = msReplaceSubstring(outstr, "[img]", repstr);
441✔
4035
  snprintf(repstr, PROCESSLINE_BUFLEN, "%s%sref%s.%s",
441✔
4036
           mapserv->map->web.imageurl, mapserv->map->name, mapserv->Id,
4037
           MS_IMAGE_EXTENSION(mapserv->map->outputformat));
441✔
4038
  outstr = msReplaceSubstring(outstr, "[ref]", repstr);
441✔
4039

4040
  if (strstr(outstr, "[errmsg")) {
441✔
4041
    char *errmsg = msGetErrorString(";");
×
4042
    if (!errmsg)
×
4043
      errmsg =
4044
          msStrdup("Error message buffer is empty."); /* should never happen,
×
4045
                                                         but just in case... */
4046
    outstr = msReplaceSubstring(outstr, "[errmsg]", errmsg);
×
4047
    encodedstr = msEncodeUrl(errmsg);
×
4048
    outstr = msReplaceSubstring(outstr, "[errmsg_esc]", encodedstr);
×
4049
    free(errmsg);
×
4050
    free(encodedstr);
×
4051
  }
4052

4053
  if (strstr(outstr, "[legend]")) {
441✔
4054
    /* if there's a template legend specified, use it */
4055
    if (mapserv->map->legend.template) {
×
4056
      char *legendTemplate;
4057

4058
      legendTemplate = generateLegendTemplate(mapserv);
×
4059
      if (legendTemplate) {
×
4060
        outstr = msReplaceSubstring(outstr, "[legend]", legendTemplate);
×
4061

4062
        free(legendTemplate);
×
4063
      } else /* error already generated by (generateLegendTemplate()) */
4064
        return NULL;
4065
    } else { /* if not display gif image with all legend icon */
4066
      snprintf(repstr, PROCESSLINE_BUFLEN, "%s%sleg%s.%s",
×
4067
               mapserv->map->web.imageurl, mapserv->map->name, mapserv->Id,
4068
               MS_IMAGE_EXTENSION(mapserv->map->outputformat));
×
4069
      outstr = msReplaceSubstring(outstr, "[legend]", repstr);
×
4070
    }
4071
  }
4072

4073
  snprintf(repstr, PROCESSLINE_BUFLEN, "%s%ssb%s.%s",
441✔
4074
           mapserv->map->web.imageurl, mapserv->map->name, mapserv->Id,
4075
           MS_IMAGE_EXTENSION(mapserv->map->outputformat));
441✔
4076
  outstr = msReplaceSubstring(outstr, "[scalebar]", repstr);
441✔
4077

4078
  if (mapserv->savequery) {
441✔
4079
    snprintf(repstr, PROCESSLINE_BUFLEN, "%s%s%s%s",
×
4080
             mapserv->map->web.imagepath, mapserv->map->name, mapserv->Id,
×
4081
             MS_QUERY_EXTENSION);
4082
    outstr = msReplaceSubstring(outstr, "[queryfile]", repstr);
×
4083
  }
4084

4085
  if (mapserv->savemap) {
441✔
4086
    snprintf(repstr, PROCESSLINE_BUFLEN, "%s%s%s.map",
×
4087
             mapserv->map->web.imagepath, mapserv->map->name, mapserv->Id);
×
4088
    outstr = msReplaceSubstring(outstr, "[map]", repstr);
×
4089
  }
4090

4091
  if (strstr(outstr, "[mapserv_onlineresource]")) {
441✔
4092
    char *ol;
4093
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
4094
    defined(USE_SOS_SVR) || defined(USE_WMS_LYR) || defined(USE_WFS_LYR)
4095
    ol = msOWSGetOnlineResource(mapserv->map, "MO", "onlineresource",
4✔
4096
                                mapserv->request);
4097
#else
4098
    ol = msBuildOnlineResource(mapserv->map, mapserv->request);
4099
#endif
4100
    outstr = msReplaceSubstring(outstr, "[mapserv_onlineresource]", ol);
4✔
4101
    msFree(ol);
4✔
4102
  }
4103

4104
  if (getenv("HTTP_HOST")) {
441✔
4105
    snprintf(repstr, PROCESSLINE_BUFLEN, "%s", getenv("HTTP_HOST"));
×
4106
    outstr = msReplaceSubstring(outstr, "[host]", repstr);
×
4107
  }
4108
  if (getenv("SERVER_PORT")) {
441✔
4109
    snprintf(repstr, PROCESSLINE_BUFLEN, "%s", getenv("SERVER_PORT"));
×
4110
    outstr = msReplaceSubstring(outstr, "[port]", repstr);
×
4111
  }
4112

4113
  snprintf(repstr, PROCESSLINE_BUFLEN, "%s", mapserv->Id);
4114
  outstr = msReplaceSubstring(outstr, "[id]", repstr);
441✔
4115

4116
  repstr[0] = '\0'; /* Layer list for a "POST" request */
441✔
4117
  for (i = 0; i < mapserv->NumLayers; i++) {
913✔
4118
    strlcat(repstr, mapserv->Layers[i], sizeof(repstr));
472✔
4119
    strlcat(repstr, " ", sizeof(repstr));
4120
  }
4121
  msStringTrimBlanks(repstr);
441✔
4122
  encodedstr = msEncodeHTMLEntities(repstr);
441✔
4123
  outstr = msReplaceSubstring(outstr, "[layers]", encodedstr);
441✔
4124
  free(encodedstr);
441✔
4125

4126
  encodedstr = msEncodeUrl(repstr);
441✔
4127
  outstr = msReplaceSubstring(outstr, "[layers_esc]", encodedstr);
441✔
4128
  free(encodedstr);
441✔
4129

4130
  strcpy(repstr, ""); /* list of ALL layers that can be toggled */
4131
  repstr[0] = '\0';
4132
  for (i = 0; i < mapserv->map->numlayers; i++) {
1,499✔
4133
    if (GET_LAYER(mapserv->map, i)->status != MS_DEFAULT &&
1,058✔
4134
        GET_LAYER(mapserv->map, i)->name != NULL) {
1,058✔
4135
      strlcat(repstr, GET_LAYER(mapserv->map, i)->name, sizeof(repstr));
4136
      strlcat(repstr, " ", sizeof(repstr));
4137
    }
4138
  }
4139
  msStringTrimBlanks(repstr);
441✔
4140
  outstr = msReplaceSubstring(outstr, "[toggle_layers]", repstr);
441✔
4141

4142
  encodedstr = msEncodeUrl(repstr);
441✔
4143
  outstr = msReplaceSubstring(outstr, "[toggle_layers_esc]", encodedstr);
441✔
4144
  free(encodedstr);
441✔
4145

4146
  for (i = 0; i < mapserv->map->numlayers;
1,499✔
4147
       i++) { /* Set form widgets (i.e. checkboxes, radio and select lists),
1,058✔
4148
                 note that default layers don't show up here */
4149
    if (isOn(mapserv, GET_LAYER(mapserv->map, i)->name,
1,058✔
4150
             GET_LAYER(mapserv->map, i)->group) == MS_TRUE) {
1,058✔
4151
      if (GET_LAYER(mapserv->map, i)->group) {
472✔
4152
        snprintf(substr, PROCESSLINE_BUFLEN, "[%s_select]",
4153
                 GET_LAYER(mapserv->map, i)->group);
4154
        outstr = msReplaceSubstring(outstr, substr, "selected=\"selected\"");
×
4155
        snprintf(substr, PROCESSLINE_BUFLEN, "[%s_check]",
×
4156
                 GET_LAYER(mapserv->map, i)->group);
×
4157
        outstr = msReplaceSubstring(outstr, substr, "checked=\"checked\"");
×
4158
      }
4159
      if (GET_LAYER(mapserv->map, i)->name) {
472✔
4160
        snprintf(substr, PROCESSLINE_BUFLEN, "[%s_select]",
4161
                 GET_LAYER(mapserv->map, i)->name);
4162
        outstr = msReplaceSubstring(outstr, substr, "selected=\"selected\"");
472✔
4163
        snprintf(substr, PROCESSLINE_BUFLEN, "[%s_check]",
472✔
4164
                 GET_LAYER(mapserv->map, i)->name);
472✔
4165
        outstr = msReplaceSubstring(outstr, substr, "checked=\"checked\"");
472✔
4166
      }
4167
    } else {
4168
      if (GET_LAYER(mapserv->map, i)->group) {
586✔
4169
        snprintf(substr, PROCESSLINE_BUFLEN, "[%s_select]",
4170
                 GET_LAYER(mapserv->map, i)->group);
4171
        outstr = msReplaceSubstring(outstr, substr, "");
50✔
4172
        snprintf(substr, PROCESSLINE_BUFLEN, "[%s_check]",
50✔
4173
                 GET_LAYER(mapserv->map, i)->group);
50✔
4174
        outstr = msReplaceSubstring(outstr, substr, "");
50✔
4175
      }
4176
      if (GET_LAYER(mapserv->map, i)->name) {
586✔
4177
        snprintf(substr, PROCESSLINE_BUFLEN, "[%s_select]",
4178
                 GET_LAYER(mapserv->map, i)->name);
4179
        outstr = msReplaceSubstring(outstr, substr, "");
586✔
4180
        snprintf(substr, PROCESSLINE_BUFLEN, "[%s_check]",
586✔
4181
                 GET_LAYER(mapserv->map, i)->name);
586✔
4182
        outstr = msReplaceSubstring(outstr, substr, "");
586✔
4183
      }
4184
    }
4185
  }
4186

4187
  for (i = -1; i <= 1; i++) { /* make zoom direction persistent */
1,764✔
4188
    if (mapserv->ZoomDirection == i) {
1,323✔
4189
      snprintf(substr, sizeof(substr), "[zoomdir_%d_select]", i);
4190
      outstr = msReplaceSubstring(outstr, substr, "selected=\"selected\"");
441✔
4191
      snprintf(substr, sizeof(substr), "[zoomdir_%d_check]", i);
4192
      outstr = msReplaceSubstring(outstr, substr, "checked=\"checked\"");
441✔
4193
    } else {
4194
      snprintf(substr, sizeof(substr), "[zoomdir_%d_select]", i);
4195
      outstr = msReplaceSubstring(outstr, substr, "");
882✔
4196
      snprintf(substr, sizeof(substr), "[zoomdir_%d_check]", i);
4197
      outstr = msReplaceSubstring(outstr, substr, "");
882✔
4198
    }
4199
  }
4200

4201
  for (i = MINZOOM; i <= MAXZOOM; i++) { /* make zoom persistent */
22,932✔
4202
    if (mapserv->Zoom == i) {
22,491✔
4203
      snprintf(substr, sizeof(substr), "[zoom_%d_select]", i);
4204
      outstr = msReplaceSubstring(outstr, substr, "selected=\"selected\"");
441✔
4205
      snprintf(substr, sizeof(substr), "[zoom_%d_check]", i);
4206
      outstr = msReplaceSubstring(outstr, substr, "checked=\"checked\"");
441✔
4207
    } else {
4208
      snprintf(substr, sizeof(substr), "[zoom_%d_select]", i);
4209
      outstr = msReplaceSubstring(outstr, substr, "");
22,050✔
4210
      snprintf(substr, sizeof(substr), "[zoom_%d_check]", i);
4211
      outstr = msReplaceSubstring(outstr, substr, "");
22,050✔
4212
    }
4213
  }
4214

4215
  /* allow web object metadata access in template */
4216

4217
  /*
4218
   * reworked by SG to use HashTable methods
4219
   */
4220

4221
  if (strstr(outstr, "web_")) {
441✔
4222
    for (j = 0; j < MS_HASHSIZE; j++) {
×
4223
      if (mapserv->map->web.metadata.items[j] != NULL) {
×
4224
        for (tp = mapserv->map->web.metadata.items[j]; tp != NULL;
×
4225
             tp = tp->next) {
×
4226
          snprintf(substr, PROCESSLINE_BUFLEN, "[web_%s]", tp->key);
×
4227
          outstr = msReplaceSubstring(outstr, substr, tp->data);
×
4228
          snprintf(substr, PROCESSLINE_BUFLEN, "[web_%s_esc]", tp->key);
×
4229

4230
          encodedstr = msEncodeUrl(tp->data);
×
4231
          outstr = msReplaceSubstring(outstr, substr, encodedstr);
×
4232
          free(encodedstr);
×
4233
        }
4234
      }
4235
    }
4236
  }
4237

4238
  /* allow layer metadata access in template */
4239
  for (i = 0; i < mapserv->map->numlayers; i++) {
1,499✔
4240
    if (GET_LAYER(mapserv->map, i)->name &&
1,058✔
4241
        strstr(outstr, GET_LAYER(mapserv->map, i)->name)) {
1,058✔
4242
      for (j = 0; j < MS_HASHSIZE; j++) {
4,788✔
4243
        if (GET_LAYER(mapserv->map, i)->metadata.items[j] != NULL) {
4,674✔
4244
          for (tp = GET_LAYER(mapserv->map, i)->metadata.items[j]; tp != NULL;
72✔
4245
               tp = tp->next) {
43✔
4246
            snprintf(substr, PROCESSLINE_BUFLEN, "[%s_%s]",
43✔
4247
                     GET_LAYER(mapserv->map, i)->name, tp->key);
43✔
4248
            if (GET_LAYER(mapserv->map, i)->status == MS_ON)
43✔
4249
              outstr = msReplaceSubstring(outstr, substr, tp->data);
43✔
4250
            else
4251
              outstr = msReplaceSubstring(outstr, substr, "");
×
4252
            snprintf(substr, PROCESSLINE_BUFLEN, "[%s_%s_esc]",
43✔
4253
                     GET_LAYER(mapserv->map, i)->name, tp->key);
43✔
4254
            if (GET_LAYER(mapserv->map, i)->status == MS_ON) {
43✔
4255
              encodedstr = msEncodeUrl(tp->data);
43✔
4256
              outstr = msReplaceSubstring(outstr, substr, encodedstr);
43✔
4257
              free(encodedstr);
43✔
4258
            } else
4259
              outstr = msReplaceSubstring(outstr, substr, "");
×
4260
          }
4261
        }
4262
      }
4263
    }
4264
  }
4265

4266
  snprintf(repstr, sizeof(repstr), "%f", mapserv->mappnt.x);
441✔
4267
  outstr = msReplaceSubstring(outstr, "[mapx]", repstr);
441✔
4268
  snprintf(repstr, sizeof(repstr), "%f", mapserv->mappnt.y);
441✔
4269
  outstr = msReplaceSubstring(outstr, "[mapy]", repstr);
441✔
4270

4271
  snprintf(repstr, sizeof(repstr), "%f",
441✔
4272
           mapserv->map->extent.minx); /* Individual mapextent elements for
441✔
4273
                                          spatial query building, deprecated. */
4274
  outstr = msReplaceSubstring(outstr, "[minx]", repstr);
441✔
4275
  snprintf(repstr, sizeof(repstr), "%f", mapserv->map->extent.maxx);
441✔
4276
  outstr = msReplaceSubstring(outstr, "[maxx]", repstr);
441✔
4277
  snprintf(repstr, sizeof(repstr), "%f", mapserv->map->extent.miny);
441✔
4278
  outstr = msReplaceSubstring(outstr, "[miny]", repstr);
441✔
4279
  snprintf(repstr, sizeof(repstr), "%f", mapserv->map->extent.maxy);
441✔
4280
  outstr = msReplaceSubstring(outstr, "[maxy]", repstr);
441✔
4281

4282
  if (processDateTag(&outstr) != MS_SUCCESS)
441✔
4283
    return (NULL);
4284

4285
  if (processExtentTag(mapserv, &outstr, "mapext", &(mapserv->map->extent),
441✔
4286
                       &(mapserv->map->projection)) != MS_SUCCESS)
441✔
4287
    return (NULL);
4288
  if (processExtentTag(mapserv, &outstr, "mapext_esc", &(mapserv->map->extent),
441✔
4289
                       &(mapserv->map->projection)) !=
441✔
4290
      MS_SUCCESS) /* deprecated */
4291
    return (NULL);
4292

4293
  snprintf(repstr, sizeof(repstr), "%f",
441✔
4294
           (mapserv->map->extent.maxx -
441✔
4295
            mapserv->map->extent
4296
                .minx)); /* useful for creating cacheable extents (i.e. 0 0 dx
441✔
4297
                            dy) with legends and scalebars */
4298
  outstr = msReplaceSubstring(outstr, "[dx]", repstr);
441✔
4299
  snprintf(repstr, sizeof(repstr), "%f",
441✔
4300
           (mapserv->map->extent.maxy - mapserv->map->extent.miny));
441✔
4301
  outstr = msReplaceSubstring(outstr, "[dy]", repstr);
441✔
4302

4303
  snprintf(repstr, sizeof(repstr), "%f",
441✔
4304
           mapserv->RawExt.minx); /* Individual raw extent elements for spatial
4305
                                     query building, deprecated. */
4306
  outstr = msReplaceSubstring(outstr, "[rawminx]", repstr);
441✔
4307
  snprintf(repstr, sizeof(repstr), "%f", mapserv->RawExt.maxx);
441✔
4308
  outstr = msReplaceSubstring(outstr, "[rawmaxx]", repstr);
441✔
4309
  snprintf(repstr, sizeof(repstr), "%f", mapserv->RawExt.miny);
441✔
4310
  outstr = msReplaceSubstring(outstr, "[rawminy]", repstr);
441✔
4311
  snprintf(repstr, sizeof(repstr), "%f", mapserv->RawExt.maxy);
441✔
4312
  outstr = msReplaceSubstring(outstr, "[rawmaxy]", repstr);
441✔
4313

4314
  if (processExtentTag(mapserv, &outstr, "rawext", &(mapserv->RawExt),
441✔
4315
                       &(mapserv->map->projection)) != MS_SUCCESS)
441✔
4316
    return (NULL);
4317
  if (processExtentTag(mapserv, &outstr, "rawext_esc", &(mapserv->RawExt),
441✔
4318
                       &(mapserv->map->projection)) !=
441✔
4319
      MS_SUCCESS) /* deprecated */
4320
    return (NULL);
4321

4322
  if ((strstr(outstr, "lat]") || strstr(outstr, "lon]") ||
441✔
4323
       strstr(outstr, "lon_esc]")) &&
441✔
4324
      mapserv->map->projection.proj != NULL) {
×
4325
    llextent = mapserv->map->extent;
×
4326
    llpoint = mapserv->mappnt;
×
4327
    if (!msProjIsGeographicCRS(&(mapserv->map->projection))) {
×
4328
      msProjectRect(&(mapserv->map->projection), &(mapserv->map->latlon),
×
4329
                    &llextent);
4330
      msProjectPoint(&(mapserv->map->projection), &(mapserv->map->latlon),
×
4331
                     &llpoint);
4332
    }
4333
    snprintf(repstr, sizeof(repstr), "%f", llpoint.x);
×
4334
    outstr = msReplaceSubstring(outstr, "[maplon]", repstr);
×
4335
    snprintf(repstr, sizeof(repstr), "%f", llpoint.y);
×
4336
    outstr = msReplaceSubstring(outstr, "[maplat]", repstr);
×
4337

4338
    snprintf(repstr, sizeof(repstr), "%f",
×
4339
             llextent.minx); /* map extent as lat/long */
4340
    outstr = msReplaceSubstring(outstr, "[minlon]", repstr);
×
4341
    snprintf(repstr, sizeof(repstr), "%f", llextent.maxx);
×
4342
    outstr = msReplaceSubstring(outstr, "[maxlon]", repstr);
×
4343
    snprintf(repstr, sizeof(repstr), "%f", llextent.miny);
×
4344
    outstr = msReplaceSubstring(outstr, "[minlat]", repstr);
×
4345
    snprintf(repstr, sizeof(repstr), "%f", llextent.maxy);
×
4346
    outstr = msReplaceSubstring(outstr, "[maxlat]", repstr);
×
4347

4348
    if (processExtentTag(mapserv, &outstr, "mapext_latlon", &(llextent),
×
4349
                         NULL) != MS_SUCCESS)
4350
      return (NULL);
4351
    if (processExtentTag(mapserv, &outstr, "mapext_latlon_esc", &(llextent),
×
4352
                         NULL) != MS_SUCCESS) /* deprecated */
4353
      return (NULL);
4354
  }
4355

4356
  /* submitted by J.F (bug 1102) */
4357
  if (mapserv->map->reference.status == MS_ON) {
441✔
4358
    snprintf(repstr, sizeof(repstr), "%f",
×
4359
             mapserv->map->reference.extent
4360
                 .minx); /* Individual reference map extent elements for spatial
4361
                            query building, deprecated. */
4362
    outstr = msReplaceSubstring(outstr, "[refminx]", repstr);
×
4363
    snprintf(repstr, sizeof(repstr), "%f", mapserv->map->reference.extent.maxx);
×
4364
    outstr = msReplaceSubstring(outstr, "[refmaxx]", repstr);
×
4365
    snprintf(repstr, sizeof(repstr), "%f", mapserv->map->reference.extent.miny);
×
4366
    outstr = msReplaceSubstring(outstr, "[refminy]", repstr);
×
4367
    snprintf(repstr, sizeof(repstr), "%f", mapserv->map->reference.extent.maxy);
×
4368
    outstr = msReplaceSubstring(outstr, "[refmaxy]", repstr);
×
4369

4370
    if (processExtentTag(mapserv, &outstr, "refext",
×
4371
                         &(mapserv->map->reference.extent),
4372
                         &(mapserv->map->projection)) != MS_SUCCESS)
×
4373
      return (NULL);
4374
    if (processExtentTag(
×
4375
            mapserv, &outstr, "refext_esc", &(mapserv->map->reference.extent),
4376
            &(mapserv->map->projection)) != MS_SUCCESS) /* deprecated */
×
4377
      return (NULL);
4378
  }
4379

4380
  snprintf(repstr, sizeof(repstr), "%d %d", mapserv->map->width,
441✔
4381
           mapserv->map->height);
441✔
4382
  outstr = msReplaceSubstring(outstr, "[mapsize]", repstr);
441✔
4383

4384
  encodedstr = msEncodeUrl(repstr);
441✔
4385
  outstr = msReplaceSubstring(outstr, "[mapsize_esc]", encodedstr);
441✔
4386
  free(encodedstr);
441✔
4387

4388
  snprintf(repstr, sizeof(repstr), "%d", mapserv->map->width);
441✔
4389
  outstr = msReplaceSubstring(outstr, "[mapwidth]", repstr);
441✔
4390
  snprintf(repstr, sizeof(repstr), "%d", mapserv->map->height);
441✔
4391
  outstr = msReplaceSubstring(outstr, "[mapheight]", repstr);
441✔
4392

4393
  snprintf(repstr, sizeof(repstr), "%f", mapserv->map->scaledenom);
441✔
4394
  outstr = msReplaceSubstring(outstr, "[scale]", repstr);
441✔
4395
  outstr = msReplaceSubstring(outstr, "[scaledenom]", repstr);
441✔
4396
  snprintf(repstr, sizeof(repstr), "%f", mapserv->map->cellsize);
441✔
4397
  outstr = msReplaceSubstring(outstr, "[cellsize]", repstr);
441✔
4398

4399
  snprintf(repstr, sizeof(repstr), "%.1f %.1f", (mapserv->map->width) / 2.0,
441✔
4400
           (mapserv->map->height) /
441✔
4401
               2.0); /* not subtracting 1 from image dimensions (see bug 633) */
4402
  outstr = msReplaceSubstring(outstr, "[center]", repstr);
441✔
4403
  snprintf(repstr, sizeof(repstr), "%.1f", (mapserv->map->width) / 2.0);
441✔
4404
  outstr = msReplaceSubstring(outstr, "[center_x]", repstr);
441✔
4405
  snprintf(repstr, sizeof(repstr), "%.1f", (mapserv->map->height) / 2.0);
441✔
4406
  outstr = msReplaceSubstring(outstr, "[center_y]", repstr);
441✔
4407

4408
  /* These are really for situations with multiple result sets only, but often
4409
   * used in header/footer   */
4410
  snprintf(repstr, sizeof(repstr), "%d",
441✔
4411
           mapserv->NR); /* total number of results */
4412
  outstr = msReplaceSubstring(outstr, "[nr]", repstr);
441✔
4413
  snprintf(repstr, sizeof(repstr), "%d",
441✔
4414
           mapserv->NL); /* total number of layers with results */
4415
  outstr = msReplaceSubstring(outstr, "[nl]", repstr);
441✔
4416

4417
  if (mapserv->resultlayer) {
441✔
4418
    if (strstr(outstr, "[items]") != NULL) {
316✔
4419
      char *itemstr = NULL;
4420

4421
      itemstr = msJoinStrings(mapserv->resultlayer->items,
×
4422
                              mapserv->resultlayer->numitems, ",");
4423
      outstr = msReplaceSubstring(outstr, "[items]", itemstr);
×
4424
      free(itemstr);
×
4425
    }
4426

4427
    snprintf(repstr, sizeof(repstr), "%d",
316✔
4428
             mapserv->NLR); /* total number of results within this layer */
4429
    outstr = msReplaceSubstring(outstr, "[nlr]", repstr);
316✔
4430
    snprintf(
316✔
4431
        repstr, sizeof(repstr), "%d",
4432
        mapserv
4433
            ->RN); /* sequential (eg. 1..n) result number within all layers */
4434
    outstr = msReplaceSubstring(outstr, "[rn]", repstr);
316✔
4435
    snprintf(
316✔
4436
        repstr, sizeof(repstr), "%d",
4437
        mapserv
4438
            ->LRN); /* sequential (eg. 1..n) result number within this layer */
4439
    outstr = msReplaceSubstring(outstr, "[lrn]", repstr);
316✔
4440
    outstr = msReplaceSubstring(
632✔
4441
        outstr, "[cl]", mapserv->resultlayer->name); /* current layer name */
316✔
4442
    /* if(resultlayer->description) outstr = msReplaceSubstring(outstr, "[cd]",
4443
     * resultlayer->description); */ /* current layer description */
4444

4445
    /* allow layer metadata access when there is a current result layer
4446
     * (implicitly a query template) */
4447
    if (strstr(outstr, "[metadata_")) {
316✔
4448
      for (i = 0; i < MS_HASHSIZE; i++) {
×
4449
        if (mapserv->resultlayer->metadata.items[i] != NULL) {
×
4450
          for (tp = mapserv->resultlayer->metadata.items[i]; tp != NULL;
×
4451
               tp = tp->next) {
×
4452
            snprintf(substr, PROCESSLINE_BUFLEN, "[metadata_%s]", tp->key);
×
4453
            outstr = msReplaceSubstring(outstr, substr, tp->data);
×
4454

4455
            snprintf(substr, PROCESSLINE_BUFLEN, "[metadata_%s_esc]", tp->key);
×
4456
            encodedstr = msEncodeUrl(tp->data);
×
4457
            outstr = msReplaceSubstring(outstr, substr, encodedstr);
×
4458
            free(encodedstr);
×
4459
          }
4460
        }
4461
      }
4462
    }
4463
  }
4464

4465
  if (mode != QUERY) {
441✔
4466
    if (processResultSetTag(mapserv, &outstr, stream) != MS_SUCCESS) {
131✔
4467
      msFree(outstr);
×
4468
      return (NULL);
×
4469
    }
4470
  } else { /* return shape and/or values */
4471

4472
    assert(mapserv->resultlayer);
4473

4474
    snprintf(
310✔
4475
        repstr, sizeof(repstr), "%f %f",
4476
        (mapserv->resultshape.bounds.maxx + mapserv->resultshape.bounds.minx) /
310✔
4477
            2,
4478
        (mapserv->resultshape.bounds.maxy + mapserv->resultshape.bounds.miny) /
310✔
4479
            2);
4480
    outstr = msReplaceSubstring(outstr, "[shpmid]", repstr);
310✔
4481
    snprintf(
310✔
4482
        repstr, sizeof(repstr), "%f",
4483
        (mapserv->resultshape.bounds.maxx + mapserv->resultshape.bounds.minx) /
310✔
4484
            2);
4485
    outstr = msReplaceSubstring(outstr, "[shpmidx]", repstr);
310✔
4486
    snprintf(
310✔
4487
        repstr, sizeof(repstr), "%f",
4488
        (mapserv->resultshape.bounds.maxy + mapserv->resultshape.bounds.miny) /
310✔
4489
            2);
4490
    outstr = msReplaceSubstring(outstr, "[shpmidy]", repstr);
310✔
4491

4492
    if (processExtentTag(mapserv, &outstr, "shpext",
310✔
4493
                         &(mapserv->resultshape.bounds),
4494
                         &(mapserv->resultlayer->projection)) != MS_SUCCESS)
310✔
4495
      return (NULL);
4496
    if (processExtentTag(
310✔
4497
            mapserv, &outstr, "shpext_esc", &(mapserv->resultshape.bounds),
4498
            &(mapserv->resultlayer->projection)) != MS_SUCCESS) /* deprecated */
310✔
4499
      return (NULL);
4500

4501
    snprintf(repstr, sizeof(repstr), "%d", mapserv->resultshape.classindex);
310✔
4502
    outstr = msReplaceSubstring(outstr, "[shpclass]", repstr);
310✔
4503

4504
    if (processShpxyTag(mapserv->resultlayer, &outstr, &mapserv->resultshape) !=
310✔
4505
        MS_SUCCESS)
4506
      return (NULL);
4507

4508
    if (processShplabelTag(mapserv->resultlayer, &outstr,
310✔
4509
                           &mapserv->resultshape) != MS_SUCCESS)
4510
      return (NULL);
4511

4512
    snprintf(repstr, sizeof(repstr), "%f", mapserv->resultshape.bounds.minx);
310✔
4513
    outstr = msReplaceSubstring(outstr, "[shpminx]", repstr);
310✔
4514
    snprintf(repstr, sizeof(repstr), "%f", mapserv->resultshape.bounds.miny);
310✔
4515
    outstr = msReplaceSubstring(outstr, "[shpminy]", repstr);
310✔
4516
    snprintf(repstr, sizeof(repstr), "%f", mapserv->resultshape.bounds.maxx);
310✔
4517
    outstr = msReplaceSubstring(outstr, "[shpmaxx]", repstr);
310✔
4518
    snprintf(repstr, sizeof(repstr), "%f", mapserv->resultshape.bounds.maxy);
310✔
4519
    outstr = msReplaceSubstring(outstr, "[shpmaxy]", repstr);
310✔
4520

4521
    snprintf(repstr, sizeof(repstr), "%ld", mapserv->resultshape.index);
310✔
4522
    outstr = msReplaceSubstring(outstr, "[shpidx]", repstr);
310✔
4523
    snprintf(repstr, sizeof(repstr), "%d", mapserv->resultshape.tileindex);
310✔
4524
    outstr = msReplaceSubstring(outstr, "[tileidx]", repstr);
310✔
4525

4526
    /* return ALL attributes in one delimited list */
4527
    if (strstr(outstr, "[values]") != NULL) {
310✔
4528
      char *valuestr = NULL;
4529

4530
      valuestr = msJoinStrings(mapserv->resultshape.values,
×
4531
                               mapserv->resultlayer->numitems, ",");
×
4532
      outstr = msReplaceSubstring(outstr, "[values]", valuestr);
×
4533
      free(valuestr);
×
4534
    }
4535

4536
    for (i = 0; i < mapserv->resultlayer->numitems; i++) {
3,748✔
4537
      /* by default let's encode attributes for HTML presentation */
4538
      snprintf(substr, PROCESSLINE_BUFLEN, "[%s]",
3,438✔
4539
               mapserv->resultlayer->items[i]);
3,438✔
4540
      if (strstr(outstr, substr) != NULL) {
3,438✔
4541
        encodedstr = msEncodeHTMLEntities(mapserv->resultshape.values[i]);
516✔
4542
        outstr = msReplaceSubstring(outstr, substr, encodedstr);
516✔
4543
        free(encodedstr);
516✔
4544
      }
4545

4546
      /* of course you might want to embed that data in URLs */
4547
      snprintf(substr, PROCESSLINE_BUFLEN, "[%s_esc]",
3,438✔
4548
               mapserv->resultlayer->items[i]);
3,438✔
4549
      if (strstr(outstr, substr) != NULL) {
3,438✔
4550
        encodedstr = msEncodeUrl(mapserv->resultshape.values[i]);
×
4551
        outstr = msReplaceSubstring(outstr, substr, encodedstr);
×
4552
        free(encodedstr);
×
4553
      }
4554

4555
      /* or you might want to access the attributes unaltered */
4556
      snprintf(substr, PROCESSLINE_BUFLEN, "[%s_raw]",
3,438✔
4557
               mapserv->resultlayer->items[i]);
3,438✔
4558
      if (strstr(outstr, substr) != NULL)
3,438✔
4559
        outstr =
×
4560
            msReplaceSubstring(outstr, substr, mapserv->resultshape.values[i]);
×
4561
    }
4562

4563
    if (processItemTag(mapserv->resultlayer, &outstr, &mapserv->resultshape) !=
310✔
4564
        MS_SUCCESS)
4565
      return (NULL);
4566

4567
    /* handle joins in this next section */
4568
    for (i = 0; i < mapserv->resultlayer->numjoins; i++) {
310✔
4569
      if (mapserv->resultlayer->joins[i].values) { /* join has data */
×
4570
        for (j = 0; j < mapserv->resultlayer->joins[i].numitems; j++) {
×
4571
          /* by default let's encode attributes for HTML presentation */
4572
          snprintf(substr, PROCESSLINE_BUFLEN, "[%s_%s]",
×
4573
                   mapserv->resultlayer->joins[i].name,
4574
                   mapserv->resultlayer->joins[i].items[j]);
×
4575
          if (strstr(outstr, substr) != NULL) {
×
4576
            encodedstr =
4577
                msEncodeHTMLEntities(mapserv->resultlayer->joins[i].values[j]);
×
4578
            outstr = msReplaceSubstring(outstr, substr, encodedstr);
×
4579
            free(encodedstr);
×
4580
          }
4581

4582
          /* of course you might want to embed that data in URLs */
4583
          snprintf(substr, PROCESSLINE_BUFLEN, "[%s_%s_esc]",
×
4584
                   mapserv->resultlayer->joins[i].name,
4585
                   mapserv->resultlayer->joins[i].items[j]);
×
4586
          if (strstr(outstr, substr) != NULL) {
×
4587
            encodedstr = msEncodeUrl(mapserv->resultlayer->joins[i].values[j]);
×
4588
            outstr = msReplaceSubstring(outstr, substr, encodedstr);
×
4589
            free(encodedstr);
×
4590
          }
4591

4592
          /* or you might want to access the attributes unaltered */
4593
          snprintf(substr, PROCESSLINE_BUFLEN, "[%s_%s_raw]",
×
4594
                   mapserv->resultlayer->joins[i].name,
4595
                   mapserv->resultlayer->joins[i].items[j]);
×
4596
          if (strstr(outstr, substr) != NULL)
×
4597
            outstr = msReplaceSubstring(
×
4598
                outstr, substr, mapserv->resultlayer->joins[i].values[j]);
×
4599
        }
4600
      } else if (mapserv->resultlayer->joins[i].type ==
×
4601
                 MS_JOIN_ONE_TO_MANY) { /* one-to-many join */
4602
        char *joinTemplate = NULL;
4603

4604
        snprintf(substr, PROCESSLINE_BUFLEN, "[join_%s]",
×
4605
                 mapserv->resultlayer->joins[i].name);
4606
        if (strstr(outstr, substr) != NULL) {
×
4607
          joinTemplate =
4608
              processOneToManyJoin(mapserv, &(mapserv->resultlayer->joins[i]));
×
4609
          if (joinTemplate) {
×
4610
            outstr = msReplaceSubstring(outstr, substr, joinTemplate);
×
4611
            free(joinTemplate);
×
4612
          } else
4613
            return NULL;
4614
        }
4615
      }
4616
    } /* next join */
4617

4618
  } /* end query mode specific substitutions */
4619

4620
  if (processIncludeTag(mapserv, &outstr, stream, mode) != MS_SUCCESS) {
441✔
4621
    msFree(outstr);
1✔
4622
    return (NULL);
1✔
4623
  }
4624

4625
  for (i = 0; i < mapserv->request->NumParams; i++) {
3,143✔
4626
    /* Replace [variable] tags using values from URL. We cannot offer a
4627
     * [variable_raw] option here due to the risk of XSS.
4628
     *
4629
     * Replacement is case-insensitive. (#4511)
4630
     */
4631
    snprintf(substr, PROCESSLINE_BUFLEN, "[%s]",
2,703✔
4632
             mapserv->request->ParamNames[i]);
2,703✔
4633
    encodedstr = msEncodeHTMLEntities(mapserv->request->ParamValues[i]);
2,703✔
4634
    outstr = msCaseReplaceSubstring(outstr, substr, encodedstr);
2,703✔
4635
    free(encodedstr);
2,703✔
4636

4637
    snprintf(substr, PROCESSLINE_BUFLEN, "[%s_esc]",
2,703✔
4638
             mapserv->request->ParamNames[i]);
2,703✔
4639
    encodedstr = msEncodeUrl(mapserv->request->ParamValues[i]);
2,703✔
4640
    outstr = msCaseReplaceSubstring(outstr, substr, encodedstr);
2,703✔
4641
    free(encodedstr);
2,703✔
4642
  }
4643

4644
  return (outstr);
440✔
4645
}
4646

4647
#define MS_TEMPLATE_BUFFER 1024 /* 1k */
4648

4649
int msReturnPage(mapservObj *mapserv, char *html, int mode,
58✔
4650
                 char **papszBuffer) {
4651
  FILE *stream;
4652
  char line[MS_BUFFER_LENGTH], *tmpline;
4653
  int nBufferSize = 0;
4654
  int nCurrentSize = 0;
4655

4656
  ms_regex_t re; /* compiled regular expression to be matched */
4657
  char szPath[MS_MAXPATHLEN];
4658

4659
  if (!html) {
58✔
4660
    msSetError(MS_WEBERR, "No template specified", "msReturnPage()");
×
4661
    return MS_FAILURE;
×
4662
  }
4663

4664
  if (ms_regcomp(&re, MS_TEMPLATE_EXPR,
58✔
4665
                 MS_REG_EXTENDED | MS_REG_NOSUB | MS_REG_ICASE) != 0) {
4666
    msSetError(MS_REGEXERR, NULL, "msReturnPage()");
×
4667
    return MS_FAILURE;
×
4668
  }
4669

4670
  if (ms_regexec(&re, html, 0, NULL, 0) != 0) { /* no match */
58✔
4671
    ms_regfree(&re);
×
4672
    msSetError(MS_WEBERR, "Malformed template name (%s).", "msReturnPage()",
×
4673
               html);
4674
    return MS_FAILURE;
×
4675
  }
4676
  ms_regfree(&re);
58✔
4677

4678
  if ((stream = fopen(msBuildPath(szPath, mapserv->map->mappath, html), "r")) ==
58✔
4679
      NULL) {
4680
    msSetError(MS_IOERR, "%s", "msReturnPage()", html);
×
4681
    return MS_FAILURE;
×
4682
  }
4683

4684
  if (isValidTemplate(stream, html) != MS_TRUE) {
58✔
4685
    fclose(stream);
×
4686
    return MS_FAILURE;
×
4687
  }
4688

4689
  if (papszBuffer) {
58✔
4690
    if ((*papszBuffer) == NULL) {
×
4691
      (*papszBuffer) = (char *)msSmallMalloc(MS_TEMPLATE_BUFFER);
×
4692
      (*papszBuffer)[0] = '\0';
×
4693
      nBufferSize = MS_TEMPLATE_BUFFER;
4694
      nCurrentSize = 0;
4695
    } else {
4696
      nCurrentSize = strlen((*papszBuffer));
×
4697
      nBufferSize = nCurrentSize;
4698
    }
4699
  }
4700

4701
  while (fgets(line, MS_BUFFER_LENGTH, stream) !=
362✔
4702
         NULL) { /* now on to the end of the file */
4703

4704
    if (strchr(line, '[') != NULL) {
305✔
4705
      tmpline = processLine(mapserv, line, stream, mode);
143✔
4706
      if (!tmpline) {
143✔
4707
        fclose(stream);
1✔
4708
        return MS_FAILURE;
1✔
4709
      }
4710

4711
      if (papszBuffer) {
142✔
4712
        if (nBufferSize <= (int)(nCurrentSize + strlen(tmpline) + 1)) {
×
4713
          const int nExpandBuffer = (strlen(tmpline) / MS_TEMPLATE_BUFFER) + 1;
×
4714
          nBufferSize =
×
4715
              MS_TEMPLATE_BUFFER * nExpandBuffer + strlen((*papszBuffer));
×
4716
          (*papszBuffer) = (char *)msSmallRealloc((*papszBuffer),
×
4717
                                                  sizeof(char) * nBufferSize);
4718
        }
4719
        strcat((*papszBuffer), tmpline);
×
4720
        nCurrentSize += strlen(tmpline);
×
4721
      } else
4722
        msIO_fwrite(tmpline, strlen(tmpline), 1, stdout);
142✔
4723

4724
      free(tmpline);
142✔
4725
    } else {
4726
      if (papszBuffer) {
162✔
4727
        if (nBufferSize <= (int)(nCurrentSize + strlen(line))) {
×
4728
          const int nExpandBuffer = (strlen(line) / MS_TEMPLATE_BUFFER) + 1;
×
4729
          nBufferSize =
×
4730
              MS_TEMPLATE_BUFFER * nExpandBuffer + strlen((*papszBuffer));
×
4731
          (*papszBuffer) = (char *)msSmallRealloc((*papszBuffer),
×
4732
                                                  sizeof(char) * nBufferSize);
4733
        }
4734
        strcat((*papszBuffer), line);
×
4735
        nCurrentSize += strlen(line);
×
4736
      } else
4737
        msIO_fwrite(line, strlen(line), 1, stdout);
162✔
4738
    }
4739
    if (!papszBuffer)
304✔
4740
      fflush(stdout);
304✔
4741
  } /* next line */
4742

4743
  fclose(stream);
57✔
4744

4745
  return MS_SUCCESS;
57✔
4746
}
4747

4748
int msReturnURL(mapservObj *ms, const char *url, int mode) {
1✔
4749
  char *tmpurl;
4750

4751
  if (url == NULL) {
1✔
4752
    msSetError(MS_WEBERR, "Empty URL.", "msReturnURL()");
×
4753
    return MS_FAILURE;
×
4754
  }
4755

4756
  tmpurl =
4757
      processLine(ms, url, NULL, mode); /* URL templates can't handle multi-line
1✔
4758
                                           tags, hence the NULL file pointer */
4759

4760
  if (!tmpurl)
1✔
4761
    return MS_FAILURE;
4762

4763
  msRedirect(tmpurl);
1✔
4764
  free(tmpurl);
1✔
4765

4766
  return MS_SUCCESS;
1✔
4767
}
4768

4769
/*
4770
** Legacy query template parsing where you use headers, footers and such...
4771
*/
4772
int msReturnNestedTemplateQuery(mapservObj *mapserv, char *pszMimeType,
3✔
4773
                                char **papszBuffer) {
4774
  int status;
4775
  int i, j, k;
4776
  char buffer[1024];
4777
  int nBufferSize = 0;
4778
  int nCurrentSize = 0;
4779
  int nExpandBuffer = 0;
4780

4781
  char *template;
4782

4783
  layerObj *lp = NULL;
4784

4785
  if (papszBuffer) {
3✔
4786
    (*papszBuffer) = (char *)msSmallMalloc(MS_TEMPLATE_BUFFER);
×
4787
    (*papszBuffer)[0] = '\0';
×
4788
    nBufferSize = MS_TEMPLATE_BUFFER;
4789
    nCurrentSize = 0;
4790
    nExpandBuffer = 1;
4791
  }
4792

4793
  msInitShape(&(mapserv->resultshape));
3✔
4794

4795
  if ((mapserv->Mode == ITEMQUERY) ||
3✔
4796
      (mapserv->Mode == QUERY)) { /* may need to handle a URL result set since
4797
                                     these modes return exactly 1 result */
4798

4799
    for (i = (mapserv->map->numlayers - 1); i >= 0; i--) {
6✔
4800
      lp = (GET_LAYER(mapserv->map, i));
6✔
4801

4802
      if (!lp->resultcache)
6✔
4803
        continue;
3✔
4804
      if (lp->resultcache->numresults > 0)
3✔
4805
        break;
4806
    }
4807

4808
    if (i >= 0) { /* at least if no result found, mapserver will display an
3✔
4809
                     empty template. */
4810

4811
      if (lp->resultcache->results[0].classindex >= 0 &&
3✔
4812
              lp->class[(int)(lp->resultcache->results[0].classindex)]
1✔
4813
          -> template)
1✔
4814
        template =
4815
            lp->class[(int)(lp->resultcache->results[0].classindex)]->template;
4816
      else
4817
        template = lp->template;
3✔
4818

4819
      if (template == NULL) {
3✔
4820
        msSetError(MS_WEBERR, "No template for layer %s or it's classes.",
×
4821
                   "msReturnNestedTemplateQuery()", lp->name);
4822
        return MS_FAILURE;
×
4823
      }
4824

4825
      if (TEMPLATE_TYPE(template) == MS_URL) {
3✔
4826
        mapserv->resultlayer = lp;
×
4827

4828
#if 0
4829
        status = msLayerOpen(lp);
4830
        if(status != MS_SUCCESS) return status;
4831

4832
        status = msLayerGetItems(lp); /* retrieve all the item names */
4833
        if(status != MS_SUCCESS) return status;
4834
#endif
4835

4836
        status = msLayerGetShape(lp, &(mapserv->resultshape),
×
4837
                                 &(lp->resultcache->results[0]));
4838
        if (status != MS_SUCCESS)
×
4839
          return status;
4840

4841
        if (lp->numjoins > 0) {
×
4842
          for (k = 0; k < lp->numjoins; k++) {
×
4843
            status = msJoinConnect(lp, &(lp->joins[k]));
×
4844
            if (status != MS_SUCCESS)
×
4845
              return status;
×
4846

4847
            msJoinPrepare(&(lp->joins[k]), &(mapserv->resultshape));
×
4848
            msJoinNext(&(lp->joins[k])); /* fetch the first row */
×
4849
          }
4850
        }
4851

4852
        if (papszBuffer == NULL) {
×
4853
          if (msReturnURL(mapserv, template, QUERY) != MS_SUCCESS)
×
4854
            return MS_FAILURE;
4855
        }
4856

4857
        msFreeShape(&(mapserv->resultshape));
×
4858
        /* msLayerClose(lp); */
4859
        mapserv->resultlayer = NULL;
×
4860

4861
        return MS_SUCCESS;
×
4862
      }
4863
    }
4864
  }
4865

4866
  /*
4867
  ** Now we know we're making a template sandwich
4868
  */
4869
  mapserv->NR = mapserv->NL = 0;
3✔
4870
  for (i = 0; i < mapserv->map->numlayers; i++) { /* compute some totals */
10✔
4871
    lp = (GET_LAYER(mapserv->map, i));
7✔
4872

4873
    if (!lp->resultcache)
7✔
4874
      continue;
4✔
4875

4876
    if (lp->resultcache->numresults > 0) {
3✔
4877
      mapserv->NL++;
3✔
4878
      mapserv->NR += lp->resultcache->numresults;
3✔
4879
    }
4880
  }
4881

4882
  /*
4883
  ** Is this step really necessary for buffered output? Legend and browse
4884
  *templates don't deal with mime-types
4885
  ** so why should this. Note that new-style templates don't buffer the
4886
  *mime-type either.
4887
  */
4888
  if (papszBuffer && mapserv->sendheaders) {
3✔
4889
    snprintf(buffer, sizeof(buffer), "Content-Type: %s%c%c", pszMimeType, 10,
4890
             10);
4891
    if (nBufferSize <= (int)(nCurrentSize + strlen(buffer) + 1)) {
×
4892
      nExpandBuffer++;
×
4893
      (*papszBuffer) = (char *)msSmallRealloc(
×
4894
          (*papszBuffer), MS_TEMPLATE_BUFFER * nExpandBuffer);
×
4895
      nBufferSize = MS_TEMPLATE_BUFFER * nExpandBuffer;
4896
    }
4897
    strcat((*papszBuffer), buffer);
×
4898
    nCurrentSize += strlen(buffer);
×
4899
  } else if (mapserv->sendheaders) {
3✔
4900
    msIO_setHeader("Content-Type", "%s", pszMimeType);
3✔
4901
    msIO_sendHeaders();
3✔
4902
  }
4903

4904
  if (mapserv->map->web.header) {
3✔
4905
    if (msReturnPage(mapserv, mapserv->map->web.header, BROWSE, papszBuffer) !=
2✔
4906
        MS_SUCCESS)
4907
      return MS_FAILURE;
4908
  }
4909

4910
  mapserv->RN = 1; /* overall result number */
3✔
4911
  for (i = 0; i < mapserv->map->numlayers; i++) {
10✔
4912
    mapserv->resultlayer = lp =
7✔
4913
        (GET_LAYER(mapserv->map, mapserv->map->layerorder[i]));
7✔
4914

4915
    if (!lp->resultcache)
7✔
4916
      continue;
4✔
4917
    if (lp->resultcache->numresults <= 0)
3✔
4918
      continue;
×
4919

4920
    mapserv->NLR = lp->resultcache->numresults;
3✔
4921

4922
#if 0
4923
    status = msLayerOpen(lp); /* open this layer */
4924
    if(status != MS_SUCCESS) return status;
4925

4926
    status = msLayerGetItems(lp); /* retrieve all the item names */
4927
    if(status != MS_SUCCESS) return status;
4928
#endif
4929

4930
    if (lp->numjoins > 0) { /* open any necessary JOINs here */
3✔
4931
      for (k = 0; k < lp->numjoins; k++) {
×
4932
        status = msJoinConnect(lp, &(lp->joins[k]));
×
4933
        if (status != MS_SUCCESS)
×
4934
          return status;
×
4935
      }
4936
    }
4937

4938
    if (lp->header) {
3✔
4939
      if (msReturnPage(mapserv, lp->header, BROWSE, papszBuffer) != MS_SUCCESS)
2✔
4940
        return MS_FAILURE;
4941
    }
4942

4943
    mapserv->LRN = 1; /* layer result number */
3✔
4944
    for (j = 0; j < lp->resultcache->numresults; j++) {
6✔
4945
      status = msLayerGetShape(lp, &(mapserv->resultshape),
3✔
4946
                               &(lp->resultcache->results[j]));
3✔
4947
      if (status != MS_SUCCESS)
3✔
4948
        return status;
×
4949

4950
      /* prepare any necessary JOINs here (one-to-one only) */
4951
      if (lp->numjoins > 0) {
3✔
4952
        for (k = 0; k < lp->numjoins; k++) {
×
4953
          if (lp->joins[k].type == MS_JOIN_ONE_TO_ONE) {
×
4954
            msJoinPrepare(&(lp->joins[k]), &(mapserv->resultshape));
×
4955
            msJoinNext(&(lp->joins[k])); /* fetch the first row */
×
4956
          }
4957
        }
4958
      }
4959

4960
      if (lp->resultcache->results[j].classindex >= 0 &&
3✔
4961
              lp->class[(int)(lp->resultcache->results[j].classindex)]
1✔
4962
          -> template)
1✔
4963
        template =
4964
            lp->class[(int)(lp->resultcache->results[j].classindex)]->template;
4965
      else
4966
        template = lp->template;
3✔
4967

4968
      if (msReturnPage(mapserv, template, QUERY, papszBuffer) != MS_SUCCESS) {
3✔
4969
        msFreeShape(&(mapserv->resultshape));
×
4970
        return MS_FAILURE;
×
4971
      }
4972

4973
      msFreeShape(&(mapserv->resultshape)); /* init too */
3✔
4974

4975
      mapserv->RN++; /* increment counters */
3✔
4976
      mapserv->LRN++;
3✔
4977
    }
4978

4979
    if (lp->footer) {
3✔
4980
      if (msReturnPage(mapserv, lp->footer, BROWSE, papszBuffer) != MS_SUCCESS)
×
4981
        return MS_FAILURE;
4982
    }
4983

4984
    /* msLayerClose(lp); */
4985
    mapserv->resultlayer = NULL;
3✔
4986
  }
4987

4988
  if (mapserv->map->web.footer)
3✔
4989
    return msReturnPage(mapserv, mapserv->map->web.footer, BROWSE, papszBuffer);
2✔
4990

4991
  return MS_SUCCESS;
4992
}
4993

4994
int msReturnOpenLayersPage(mapservObj *mapserv) {
4✔
4995
  int i;
4996
  char *buffer = NULL, *layer = NULL;
4997
  const char *tmpUrl = NULL;
4998
  const char *openlayersUrl = olUrl;
4999
  const char *openlayersCssUrl = olCssUrl;
5000
  char *projection = NULL;
5001
  char *format = NULL;
5002

5003
  /* 2 CGI parameters are used in the template. we need to transform them
5004
   * to be sure the case match during the template processing. We also
5005
   * need to search the SRS/CRS parameter to get the projection info. OGC
5006
   * services version >= 1.3.0 uses CRS rather than SRS */
5007
  for (i = 0; i < mapserv->request->NumParams; i++) {
44✔
5008
    if ((strcasecmp(mapserv->request->ParamNames[i], "SRS") == 0) ||
40✔
5009
        (strcasecmp(mapserv->request->ParamNames[i], "CRS") == 0)) {
40✔
5010
      projection = mapserv->request->ParamValues[i];
3✔
5011
    } else if (strcasecmp(mapserv->request->ParamNames[i], "LAYERS") == 0) {
37✔
5012
      free(mapserv->request->ParamNames[i]);
4✔
5013
      mapserv->request->ParamNames[i] = msStrdup("LAYERS");
4✔
5014
    } else if (strcasecmp(mapserv->request->ParamNames[i], "VERSION") == 0) {
33✔
5015
      free(mapserv->request->ParamNames[i]);
3✔
5016
      mapserv->request->ParamNames[i] = msStrdup("VERSION");
3✔
5017
    }
5018
  }
5019
  if (mapserv->map->outputformat->mimetype &&
4✔
5020
      *mapserv->map->outputformat->mimetype) {
4✔
5021
    format = mapserv->map->outputformat->mimetype;
5022
  }
5023

5024
  /* check if the environment variable or config MS_OPENLAYERS_JS_URL is set */
5025
  tmpUrl = msGetConfigOption(mapserv->map, "MS_OPENLAYERS_JS_URL");
4✔
5026
  if (tmpUrl == NULL)
4✔
5027
    tmpUrl = CPLGetConfigOption("MS_OPENLAYERS_JS_URL", NULL);
4✔
5028

5029
  if (tmpUrl)
4✔
5030
    openlayersUrl = tmpUrl;
5031

5032
  /* now do the same for the MS_OPENLAYERS_CSS_URL */
5033
  tmpUrl = msGetConfigOption(mapserv->map, "MS_OPENLAYERS_CSS_URL");
4✔
5034
  if (tmpUrl == NULL)
4✔
5035
    tmpUrl = CPLGetConfigOption("MS_OPENLAYERS_CSS_URL", NULL);
4✔
5036

5037
  if (tmpUrl)
4✔
5038
    openlayersCssUrl = tmpUrl;
5039

5040
  if (mapserv->Mode == BROWSE) {
4✔
5041
    layer = processLine(mapserv, olLayerMapServerTag, NULL, BROWSE);
1✔
5042
  } else
5043
    layer = processLine(mapserv, olLayerWMSTag, NULL, BROWSE);
3✔
5044

5045
  buffer = processLine(mapserv, olTemplate, NULL, BROWSE);
4✔
5046
  buffer = msReplaceSubstring(buffer, "[openlayers_js_url]", openlayersUrl);
4✔
5047
  buffer = msReplaceSubstring(buffer, "[openlayers_css_url]", openlayersCssUrl);
4✔
5048
  buffer = msReplaceSubstring(buffer, "[openlayers_layer]", layer);
4✔
5049
  if (projection)
4✔
5050
    buffer = msReplaceSubstring(buffer, "[openlayers_projection]", projection);
3✔
5051
  if (format)
4✔
5052
    buffer = msReplaceSubstring(buffer, "[openlayers_format]", format);
4✔
5053
  else
5054
    buffer = msReplaceSubstring(buffer, "[openlayers_format]", "image/png");
×
5055
  msIO_fwrite(buffer, strlen(buffer), 1, stdout);
4✔
5056
  free(layer);
4✔
5057
  free(buffer);
4✔
5058

5059
  return MS_SUCCESS;
4✔
5060
}
5061

5062
mapservObj *msAllocMapServObj() {
2,021✔
5063
  mapservObj *mapserv = msSmallMalloc(sizeof(mapservObj));
2,021✔
5064

5065
  mapserv->savemap = MS_FALSE;
2,021✔
5066
  mapserv->savequery = MS_FALSE; /* should the query and/or map be saved  */
2,021✔
5067

5068
  mapserv->sendheaders = MS_TRUE;
2,021✔
5069

5070
  mapserv->request = msAllocCgiObj();
2,021✔
5071

5072
  mapserv->map = NULL;
2,021✔
5073

5074
  mapserv->NumLayers = 0; /* number of layers specified by a user */
2,021✔
5075
  mapserv->MaxLayers = 0; /* allocated size of Layers[] array */
2,021✔
5076
  mapserv->Layers = NULL;
2,021✔
5077

5078
  mapserv->icon = NULL;
2,021✔
5079

5080
  mapserv->RawExt.minx = -1;
2,021✔
5081
  mapserv->RawExt.miny = -1;
2,021✔
5082
  mapserv->RawExt.maxx = -1;
2,021✔
5083
  mapserv->RawExt.maxy = -1;
2,021✔
5084

5085
  mapserv->fZoom = 1;
2,021✔
5086
  mapserv->Zoom = 1; /* default for browsing */
2,021✔
5087

5088
  mapserv->resultlayer = NULL;
2,021✔
5089

5090
  mapserv->UseShapes = MS_FALSE;
2,021✔
5091

5092
  mapserv->mappnt.x = -1;
2,021✔
5093
  mapserv->mappnt.y = -1;
2,021✔
5094

5095
  mapserv->ZoomDirection =
2,021✔
5096
      0; /* whether zooming in or out, default is pan or 0 */
5097

5098
  mapserv->Mode = BROWSE; /* can be BROWSE, QUERY, etc. */
2,021✔
5099

5100
  sprintf(mapserv->Id, "%ld%d", (long)time(NULL), (int)getpid());
2,021✔
5101

5102
  mapserv->CoordSource = NONE;
2,021✔
5103
  mapserv->ScaleDenom = 0;
2,021✔
5104

5105
  mapserv->ImgRows = -1;
2,021✔
5106
  mapserv->ImgCols = -1;
2,021✔
5107

5108
  mapserv->ImgExt.minx = -1;
2,021✔
5109
  mapserv->ImgExt.miny = -1;
2,021✔
5110
  mapserv->ImgExt.maxx = -1;
2,021✔
5111
  mapserv->ImgExt.maxy = -1;
2,021✔
5112

5113
  mapserv->ImgBox.minx = -1;
2,021✔
5114
  mapserv->ImgBox.miny = -1;
2,021✔
5115
  mapserv->ImgBox.maxx = -1;
2,021✔
5116
  mapserv->ImgBox.maxy = -1;
2,021✔
5117

5118
  mapserv->RefPnt.x = -1;
2,021✔
5119
  mapserv->RefPnt.y = -1;
2,021✔
5120
  mapserv->ImgPnt.x = -1;
2,021✔
5121
  mapserv->ImgPnt.y = -1;
2,021✔
5122

5123
  mapserv->Buffer = 0;
2,021✔
5124

5125
  /*
5126
  ** variables for multiple query results processing
5127
  */
5128
  mapserv->RN = 0;  /* overall result number */
2,021✔
5129
  mapserv->LRN = 0; /* result number within a layer */
2,021✔
5130
  mapserv->NL = 0;  /* total number of layers with results */
2,021✔
5131
  mapserv->NR = 0;  /* total number or results */
2,021✔
5132
  mapserv->NLR = 0; /* number of results in a layer */
2,021✔
5133

5134
  mapserv->SearchMap =
2,021✔
5135
      MS_FALSE; /* apply pan/zoom BEFORE doing the query (e.g. query the output
5136
                   image rather than the input image) */
5137

5138
  mapserv->QueryFile = NULL;
2,021✔
5139
  mapserv->QueryLayer = NULL;
2,021✔
5140
  mapserv->SelectLayer = NULL;
2,021✔
5141
  mapserv->QueryLayerIndex = -1;
2,021✔
5142
  mapserv->SelectLayerIndex = -1;
2,021✔
5143
  mapserv->QueryItem = NULL;
2,021✔
5144
  mapserv->QueryString = NULL;
2,021✔
5145
  mapserv->ShapeIndex = -1;
2,021✔
5146
  mapserv->TileIndex = -1;
2,021✔
5147
  mapserv->TileMode = TILE_GMAP;
2,021✔
5148
  mapserv->TileCoords = NULL;
2,021✔
5149
  mapserv->TileWidth = -1;
2,021✔
5150
  mapserv->TileHeight = -1;
2,021✔
5151
  mapserv->QueryCoordSource = NONE;
2,021✔
5152
  mapserv->ZoomSize = 0; /* zoom absolute magnitude (i.e. > 0) */
2,021✔
5153

5154
  mapserv->hittest = NULL;
2,021✔
5155

5156
  return mapserv;
2,021✔
5157
}
5158

5159
void msFreeMapServObj(mapservObj *mapserv) {
2,021✔
5160
  int i;
5161

5162
  if (mapserv) {
2,021✔
5163
    if (mapserv->map) {
2,021✔
5164
      if (mapserv->hittest) {
1,972✔
5165
        freeMapHitTests(mapserv->map, mapserv->hittest);
2✔
5166
        free(mapserv->hittest);
2✔
5167
      }
5168
      msFreeMap(mapserv->map);
1,972✔
5169
      mapserv->map = NULL;
1,972✔
5170
    }
5171

5172
    if (mapserv->request) {
2,021✔
5173
      msFreeCgiObj(mapserv->request);
1,994✔
5174
      mapserv->request = NULL;
5175
    }
5176

5177
    for (i = 0; i < mapserv->NumLayers; i++)
2,099✔
5178
      msFree(mapserv->Layers[i]);
78✔
5179
    msFree(mapserv->Layers);
2,021✔
5180

5181
    msFree(mapserv->icon);
2,021✔
5182

5183
    msFree(mapserv->QueryItem);
2,021✔
5184
    msFree(mapserv->QueryString);
2,021✔
5185
    msFree(mapserv->QueryLayer);
2,021✔
5186
    msFree(mapserv->SelectLayer);
2,021✔
5187
    msFree(mapserv->QueryFile);
2,021✔
5188

5189
    msFree(mapserv->TileCoords);
2,021✔
5190

5191
    msFree(mapserv);
2,021✔
5192
  }
5193
}
2,021✔
5194

5195
/*
5196
** Ensure there is at least one free entry in the Layers array.
5197
**
5198
** This function is safe to use for the initial allocation of the Layers[]
5199
** array as well (i.e. when MaxLayers==0 and Layers==NULL)
5200
**
5201
** Returns MS_SUCCESS/MS_FAILURE
5202
*/
5203
int msGrowMapservLayers(mapservObj *mapserv) {
78✔
5204
  /* Do we need to increase the size of Layers[] by MS_LAYER_ALLOCSIZE? */
5205
  if (mapserv->NumLayers == mapserv->MaxLayers) {
78✔
5206
    int i;
5207

5208
    if (mapserv->MaxLayers == 0) {
52✔
5209
      /* initial allocation of array */
5210
      mapserv->MaxLayers += MS_LAYER_ALLOCSIZE;
52✔
5211
      mapserv->NumLayers = 0;
52✔
5212
      mapserv->Layers =
52✔
5213
          (char **)msSmallMalloc(mapserv->MaxLayers * sizeof(char *));
52✔
5214
    } else {
5215
      /* realloc existing array */
5216
      mapserv->MaxLayers += MS_LAYER_ALLOCSIZE;
×
5217
      mapserv->Layers = (char **)msSmallRealloc(
×
5218
          mapserv->Layers, mapserv->MaxLayers * sizeof(char *));
×
5219
    }
5220

5221
    if (mapserv->Layers == NULL) {
52✔
5222
      msSetError(MS_MEMERR, "Failed to allocate memory for Layers array.",
×
5223
                 "msGrowMappservLayers()");
5224
      return MS_FAILURE;
×
5225
    }
5226

5227
    for (i = mapserv->NumLayers; i < mapserv->MaxLayers; i++) {
3,380✔
5228
      mapserv->Layers[i] = NULL;
3,328✔
5229
    }
5230
  }
5231

5232
  return MS_SUCCESS;
5233
}
5234

5235
/*
5236
** Utility function to generate map, legend, scalebar and reference images.
5237
**
5238
** Parameters:
5239
**   - mapserv: mapserv object (used to extract the map object).
5240
**   - bQueryMap: if set to TRUE a query map will be created instead of a
5241
*regular map.
5242
**   - bReturnOnError: if set to TRUE, the function will return on the first
5243
*error, else it will try to generate all the images.
5244
*/
5245
int msGenerateImages(mapservObj *mapserv, int bQueryMap, int bReturnOnError) {
3✔
5246
  char buffer[1024];
5247

5248
  if (mapserv) {
3✔
5249

5250
    /* render the map OR query map */
5251
    if ((!bQueryMap && mapserv->map->status == MS_ON) ||
3✔
5252
        (bQueryMap && mapserv->map->querymap.status == MS_ON)) {
2✔
5253
      imageObj *image = NULL;
5254

5255
      image = msDrawMap(mapserv->map, bQueryMap);
3✔
5256
      if (image) {
3✔
5257
        snprintf(buffer, sizeof(buffer), "%s%s%s.%s",
6✔
5258
                 mapserv->map->web.imagepath, mapserv->map->name, mapserv->Id,
3✔
5259
                 MS_IMAGE_EXTENSION(mapserv->map->outputformat));
3✔
5260

5261
        if (msSaveImage(mapserv->map, image, buffer) != MS_SUCCESS &&
3✔
5262
            bReturnOnError) {
5263
          msFreeImage(image);
×
5264
          return MS_FAILURE;
×
5265
        }
5266
        msFreeImage(image);
3✔
5267
      } else if (bReturnOnError)
×
5268
        return MS_FAILURE;
5269
    }
5270

5271
    /* render the legend */
5272
    if (mapserv->map->legend.status == MS_ON) {
3✔
5273
      imageObj *image = NULL;
5274
      image = msDrawLegend(mapserv->map, MS_FALSE, NULL);
×
5275
      if (image) {
×
5276
        snprintf(buffer, sizeof(buffer), "%s%sleg%s.%s",
×
5277
                 mapserv->map->web.imagepath, mapserv->map->name, mapserv->Id,
×
5278
                 MS_IMAGE_EXTENSION(mapserv->map->outputformat));
×
5279

5280
        if (msSaveImage(mapserv->map, image, buffer) != MS_SUCCESS &&
×
5281
            bReturnOnError) {
5282
          msFreeImage(image);
×
5283
          return MS_FAILURE;
×
5284
        }
5285
        msFreeImage(image);
×
5286
      } else if (bReturnOnError)
×
5287
        return MS_FAILURE;
5288
    }
5289

5290
    /* render the scalebar */
5291
    if (mapserv->map->scalebar.status == MS_ON) {
3✔
5292
      imageObj *image = NULL;
5293
      image = msDrawScalebar(mapserv->map);
×
5294
      if (image) {
×
5295
        snprintf(buffer, sizeof(buffer), "%s%ssb%s.%s",
×
5296
                 mapserv->map->web.imagepath, mapserv->map->name, mapserv->Id,
×
5297
                 MS_IMAGE_EXTENSION(mapserv->map->outputformat));
×
5298
        if (msSaveImage(mapserv->map, image, buffer) != MS_SUCCESS &&
×
5299
            bReturnOnError) {
5300
          msFreeImage(image);
×
5301
          return MS_FAILURE;
×
5302
        }
5303
        msFreeImage(image);
×
5304
      } else if (bReturnOnError)
×
5305
        return MS_FAILURE;
5306
    }
5307

5308
    /* render the reference map */
5309
    if (mapserv->map->reference.status == MS_ON) {
3✔
5310
      imageObj *image;
5311
      image = msDrawReferenceMap(mapserv->map);
×
5312
      if (image) {
×
5313
        snprintf(buffer, sizeof(buffer), "%s%sref%s.%s",
×
5314
                 mapserv->map->web.imagepath, mapserv->map->name, mapserv->Id,
×
5315
                 MS_IMAGE_EXTENSION(mapserv->map->outputformat));
×
5316
        if (msSaveImage(mapserv->map, image, buffer) != MS_SUCCESS &&
×
5317
            bReturnOnError) {
5318
          msFreeImage(image);
×
5319
          return MS_FAILURE;
×
5320
        }
5321
        msFreeImage(image);
×
5322
      } else if (bReturnOnError)
×
5323
        return MS_FAILURE;
5324
    }
5325
  }
5326

5327
  return MS_SUCCESS;
5328
}
5329

5330
/*
5331
** Utility function to open a template file, process it and
5332
** and return into a buffer the processed template. Uses the
5333
** template file from the web object. Returns NULL if there is
5334
** an error.
5335
*/
5336
char *msProcessTemplate(mapObj *map, int bGenerateImages, char **names,
×
5337
                        char **values, int numentries) {
5338
  char *pszBuffer = NULL;
×
5339

5340
  if (map) {
×
5341

5342
    /* Initialize object and set appropriate defaults. */
5343
    mapservObj *mapserv = NULL;
5344
    mapserv = msAllocMapServObj();
×
5345

5346
    mapserv->map = map;
×
5347
    mapserv->Mode = BROWSE;
×
5348

5349
    if (names && values && numentries > 0) {
×
5350
      msFreeCharArray(mapserv->request->ParamNames,
×
5351
                      mapserv->request->NumParams);
×
5352
      msFreeCharArray(mapserv->request->ParamValues,
×
5353
                      mapserv->request->NumParams);
×
5354
      mapserv->request->ParamNames = names;
×
5355
      mapserv->request->ParamValues = values;
×
5356
      mapserv->request->NumParams = numentries;
×
5357
    }
5358

5359
    /*
5360
    ** ISSUE/TODO : some of the name/values should be extracted and
5361
    ** processed (ex imgext, layers, ...) as it is done in function
5362
    ** loadform.
5363
    */
5364

5365
    if (bGenerateImages)
×
5366
      msGenerateImages(mapserv, MS_FALSE, MS_FALSE);
×
5367

5368
    /*
5369
    ** Process the template.
5370
    **
5371
    ** TODO : use web minscaledenom/maxscaledenom depending on the scale.
5372
    */
5373
    if (msReturnPage(mapserv, mapserv->map->web.template, BROWSE, &pszBuffer) !=
×
5374
        MS_SUCCESS) {
5375
      msFree(pszBuffer);
×
5376
      pszBuffer = NULL;
×
5377
    }
5378

5379
    /* Don't free the map and names and values arrays since they were passed by
5380
     * reference. */
5381
    mapserv->map = NULL;
×
5382
    mapserv->request->ParamNames = mapserv->request->ParamValues = NULL;
×
5383
    mapserv->request->NumParams = 0;
×
5384
    msFreeMapServObj(mapserv);
×
5385
  }
5386

5387
  return pszBuffer;
×
5388
}
5389

5390
/*
5391
** Utility method to process the legend template.
5392
*/
5393
char *msProcessLegendTemplate(mapObj *map, char **names, char **values,
×
5394
                              int numentries) {
5395
  char *pszOutBuf = NULL;
5396

5397
  if (map && map->legend.template) {
×
5398

5399
    /* Initialize object and set appropriate defaults. */
5400
    mapservObj *mapserv = NULL;
5401
    mapserv = msAllocMapServObj();
×
5402

5403
    mapserv->map = map;
×
5404
    mapserv->Mode = BROWSE;
×
5405

5406
    if (names && values && numentries > 0) {
×
5407
      msFreeCharArray(mapserv->request->ParamNames,
×
5408
                      mapserv->request->NumParams);
×
5409
      msFreeCharArray(mapserv->request->ParamValues,
×
5410
                      mapserv->request->NumParams);
×
5411
      mapserv->request->ParamNames = names;
×
5412
      mapserv->request->ParamValues = values;
×
5413
      mapserv->request->NumParams = numentries;
×
5414
    }
5415

5416
    pszOutBuf = generateLegendTemplate(mapserv);
×
5417

5418
    /* Don't free the map and names and values arrays since they were passed by
5419
     * reference. */
5420
    mapserv->map = NULL;
×
5421
    mapserv->request->ParamNames = mapserv->request->ParamValues = NULL;
×
5422
    mapserv->request->NumParams = 0;
×
5423
    msFreeMapServObj(mapserv);
×
5424
  }
5425

5426
  return pszOutBuf;
×
5427
}
5428

5429
/*
5430
** Utility function that process a template file(s) used in the
5431
** query and return the processed template(s) in a buffer.
5432
*/
5433
char *msProcessQueryTemplate(mapObj *map, int bGenerateImages, char **names,
×
5434
                             char **values, int numentries) {
5435
  char *pszBuffer = NULL;
×
5436

5437
  if (map) {
×
5438

5439
    /* Initialize object and set appropriate defaults. */
5440
    mapservObj *mapserv = NULL;
5441
    mapserv = msAllocMapServObj();
×
5442

5443
    mapserv->map = map;
×
5444
    mapserv->Mode = QUERY;
×
5445

5446
    if (names && values && numentries > 0) {
×
5447
      msFreeCharArray(mapserv->request->ParamNames,
×
5448
                      mapserv->request->NumParams);
×
5449
      msFreeCharArray(mapserv->request->ParamValues,
×
5450
                      mapserv->request->NumParams);
×
5451
      mapserv->request->ParamNames = names;
×
5452
      mapserv->request->ParamValues = values;
×
5453
      mapserv->request->NumParams = numentries;
×
5454
    }
5455

5456
    if (bGenerateImages)
×
5457
      msGenerateImages(mapserv, MS_TRUE, MS_FALSE);
×
5458

5459
    mapserv->sendheaders = MS_FALSE;
×
5460
    IGNORE_RET_VAL(msReturnTemplateQuery(mapserv, mapserv->map->web.queryformat,
×
5461
                                         &pszBuffer));
5462

5463
    mapserv->map = NULL;
×
5464
    mapserv->request->ParamNames = mapserv->request->ParamValues = NULL;
×
5465
    mapserv->request->NumParams = 0;
×
5466
    msFreeMapServObj(mapserv);
×
5467
  }
5468

5469
  return pszBuffer;
×
5470
}
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