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

geographika / mapserver / 23004945429

12 Mar 2026 01:39PM UTC coverage: 42.423% (+0.003%) from 42.42%
23004945429

push

github

geographika
Use std::string and avoid strlcpy

8 of 9 new or added lines in 1 file covered. (88.89%)

955 existing lines in 4 files now uncovered.

64589 of 152250 relevant lines covered (42.42%)

27323.37 hits per line

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

75.76
/src/mapogcfilter.cpp
1
/**********************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  OGC Filter Encoding implementation
6
 * Author:   Y. Assefa, DM Solutions Group (assefa@dmsolutions.ca)
7
 *
8
 **********************************************************************
9
 * Copyright (c) 2003, Y. Assefa, DM Solutions Group Inc
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 OR
22
 * 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
 ****************************************************************************/
28

29
#include "mapserver-config.h"
30

31
#include "cpl_minixml.h"
32
#include "cpl_string.h"
33

34
#include "mapogcfilter.h"
35
#include "mapserver.h"
36
#include "mapowscommon.h"
37
#include "maptime.h"
38
#include "mapows.h"
39
#include <ctype.h>
40

41
#if 0
42
static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode);
43
#endif
44

45
#if !(defined(_WIN32) && !defined(__CYGWIN__))
46
static inline void IGUR_double(double ignored) {
47
  (void)ignored;
48
} /* Ignore GCC Unused Result */
49
#endif
50

51
int FLTIsNumeric(const char *pszValue) {
359✔
52
  if (pszValue != NULL && *pszValue != '\0' && !isspace(*pszValue)) {
359✔
53
    /*the regex seems to have a problem on windows when mapserver is built using
54
      PHP regex*/
55
#if defined(_WIN32) && !defined(__CYGWIN__)
56
    int i = 0, nLength = 0, bString = 0;
57

58
    nLength = strlen(pszValue);
59
    for (i = 0; i < nLength; i++) {
60
      if (i == 0) {
61
        if (!isdigit(pszValue[i]) && pszValue[i] != '-') {
62
          bString = 1;
63
          break;
64
        }
65
      } else if (!isdigit(pszValue[i]) && pszValue[i] != '.') {
66
        bString = 1;
67
        break;
68
      }
69
    }
70
    if (!bString)
71
      return MS_TRUE;
72
#else
73
    char *p;
74
    IGUR_double(strtod(pszValue, &p));
359✔
75
    if (p != pszValue && *p == '\0')
359✔
76
      return MS_TRUE;
224✔
77
#endif
78
  }
79

80
  return MS_FALSE;
81
}
82

83
/*
84
** Apply an expression to the layer's filter element.
85
**
86
*/
87
int FLTApplyExpressionToLayer(layerObj *lp, const char *pszExpression) {
13✔
88
  char *pszFinalExpression = NULL, *pszBuffer = NULL;
89
  /*char *escapedTextString=NULL;*/
90
  int bConcatWhere = 0, bHasAWhere = 0;
91

92
  if (lp && pszExpression) {
13✔
93
    if (lp->connectiontype == MS_POSTGIS ||
13✔
94
        lp->connectiontype == MS_ORACLESPATIAL ||
13✔
95
        lp->connectiontype == MS_PLUGIN) {
96
      pszFinalExpression = msStrdup("(");
×
97
      pszFinalExpression =
98
          msStringConcatenate(pszFinalExpression, pszExpression);
×
99
      pszFinalExpression = msStringConcatenate(pszFinalExpression, ")");
×
100
    } else if (lp->connectiontype == MS_OGR) {
13✔
101
      pszFinalExpression = msStrdup(pszExpression);
×
102
      if (lp->filter.type != MS_EXPRESSION) {
×
103
        bConcatWhere = 1;
104
      } else {
105
        if (lp->filter.string && EQUALN(lp->filter.string, "WHERE ", 6)) {
×
106
          bHasAWhere = 1;
107
          bConcatWhere = 1;
108
        }
109
      }
110

111
    } else
112
      pszFinalExpression = msStrdup(pszExpression);
13✔
113

114
    if (bConcatWhere)
13✔
115
      pszBuffer = msStringConcatenate(pszBuffer, "WHERE ");
×
116
    /* if the filter is set and it's an expression type, concatenate it with
117
                this filter. If not just free it */
118
    if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
13✔
119
      pszBuffer = msStringConcatenate(pszBuffer, "((");
×
120
      if (bHasAWhere)
×
121
        pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string + 6);
×
122
      else
123
        pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
×
124
      pszBuffer = msStringConcatenate(pszBuffer, ") and ");
×
125
    } else if (lp->filter.string)
13✔
126
      msFreeExpression(&lp->filter);
×
127

128
    pszBuffer = msStringConcatenate(pszBuffer, pszFinalExpression);
13✔
129

130
    if (lp->filter.string && lp->filter.type == MS_EXPRESSION)
13✔
131
      pszBuffer = msStringConcatenate(pszBuffer, ")");
×
132

133
    /*assuming that expression was properly escaped
134
          escapedTextString = msStringEscape(pszBuffer);
135
          msLoadExpressionString(&lp->filter,
136
                                 (char*)CPLSPrintf("%s", escapedTextString));
137
          msFree(escapedTextString);
138
    */
139
    msLoadExpressionString(&lp->filter, pszBuffer);
13✔
140

141
    msFree(pszFinalExpression);
13✔
142

143
    if (pszBuffer)
13✔
144
      msFree(pszBuffer);
13✔
145

146
    return MS_TRUE;
13✔
147
  }
148

149
  return MS_FALSE;
150
}
151

152
char *FLTGetExpressionForValuesRanges(layerObj *lp, const char *item,
13✔
153
                                      const char *value, int forcecharcter) {
154
  int bIscharacter;
155
  char *pszExpression = NULL, *pszTmpExpression = NULL;
156
  char **paszElements = NULL, **papszRangeElements = NULL;
157
  int numelements, i, nrangeelements;
158

159
  /* double minval, maxval; */
160
  if (lp && item && value) {
13✔
161
    if (strstr(value, "/") == NULL) {
13✔
162
      /*value(s)*/
163
      paszElements = msStringSplit(value, ',', &numelements);
9✔
164
      if (paszElements && numelements > 0) {
9✔
165
        if (forcecharcter)
9✔
166
          bIscharacter = MS_TRUE;
167
        else
168
          bIscharacter = !FLTIsNumeric(paszElements[0]);
9✔
169

170
        pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
9✔
171
        for (i = 0; i < numelements; i++) {
22✔
172
          pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
13✔
173
          {
174
            if (bIscharacter)
13✔
175
              pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
3✔
176
            pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
13✔
177
            pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
13✔
178
            pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
13✔
179
            if (bIscharacter)
13✔
180
              pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
3✔
181
          }
182
          if (bIscharacter) {
183
            pszTmpExpression = msStringConcatenate(pszTmpExpression, " = \"");
3✔
184
          } else
185
            pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");
10✔
186

187
          {
188
            char *pszEscapedStr = msLayerEscapeSQLParam(lp, paszElements[i]);
13✔
189
            pszTmpExpression =
190
                msStringConcatenate(pszTmpExpression, pszEscapedStr);
13✔
191
            msFree(pszEscapedStr);
13✔
192
          }
193

194
          if (bIscharacter) {
13✔
195
            pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
3✔
196
          }
197
          pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
13✔
198

199
          if (pszExpression != NULL)
13✔
200
            pszExpression = msStringConcatenate(pszExpression, " OR ");
4✔
201

202
          pszExpression = msStringConcatenate(pszExpression, pszTmpExpression);
13✔
203

204
          msFree(pszTmpExpression);
13✔
205
          pszTmpExpression = NULL;
206
        }
207
        pszExpression = msStringConcatenate(pszExpression, ")");
9✔
208
      }
209
      msFreeCharArray(paszElements, numelements);
9✔
210
    } else {
211
      /*range(s)*/
212
      paszElements = msStringSplit(value, ',', &numelements);
4✔
213
      if (paszElements && numelements > 0) {
4✔
214
        pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
4✔
215
        for (i = 0; i < numelements; i++) {
10✔
216
          papszRangeElements =
217
              msStringSplit(paszElements[i], '/', &nrangeelements);
6✔
218
          if (papszRangeElements && nrangeelements > 0) {
6✔
219
            pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
6✔
220
            if (nrangeelements == 2 || nrangeelements == 3) {
6✔
221
              /*
222
              minval = atof(papszRangeElements[0]);
223
              maxval = atof(papszRangeElements[1]);
224
              */
225
              {
226
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
6✔
227
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
6✔
228
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
6✔
229
              }
230

231
              pszTmpExpression = msStringConcatenate(pszTmpExpression, " >= ");
6✔
232

233
              {
234
                char *pszEscapedStr =
235
                    msLayerEscapeSQLParam(lp, papszRangeElements[0]);
6✔
236
                pszTmpExpression =
237
                    msStringConcatenate(pszTmpExpression, pszEscapedStr);
6✔
238
                msFree(pszEscapedStr);
6✔
239
              }
240

241
              pszTmpExpression = msStringConcatenate(pszTmpExpression, " AND ");
6✔
242

243
              {
244
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
6✔
245
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
6✔
246
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
6✔
247
              }
248

249
              pszTmpExpression = msStringConcatenate(pszTmpExpression, " <= ");
6✔
250

251
              {
252
                char *pszEscapedStr =
253
                    msLayerEscapeSQLParam(lp, papszRangeElements[1]);
6✔
254
                pszTmpExpression =
255
                    msStringConcatenate(pszTmpExpression, pszEscapedStr);
6✔
256
                msFree(pszEscapedStr);
6✔
257
              }
258

259
              pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
6✔
260
            } else if (nrangeelements == 1) {
×
261
              pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
×
262
              {
263
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
×
264
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
×
265
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
×
266
              }
267

268
              pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");
×
269

270
              {
271
                char *pszEscapedStr =
272
                    msLayerEscapeSQLParam(lp, papszRangeElements[0]);
×
273
                pszTmpExpression =
274
                    msStringConcatenate(pszTmpExpression, pszEscapedStr);
×
275
                msFree(pszEscapedStr);
×
276
              }
277

278
              pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
×
279
            }
280

281
            if (pszExpression != NULL)
6✔
282
              pszExpression = msStringConcatenate(pszExpression, " OR ");
2✔
283

284
            pszExpression =
285
                msStringConcatenate(pszExpression, pszTmpExpression);
6✔
286
            msFree(pszTmpExpression);
6✔
287
            pszTmpExpression = NULL;
288
          }
289
          msFreeCharArray(papszRangeElements, nrangeelements);
6✔
290
        }
291
        pszExpression = msStringConcatenate(pszExpression, ")");
4✔
292
      }
293
      msFreeCharArray(paszElements, numelements);
4✔
294
    }
295
  }
296
  msFree(pszTmpExpression);
13✔
297
  return pszExpression;
13✔
298
}
299

300
static int FLTShapeFromGMLTree(CPLXMLNode *psTree, shapeObj *psShape,
92✔
301
                               char **ppszSRS) {
302
  const char *pszSRS = NULL;
303
  if (psTree && psShape) {
92✔
304
    CPLXMLNode *psNext = psTree->psNext;
92✔
305
    OGRGeometryH hGeometry = NULL;
306

307
    psTree->psNext = NULL;
92✔
308
    hGeometry = OGR_G_CreateFromGMLTree(psTree);
92✔
309
    psTree->psNext = psNext;
92✔
310

311
    if (hGeometry) {
92✔
312
      OGRwkbGeometryType nType;
313
      nType = OGR_G_GetGeometryType(hGeometry);
90✔
314
      if (nType == wkbPolygon25D || nType == wkbMultiPolygon25D)
90✔
315
        nType = wkbPolygon;
316
      else if (nType == wkbLineString25D || nType == wkbMultiLineString25D)
317
        nType = wkbLineString;
318
      else if (nType == wkbPoint25D || nType == wkbMultiPoint25D)
319
        nType = wkbPoint;
320
      msOGRGeometryToShape(hGeometry, psShape, nType);
90✔
321

322
      OGR_G_DestroyGeometry(hGeometry);
90✔
323

324
      pszSRS = CPLGetXMLValue(psTree, "srsName", NULL);
90✔
325
      if (ppszSRS && pszSRS)
90✔
326
        *ppszSRS = msStrdup(pszSRS);
22✔
327

328
      return MS_TRUE;
90✔
329
    }
330
  }
331

332
  return MS_FALSE;
333
}
334

335
/************************************************************************/
336
/*                            FLTSplitFilters                           */
337
/*                                                                      */
338
/*    Split filters separated by parentheses into an array of strings.  */
339
/************************************************************************/
340
char **FLTSplitFilters(const char *pszStr, int *pnTokens) {
59✔
341
  const char *pszTokenBegin;
342
  char **papszRet = NULL;
343
  int nTokens = 0;
344
  char chStringQuote = '\0';
345
  int nXMLIndent = 0;
346
  int bInBracket = FALSE;
347

348
  if (*pszStr != '(') {
59✔
349
    *pnTokens = 0;
×
350
    return NULL;
×
351
  }
352
  pszStr++;
59✔
353
  pszTokenBegin = pszStr;
354
  while (*pszStr != '\0') {
9,634✔
355
    /* Ignore any character until end of quoted string */
356
    if (chStringQuote != '\0') {
9,634✔
357
      if (*pszStr == chStringQuote)
759✔
358
        chStringQuote = 0;
359
    }
360
    /* Detect begin of quoted string only for an XML attribute, i.e. between <
361
       and > */
362
    else if (bInBracket && (*pszStr == '\'' || *pszStr == '"')) {
8,875✔
363
      chStringQuote = *pszStr;
364
    }
365
    /* Begin of XML element */
366
    else if (*pszStr == '<') {
8,805✔
367
      bInBracket = TRUE;
368
      if (pszStr[1] == '/')
535✔
369
        nXMLIndent--;
251✔
370
      else if (pszStr[1] != '!')
284✔
371
        nXMLIndent++;
283✔
372
    }
373
    /* <something /> case */
374
    else if (*pszStr == '/' && pszStr[1] == '>') {
8,270✔
375
      bInBracket = FALSE;
376
      nXMLIndent--;
32✔
377
      pszStr++;
32✔
378
    }
379
    /* End of XML element */
380
    else if (*pszStr == '>') {
8,238✔
381
      bInBracket = FALSE;
382
    }
383
    /* Only detect and of filter when XML indentation goes back to zero */
384
    else if (nXMLIndent == 0 && *pszStr == ')') {
7,735✔
385
      papszRet =
386
          (char **)msSmallRealloc(papszRet, sizeof(char *) * (nTokens + 1));
78✔
387
      papszRet[nTokens] = msStrdup(pszTokenBegin);
78✔
388
      papszRet[nTokens][pszStr - pszTokenBegin] = '\0';
78✔
389
      nTokens++;
390
      if (pszStr[1] != '(') {
78✔
391
        break;
392
      }
393
      pszStr++;
19✔
394
      pszTokenBegin = pszStr + 1;
19✔
395
    }
396
    pszStr++;
9,575✔
397
  }
398
  *pnTokens = nTokens;
59✔
399
  return papszRet;
59✔
400
}
401

402
/************************************************************************/
403
/*                            FLTIsSimpleFilter                         */
404
/*                                                                      */
405
/*      Filter encoding with only attribute queries and only one bbox.  */
406
/************************************************************************/
407
int FLTIsSimpleFilter(FilterEncodingNode *psNode) {
×
408
  if (FLTValidForBBoxFilter(psNode)) {
×
409
    if (FLTNumberOfFilterType(psNode, "DWithin") == 0 &&
×
410
        FLTNumberOfFilterType(psNode, "Intersect") == 0 &&
×
411
        FLTNumberOfFilterType(psNode, "Intersects") == 0 &&
×
412
        FLTNumberOfFilterType(psNode, "Equals") == 0 &&
×
413
        FLTNumberOfFilterType(psNode, "Disjoint") == 0 &&
×
414
        FLTNumberOfFilterType(psNode, "Touches") == 0 &&
×
415
        FLTNumberOfFilterType(psNode, "Crosses") == 0 &&
×
416
        FLTNumberOfFilterType(psNode, "Within") == 0 &&
×
417
        FLTNumberOfFilterType(psNode, "Contains") == 0 &&
×
418
        FLTNumberOfFilterType(psNode, "Overlaps") == 0 &&
×
419
        FLTNumberOfFilterType(psNode, "Beyond") == 0)
×
420
      return TRUE;
421
  }
422

423
  return FALSE;
424
}
425

426
/************************************************************************/
427
/*                          FLTApplyFilterToLayer                       */
428
/*                                                                      */
429
/*      Use the filter encoding node to create mapserver expressions    */
430
/*      and apply it to the layer.                                      */
431
/************************************************************************/
432
int FLTApplyFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
390✔
433
                          int iLayerIndex) {
434
  layerObj *layer = GET_LAYER(map, iLayerIndex);
390✔
435

436
  if (!layer->vtable) {
390✔
437
    int rv = msInitializeVirtualTable(layer);
20✔
438
    if (rv != MS_SUCCESS)
20✔
439
      return rv;
440
  }
441
  if (!layer->vtable)
390✔
442
    return MS_FAILURE;
443
  return layer->vtable->LayerApplyFilterToLayer(psNode, map, iLayerIndex);
390✔
444
}
445

446
/************************************************************************/
447
/*                           FLTGetTopBBOX                              */
448
/*                                                                      */
449
/* Return the "top" BBOX if there's a unique one.                       */
450
/************************************************************************/
451
static int FLTGetTopBBOXInternal(FilterEncodingNode *psNode,
446✔
452
                                 FilterEncodingNode **ppsTopBBOX,
453
                                 int *pnCount) {
454
  if (psNode->pszValue && strcasecmp(psNode->pszValue, "BBOX") == 0) {
446✔
455
    (*pnCount)++;
38✔
456
    if (*pnCount == 1) {
38✔
457
      *ppsTopBBOX = psNode;
38✔
458
      return TRUE;
38✔
459
    }
460
    *ppsTopBBOX = NULL;
×
461
    return FALSE;
×
462
  } else if (psNode->pszValue && strcasecmp(psNode->pszValue, "AND") == 0) {
408✔
463
    return FLTGetTopBBOXInternal(psNode->psLeftNode, ppsTopBBOX, pnCount) &&
56✔
464
           FLTGetTopBBOXInternal(psNode->psRightNode, ppsTopBBOX, pnCount);
56✔
465
  } else {
466
    return TRUE;
467
  }
468
}
469

470
static FilterEncodingNode *FLTGetTopBBOX(FilterEncodingNode *psNode) {
471
  int nCount = 0;
390✔
472
  FilterEncodingNode *psTopBBOX = NULL;
390✔
473
  FLTGetTopBBOXInternal(psNode, &psTopBBOX, &nCount);
390✔
474
  return psTopBBOX;
390✔
475
}
476

477
/************************************************************************/
478
/*                   FLTLayerSetInvalidRectIfSupported                  */
479
/*                                                                      */
480
/*  This function will set in *rect a very huge extent if the layer     */
481
/*  wfs_use_default_extent_for_getfeature metadata item is set to false */
482
/*  and the layer supports such degenerate rectangle, as a hint that    */
483
/*  they should not issue a spatial filter.                             */
484
/************************************************************************/
485

486
int FLTLayerSetInvalidRectIfSupported(mapObj *map, layerObj *lp, rectObj *rect,
720✔
487
                                      const char *metadata_namespaces) {
488
  // Check LAYER metadata first, then fall back to MAP WEB metadata
489
  const char *pszUseDefaultExtent =
490
      msOWSLookupMetadata(&(lp->metadata), metadata_namespaces,
720✔
491
                          "use_default_extent_for_getfeature");
492
  if (!pszUseDefaultExtent) {
720✔
493
    pszUseDefaultExtent =
494
        msOWSLookupMetadata(&(map->web.metadata), metadata_namespaces,
651✔
495
                            "use_default_extent_for_getfeature");
496
  }
497

498
  if (pszUseDefaultExtent && !CSLTestBoolean(pszUseDefaultExtent) &&
720✔
499
      (lp->connectiontype == MS_OGR || lp->connectiontype == MS_POSTGIS ||
11✔
UNCOV
500
       ((lp->connectiontype == MS_PLUGIN) &&
×
UNCOV
501
        (strstr(lp->plugin_library, "msplugin_mssql2008") != NULL)))) {
×
502
    const rectObj rectInvalid = MS_INIT_INVALID_RECT;
503
    *rect = rectInvalid;
6✔
504
    return MS_TRUE;
505
  }
506
  return MS_FALSE;
507
}
508

509
/************************************************************************/
510
/*                   FLTLayerApplyPlainFilterToLayer                    */
511
/*                                                                      */
512
/* Helper function for layer virtual table architecture                 */
513
/************************************************************************/
514
int FLTLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
390✔
515
                                    int iLayerIndex) {
516
  char *pszExpression = NULL;
517
  int status = MS_FALSE;
518
  layerObj *lp = GET_LAYER(map, iLayerIndex);
390✔
519

520
  pszExpression = FLTGetCommonExpression(psNode, lp);
390✔
521
  if (pszExpression) {
390✔
522
    FilterEncodingNode *psTopBBOX;
523
    rectObj rect = map->extent;
390✔
524

525
    // if this is a WMS request do not remove any BBOXes
526
    if (msOWSLookupMetadata(&(lp->metadata), "G", "wmsfilter_flag") == NULL) {
390✔
527
      FLTLayerSetInvalidRectIfSupported(map, lp, &rect, "OF");
375✔
528
    }
529
    psTopBBOX = FLTGetTopBBOX(psNode);
530
    if (psTopBBOX) {
390✔
531
      int can_remove_expression = MS_TRUE;
532
      const char *pszEPSG = FLTGetBBOX(psNode, &rect);
38✔
533
      if (pszEPSG && map->projection.numargs > 0) {
38✔
534
        projectionObj sProjTmp;
535
        msInitProjection(&sProjTmp);
37✔
536
        msProjectionInheritContextFrom(&sProjTmp, &map->projection);
37✔
537
        /* Use the non EPSG variant since axis swapping is done in
538
         * FLTDoAxisSwappingIfNecessary */
539
        if (msLoadProjectionString(&sProjTmp, pszEPSG) == 0) {
37✔
540
          rectObj oldRect = rect;
37✔
541
          msProjectRect(&sProjTmp, &map->projection, &rect);
37✔
542
          /* If reprojection is involved, do not remove the expression */
543
          if (rect.minx != oldRect.minx || rect.miny != oldRect.miny ||
37✔
544
              rect.maxx != oldRect.maxx || rect.maxy != oldRect.maxy) {
32✔
545
            can_remove_expression = MS_FALSE;
546
          }
547
        }
548
        msFreeProjection(&sProjTmp);
37✔
549
      }
550

551
      /* Small optimization: if the query is just a BBOX, then do a */
552
      /* msQueryByRect() */
553
      if (psTopBBOX == psNode && can_remove_expression) {
38✔
554
        msFree(pszExpression);
25✔
555
        pszExpression = NULL;
556
      }
557
    }
558

559
    if (map->debug == MS_DEBUGLEVEL_VVV) {
390✔
UNCOV
560
      if (pszExpression)
×
UNCOV
561
        msDebug("FLTLayerApplyPlainFilterToLayer(): %s, "
×
562
                "rect=%.15g,%.15g,%.15g,%.15g\n",
563
                pszExpression, rect.minx, rect.miny, rect.maxx, rect.maxy);
564
      else
UNCOV
565
        msDebug(
×
566
            "FLTLayerApplyPlainFilterToLayer(): rect=%.15g,%.15g,%.15g,%.15g\n",
567
            rect.minx, rect.miny, rect.maxx, rect.maxy);
568
    }
569

570
    status = FLTApplyFilterToLayerCommonExpressionWithRect(map, iLayerIndex,
390✔
571
                                                           pszExpression, rect);
572
    msFree(pszExpression);
390✔
573
  }
574

575
  return status;
390✔
576
}
577

578
/************************************************************************/
579
/*            FilterNode *FLTPaserFilterEncoding(char *szXMLString)     */
580
/*                                                                      */
581
/*      Parses an Filter Encoding XML string and creates a              */
582
/*      FilterEncodingNodes corresponding to the string.                */
583
/*      Returns a pointer to the first node or NULL if                  */
584
/*      unsuccessful.                                                  */
585
/*      Calling function should use FreeFilterEncodingNode function     */
586
/*      to free memory.                                                */
587
/************************************************************************/
588
FilterEncodingNode *FLTParseFilterEncoding(const char *szXMLString) {
460✔
589
  CPLXMLNode *psRoot = NULL, *psChild = NULL, *psFilter = NULL;
590
  FilterEncodingNode *psFilterNode = NULL;
591

592
  if (szXMLString == NULL || strlen(szXMLString) == 0 ||
460✔
593
      (strstr(szXMLString, "Filter") == NULL))
594
    return NULL;
595

596
  psRoot = CPLParseXMLString(szXMLString);
459✔
597

598
  if (psRoot == NULL)
459✔
599
    return NULL;
600

601
  /* strip namespaces. We srtip all name spaces (#1350)*/
602
  CPLStripXMLNamespace(psRoot, NULL, 1);
456✔
603

604
  /* -------------------------------------------------------------------- */
605
  /*      get the root element (Filter).                                  */
606
  /* -------------------------------------------------------------------- */
607
  psFilter = CPLGetXMLNode(psRoot, "=Filter");
456✔
608
  if (!psFilter) {
456✔
609
    CPLDestroyXMLNode(psRoot);
1✔
610
    return NULL;
1✔
611
  }
612

613
  psChild = psFilter->psChild;
455✔
614
  while (psChild) {
467✔
615
    if (FLTIsSupportedFilterType(psChild)) {
465✔
616
      psFilterNode = FLTCreateFilterEncodingNode();
453✔
617
      FLTInsertElementInNode(psFilterNode, psChild);
453✔
618
      break;
453✔
619
    } else
620
      psChild = psChild->psNext;
12✔
621
  }
622

623
  CPLDestroyXMLNode(psRoot);
455✔
624

625
  /* -------------------------------------------------------------------- */
626
  /*      validate the node tree to make sure that all the nodes are valid.*/
627
  /* -------------------------------------------------------------------- */
628
  if (!FLTValidFilterNode(psFilterNode)) {
455✔
629
    FLTFreeFilterEncodingNode(psFilterNode);
14✔
630
    return NULL;
14✔
631
  }
632

633
  return psFilterNode;
634
}
635

636
/************************************************************************/
637
/*      int FLTValidFilterNode(FilterEncodingNode *psFilterNode)        */
638
/*                                                                      */
639
/*      Validate that all the nodes are filled properly. We could       */
640
/*      have parts of the nodes that are correct and part which         */
641
/*      could be incorrect if the filter string sent is corrupted       */
642
/*      (eg missing a value :<PropertyName><PropertyName>)              */
643
/************************************************************************/
644
int FLTValidFilterNode(FilterEncodingNode *psFilterNode) {
1,043✔
645
  if (!psFilterNode)
1,590✔
646
    return 0;
647

648
  if (psFilterNode->eType == FILTER_NODE_TYPE_UNDEFINED)
1,588✔
649
    return 0;
650

651
  if (psFilterNode->psLeftNode) {
1,576✔
652
    const int bReturn = FLTValidFilterNode(psFilterNode->psLeftNode);
588✔
653
    if (bReturn == 0)
588✔
654
      return 0;
655
    else if (psFilterNode->psRightNode)
588✔
656
      return FLTValidFilterNode(psFilterNode->psRightNode);
657
  }
658

659
  return 1;
660
}
661

662
/************************************************************************/
663
/*                       FLTIsGeometryFilterNodeType                    */
664
/************************************************************************/
665

666
static int FLTIsGeometryFilterNodeType(int eType) {
667
  return (eType == FILTER_NODE_TYPE_GEOMETRY_POINT ||
668
          eType == FILTER_NODE_TYPE_GEOMETRY_LINE ||
504✔
669
          eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON);
670
}
671

672
/************************************************************************/
673
/*                          FLTFreeFilterEncodingNode                   */
674
/*                                                                      */
675
/*      recursive freeing of Filter Encoding nodes.                      */
676
/************************************************************************/
677
void FLTFreeFilterEncodingNode(FilterEncodingNode *psFilterNode) {
1,804✔
678
  if (psFilterNode) {
1,804✔
679
    if (psFilterNode->psLeftNode) {
1,789✔
680
      FLTFreeFilterEncodingNode(psFilterNode->psLeftNode);
643✔
681
      psFilterNode->psLeftNode = NULL;
643✔
682
    }
683
    if (psFilterNode->psRightNode) {
1,789✔
684
      FLTFreeFilterEncodingNode(psFilterNode->psRightNode);
616✔
685
      psFilterNode->psRightNode = NULL;
616✔
686
    }
687

688
    if (psFilterNode->pszSRS)
1,789✔
689
      free(psFilterNode->pszSRS);
63✔
690

691
    if (psFilterNode->pOther) {
1,789✔
692
      if (psFilterNode->pszValue != NULL &&
433✔
693
          strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
320✔
694
        FEPropertyIsLike *propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
695
        if (propIsLike->pszWildCard)
30✔
696
          free(propIsLike->pszWildCard);
30✔
697
        if (propIsLike->pszSingleChar)
30✔
698
          free(propIsLike->pszSingleChar);
30✔
699
        if (propIsLike->pszEscapeChar)
30✔
700
          free(propIsLike->pszEscapeChar);
30✔
701
      } else if (FLTIsGeometryFilterNodeType(psFilterNode->eType)) {
403✔
702
        msFreeShape((shapeObj *)(psFilterNode->pOther));
90✔
703
      }
704
      /* else */
705
      /* TODO free pOther special fields */
706
      free(psFilterNode->pOther);
433✔
707
    }
708

709
    /* Cannot free pszValue before, 'cause we are testing it above */
710
    if (psFilterNode->pszValue)
1,789✔
711
      free(psFilterNode->pszValue);
1,671✔
712

713
    free(psFilterNode);
1,789✔
714
  }
715
}
1,804✔
716

717
/************************************************************************/
718
/*                         FLTCreateFilterEncodingNode                  */
719
/*                                                                      */
720
/*      return a FilterEncoding node.                                    */
721
/************************************************************************/
722
FilterEncodingNode *FLTCreateFilterEncodingNode(void) {
1,789✔
723
  FilterEncodingNode *psFilterNode = NULL;
724

725
  psFilterNode = (FilterEncodingNode *)malloc(sizeof(FilterEncodingNode));
1,789✔
726
  psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1,789✔
727
  psFilterNode->pszValue = NULL;
1,789✔
728
  psFilterNode->pOther = NULL;
1,789✔
729
  psFilterNode->pszSRS = NULL;
1,789✔
730
  psFilterNode->psLeftNode = NULL;
1,789✔
731
  psFilterNode->psRightNode = NULL;
1,789✔
732

733
  return psFilterNode;
1,789✔
734
}
735

736
FilterEncodingNode *FLTCreateBinaryCompFilterEncodingNode(void) {
271✔
737
  FilterEncodingNode *psFilterNode = NULL;
738

739
  psFilterNode = FLTCreateFilterEncodingNode();
271✔
740
  /* used to store case sensitivity flag. Default is 0 meaning the
741
     comparing is case sensititive */
742
  psFilterNode->pOther = (int *)malloc(sizeof(int));
271✔
743
  (*(int *)(psFilterNode->pOther)) = 0;
271✔
744

745
  return psFilterNode;
271✔
746
}
747

748
/************************************************************************/
749
/*                           FLTFindGeometryNode                        */
750
/*                                                                      */
751
/************************************************************************/
752

753
static CPLXMLNode *FLTFindGeometryNode(CPLXMLNode *psXMLNode, int *pbPoint,
94✔
754
                                       int *pbLine, int *pbPolygon) {
755
  CPLXMLNode *psGMLElement = NULL;
756

757
  psGMLElement = CPLGetXMLNode(psXMLNode, "Point");
94✔
758
  if (!psGMLElement)
94✔
759
    psGMLElement = CPLGetXMLNode(psXMLNode, "PointType");
66✔
760
  if (psGMLElement)
66✔
761
    *pbPoint = 1;
28✔
762
  else {
763
    psGMLElement = CPLGetXMLNode(psXMLNode, "Polygon");
66✔
764
    if (psGMLElement)
66✔
765
      *pbPolygon = 1;
54✔
766
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiPolygon")))
12✔
767
      *pbPolygon = 1;
3✔
768
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Surface")))
9✔
UNCOV
769
      *pbPolygon = 1;
×
770
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiSurface")))
9✔
UNCOV
771
      *pbPolygon = 1;
×
772
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Box")))
9✔
UNCOV
773
      *pbPolygon = 1;
×
774
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Envelope")))
9✔
775
      *pbPolygon = 1;
1✔
776
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "LineString")))
8✔
777
      *pbLine = 1;
4✔
778
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiLineString")))
4✔
UNCOV
779
      *pbLine = 1;
×
780
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Curve")))
4✔
UNCOV
781
      *pbLine = 1;
×
782
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiCurve")))
4✔
UNCOV
783
      *pbLine = 1;
×
784
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiPoint")))
4✔
785
      *pbPoint = 1;
2✔
786
  }
787
  return psGMLElement;
94✔
788
}
789

790
/************************************************************************/
791
/*                           FLTGetPropertyName                         */
792
/************************************************************************/
793
static const char *FLTGetPropertyName(CPLXMLNode *psXMLNode) {
494✔
794
  const char *pszPropertyName;
795

796
  pszPropertyName = CPLGetXMLValue(psXMLNode, "PropertyName", NULL);
494✔
797
  if (pszPropertyName == NULL) /* FE 2.0 ? */
494✔
798
    pszPropertyName = CPLGetXMLValue(psXMLNode, "ValueReference", NULL);
60✔
799
  return pszPropertyName;
494✔
800
}
801

802
/************************************************************************/
803
/*                          FLTGetFirstChildNode                        */
804
/************************************************************************/
805
static CPLXMLNode *FLTGetFirstChildNode(CPLXMLNode *psXMLNode) {
806
  if (psXMLNode == NULL)
807
    return NULL;
808
  psXMLNode = psXMLNode->psChild;
104✔
809
  while (psXMLNode != NULL) {
104✔
810
    if (psXMLNode->eType == CXT_Element)
102✔
811
      return psXMLNode;
UNCOV
812
    psXMLNode = psXMLNode->psNext;
×
813
  }
814
  return NULL;
815
}
816

817
/************************************************************************/
818
/*                        FLTGetNextSibblingNode                        */
819
/************************************************************************/
820
static CPLXMLNode *FLTGetNextSibblingNode(CPLXMLNode *psXMLNode) {
821
  if (psXMLNode == NULL)
86✔
822
    return NULL;
823
  psXMLNode = psXMLNode->psNext;
169✔
824
  while (psXMLNode != NULL) {
169✔
825
    if (psXMLNode->eType == CXT_Element)
87✔
826
      return psXMLNode;
UNCOV
827
    psXMLNode = psXMLNode->psNext;
×
828
  }
829
  return NULL;
830
}
831

832
/************************************************************************/
833
/*                           FLTInsertElementInNode                     */
834
/*                                                                      */
835
/*      Utility function to parse an XML node and transfer the          */
836
/*      contents into the Filter Encoding node structure.               */
837
/************************************************************************/
838
void FLTInsertElementInNode(FilterEncodingNode *psFilterNode,
536✔
839
                            CPLXMLNode *psXMLNode) {
840
  char *pszTmp = NULL;
841
  FilterEncodingNode *psCurFilNode = NULL;
842
  CPLXMLNode *psCurXMLNode = NULL;
843
  CPLXMLNode *psTmpNode = NULL;
844
  CPLXMLNode *psFeatureIdNode = NULL;
845
  const char *pszFeatureId = NULL;
846
  char *pszFeatureIdList = NULL;
847

848
  if (psFilterNode && psXMLNode && psXMLNode->pszValue) {
638✔
849
    psFilterNode->pszValue = msStrdup(psXMLNode->pszValue);
638✔
850
    psFilterNode->psLeftNode = NULL;
638✔
851
    psFilterNode->psRightNode = NULL;
638✔
852

853
    /* -------------------------------------------------------------------- */
854
    /*      Logical filter. AND, OR and NOT are supported. Example of       */
855
    /*      filer using logical filters :                                   */
856
    /*      <Filter>                                                        */
857
    /*        <And>                                                         */
858
    /*          <PropertyIsGreaterThan>                                     */
859
    /*            <PropertyName>Person/Age</PropertyName>                   */
860
    /*            <Literal>50</Literal>                                     */
861
    /*          </PropertyIsGreaterThan>                                    */
862
    /*          <PropertyIsEqualTo>                                         */
863
    /*             <PropertyName>Person/Address/City</PropertyName>         */
864
    /*             <Literal>Toronto</Literal>                               */
865
    /*          </PropertyIsEqualTo>                                        */
866
    /*        </And>                                                        */
867
    /*      </Filter>                                                       */
868
    /* -------------------------------------------------------------------- */
869
    if (FLTIsLogicalFilterType(psXMLNode->pszValue)) {
638✔
870
      psFilterNode->eType = FILTER_NODE_TYPE_LOGICAL;
104✔
871
      if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
104✔
872
          strcasecmp(psFilterNode->pszValue, "OR") == 0) {
59✔
873
        CPLXMLNode *psFirstNode = FLTGetFirstChildNode(psXMLNode);
874
        CPLXMLNode *psSecondNode = FLTGetNextSibblingNode(psFirstNode);
875
        if (psFirstNode && psSecondNode) {
83✔
876
          /*2 operators */
877
          CPLXMLNode *psNextNode = FLTGetNextSibblingNode(psSecondNode);
878
          if (psNextNode == NULL) {
82✔
879
            psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
81✔
880
            FLTInsertElementInNode(psFilterNode->psLeftNode, psFirstNode);
81✔
881
            psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
81✔
882
            FLTInsertElementInNode(psFilterNode->psRightNode, psSecondNode);
883
          } else {
884
            psCurXMLNode = psFirstNode;
885
            psCurFilNode = psFilterNode;
886
            while (psCurXMLNode) {
887
              psNextNode = FLTGetNextSibblingNode(psCurXMLNode);
888
              if (FLTGetNextSibblingNode(psNextNode)) {
2✔
889
                psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
1✔
890
                FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
1✔
891
                psCurFilNode->psRightNode = FLTCreateFilterEncodingNode();
1✔
892
                psCurFilNode->psRightNode->eType = FILTER_NODE_TYPE_LOGICAL;
1✔
893
                psCurFilNode->psRightNode->pszValue =
1✔
894
                    msStrdup(psFilterNode->pszValue);
1✔
895

896
                psCurFilNode = psCurFilNode->psRightNode;
1✔
897
                psCurXMLNode = psNextNode;
898
              } else { /*last 2 operators*/
899
                psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
1✔
900
                FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
1✔
901

902
                psCurFilNode->psRightNode = FLTCreateFilterEncodingNode();
1✔
903
                FLTInsertElementInNode(psCurFilNode->psRightNode, psNextNode);
904
                break;
1✔
905
              }
906
            }
907
          }
908
        } else
909
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
910
      } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
21✔
911
        CPLXMLNode *psFirstNode = FLTGetFirstChildNode(psXMLNode);
912
        if (psFirstNode) {
21✔
913
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
20✔
914
          FLTInsertElementInNode(psFilterNode->psLeftNode, psFirstNode);
915
        } else
916
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
917
      } else
UNCOV
918
        psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
×
919
    } /* end if is logical */
920
    /* -------------------------------------------------------------------- */
921
    /*      Spatial Filter.                                                 */
922
    /*      BBOX :                                                          */
923
    /*      <Filter>                                                        */
924
    /*       <BBOX>                                                         */
925
    /*        <PropertyName>Geometry</PropertyName>                         */
926
    /*        <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
927
    /*          <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
928
    /*        </gml:Box>                                                    */
929
    /*       </BBOX>                                                        */
930
    /*      </Filter>                                                       */
931
    /*                                                                      */
932
    /*       DWithin                                                        */
933
    /*                                                                      */
934
    /*      <xsd:element name="DWithin"                                     */
935
    /*      type="ogc:DistanceBufferType"                                   */
936
    /*      substitutionGroup="ogc:spatialOps"/>                            */
937
    /*                                                                      */
938
    /*      <xsd:complexType name="DistanceBufferType">                     */
939
    /*         <xsd:complexContent>                                         */
940
    /*            <xsd:extension base="ogc:SpatialOpsType">                 */
941
    /*               <xsd:sequence>                                         */
942
    /*                  <xsd:element ref="ogc:PropertyName"/>               */
943
    /*                  <xsd:element ref="gml:_Geometry"/>                  */
944
    /*                  <xsd:element name="Distance" type="ogc:DistanceType"/>*/
945
    /*               </xsd:sequence>                                        */
946
    /*            </xsd:extension>                                          */
947
    /*         </xsd:complexContent>                                        */
948
    /*      </xsd:complexType>                                              */
949
    /*                                                                      */
950
    /*                                                                      */
951
    /*       <Filter>                                                       */
952
    /*       <DWithin>                                                      */
953
    /*        <PropertyName>Geometry</PropertyName>                         */
954
    /*        <gml:Point>                                                   */
955
    /*          <gml:coordinates>13.0983,31.5899</gml:coordinates>          */
956
    /*        </gml:Point>                                                  */
957
    /*        <Distance units="url#m">10</Distance>                         */
958
    /*       </DWithin>                                                     */
959
    /*      </Filter>                                                       */
960
    /*                                                                      */
961
    /*       Intersect                                                      */
962
    /*                                                                      */
963
    /*       type="ogc:BinarySpatialOpType"
964
       substitutionGroup="ogc:spatialOps"/>*/
965
    /*      <xsd:element name="Intersects"                                  */
966
    /*      type="ogc:BinarySpatialOpType"                                  */
967
    /*      substitutionGroup="ogc:spatialOps"/>                            */
968
    /*                                                                      */
969
    /*      <xsd:complexType name="BinarySpatialOpType">                    */
970
    /*      <xsd:complexContent>                                            */
971
    /*      <xsd:extension base="ogc:SpatialOpsType">                       */
972
    /*      <xsd:sequence>                                                  */
973
    /*      <xsd:element ref="ogc:PropertyName"/>                           */
974
    /*      <xsd:choice>                                                    */
975
    /*      <xsd:element ref="gml:_Geometry"/>                              */
976
    /*      <xsd:element ref="gml:Box"/>                                    */
977
    /*      </xsd:sequence>                                                 */
978
    /*      </xsd:extension>                                                */
979
    /*      </xsd:complexContent>                                           */
980
    /*      </xsd:complexType>                                              */
981
    /* -------------------------------------------------------------------- */
982
    else if (FLTIsSpatialFilterType(psXMLNode->pszValue)) {
534✔
983
      psFilterNode->eType = FILTER_NODE_TYPE_SPATIAL;
137✔
984

985
      if (strcasecmp(psXMLNode->pszValue, "BBOX") == 0) {
137✔
986
        char *pszSRS = NULL;
43✔
987
        const char *pszPropertyName = NULL;
988
        CPLXMLNode *psBox = NULL, *psEnvelope = NULL;
989
        rectObj sBox = {0, 0, 0, 0};
43✔
990

991
        int bCoordinatesValid = 0;
992

993
        pszPropertyName = FLTGetPropertyName(psXMLNode);
43✔
994
        psBox = CPLGetXMLNode(psXMLNode, "Box");
43✔
995
        if (!psBox)
43✔
996
          psBox = CPLGetXMLNode(psXMLNode, "BoxType");
13✔
997

998
        /*FE 1.0 used box FE1.1 uses envelop*/
999
        if (psBox)
13✔
1000
          bCoordinatesValid = FLTParseGMLBox(psBox, &sBox, &pszSRS);
30✔
1001
        else if ((psEnvelope = CPLGetXMLNode(psXMLNode, "Envelope")))
13✔
1002
          bCoordinatesValid = FLTParseGMLEnvelope(psEnvelope, &sBox, &pszSRS);
12✔
1003

1004
        if (bCoordinatesValid) {
42✔
1005
          /*set the srs if available*/
1006
          if (pszSRS)
42✔
1007
            psFilterNode->pszSRS = pszSRS;
41✔
1008

1009
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
42✔
1010
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
42✔
1011
          /* PropertyName is optional since FE 1.1.0, in which case */
1012
          /* the BBOX must apply to all geometry fields. As we support */
1013
          /* currently only one geometry field, this doesn't make much */
1014
          /* difference to further processing. */
1015
          if (pszPropertyName != NULL) {
42✔
1016
            psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
37✔
1017
          }
1018

1019
          /* coordinates */
1020
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
42✔
1021
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BBOX;
42✔
1022
          psFilterNode->psRightNode->pOther =
42✔
1023
              (rectObj *)msSmallMalloc(sizeof(rectObj));
42✔
1024
          ((rectObj *)psFilterNode->psRightNode->pOther)->minx = sBox.minx;
42✔
1025
          ((rectObj *)psFilterNode->psRightNode->pOther)->miny = sBox.miny;
42✔
1026
          ((rectObj *)psFilterNode->psRightNode->pOther)->maxx = sBox.maxx;
42✔
1027
          ((rectObj *)psFilterNode->psRightNode->pOther)->maxy = sBox.maxy;
42✔
1028
        } else {
1029
          msFree(pszSRS);
1✔
1030
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1031
        }
1032
      } else if (strcasecmp(psXMLNode->pszValue, "DWithin") == 0 ||
94✔
1033
                 strcasecmp(psXMLNode->pszValue, "Beyond") == 0)
75✔
1034

1035
      {
1036
        shapeObj *psShape = NULL;
1037
        int bPoint = 0, bLine = 0, bPolygon = 0;
25✔
1038
        const char *pszUnits = NULL;
1039
        const char *pszDistance = NULL;
1040
        const char *pszPropertyName;
1041
        char *pszSRS = NULL;
25✔
1042

1043
        CPLXMLNode *psGMLElement = NULL, *psDistance = NULL;
1044

1045
        pszPropertyName = FLTGetPropertyName(psXMLNode);
25✔
1046

1047
        psGMLElement =
1048
            FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
25✔
1049

1050
        psDistance = CPLGetXMLNode(psXMLNode, "Distance");
25✔
1051
        if (psDistance != NULL)
25✔
1052
          pszDistance = CPLGetXMLValue(psDistance, NULL, NULL);
24✔
1053
        if (pszPropertyName != NULL && psGMLElement && psDistance != NULL) {
25✔
1054
          pszUnits = CPLGetXMLValue(psDistance, "units", NULL);
24✔
1055
          if (pszUnits == NULL) /* FE 2.0 */
24✔
1056
            pszUnits = CPLGetXMLValue(psDistance, "uom", NULL);
5✔
1057
          psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
24✔
1058
          msInitShape(psShape);
24✔
1059
          if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS)) {
24✔
1060
            /*set the srs if available*/
1061
            if (pszSRS)
23✔
1062
              psFilterNode->pszSRS = pszSRS;
1✔
1063

1064
            psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
23✔
1065
            psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
23✔
1066
            psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
23✔
1067

1068
            psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
23✔
1069
            if (bPoint)
23✔
1070
              psFilterNode->psRightNode->eType =
19✔
1071
                  FILTER_NODE_TYPE_GEOMETRY_POINT;
1072
            else if (bLine)
4✔
UNCOV
1073
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
×
1074
            else if (bPolygon)
4✔
1075
              psFilterNode->psRightNode->eType =
4✔
1076
                  FILTER_NODE_TYPE_GEOMETRY_POLYGON;
1077
            psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
23✔
1078
            /*the value will be distance;units*/
1079
            psFilterNode->psRightNode->pszValue = msStrdup(pszDistance);
23✔
1080
            if (pszUnits) {
23✔
1081
              psFilterNode->psRightNode->pszValue =
19✔
1082
                  msStringConcatenate(psFilterNode->psRightNode->pszValue, ";");
19✔
1083
              psFilterNode->psRightNode->pszValue = msStringConcatenate(
19✔
1084
                  psFilterNode->psRightNode->pszValue, pszUnits);
19✔
1085
            }
1086
          } else {
1087
            free(psShape);
1✔
1088
            msFree(pszSRS);
1✔
1089
            psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1090
          }
1091
        } else
1092
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1093
      } else if (strcasecmp(psXMLNode->pszValue, "Intersect") == 0 ||
94✔
1094
                 strcasecmp(psXMLNode->pszValue, "Intersects") == 0 ||
67✔
1095
                 strcasecmp(psXMLNode->pszValue, "Equals") == 0 ||
50✔
1096
                 strcasecmp(psXMLNode->pszValue, "Disjoint") == 0 ||
40✔
1097
                 strcasecmp(psXMLNode->pszValue, "Touches") == 0 ||
34✔
1098
                 strcasecmp(psXMLNode->pszValue, "Crosses") == 0 ||
28✔
1099
                 strcasecmp(psXMLNode->pszValue, "Within") == 0 ||
20✔
1100
                 strcasecmp(psXMLNode->pszValue, "Contains") == 0 ||
12✔
1101
                 strcasecmp(psXMLNode->pszValue, "Overlaps") == 0) {
6✔
1102
        shapeObj *psShape = NULL;
1103
        int bLine = 0, bPolygon = 0, bPoint = 0;
69✔
1104
        char *pszSRS = NULL;
69✔
1105
        const char *pszPropertyName;
1106

1107
        CPLXMLNode *psGMLElement = NULL;
1108

1109
        pszPropertyName = FLTGetPropertyName(psXMLNode);
69✔
1110

1111
        psGMLElement =
1112
            FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
69✔
1113

1114
        if (pszPropertyName != NULL && psGMLElement) {
69✔
1115
          psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
68✔
1116
          msInitShape(psShape);
68✔
1117
          if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS)) {
68✔
1118
            /*set the srs if available*/
1119
            if (pszSRS)
67✔
1120
              psFilterNode->pszSRS = pszSRS;
21✔
1121

1122
            psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
67✔
1123
            psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
67✔
1124
            psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
67✔
1125

1126
            psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
67✔
1127
            if (bPoint)
67✔
1128
              psFilterNode->psRightNode->eType =
11✔
1129
                  FILTER_NODE_TYPE_GEOMETRY_POINT;
1130
            else if (bLine)
56✔
1131
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
4✔
1132
            else if (bPolygon)
52✔
1133
              psFilterNode->psRightNode->eType =
52✔
1134
                  FILTER_NODE_TYPE_GEOMETRY_POLYGON;
1135
            psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
67✔
1136

1137
          } else {
1138
            free(psShape);
1✔
1139
            msFree(pszSRS);
1✔
1140
            psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1141
          }
1142
        } else
1143
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1144
      }
1145

1146
    } /* end of is spatial */
1147

1148
    /* -------------------------------------------------------------------- */
1149
    /*      Comparison Filter                                               */
1150
    /* -------------------------------------------------------------------- */
1151
    else if (FLTIsComparisonFilterType(psXMLNode->pszValue)) {
397✔
1152
      psFilterNode->eType = FILTER_NODE_TYPE_COMPARISON;
338✔
1153
      /* -------------------------------------------------------------------- */
1154
      /*      binary comaparison types. Example :                             */
1155
      /*                                                                      */
1156
      /*      <Filter>                                                        */
1157
      /*        <PropertyIsEqualTo>                                           */
1158
      /*          <PropertyName>SomeProperty</PropertyName>                   */
1159
      /*          <Literal>100</Literal>                                      */
1160
      /*        </PropertyIsEqualTo>                                          */
1161
      /*      </Filter>                                                       */
1162
      /* -------------------------------------------------------------------- */
1163
      if (FLTIsBinaryComparisonFilterType(psXMLNode->pszValue)) {
338✔
1164
        const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
268✔
1165
        if (pszPropertyName != NULL) {
268✔
1166

1167
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
266✔
1168
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
266✔
1169
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
266✔
1170

1171
          psTmpNode = CPLSearchXMLNode(psXMLNode, "Literal");
266✔
1172
          if (psTmpNode) {
266✔
1173
            const char *pszLiteral = CPLGetXMLValue(psTmpNode, NULL, NULL);
266✔
1174

1175
            psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
266✔
1176
            psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
266✔
1177

1178
            if (pszLiteral != NULL) {
266✔
1179
              const char *pszMatchCase;
1180

1181
              psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
262✔
1182

1183
              pszMatchCase = CPLGetXMLValue(psXMLNode, "matchCase", NULL);
262✔
1184

1185
              /*check if the matchCase attribute is set*/
1186
              if (pszMatchCase != NULL &&
262✔
1187
                  strcasecmp(pszMatchCase, "false") == 0) {
11✔
1188
                (*(int *)psFilterNode->psRightNode->pOther) = 1;
11✔
1189
              }
1190

1191
            }
1192
            /* special case where the user puts an empty value */
1193
            /* for the Literal so it can end up as an empty  */
1194
            /* string query in the expression */
1195
            else
1196
              psFilterNode->psRightNode->pszValue = NULL;
4✔
1197
          }
1198
        }
1199
        if (psFilterNode->psLeftNode == NULL ||
268✔
1200
            psFilterNode->psRightNode == NULL)
266✔
1201
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
2✔
1202
      }
1203

1204
      /* -------------------------------------------------------------------- */
1205
      /*      PropertyIsBetween filter : extract property name and boundary */
1206
      /*      values. The boundary  values are stored in the right            */
1207
      /*      node. The values are separated by a semi-column (;)             */
1208
      /*      Eg of Filter :                                                  */
1209
      /*      <PropertyIsBetween>                                             */
1210
      /*         <PropertyName>DEPTH</PropertyName>                           */
1211
      /*         <LowerBoundary><Literal>400</Literal></LowerBoundary>        */
1212
      /*         <UpperBoundary><Literal>800</Literal></UpperBoundary>        */
1213
      /*      </PropertyIsBetween>                                            */
1214
      /*                                                                      */
1215
      /*      Or                                                              */
1216
      /*      <PropertyIsBetween>                                             */
1217
      /*         <PropertyName>DEPTH</PropertyName>                           */
1218
      /*         <LowerBoundary>400</LowerBoundary>                           */
1219
      /*         <UpperBoundary>800</UpperBoundary>                           */
1220
      /*      </PropertyIsBetween>                                            */
1221
      /* -------------------------------------------------------------------- */
1222
      else if (strcasecmp(psXMLNode->pszValue, "PropertyIsBetween") == 0) {
70✔
1223
        const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
18✔
1224
        CPLXMLNode *psLowerBoundary = CPLGetXMLNode(psXMLNode, "LowerBoundary");
18✔
1225
        CPLXMLNode *psUpperBoundary = CPLGetXMLNode(psXMLNode, "UpperBoundary");
18✔
1226
        const char *pszLowerNode = NULL;
1227
        const char *pszUpperNode = NULL;
1228
        if (psLowerBoundary != NULL) {
18✔
1229
          /* check if the <Literal> is there */
1230
          if (CPLGetXMLNode(psLowerBoundary, "Literal") != NULL)
17✔
1231
            pszLowerNode = CPLGetXMLValue(psLowerBoundary, "Literal", NULL);
4✔
1232
          else
1233
            pszLowerNode = CPLGetXMLValue(psLowerBoundary, NULL, NULL);
13✔
1234
        }
1235
        if (psUpperBoundary != NULL) {
18✔
1236
          if (CPLGetXMLNode(psUpperBoundary, "Literal") != NULL)
17✔
1237
            pszUpperNode = CPLGetXMLValue(psUpperBoundary, "Literal", NULL);
4✔
1238
          else
1239
            pszUpperNode = CPLGetXMLValue(psUpperBoundary, NULL, NULL);
13✔
1240
        }
1241
        if (pszPropertyName != NULL && pszLowerNode != NULL &&
18✔
1242
            pszUpperNode != NULL) {
1243
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
17✔
1244

1245
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
17✔
1246
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
17✔
1247

1248
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
17✔
1249
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BOUNDARY;
17✔
1250

1251
          /* adding a ; between boundary values */
1252
          const int nStrLength =
17✔
1253
              strlen(pszLowerNode) + strlen(pszUpperNode) + 2;
17✔
1254

1255
          psFilterNode->psRightNode->pszValue =
17✔
1256
              (char *)malloc(sizeof(char) * (nStrLength));
17✔
1257
          strcpy(psFilterNode->psRightNode->pszValue, pszLowerNode);
1258
          strlcat(psFilterNode->psRightNode->pszValue, ";", nStrLength);
17✔
1259
          strlcat(psFilterNode->psRightNode->pszValue, pszUpperNode,
17✔
1260
                  nStrLength);
1261

1262
        } else
1263
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1264

1265
      } /* end of PropertyIsBetween  */
1266
      /* -------------------------------------------------------------------- */
1267
      /*      PropertyIsLike                                                  */
1268
      /*                                                                      */
1269
      /*      <Filter>                                                        */
1270
      /*      <PropertyIsLike wildCard="*" singleChar="#" escape="!">         */
1271
      /*      <PropertyName>LAST_NAME</PropertyName>                          */
1272
      /*      <Literal>JOHN*</Literal>                                        */
1273
      /*      </PropertyIsLike>                                               */
1274
      /*      </Filter>                                                       */
1275
      /* -------------------------------------------------------------------- */
1276
      else if (strcasecmp(psXMLNode->pszValue, "PropertyIsLike") == 0) {
52✔
1277
        const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
31✔
1278
        const char *pszLiteral = CPLGetXMLValue(psXMLNode, "Literal", NULL);
31✔
1279
        const char *pszWildCard = CPLGetXMLValue(psXMLNode, "wildCard", NULL);
31✔
1280
        const char *pszSingleChar =
1281
            CPLGetXMLValue(psXMLNode, "singleChar", NULL);
31✔
1282
        const char *pszEscapeChar = CPLGetXMLValue(psXMLNode, "escape", NULL);
31✔
1283
        if (pszEscapeChar == NULL)
31✔
1284
          pszEscapeChar = CPLGetXMLValue(psXMLNode, "escapeChar", NULL);
4✔
1285
        if (pszPropertyName != NULL && pszLiteral != NULL &&
31✔
1286
            pszWildCard != NULL && pszSingleChar != NULL &&
30✔
1287
            pszEscapeChar != NULL) {
1288
          FEPropertyIsLike *propIsLike;
1289

1290
          propIsLike = (FEPropertyIsLike *)malloc(sizeof(FEPropertyIsLike));
30✔
1291

1292
          psFilterNode->pOther = propIsLike;
30✔
1293
          propIsLike->bCaseInsensitive = 0;
30✔
1294
          propIsLike->pszWildCard = msStrdup(pszWildCard);
30✔
1295
          propIsLike->pszSingleChar = msStrdup(pszSingleChar);
30✔
1296
          propIsLike->pszEscapeChar = msStrdup(pszEscapeChar);
30✔
1297

1298
          pszTmp = (char *)CPLGetXMLValue(psXMLNode, "matchCase", NULL);
30✔
1299
          if (pszTmp && strcasecmp(pszTmp, "false") == 0) {
30✔
1300
            propIsLike->bCaseInsensitive = 1;
4✔
1301
          }
1302
          /* --------------------------------------------------------------------
1303
           */
1304
          /*      Create left and right node for the attribute and the value. */
1305
          /* --------------------------------------------------------------------
1306
           */
1307
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
30✔
1308

1309
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
30✔
1310
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
30✔
1311

1312
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
30✔
1313

1314
          psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
30✔
1315

1316
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
30✔
1317
        } else
30✔
1318
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1319

1320
      }
1321

1322
      else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNull") == 0) {
21✔
1323
        const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
16✔
1324
        if (pszPropertyName != NULL) {
16✔
1325
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
16✔
1326
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
16✔
1327
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
16✔
1328
        } else
UNCOV
1329
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
×
1330
      }
1331

1332
      else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNil") == 0) {
5✔
1333
        const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
5✔
1334
        if (pszPropertyName != NULL) {
5✔
1335
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
5✔
1336
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
5✔
1337
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
5✔
1338
        } else
UNCOV
1339
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
×
1340
      }
1341
    }
1342
    /* -------------------------------------------------------------------- */
1343
    /*      FeatureId Filter                                                */
1344
    /*                                                                      */
1345
    /*      <ogc:Filter>                                                    */
1346
    /*      <ogc:FeatureId fid="INWATERA_1M.1013"/>                         */
1347
    /*      <ogc:FeatureId fid="INWATERA_1M.10"/>                           */
1348
    /*      <ogc:FeatureId fid="INWATERA_1M.13"/>                           */
1349
    /*      <ogc:FeatureId fid="INWATERA_1M.140"/>                          */
1350
    /*      <ogc:FeatureId fid="INWATERA_1M.5001"/>                         */
1351
    /*      <ogc:FeatureId fid="INWATERA_1M.2001"/>                         */
1352
    /*      </ogc:Filter>                                                   */
1353
    /*                                                                      */
1354
    /*                                                                      */
1355
    /*      Note that for FES1.1.0 the featureid has been deprecated in     */
1356
    /*      favor of GmlObjectId                                            */
1357
    /*      <GmlObjectId gml:id="TREESA_1M.1234"/>                          */
1358
    /*                                                                      */
1359
    /*      And in FES 2.0, in favor of <fes:ResourceId rid="foo.1234"/>    */
1360
    /* -------------------------------------------------------------------- */
1361
    else if (FLTIsFeatureIdFilterType(psXMLNode->pszValue)) {
59✔
1362
      psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
40✔
1363
      pszFeatureIdList = NULL;
1364

1365
      psFeatureIdNode = psXMLNode;
1366
      while (psFeatureIdNode) {
82✔
1367
        pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "fid", NULL);
42✔
1368
        if (!pszFeatureId)
42✔
1369
          pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "id", NULL);
42✔
1370
        if (!pszFeatureId)
42✔
1371
          pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "rid", NULL);
42✔
1372

1373
        if (pszFeatureId) {
42✔
1374
          if (pszFeatureIdList)
41✔
1375
            pszFeatureIdList = msStringConcatenate(pszFeatureIdList, ",");
2✔
1376

1377
          pszFeatureIdList =
1378
              msStringConcatenate(pszFeatureIdList, pszFeatureId);
41✔
1379
        }
1380
        psFeatureIdNode = psFeatureIdNode->psNext;
42✔
1381
      }
1382

1383
      if (pszFeatureIdList) {
40✔
1384
        msFree(psFilterNode->pszValue);
39✔
1385
        psFilterNode->pszValue = msStrdup(pszFeatureIdList);
39✔
1386
        msFree(pszFeatureIdList);
39✔
1387
      } else
1388
        psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1389
    }
1390

1391
    /* -------------------------------------------------------------------- */
1392
    /*      Temporal Filter.                                                */
1393
    /*
1394
    <fes:During>
1395
    <fes:ValueReference>gml:TimeInstant</fes:ValueReference>
1396
    <gml:TimePeriod gml:id="TP1">
1397
    <gml:begin>
1398
    <gml:TimeInstant gml:id="TI1">
1399
    <gml:timePosition>2005-05-17T00:00:00Z</gml:timePosition>
1400
    </gml:TimeInstant>
1401
    </gml:begin>
1402
    <gml:end>
1403
    <gml:TimeInstant gml:id="TI2">
1404
    <gml:timePosition>2005-05-23T00:00:00Z</gml:timePosition>
1405
    </gml:TimeInstant>
1406
    </gml:end>
1407
    </gml:TimePeriod>
1408
    </fes:During>
1409
    */
1410
    /* -------------------------------------------------------------------- */
1411
    else if (FLTIsTemporalFilterType(psXMLNode->pszValue)) {
19✔
1412
      psFilterNode->eType = FILTER_NODE_TYPE_TEMPORAL;
19✔
1413

1414
      if (strcasecmp(psXMLNode->pszValue, "During") == 0) {
19✔
1415
        const char *pszPropertyName = NULL;
1416
        const char *pszBeginTime;
1417
        const char *pszEndTime;
1418

1419
        pszPropertyName = FLTGetPropertyName(psXMLNode);
19✔
1420
        pszBeginTime = CPLGetXMLValue(
19✔
1421
            psXMLNode, "TimePeriod.begin.TimeInstant.timePosition", NULL);
1422
        if (pszBeginTime == NULL)
19✔
1423
          pszBeginTime =
1424
              CPLGetXMLValue(psXMLNode, "TimePeriod.beginPosition", NULL);
1✔
1425
        pszEndTime = CPLGetXMLValue(
19✔
1426
            psXMLNode, "TimePeriod.end.TimeInstant.timePosition", NULL);
1427
        if (pszEndTime == NULL)
19✔
1428
          pszEndTime =
1429
              CPLGetXMLValue(psXMLNode, "TimePeriod.endPosition", NULL);
1✔
1430

1431
        if (pszPropertyName && pszBeginTime && pszEndTime &&
19✔
1432
            strchr(pszBeginTime, '\'') == NULL &&
19✔
1433
            strchr(pszBeginTime, '\\') == NULL &&
19✔
1434
            strchr(pszEndTime, '\'') == NULL &&
19✔
1435
            strchr(pszEndTime, '\\') == NULL &&
19✔
1436
            msTimeGetResolution(pszBeginTime) >= 0 &&
57✔
1437
            msTimeGetResolution(pszEndTime) >= 0) {
19✔
1438

1439
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
19✔
1440
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
19✔
1441
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
19✔
1442

1443
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
19✔
1444
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_TIME_PERIOD;
19✔
1445
          const size_t nSize = strlen(pszBeginTime) + strlen(pszEndTime) + 2;
19✔
1446
          psFilterNode->psRightNode->pszValue =
19✔
1447
              static_cast<char *>(msSmallMalloc(nSize));
19✔
1448
          snprintf(psFilterNode->psRightNode->pszValue, nSize, "%s/%s",
19✔
1449
                   pszBeginTime, pszEndTime);
1450
        } else
UNCOV
1451
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
×
1452
      } else {
UNCOV
1453
        psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
×
1454
      }
1455

1456
    } /* end of is temporal */
1457
  }
1458
}
536✔
1459

1460
/************************************************************************/
1461
/*            int FLTIsLogicalFilterType((char *pszValue)                  */
1462
/*                                                                      */
1463
/*      return TRUE if the value of the node is of logical filter       */
1464
/*       encoding type.                                                 */
1465
/************************************************************************/
1466
int FLTIsLogicalFilterType(const char *pszValue) {
1,103✔
1467
  if (pszValue) {
1,103✔
1468
    if (strcasecmp(pszValue, "AND") == 0 || strcasecmp(pszValue, "OR") == 0 ||
1,103✔
1469
        strcasecmp(pszValue, "NOT") == 0)
938✔
1470
      return MS_TRUE;
194✔
1471
  }
1472

1473
  return MS_FALSE;
1474
}
1475

1476
/************************************************************************/
1477
/*         int FLTIsBinaryComparisonFilterType(char *pszValue)             */
1478
/*                                                                      */
1479
/*      Binary comparison filter type.                                  */
1480
/************************************************************************/
1481
int FLTIsBinaryComparisonFilterType(const char *pszValue) {
1,301✔
1482
  if (pszValue) {
1,301✔
1483
    if (strcasecmp(pszValue, "PropertyIsEqualTo") == 0 ||
1,301✔
1484
        strcasecmp(pszValue, "PropertyIsNotEqualTo") == 0 ||
639✔
1485
        strcasecmp(pszValue, "PropertyIsLessThan") == 0 ||
591✔
1486
        strcasecmp(pszValue, "PropertyIsGreaterThan") == 0 ||
483✔
1487
        strcasecmp(pszValue, "PropertyIsLessThanOrEqualTo") == 0 ||
439✔
1488
        strcasecmp(pszValue, "PropertyIsGreaterThanOrEqualTo") == 0)
405✔
1489
      return MS_TRUE;
960✔
1490
  }
1491

1492
  return MS_FALSE;
1493
}
1494

1495
/************************************************************************/
1496
/*            int FLTIsComparisonFilterType(char *pszValue)                */
1497
/*                                                                      */
1498
/*      return TRUE if the value of the node is of comparison filter    */
1499
/*      encoding type.                                                  */
1500
/************************************************************************/
1501
int FLTIsComparisonFilterType(const char *pszValue) {
646✔
1502
  if (pszValue) {
646✔
1503
    if (FLTIsBinaryComparisonFilterType(pszValue) ||
646✔
1504
        strcasecmp(pszValue, "PropertyIsLike") == 0 ||
224✔
1505
        strcasecmp(pszValue, "PropertyIsBetween") == 0 ||
172✔
1506
        strcasecmp(pszValue, "PropertyIsNull") == 0 ||
791✔
1507
        strcasecmp(pszValue, "PropertyIsNil") == 0)
121✔
1508
      return MS_TRUE;
1509
  }
1510

1511
  return MS_FALSE;
1512
}
1513

1514
/************************************************************************/
1515
/*            int FLTIsFeatureIdFilterType(char *pszValue)              */
1516
/*                                                                      */
1517
/*      return TRUE if the value of the node is of featureid filter     */
1518
/*      encoding type.                                                  */
1519
/************************************************************************/
1520
int FLTIsFeatureIdFilterType(const char *pszValue) {
113✔
1521
  if (pszValue && (strcasecmp(pszValue, "FeatureId") == 0 ||
113✔
1522
                   strcasecmp(pszValue, "GmlObjectId") == 0 ||
109✔
1523
                   strcasecmp(pszValue, "ResourceId") == 0))
109✔
1524

1525
    return MS_TRUE;
72✔
1526

1527
  return MS_FALSE;
1528
}
1529

1530
/************************************************************************/
1531
/*            int FLTIsSpatialFilterType(char *pszValue)                */
1532
/*                                                                      */
1533
/*      return TRUE if the value of the node is of spatial filter       */
1534
/*      encoding type.                                                  */
1535
/************************************************************************/
1536
int FLTIsSpatialFilterType(const char *pszValue) {
909✔
1537
  if (pszValue) {
909✔
1538
    if (strcasecmp(pszValue, "BBOX") == 0 ||
909✔
1539
        strcasecmp(pszValue, "DWithin") == 0 ||
834✔
1540
        strcasecmp(pszValue, "Intersect") == 0 ||
796✔
1541
        strcasecmp(pszValue, "Intersects") == 0 ||
792✔
1542
        strcasecmp(pszValue, "Equals") == 0 ||
758✔
1543
        strcasecmp(pszValue, "Disjoint") == 0 ||
738✔
1544
        strcasecmp(pszValue, "Touches") == 0 ||
726✔
1545
        strcasecmp(pszValue, "Crosses") == 0 ||
714✔
1546
        strcasecmp(pszValue, "Within") == 0 ||
698✔
1547
        strcasecmp(pszValue, "Contains") == 0 ||
682✔
1548
        strcasecmp(pszValue, "Overlaps") == 0 ||
670✔
1549
        strcasecmp(pszValue, "Beyond") == 0)
658✔
1550
      return MS_TRUE;
263✔
1551
  }
1552

1553
  return MS_FALSE;
1554
}
1555

1556
/************************************************************************/
1557
/*            int FLTIsTemportalFilterType(char *pszValue)              */
1558
/*                                                                      */
1559
/*      return TRUE if the value of the node is of temporal filter      */
1560
/*      encoding type.                                                  */
1561
/************************************************************************/
1562
int FLTIsTemporalFilterType(const char *pszValue) {
41✔
1563
  if (pszValue) {
41✔
1564
    if (strcasecmp(pszValue, "During") == 0)
41✔
1565
      return MS_TRUE;
29✔
1566
  }
1567

1568
  return MS_FALSE;
1569
}
1570

1571
/************************************************************************/
1572
/*           int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode)           */
1573
/*                                                                      */
1574
/*      Verify if the value of the node is one of the supported        */
1575
/*      filter type.                                                    */
1576
/************************************************************************/
1577
int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode) {
465✔
1578
  if (psXMLNode) {
465✔
1579
    if (FLTIsLogicalFilterType(psXMLNode->pszValue) ||
840✔
1580
        FLTIsSpatialFilterType(psXMLNode->pszValue) ||
624✔
1581
        FLTIsComparisonFilterType(psXMLNode->pszValue) ||
303✔
1582
        FLTIsFeatureIdFilterType(psXMLNode->pszValue) ||
541✔
1583
        FLTIsTemporalFilterType(psXMLNode->pszValue))
22✔
1584
      return MS_TRUE;
453✔
1585
  }
1586

1587
  return MS_FALSE;
1588
}
1589

1590
/************************************************************************/
1591
/*                          FLTNumberOfFilterType                       */
1592
/*                                                                      */
1593
/*      Loop trhough the nodes and return the number of nodes of        */
1594
/*      specified value.                                                */
1595
/************************************************************************/
UNCOV
1596
int FLTNumberOfFilterType(FilterEncodingNode *psFilterNode,
×
1597
                          const char *szType) {
1598
  int nCount = 0;
1599
  int nLeftNode = 0, nRightNode = 0;
1600

1601
  if (!psFilterNode || !szType || !psFilterNode->pszValue)
×
1602
    return 0;
1603

UNCOV
1604
  if (strcasecmp(psFilterNode->pszValue, (char *)szType) == 0)
×
1605
    nCount++;
1606

1607
  if (psFilterNode->psLeftNode)
×
UNCOV
1608
    nLeftNode = FLTNumberOfFilterType(psFilterNode->psLeftNode, szType);
×
1609

UNCOV
1610
  nCount += nLeftNode;
×
1611

UNCOV
1612
  if (psFilterNode->psRightNode)
×
1613
    nRightNode = FLTNumberOfFilterType(psFilterNode->psRightNode, szType);
UNCOV
1614
  nCount += nRightNode;
×
1615

UNCOV
1616
  return nCount;
×
1617
}
1618

1619
/************************************************************************/
1620
/*                          FLTValidForBBoxFilter                       */
1621
/*                                                                      */
1622
/*      Validate if there is only one BBOX filter node. Here is what    */
1623
/*      is supported (is valid) :                                       */
1624
/*        - one node which is a BBOX                                    */
1625
/*        - a logical AND with a valid BBOX                             */
1626
/*                                                                      */
1627
/*      eg 1: <Filter>                                                  */
1628
/*            <BBOX>                                                    */
1629
/*              <PropertyName>Geometry</PropertyName>                   */
1630
/*              <gml:Box
1631
 * srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
1632
/*                <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
1633
/*              </gml:Box>                                              */
1634
/*            </BBOX>                                                   */
1635
/*          </Filter>                                                   */
1636
/*                                                                      */
1637
/*      eg 2 :<Filter>                                                  */
1638
/*              <AND>                                                   */
1639
/*               <BBOX>                                                 */
1640
/*                <PropertyName>Geometry</PropertyName>                  */
1641
/*                <gml:Box
1642
 * srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
1643
/*                  <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
1644
/*                </gml:Box>                                            */
1645
/*               </BBOX>                                                */
1646
/*               <PropertyIsEqualTo>                                    */
1647
/*               <PropertyName>SomeProperty</PropertyName>              */
1648
/*                <Literal>100</Literal>                                */
1649
/*              </PropertyIsEqualTo>                                    */
1650
/*             </AND>                                                   */
1651
/*           </Filter>                                                  */
1652
/*                                                                      */
1653
/************************************************************************/
UNCOV
1654
int FLTValidForBBoxFilter(FilterEncodingNode *psFilterNode) {
×
1655
  int nCount = 0;
1656

UNCOV
1657
  if (!psFilterNode || !psFilterNode->pszValue)
×
1658
    return 1;
1659

UNCOV
1660
  nCount = FLTNumberOfFilterType(psFilterNode, "BBOX");
×
1661

1662
  if (nCount > 1)
×
1663
    return 0;
1664
  else if (nCount == 0)
×
1665
    return 1;
1666

1667
  /* nCount ==1  */
UNCOV
1668
  if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
×
1669
    return 1;
1670

UNCOV
1671
  if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
×
UNCOV
1672
    return FLTValidForBBoxFilter(psFilterNode->psLeftNode) &&
×
UNCOV
1673
           FLTValidForBBoxFilter(psFilterNode->psRightNode);
×
1674
  }
1675

1676
  return 0;
1677
}
1678

1679
#if 0
1680
static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode)
1681
{
1682
  int nCount = 0;
1683

1684
  if (!psFilterNode || !psFilterNode->pszValue)
1685
    return 1;
1686

1687
  nCount = FLTNumberOfFilterType(psFilterNode, "During");
1688

1689
  if (nCount > 1)
1690
    return 0;
1691
  else if (nCount == 0)
1692
    return 1;
1693

1694
  /* nCount ==1  */
1695
  if (strcasecmp(psFilterNode->pszValue, "During") == 0)
1696
    return 1;
1697

1698
  if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
1699
    return FLTHasUniqueTopLevelDuringFilter(psFilterNode->psLeftNode) &&
1700
           FLTHasUniqueTopLevelDuringFilter(psFilterNode->psRightNode);
1701
  }
1702

1703
  return 0;
1704
}
1705
#endif
1706

UNCOV
1707
int FLTIsLineFilter(FilterEncodingNode *psFilterNode) {
×
UNCOV
1708
  if (!psFilterNode || !psFilterNode->pszValue)
×
1709
    return 0;
1710

1711
  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
×
UNCOV
1712
      psFilterNode->psRightNode &&
×
UNCOV
1713
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_LINE)
×
1714
    return 1;
×
1715

1716
  return 0;
1717
}
1718

UNCOV
1719
int FLTIsPolygonFilter(FilterEncodingNode *psFilterNode) {
×
UNCOV
1720
  if (!psFilterNode || !psFilterNode->pszValue)
×
1721
    return 0;
1722

1723
  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
×
UNCOV
1724
      psFilterNode->psRightNode &&
×
UNCOV
1725
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON)
×
1726
    return 1;
×
1727

1728
  return 0;
1729
}
1730

UNCOV
1731
int FLTIsPointFilter(FilterEncodingNode *psFilterNode) {
×
UNCOV
1732
  if (!psFilterNode || !psFilterNode->pszValue)
×
1733
    return 0;
1734

UNCOV
1735
  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
×
UNCOV
1736
      psFilterNode->psRightNode &&
×
UNCOV
1737
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POINT)
×
UNCOV
1738
    return 1;
×
1739

1740
  return 0;
1741
}
1742

1743
int FLTIsBBoxFilter(FilterEncodingNode *psFilterNode) {
132✔
1744
  if (!psFilterNode || !psFilterNode->pszValue)
132✔
1745
    return 0;
1746

1747
  if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
132✔
1748
    return 1;
42✔
1749

1750
  return 0;
1751
}
1752

1753
shapeObj *FLTGetShape(FilterEncodingNode *psFilterNode, double *pdfDistance,
90✔
1754
                      int *pnUnit) {
1755
  char **tokens = NULL;
1756
  int nTokens = 0;
90✔
1757
  FilterEncodingNode *psNode = psFilterNode;
1758
  char *szUnitStr = NULL;
1759
  char *szUnit = NULL;
1760

1761
  if (psNode) {
90✔
1762
    if (psNode->eType == FILTER_NODE_TYPE_SPATIAL && psNode->psRightNode)
90✔
1763
      psNode = psNode->psRightNode;
1764

1765
    if (FLTIsGeometryFilterNodeType(psNode->eType)) {
90✔
1766

1767
      if (psNode->pszValue && pdfDistance) {
90✔
1768
        /*
1769
        syntax expected is "distance;unit" or just "distance"
1770
        if unit is there syntax is "URI#unit" (eg http://..../#m)
1771
        or just "unit"
1772
        */
1773
        tokens = msStringSplit(psNode->pszValue, ';', &nTokens);
23✔
1774
        if (tokens && nTokens >= 1) {
23✔
1775
          *pdfDistance = atof(tokens[0]);
23✔
1776

1777
          if (nTokens == 2 && pnUnit) {
23✔
1778
            szUnitStr = msStrdup(tokens[1]);
19✔
1779
            msFreeCharArray(tokens, nTokens);
19✔
1780
            nTokens = 0;
19✔
1781
            tokens = msStringSplit(szUnitStr, '#', &nTokens);
19✔
1782
            msFree(szUnitStr);
19✔
1783
            if (tokens && nTokens >= 1) {
19✔
1784
              if (nTokens == 1)
19✔
1785
                szUnit = tokens[0];
19✔
1786
              else
UNCOV
1787
                szUnit = tokens[1];
×
1788

1789
              if (strcasecmp(szUnit, "m") == 0 ||
19✔
1790
                  strcasecmp(szUnit, "meters") == 0)
18✔
1791
                *pnUnit = MS_METERS;
1✔
1792
              else if (strcasecmp(szUnit, "km") == 0 ||
18✔
1793
                       strcasecmp(szUnit, "kilometers") == 0)
18✔
1794
                *pnUnit = MS_KILOMETERS;
3✔
1795
              else if (strcasecmp(szUnit, "NM") == 0 ||
15✔
1796
                       strcasecmp(szUnit, "nauticalmiles") == 0)
15✔
1797
                *pnUnit = MS_NAUTICALMILES;
×
1798
              else if (strcasecmp(szUnit, "mi") == 0 ||
15✔
1799
                       strcasecmp(szUnit, "miles") == 0)
15✔
UNCOV
1800
                *pnUnit = MS_MILES;
×
1801
              else if (strcasecmp(szUnit, "in") == 0 ||
15✔
1802
                       strcasecmp(szUnit, "inches") == 0)
15✔
UNCOV
1803
                *pnUnit = MS_INCHES;
×
1804
              else if (strcasecmp(szUnit, "ft") == 0 ||
15✔
1805
                       strcasecmp(szUnit, "feet") == 0)
15✔
UNCOV
1806
                *pnUnit = MS_FEET;
×
1807
              else if (strcasecmp(szUnit, "deg") == 0 ||
15✔
1808
                       strcasecmp(szUnit, "dd") == 0)
15✔
1809
                *pnUnit = MS_DD;
15✔
UNCOV
1810
              else if (strcasecmp(szUnit, "px") == 0)
×
UNCOV
1811
                *pnUnit = MS_PIXELS;
×
1812
            }
1813
          }
1814
        }
1815
        msFreeCharArray(tokens, nTokens);
23✔
1816
      }
1817

1818
      return (shapeObj *)psNode->pOther;
90✔
1819
    }
1820
  }
1821
  return NULL;
1822
}
1823

1824
/************************************************************************/
1825
/*                                FLTGetBBOX                            */
1826
/*                                                                      */
1827
/*      Loop through the nodes are return the coordinates of the        */
1828
/*      first bbox node found. The return value is the epsg code of     */
1829
/*      the bbox.                                                       */
1830
/************************************************************************/
1831
const char *FLTGetBBOX(FilterEncodingNode *psFilterNode, rectObj *psRect) {
88✔
1832
  const char *pszReturn = NULL;
1833

1834
  if (!psFilterNode || !psRect)
88✔
1835
    return NULL;
1836

1837
  if (psFilterNode->pszValue &&
88✔
1838
      strcasecmp(psFilterNode->pszValue, "BBOX") == 0) {
88✔
1839
    if (psFilterNode->psRightNode && psFilterNode->psRightNode->pOther) {
80✔
1840
      rectObj *pRect = (rectObj *)psFilterNode->psRightNode->pOther;
1841
      psRect->minx = pRect->minx;
80✔
1842
      psRect->miny = pRect->miny;
80✔
1843
      psRect->maxx = pRect->maxx;
80✔
1844
      psRect->maxy = pRect->maxy;
80✔
1845

1846
      return psFilterNode->pszSRS;
80✔
1847
    }
1848
  } else {
1849
    pszReturn = FLTGetBBOX(psFilterNode->psLeftNode, psRect);
8✔
1850
    if (pszReturn)
8✔
1851
      return pszReturn;
1852
    else
UNCOV
1853
      return FLTGetBBOX(psFilterNode->psRightNode, psRect);
×
1854
  }
1855

1856
  return pszReturn;
1857
}
1858

1859
const char *FLTGetDuring(FilterEncodingNode *psFilterNode,
19✔
1860
                         const char **ppszTimeField) {
1861
  const char *pszReturn = NULL;
1862

1863
  if (!psFilterNode || !ppszTimeField)
19✔
1864
    return NULL;
1865

1866
  if (psFilterNode->pszValue &&
19✔
1867
      strcasecmp(psFilterNode->pszValue, "During") == 0) {
19✔
1868
    *ppszTimeField = psFilterNode->psLeftNode->pszValue;
19✔
1869
    return psFilterNode->psRightNode->pszValue;
19✔
1870
  } else {
UNCOV
1871
    pszReturn = FLTGetDuring(psFilterNode->psLeftNode, ppszTimeField);
×
UNCOV
1872
    if (pszReturn)
×
1873
      return pszReturn;
1874
    else
UNCOV
1875
      return FLTGetDuring(psFilterNode->psRightNode, ppszTimeField);
×
1876
  }
1877

1878
  return pszReturn;
1879
}
1880

1881
/************************************************************************/
1882
/*                           FLTGetSQLExpression                        */
1883
/*                                                                      */
1884
/*      Build SQL expressions from the mapserver nodes.                 */
1885
/************************************************************************/
1886
char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp) {
×
1887
  char *pszExpression = NULL;
1888

1889
  if (psFilterNode == NULL || lp == NULL)
×
1890
    return NULL;
1891

UNCOV
1892
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
×
UNCOV
1893
    if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
×
1894
      if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue)) {
×
1895
        pszExpression = FLTGetBinaryComparisonSQLExpresssion(psFilterNode, lp);
×
1896
      } else if (strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0) {
×
1897
        pszExpression =
UNCOV
1898
            FLTGetIsBetweenComparisonSQLExpresssion(psFilterNode, lp);
×
1899
      } else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
×
1900
        pszExpression = FLTGetIsLikeComparisonSQLExpression(psFilterNode, lp);
×
1901
      }
1902
    }
UNCOV
1903
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
×
1904
    if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
×
UNCOV
1905
        strcasecmp(psFilterNode->pszValue, "OR") == 0) {
×
1906
      pszExpression = FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
×
1907

UNCOV
1908
    } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
×
1909
      pszExpression = FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
×
1910
    }
1911
  }
1912

1913
  else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
×
1914
    /* TODO */
UNCOV
1915
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
×
1916
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1917
    defined(USE_SOS_SVR)
UNCOV
1918
    if (psFilterNode->pszValue) {
×
1919
      const char *pszAttribute =
UNCOV
1920
          msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
×
1921
      if (pszAttribute) {
×
1922
        int nTokens = 0;
×
UNCOV
1923
        char **tokens = msStringSplit(psFilterNode->pszValue, ',', &nTokens);
×
1924
        int bString = 0;
1925
        if (tokens && nTokens > 0) {
×
UNCOV
1926
          for (int i = 0; i < nTokens; i++) {
×
1927
            char *pszEscapedStr = NULL;
UNCOV
1928
            const char *pszId = tokens[i];
×
1929
            const char *pszDot = strchr(pszId, '.');
1930
            if (pszDot)
×
UNCOV
1931
              pszId = pszDot + 1;
×
1932

1933
            if (strlen(pszId) <= 0)
×
UNCOV
1934
              continue;
×
1935

UNCOV
1936
            if (FLTIsNumeric(pszId) == MS_FALSE)
×
1937
              bString = 1;
1938

UNCOV
1939
            pszEscapedStr = msLayerEscapeSQLParam(lp, pszId);
×
1940
            char szTmp[256];
UNCOV
1941
            if (bString) {
×
UNCOV
1942
              if (lp->connectiontype == MS_OGR ||
×
1943
                  lp->connectiontype == MS_POSTGIS)
1944
                snprintf(szTmp, sizeof(szTmp),
1945
                         "(CAST(%s AS CHARACTER(255)) = '%s')", pszAttribute,
1946
                         pszEscapedStr);
1947
              else
1948
                snprintf(szTmp, sizeof(szTmp), "(%s = '%s')", pszAttribute,
1949
                         pszEscapedStr);
1950
            } else
1951
              snprintf(szTmp, sizeof(szTmp), "(%s = %s)", pszAttribute,
1952
                       pszEscapedStr);
1953

1954
            msFree(pszEscapedStr);
×
1955
            pszEscapedStr = NULL;
1956

1957
            if (pszExpression != NULL)
×
UNCOV
1958
              pszExpression = msStringConcatenate(pszExpression, " OR ");
×
1959
            else
1960
              /*opening and closing brackets*/
1961
              pszExpression = msStringConcatenate(pszExpression, "(");
×
1962

UNCOV
1963
            pszExpression = msStringConcatenate(pszExpression, szTmp);
×
1964
          }
1965
        }
UNCOV
1966
        msFreeCharArray(tokens, nTokens);
×
1967
      }
1968
      /*opening and closing brackets*/
1969
      if (pszExpression)
×
UNCOV
1970
        pszExpression = msStringConcatenate(pszExpression, ")");
×
1971
    }
1972
#else
1973
    msSetError(MS_MISCERR, "OWS support is not available.",
1974
               "FLTGetSQLExpression()");
1975
    return NULL;
1976
#endif
1977

UNCOV
1978
  } else if (lp->connectiontype != MS_OGR &&
×
1979
             psFilterNode->eType == FILTER_NODE_TYPE_TEMPORAL)
UNCOV
1980
    pszExpression = msStrdup(FLTGetTimeExpression(psFilterNode, lp).c_str());
×
1981

1982
  return pszExpression;
1983
}
1984

1985
/************************************************************************/
1986
/*                     FLTGetLogicalComparisonSQLExpresssion            */
1987
/*                                                                      */
1988
/*      Return the expression for logical comparison expression.        */
1989
/************************************************************************/
UNCOV
1990
char *FLTGetLogicalComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
×
1991
                                            layerObj *lp) {
1992
  char *pszBuffer = NULL;
1993
  char *pszTmp = NULL;
1994

1995
  if (lp == NULL)
×
1996
    return NULL;
1997

1998
  /* ==================================================================== */
1999
  /*      special case for BBOX node.                                     */
2000
  /* ==================================================================== */
UNCOV
2001
  if (psFilterNode->psLeftNode && psFilterNode->psRightNode &&
×
UNCOV
2002
      ((strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") == 0) ||
×
2003
       (strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") == 0))) {
×
2004
    if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") != 0)
×
UNCOV
2005
      pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
×
2006
    else
UNCOV
2007
      pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
×
2008

UNCOV
2009
    if (!pszTmp)
×
2010
      return NULL;
2011

2012
    const size_t nSize = strlen(pszTmp) + 1;
×
2013
    pszBuffer = (char *)malloc(nSize);
×
2014
    snprintf(pszBuffer, nSize, "%s", pszTmp);
2015
  }
2016

2017
  /* ==================================================================== */
2018
  /*      special case for temporal filter node (OGR layer only)          */
2019
  /* ==================================================================== */
2020
  else if (lp->connectiontype == MS_OGR && psFilterNode->psLeftNode &&
×
UNCOV
2021
           psFilterNode->psRightNode &&
×
UNCOV
2022
           (psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_TEMPORAL ||
×
2023
            psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_TEMPORAL)) {
×
2024
    if (psFilterNode->psLeftNode->eType != FILTER_NODE_TYPE_TEMPORAL)
×
UNCOV
2025
      pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
×
2026
    else
UNCOV
2027
      pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
×
2028

UNCOV
2029
    if (!pszTmp)
×
2030
      return NULL;
2031

2032
    const size_t nSize = strlen(pszTmp) + 1;
×
2033
    pszBuffer = (char *)malloc(nSize);
×
2034
    snprintf(pszBuffer, nSize, "%s", pszTmp);
2035
  }
2036

2037
  /* -------------------------------------------------------------------- */
2038
  /*      OR and AND                                                      */
2039
  /* -------------------------------------------------------------------- */
UNCOV
2040
  else if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
×
UNCOV
2041
    pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
×
UNCOV
2042
    if (!pszTmp)
×
2043
      return NULL;
2044

2045
    pszBuffer = (char *)malloc(
×
UNCOV
2046
        sizeof(char) * (strlen(pszTmp) + strlen(psFilterNode->pszValue) + 5));
×
2047
    pszBuffer[0] = '\0';
×
2048
    strcat(pszBuffer, " (");
2049
    strcat(pszBuffer, pszTmp);
2050
    strcat(pszBuffer, " ");
2051
    strcat(pszBuffer, psFilterNode->pszValue);
2052
    strcat(pszBuffer, " ");
2053

2054
    free(pszTmp);
×
2055

UNCOV
2056
    const size_t nTmp = strlen(pszBuffer);
×
UNCOV
2057
    pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
×
UNCOV
2058
    if (!pszTmp) {
×
UNCOV
2059
      free(pszBuffer);
×
UNCOV
2060
      return NULL;
×
2061
    }
2062

2063
    pszBuffer = (char *)msSmallRealloc(
×
2064
        pszBuffer, sizeof(char) * (strlen(pszTmp) + nTmp + 3));
×
2065
    strcat(pszBuffer, pszTmp);
2066
    strcat(pszBuffer, ") ");
2067
  }
2068
  /* -------------------------------------------------------------------- */
2069
  /*      NOT                                                             */
2070
  /* -------------------------------------------------------------------- */
UNCOV
2071
  else if (psFilterNode->psLeftNode &&
×
UNCOV
2072
           strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
×
UNCOV
2073
    pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
×
UNCOV
2074
    if (!pszTmp)
×
2075
      return NULL;
2076

UNCOV
2077
    pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 9));
×
UNCOV
2078
    pszBuffer[0] = '\0';
×
2079

2080
    strcat(pszBuffer, " (NOT ");
2081
    strcat(pszBuffer, pszTmp);
2082
    strcat(pszBuffer, ") ");
2083
  } else
2084
    return NULL;
2085

2086
  /* -------------------------------------------------------------------- */
2087
  /*      Cleanup.                                                        */
2088
  /* -------------------------------------------------------------------- */
2089
  if (pszTmp != NULL)
2090
    free(pszTmp);
×
UNCOV
2091
  return pszBuffer;
×
2092
}
2093

2094
/************************************************************************/
2095
/*                      FLTGetBinaryComparisonSQLExpresssion            */
2096
/*                                                                      */
2097
/*      Return the expression for a binary comparison filter node.      */
2098
/************************************************************************/
2099
char *FLTGetBinaryComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
×
2100
                                           layerObj *lp) {
2101
  const size_t bufferSize = 1024;
2102
  char szBuffer[1024];
2103
  int bString = 0;
2104
  char szTmp[256];
2105
  char *pszEscapedStr = NULL;
2106

2107
  szBuffer[0] = '\0';
×
UNCOV
2108
  if (!psFilterNode || !FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
×
2109
    return NULL;
×
2110

2111
  /* -------------------------------------------------------------------- */
2112
  /*      check if the value is a numeric value or alphanumeric. If it    */
2113
  /*      is alphanumeric, add quotes around attribute and values.        */
2114
  /* -------------------------------------------------------------------- */
2115
  bString = 0;
UNCOV
2116
  if (psFilterNode->psRightNode->pszValue) {
×
2117
    const char *pszOFGType;
UNCOV
2118
    snprintf(szTmp, sizeof(szTmp), "%s_type",
×
UNCOV
2119
             psFilterNode->psLeftNode->pszValue);
×
2120
    pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
×
UNCOV
2121
    if (pszOFGType != NULL && strcasecmp(pszOFGType, "Character") == 0)
×
2122
      bString = 1;
2123

UNCOV
2124
    else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
×
2125
      bString = 1;
2126
  }
2127

2128
  /* special case to be able to have empty strings in the expression. */
UNCOV
2129
  if (psFilterNode->psRightNode->pszValue == NULL)
×
2130
    bString = 1;
2131

2132
  /*opening bracket*/
2133
  strlcat(szBuffer, " (", bufferSize);
2134

2135
  pszEscapedStr =
UNCOV
2136
      msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
×
2137

2138
  /* attribute */
2139
  /*case insensitive set ? */
UNCOV
2140
  if (bString && strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0 &&
×
UNCOV
2141
      psFilterNode->psRightNode->pOther &&
×
UNCOV
2142
      (*(int *)psFilterNode->psRightNode->pOther) == 1) {
×
2143
    snprintf(szTmp, sizeof(szTmp), "lower(%s) ", pszEscapedStr);
2144
    strlcat(szBuffer, szTmp, bufferSize);
2145
  } else
2146
    strlcat(szBuffer, pszEscapedStr, bufferSize);
2147

UNCOV
2148
  msFree(pszEscapedStr);
×
2149
  pszEscapedStr = NULL;
2150

2151
  /* logical operator */
UNCOV
2152
  if (strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0)
×
2153
    strlcat(szBuffer, "=", bufferSize);
2154
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsNotEqualTo") == 0)
×
2155
    strlcat(szBuffer, "<>", bufferSize);
UNCOV
2156
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThan") == 0)
×
2157
    strlcat(szBuffer, "<", bufferSize);
UNCOV
2158
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsGreaterThan") == 0)
×
2159
    strlcat(szBuffer, ">", bufferSize);
UNCOV
2160
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThanOrEqualTo") ==
×
2161
           0)
2162
    strlcat(szBuffer, "<=", bufferSize);
2163
  else if (strcasecmp(psFilterNode->pszValue,
×
2164
                      "PropertyIsGreaterThanOrEqualTo") == 0)
2165
    strlcat(szBuffer, ">=", bufferSize);
2166

2167
  strlcat(szBuffer, " ", bufferSize);
2168

2169
  /* value */
2170

UNCOV
2171
  if (bString && psFilterNode->psRightNode->pszValue &&
×
UNCOV
2172
      strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0 &&
×
UNCOV
2173
      psFilterNode->psRightNode->pOther &&
×
UNCOV
2174
      (*(int *)psFilterNode->psRightNode->pOther) == 1) {
×
2175
    char *pszEscapedStr;
2176
    pszEscapedStr =
2177
        msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
×
2178
    snprintf(szTmp, sizeof(szTmp), "lower('%s') ", pszEscapedStr);
UNCOV
2179
    msFree(pszEscapedStr);
×
2180
    strlcat(szBuffer, szTmp, bufferSize);
2181
  } else {
2182
    if (bString)
2183
      strlcat(szBuffer, "'", bufferSize);
2184

UNCOV
2185
    if (psFilterNode->psRightNode->pszValue) {
×
UNCOV
2186
      if (bString) {
×
2187
        char *pszEscapedStr;
2188
        pszEscapedStr =
UNCOV
2189
            msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
×
2190
        strlcat(szBuffer, pszEscapedStr, bufferSize);
UNCOV
2191
        msFree(pszEscapedStr);
×
2192
        pszEscapedStr = NULL;
2193
      } else
2194
        strlcat(szBuffer, psFilterNode->psRightNode->pszValue, bufferSize);
2195
    }
2196

UNCOV
2197
    if (bString)
×
2198
      strlcat(szBuffer, "'", bufferSize);
2199
  }
2200
  /*closing bracket*/
2201
  strlcat(szBuffer, ") ", bufferSize);
2202

UNCOV
2203
  return msStrdup(szBuffer);
×
2204
}
2205

2206
/************************************************************************/
2207
/*                    FLTGetIsBetweenComparisonSQLExpresssion           */
2208
/*                                                                      */
2209
/*      Build an SQL expression for IsBteween Filter.                  */
2210
/************************************************************************/
UNCOV
2211
char *FLTGetIsBetweenComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
×
2212
                                              layerObj *lp) {
2213
  const size_t bufferSize = 1024;
2214
  char szBuffer[1024];
2215
  char **aszBounds = NULL;
UNCOV
2216
  int nBounds = 0;
×
2217
  int bString = 0;
2218
  char szTmp[256];
2219
  char *pszEscapedStr;
2220

UNCOV
2221
  szBuffer[0] = '\0';
×
UNCOV
2222
  if (!psFilterNode ||
×
2223
      !(strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0))
×
2224
    return NULL;
2225

2226
  if (!psFilterNode->psLeftNode || !psFilterNode->psRightNode)
×
2227
    return NULL;
2228

2229
  /* -------------------------------------------------------------------- */
2230
  /*      Get the bounds value which are stored like boundmin;boundmax    */
2231
  /* -------------------------------------------------------------------- */
UNCOV
2232
  aszBounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';', &nBounds);
×
2233
  if (nBounds != 2) {
×
UNCOV
2234
    msFreeCharArray(aszBounds, nBounds);
×
2235
    return NULL;
×
2236
  }
2237
  /* -------------------------------------------------------------------- */
2238
  /*      check if the value is a numeric value or alphanumeric. If it    */
2239
  /*      is alphanumeric, add quotes around attribute and values.        */
2240
  /* -------------------------------------------------------------------- */
2241
  bString = 0;
UNCOV
2242
  if (aszBounds[0]) {
×
2243
    const char *pszOFGType;
2244
    snprintf(szTmp, sizeof(szTmp), "%s_type",
×
2245
             psFilterNode->psLeftNode->pszValue);
×
UNCOV
2246
    pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
×
UNCOV
2247
    if (pszOFGType != NULL && strcasecmp(pszOFGType, "Character") == 0)
×
2248
      bString = 1;
UNCOV
2249
    else if (FLTIsNumeric(aszBounds[0]) == MS_FALSE)
×
2250
      bString = 1;
2251
  }
2252
  if (!bString) {
UNCOV
2253
    if (aszBounds[1]) {
×
UNCOV
2254
      if (FLTIsNumeric(aszBounds[1]) == MS_FALSE)
×
2255
        bString = 1;
2256
    }
2257
  }
2258

2259
  /* -------------------------------------------------------------------- */
2260
  /*      build expression.                                              */
2261
  /* -------------------------------------------------------------------- */
2262
  /*opening parenthesis */
2263
  strlcat(szBuffer, " (", bufferSize);
2264

2265
  /* attribute */
2266
  pszEscapedStr =
UNCOV
2267
      msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
×
2268

2269
  strlcat(szBuffer, pszEscapedStr, bufferSize);
2270
  msFree(pszEscapedStr);
×
2271
  pszEscapedStr = NULL;
2272

2273
  /*between*/
2274
  strlcat(szBuffer, " BETWEEN ", bufferSize);
2275

2276
  /*bound 1*/
UNCOV
2277
  if (bString)
×
2278
    strlcat(szBuffer, "'", bufferSize);
UNCOV
2279
  pszEscapedStr = msLayerEscapeSQLParam(lp, aszBounds[0]);
×
2280
  strlcat(szBuffer, pszEscapedStr, bufferSize);
2281
  msFree(pszEscapedStr);
×
2282
  pszEscapedStr = NULL;
2283

UNCOV
2284
  if (bString)
×
2285
    strlcat(szBuffer, "'", bufferSize);
2286

2287
  strlcat(szBuffer, " AND ", bufferSize);
2288

2289
  /*bound 2*/
UNCOV
2290
  if (bString)
×
2291
    strlcat(szBuffer, "'", bufferSize);
UNCOV
2292
  pszEscapedStr = msLayerEscapeSQLParam(lp, aszBounds[1]);
×
2293
  strlcat(szBuffer, pszEscapedStr, bufferSize);
2294
  msFree(pszEscapedStr);
×
2295
  pszEscapedStr = NULL;
2296

UNCOV
2297
  if (bString)
×
2298
    strlcat(szBuffer, "'", bufferSize);
2299

2300
  /*closing parenthesis*/
2301
  strlcat(szBuffer, ")", bufferSize);
2302

UNCOV
2303
  msFreeCharArray(aszBounds, nBounds);
×
2304

UNCOV
2305
  return msStrdup(szBuffer);
×
2306
}
2307

2308
/************************************************************************/
2309
/*                      FLTGetIsLikeComparisonSQLExpression             */
2310
/*                                                                      */
2311
/*      Build an sql expression for IsLike filter.                      */
2312
/************************************************************************/
UNCOV
2313
char *FLTGetIsLikeComparisonSQLExpression(FilterEncodingNode *psFilterNode,
×
2314
                                          layerObj *lp) {
2315
  const size_t bufferSize = 1024;
2316
  char szBuffer[1024];
2317
  char *pszValue = NULL;
2318

2319
  const char *pszWild = NULL;
2320
  const char *pszSingle = NULL;
2321
  const char *pszEscape = NULL;
2322
  char szTmp[4];
2323

2324
  int nLength = 0, i = 0, j = 0;
2325
  int bCaseInsensitive = 0;
2326

2327
  char *pszEscapedStr = NULL;
2328
  FEPropertyIsLike *propIsLike;
2329

UNCOV
2330
  if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
×
2331
      !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
×
2332
    return NULL;
2333

2334
  propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
2335
  pszWild = propIsLike->pszWildCard;
×
UNCOV
2336
  pszSingle = propIsLike->pszSingleChar;
×
UNCOV
2337
  pszEscape = propIsLike->pszEscapeChar;
×
UNCOV
2338
  bCaseInsensitive = propIsLike->bCaseInsensitive;
×
2339

UNCOV
2340
  if (!pszWild || strlen(pszWild) == 0 || !pszSingle ||
×
UNCOV
2341
      strlen(pszSingle) == 0 || !pszEscape || strlen(pszEscape) == 0)
×
2342
    return NULL;
2343

UNCOV
2344
  if (pszEscape[0] == '\'') {
×
2345
    /* This might be valid, but the risk of SQL injection is too high */
2346
    /* and the below code is not ready for that */
2347
    /* Someone who does this has clearly suspect intentions ! */
UNCOV
2348
    msSetError(
×
2349
        MS_MISCERR,
2350
        "Single quote character is not allowed as an escaping character.",
2351
        "FLTGetIsLikeComparisonSQLExpression()");
2352
    return NULL;
×
2353
  }
2354

2355
  szBuffer[0] = '\0';
×
2356
  /*opening bracket*/
2357
  strlcat(szBuffer, " (", bufferSize);
2358

2359
  /* attribute name */
2360
  pszEscapedStr =
UNCOV
2361
      msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
×
2362

2363
  strlcat(szBuffer, pszEscapedStr, bufferSize);
UNCOV
2364
  msFree(pszEscapedStr);
×
2365
  pszEscapedStr = NULL;
2366

2367
  if (lp->connectiontype == MS_POSTGIS) {
×
UNCOV
2368
    if (bCaseInsensitive == 1)
×
2369
      strlcat(szBuffer, "::text ilike '", bufferSize);
2370
    else
2371
      strlcat(szBuffer, "::text like '", bufferSize);
2372
  } else
2373
    strlcat(szBuffer, " like '", bufferSize);
2374

2375
  pszValue = psFilterNode->psRightNode->pszValue;
×
2376
  nLength = strlen(pszValue);
×
2377

2378
  pszEscapedStr = (char *)msSmallMalloc(3 * nLength + 1);
×
2379

UNCOV
2380
  for (i = 0, j = 0; i < nLength; i++) {
×
2381
    char c = pszValue[i];
×
2382
    if (c != pszWild[0] && c != pszSingle[0] && c != pszEscape[0]) {
×
2383
      if (c == '\'') {
×
2384
        pszEscapedStr[j++] = '\'';
×
2385
        pszEscapedStr[j++] = '\'';
×
2386
      } else if (c == '\\') {
×
2387
        pszEscapedStr[j++] = '\\';
×
UNCOV
2388
        pszEscapedStr[j++] = '\\';
×
2389
      } else
2390
        pszEscapedStr[j++] = c;
×
2391
    } else if (c == pszSingle[0]) {
×
UNCOV
2392
      pszEscapedStr[j++] = '_';
×
2393
    } else if (c == pszEscape[0]) {
×
UNCOV
2394
      pszEscapedStr[j++] = pszEscape[0];
×
2395
      if (i + 1 < nLength) {
×
2396
        char nextC = pszValue[i + 1];
×
2397
        i++;
UNCOV
2398
        if (nextC == '\'') {
×
2399
          pszEscapedStr[j++] = '\'';
×
UNCOV
2400
          pszEscapedStr[j++] = '\'';
×
2401
        } else
UNCOV
2402
          pszEscapedStr[j++] = nextC;
×
2403
      }
2404
    } else if (c == pszWild[0]) {
×
2405
      pszEscapedStr[j++] = '%';
×
2406
    }
2407
  }
UNCOV
2408
  pszEscapedStr[j++] = 0;
×
2409
  strlcat(szBuffer, pszEscapedStr, bufferSize);
2410
  msFree(pszEscapedStr);
×
2411

2412
  strlcat(szBuffer, "'", bufferSize);
2413
  if (lp->connectiontype != MS_OGR) {
×
UNCOV
2414
    if (lp->connectiontype == MS_POSTGIS && pszEscape[0] == '\\')
×
2415
      strlcat(szBuffer, " escape E'", bufferSize);
2416
    else
2417
      strlcat(szBuffer, " escape '", bufferSize);
UNCOV
2418
    szTmp[0] = pszEscape[0];
×
UNCOV
2419
    if (pszEscape[0] == '\\') {
×
UNCOV
2420
      szTmp[1] = '\\';
×
UNCOV
2421
      szTmp[2] = '\'';
×
UNCOV
2422
      szTmp[3] = '\0';
×
2423
    } else {
UNCOV
2424
      szTmp[1] = '\'';
×
UNCOV
2425
      szTmp[2] = '\0';
×
2426
    }
2427

2428
    strlcat(szBuffer, szTmp, bufferSize);
2429
  }
2430
  strlcat(szBuffer, ") ", bufferSize);
2431

2432
  return msStrdup(szBuffer);
×
2433
}
2434

2435
/************************************************************************/
2436
/*                           FLTHasSpatialFilter                        */
2437
/*                                                                      */
2438
/*      Utility function to see if a spatial filter is included in      */
2439
/*      the node.                                                       */
2440
/************************************************************************/
UNCOV
2441
int FLTHasSpatialFilter(FilterEncodingNode *psNode) {
×
2442
  int bResult = MS_FALSE;
2443

UNCOV
2444
  if (!psNode)
×
2445
    return MS_FALSE;
2446

UNCOV
2447
  if (psNode->eType == FILTER_NODE_TYPE_LOGICAL) {
×
2448
    if (psNode->psLeftNode)
×
UNCOV
2449
      bResult = FLTHasSpatialFilter(psNode->psLeftNode);
×
2450

2451
    if (bResult)
×
2452
      return MS_TRUE;
2453

UNCOV
2454
    if (psNode->psRightNode)
×
UNCOV
2455
      bResult = FLTHasSpatialFilter(psNode->psRightNode);
×
2456

UNCOV
2457
    if (bResult)
×
2458
      return MS_TRUE;
UNCOV
2459
  } else if (FLTIsBBoxFilter(psNode) || FLTIsPointFilter(psNode) ||
×
UNCOV
2460
             FLTIsLineFilter(psNode) || FLTIsPolygonFilter(psNode))
×
UNCOV
2461
    return MS_TRUE;
×
2462

2463
  return MS_FALSE;
2464
}
2465

2466
/************************************************************************/
2467
/*                     FLTCreateFeatureIdFilterEncoding                 */
2468
/*                                                                      */
2469
/*      Utility function to create a filter node of FeatureId type.     */
2470
/************************************************************************/
2471
FilterEncodingNode *FLTCreateFeatureIdFilterEncoding(const char *pszString) {
30✔
2472
  FilterEncodingNode *psFilterNode = NULL;
2473

2474
  if (pszString) {
30✔
2475
    psFilterNode = FLTCreateFilterEncodingNode();
30✔
2476
    psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
30✔
2477
    psFilterNode->pszValue = msStrdup(pszString);
30✔
2478
    return psFilterNode;
30✔
2479
  }
2480
  return NULL;
2481
}
2482

2483
/************************************************************************/
2484
/*                              FLTParseGMLBox                          */
2485
/*                                                                      */
2486
/*      Parse gml box. Used for FE 1.0                                  */
2487
/************************************************************************/
2488
int FLTParseGMLBox(CPLXMLNode *psBox, rectObj *psBbox, char **ppszSRS) {
30✔
2489
  int bCoordinatesValid = 0;
2490
  CPLXMLNode *psCoordinates = NULL;
2491
  CPLXMLNode *psCoord1 = NULL, *psCoord2 = NULL;
2492
  char **papszCoords = NULL, **papszMin = NULL, **papszMax = NULL;
2493
  int nCoords = 0, nCoordsMin = 0, nCoordsMax = 0;
30✔
2494
  const char *pszTmpCoord = NULL;
2495
  const char *pszSRS = NULL;
2496
  const char *pszTS = NULL;
2497
  const char *pszCS = NULL;
2498
  double minx = 0.0, miny = 0.0, maxx = 0.0, maxy = 0.0;
2499

2500
  if (psBox) {
30✔
2501
    pszSRS = CPLGetXMLValue(psBox, "srsName", NULL);
30✔
2502
    if (ppszSRS && pszSRS)
30✔
2503
      *ppszSRS = msStrdup(pszSRS);
30✔
2504

2505
    psCoordinates = CPLGetXMLNode(psBox, "coordinates");
30✔
2506
    pszTS = CPLGetXMLValue(psCoordinates, "ts", NULL);
30✔
2507
    if (pszTS == NULL)
30✔
2508
      pszTS = " ";
2509
    pszCS = CPLGetXMLValue(psCoordinates, "cs", NULL);
30✔
2510
    if (pszCS == NULL)
30✔
2511
      pszCS = ",";
2512
    pszTmpCoord = CPLGetXMLValue(psCoordinates, NULL, NULL);
30✔
2513

2514
    if (pszTmpCoord) {
30✔
2515
      papszCoords = msStringSplit(pszTmpCoord, pszTS[0], &nCoords);
29✔
2516
      if (papszCoords && nCoords == 2) {
29✔
2517
        papszMin = msStringSplit(papszCoords[0], pszCS[0], &nCoordsMin);
29✔
2518
        if (papszMin && nCoordsMin == 2) {
29✔
2519
          papszMax = msStringSplit(papszCoords[1], pszCS[0], &nCoordsMax);
29✔
2520
        }
2521
        if (papszMax && nCoordsMax == 2) {
29✔
2522
          bCoordinatesValid = 1;
2523
          minx = atof(papszMin[0]);
29✔
2524
          miny = atof(papszMin[1]);
29✔
2525
          maxx = atof(papszMax[0]);
29✔
2526
          maxy = atof(papszMax[1]);
29✔
2527
        }
2528

2529
        msFreeCharArray(papszMin, nCoordsMin);
29✔
2530
        msFreeCharArray(papszMax, nCoordsMax);
29✔
2531
      }
2532

2533
      msFreeCharArray(papszCoords, nCoords);
29✔
2534
    } else {
2535
      psCoord1 = CPLGetXMLNode(psBox, "coord");
1✔
2536
      psCoord2 = FLTGetNextSibblingNode(psCoord1);
2537
      if (psCoord1 && psCoord2 && strcmp(psCoord2->pszValue, "coord") == 0) {
1✔
2538
        const char *pszX = CPLGetXMLValue(psCoord1, "X", NULL);
1✔
2539
        const char *pszY = CPLGetXMLValue(psCoord1, "Y", NULL);
1✔
2540
        if (pszX && pszY) {
1✔
2541
          minx = atof(pszX);
2542
          miny = atof(pszY);
2543

2544
          pszX = CPLGetXMLValue(psCoord2, "X", NULL);
1✔
2545
          pszY = CPLGetXMLValue(psCoord2, "Y", NULL);
1✔
2546
          if (pszX && pszY) {
1✔
2547
            maxx = atof(pszX);
2548
            maxy = atof(pszY);
2549
            bCoordinatesValid = 1;
2550
          }
2551
        }
2552
      }
2553
    }
2554
  }
2555

2556
  if (bCoordinatesValid) {
30✔
2557
    psBbox->minx = minx;
30✔
2558
    psBbox->miny = miny;
30✔
2559

2560
    psBbox->maxx = maxx;
30✔
2561
    psBbox->maxy = maxy;
30✔
2562
  }
2563

2564
  return bCoordinatesValid;
30✔
2565
}
2566
/************************************************************************/
2567
/*                           FLTParseGMLEnvelope                        */
2568
/*                                                                      */
2569
/*      Utility function to parse a gml:Envelope (used for SOS and FE1.1)*/
2570
/************************************************************************/
2571
int FLTParseGMLEnvelope(CPLXMLNode *psRoot, rectObj *psBbox, char **ppszSRS) {
14✔
2572
  CPLXMLNode *psUpperCorner = NULL, *psLowerCorner = NULL;
2573
  const char *pszLowerCorner = NULL, *pszUpperCorner = NULL;
2574
  int bValid = 0;
2575
  char **tokens;
2576
  int n;
2577

2578
  if (psRoot && psBbox && psRoot->eType == CXT_Element &&
14✔
2579
      EQUAL(psRoot->pszValue, "Envelope")) {
14✔
2580
    /*Get the srs if available*/
2581
    if (ppszSRS) {
14✔
2582
      const char *pszSRS = CPLGetXMLValue(psRoot, "srsName", NULL);
14✔
2583
      if (pszSRS != NULL)
14✔
2584
        *ppszSRS = msStrdup(pszSRS);
11✔
2585
    }
2586
    psLowerCorner = CPLSearchXMLNode(psRoot, "lowerCorner");
14✔
2587
    psUpperCorner = CPLSearchXMLNode(psRoot, "upperCorner");
14✔
2588

2589
    if (psLowerCorner && psUpperCorner) {
14✔
2590
      pszLowerCorner = CPLGetXMLValue(psLowerCorner, NULL, NULL);
14✔
2591
      pszUpperCorner = CPLGetXMLValue(psUpperCorner, NULL, NULL);
14✔
2592

2593
      if (pszLowerCorner && pszUpperCorner) {
14✔
2594
        tokens = msStringSplit(pszLowerCorner, ' ', &n);
14✔
2595
        if (tokens && n >= 2) {
14✔
2596
          psBbox->minx = atof(tokens[0]);
14✔
2597
          psBbox->miny = atof(tokens[1]);
14✔
2598

2599
          msFreeCharArray(tokens, n);
14✔
2600

2601
          tokens = msStringSplit(pszUpperCorner, ' ', &n);
14✔
2602
          if (tokens && n >= 2) {
14✔
2603
            psBbox->maxx = atof(tokens[0]);
14✔
2604
            psBbox->maxy = atof(tokens[1]);
14✔
2605
            bValid = 1;
2606
          }
2607
        }
2608
        msFreeCharArray(tokens, n);
14✔
2609
      }
2610
    }
2611
  }
2612

2613
  return bValid;
14✔
2614
}
2615

2616
/************************************************************************/
2617
/*                        FLTNeedSRSSwapping                            */
2618
/************************************************************************/
2619

2620
static int FLTNeedSRSSwapping(mapObj *map, const char *pszSRS) {
20✔
2621
  int bNeedSwapping = MS_FALSE;
2622
  projectionObj sProjTmp;
2623
  msInitProjection(&sProjTmp);
20✔
2624
  if (map) {
20✔
2625
    msProjectionInheritContextFrom(&sProjTmp, &map->projection);
20✔
2626
  }
2627
  if (msLoadProjectionStringEPSG(&sProjTmp, pszSRS) == 0) {
20✔
2628
    bNeedSwapping = msIsAxisInvertedProj(&sProjTmp);
20✔
2629
  }
2630
  msFreeProjection(&sProjTmp);
20✔
2631
  return bNeedSwapping;
20✔
2632
}
2633

2634
/************************************************************************/
2635
/*                      FLTDoAxisSwappingIfNecessary                    */
2636
/*                                                                      */
2637
/*      Explore all geometries and BBOX to do axis swapping when the    */
2638
/*      SRS requires it. If no explicit SRS is attached to the geometry */
2639
/*      the bDefaultSRSNeedsAxisSwapping is taken into account. The     */
2640
/*      caller will have to determine its value from a more general     */
2641
/*      context.                                                        */
2642
/************************************************************************/
2643
void FLTDoAxisSwappingIfNecessary(mapObj *map, FilterEncodingNode *psFilterNode,
374✔
2644
                                  int bDefaultSRSNeedsAxisSwapping) {
2645
  if (psFilterNode == NULL)
625✔
2646
    return;
2647

2648
  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
279✔
2649
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_BBOX) {
28✔
2650
    rectObj *rect = (rectObj *)psFilterNode->psRightNode->pOther;
17✔
2651
    const char *pszSRS = psFilterNode->pszSRS;
17✔
2652
    if ((pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
17✔
2653
        (pszSRS == NULL && bDefaultSRSNeedsAxisSwapping)) {
5✔
2654
      double tmp;
2655

2656
      tmp = rect->minx;
13✔
2657
      rect->minx = rect->miny;
13✔
2658
      rect->miny = tmp;
13✔
2659

2660
      tmp = rect->maxx;
13✔
2661
      rect->maxx = rect->maxy;
13✔
2662
      rect->maxy = tmp;
13✔
2663
    }
2664
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
262✔
2665
             FLTIsGeometryFilterNodeType(psFilterNode->psRightNode->eType)) {
11✔
2666
    shapeObj *shape = (shapeObj *)(psFilterNode->psRightNode->pOther);
11✔
2667
    const char *pszSRS = psFilterNode->pszSRS;
11✔
2668
    if ((pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
11✔
2669
        (pszSRS == NULL && bDefaultSRSNeedsAxisSwapping)) {
10✔
2670
      msAxisSwapShape(shape);
8✔
2671
    }
2672
  } else {
2673
    FLTDoAxisSwappingIfNecessary(map, psFilterNode->psLeftNode,
251✔
2674
                                 bDefaultSRSNeedsAxisSwapping);
2675
    FLTDoAxisSwappingIfNecessary(map, psFilterNode->psRightNode,
251✔
2676
                                 bDefaultSRSNeedsAxisSwapping);
2677
  }
2678
}
2679

2680
static void FLTReplacePropertyName(FilterEncodingNode *psFilterNode,
99✔
2681
                                   const char *pszOldName,
2682
                                   const char *pszNewName) {
2683
  if (psFilterNode && pszOldName && pszNewName) {
128✔
2684
    if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
128✔
2685
      if (psFilterNode->pszValue &&
38✔
2686
          strcasecmp(psFilterNode->pszValue, pszOldName) == 0) {
38✔
2687
        msFree(psFilterNode->pszValue);
11✔
2688
        psFilterNode->pszValue = msStrdup(pszNewName);
11✔
2689
      }
2690
    }
2691
    if (psFilterNode->psLeftNode)
128✔
2692
      FLTReplacePropertyName(psFilterNode->psLeftNode, pszOldName, pszNewName);
57✔
2693
    if (psFilterNode->psRightNode)
128✔
2694
      FLTReplacePropertyName(psFilterNode->psRightNode, pszOldName, pszNewName);
2695
  }
2696
}
99✔
2697

2698
static int FLTIsGMLDefaultProperty(const char *pszName) {
367✔
2699
  return (strcmp(pszName, "gml:name") == 0 ||
702✔
2700
          strcmp(pszName, "gml:description") == 0 ||
335✔
2701
          strcmp(pszName, "gml:descriptionReference") == 0 ||
335✔
2702
          strcmp(pszName, "gml:identifier") == 0 ||
335✔
2703
          strcmp(pszName, "gml:boundedBy") == 0 ||
702✔
2704
          strcmp(pszName, "@gml:id") == 0);
328✔
2705
}
2706

2707
static void
2708
FLTStripNameSpacesFromPropertyName(FilterEncodingNode *psFilterNode) {
1,524✔
2709
  char **tokens = NULL;
2710
  int n = 0;
1,524✔
2711

2712
  if (psFilterNode) {
1,524✔
2713

2714
    if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
1,849✔
2715
        psFilterNode->psLeftNode != NULL &&
325✔
2716
        psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
1,849✔
2717
        FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
325✔
2718
      return;
11✔
2719
    }
2720

2721
    if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
1,513✔
2722
      if (psFilterNode->pszValue && strstr(psFilterNode->pszValue, ":")) {
459✔
2723
        tokens = msStringSplit(psFilterNode->pszValue, ':', &n);
2✔
2724
        if (tokens && n == 2) {
2✔
2725
          msFree(psFilterNode->pszValue);
1✔
2726
          psFilterNode->pszValue = msStrdup(tokens[1]);
1✔
2727
        }
2728
        msFreeCharArray(tokens, n);
2✔
2729
      }
2730
    }
2731
    if (psFilterNode->psLeftNode)
1,513✔
2732
      FLTStripNameSpacesFromPropertyName(psFilterNode->psLeftNode);
562✔
2733
    if (psFilterNode->psRightNode)
1,513✔
2734
      FLTStripNameSpacesFromPropertyName(psFilterNode->psRightNode);
536✔
2735
  }
2736
}
2737

2738
static void FLTRemoveGroupName(FilterEncodingNode *psFilterNode,
4✔
2739
                               gmlGroupListObj *groupList) {
2740
  int i;
2741

2742
  if (psFilterNode) {
6✔
2743

2744
    if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
6✔
2745
      if (psFilterNode->pszValue != NULL) {
2✔
2746
        const char *pszPropertyName = psFilterNode->pszValue;
2747
        const char *pszSlash = strchr(pszPropertyName, '/');
2748
        if (pszSlash != NULL) {
2✔
2749
          const char *pszColon = strchr(pszPropertyName, ':');
2750
          if (pszColon != NULL && pszColon < pszSlash)
2✔
2751
            pszPropertyName = pszColon + 1;
1✔
2752
          for (i = 0; i < groupList->numgroups; i++) {
2✔
2753
            const char *pszGroupName = groupList->groups[i].name;
2✔
2754
            size_t nGroupNameLen = strlen(pszGroupName);
2✔
2755
            if (strncasecmp(pszPropertyName, pszGroupName, nGroupNameLen) ==
2✔
2756
                    0 &&
2✔
2757
                pszPropertyName[nGroupNameLen] == '/') {
2✔
2758
              char *pszTmp;
2759
              pszPropertyName = pszPropertyName + nGroupNameLen + 1;
2✔
2760
              pszColon = strchr(pszPropertyName, ':');
2761
              if (pszColon != NULL)
2✔
2762
                pszPropertyName = pszColon + 1;
1✔
2763
              pszTmp = msStrdup(pszPropertyName);
2✔
2764
              msFree(psFilterNode->pszValue);
2✔
2765
              psFilterNode->pszValue = pszTmp;
2✔
2766
              break;
2✔
2767
            }
2768
          }
2769
        }
2770
      }
2771
    }
2772

2773
    if (psFilterNode->psLeftNode)
6✔
2774
      FLTRemoveGroupName(psFilterNode->psLeftNode, groupList);
2✔
2775
    if (psFilterNode->psRightNode)
6✔
2776
      FLTRemoveGroupName(psFilterNode->psRightNode, groupList);
2777
  }
2778
}
4✔
2779

2780
/************************************************************************/
2781
/*                    FLTPreParseFilterForAliasAndGroup                 */
2782
/*                                                                      */
2783
/*      Utility function to replace aliased' and grouped attributes     */
2784
/*      with their internal name.                                       */
2785
/************************************************************************/
2786
void FLTPreParseFilterForAliasAndGroup(FilterEncodingNode *psFilterNode,
426✔
2787
                                       mapObj *map, int i,
2788
                                       const char *namespaces) {
2789
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
2790
    defined(USE_SOS_SVR)
2791
  if (psFilterNode && map && i >= 0 && i < map->numlayers) {
426✔
2792
    /*strip name spaces before hand*/
2793
    FLTStripNameSpacesFromPropertyName(psFilterNode);
426✔
2794

2795
    layerObj *lp = GET_LAYER(map, i);
426✔
2796
    int layerWasOpened = msLayerIsOpen(lp);
426✔
2797
    if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
426✔
2798

2799
      /* Remove group names from property names if using groupname/itemname
2800
       * syntax */
2801
      gmlGroupListObj *groupList = msGMLGetGroups(lp, namespaces);
426✔
2802
      if (groupList && groupList->numgroups > 0)
426✔
2803
        FLTRemoveGroupName(psFilterNode, groupList);
2✔
2804
      msGMLFreeGroups(groupList);
426✔
2805

2806
      for (i = 0; i < lp->numitems; i++) {
5,212✔
2807
        if (!lp->items[i] || strlen(lp->items[i]) <= 0)
4,786✔
UNCOV
2808
          continue;
×
2809
        char szTmp[256];
2810
        snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[i]);
2811
        const char *pszFullName =
2812
            msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
4,786✔
2813
        if (pszFullName) {
4,786✔
2814
          FLTReplacePropertyName(psFilterNode, pszFullName, lp->items[i]);
42✔
2815
        }
2816
      }
2817
      if (!layerWasOpened) /* do not close the layer if it has been opened
426✔
2818
                              somewhere else (paging?) */
2819
        msLayerClose(lp);
67✔
2820
    }
2821
  }
2822
#else
2823
  msSetError(MS_MISCERR, "OWS support is not available.",
2824
             "FLTPreParseFilterForAlias()");
2825

2826
#endif
2827
}
426✔
2828

2829
/************************************************************************/
2830
/*                        FLTCheckFeatureIdFilters                      */
2831
/*                                                                      */
2832
/*      Check that FeatureId filters match features in the active       */
2833
/*      layer.                                                          */
2834
/************************************************************************/
2835
int FLTCheckFeatureIdFilters(FilterEncodingNode *psFilterNode, mapObj *map,
831✔
2836
                             int i) {
2837
  int status = MS_SUCCESS;
2838

2839
  if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
1,267✔
2840
    char **tokens;
2841
    int nTokens = 0;
39✔
2842
    layerObj *lp;
2843
    int j;
2844

2845
    lp = GET_LAYER(map, i);
39✔
2846
    tokens = msStringSplit(psFilterNode->pszValue, ',', &nTokens);
39✔
2847
    for (j = 0; j < nTokens; j++) {
79✔
2848
      const char *pszId = tokens[j];
41✔
2849
      const char *pszDot = strrchr(pszId, '.');
2850
      if (pszDot) {
41✔
2851
        if (static_cast<size_t>(pszDot - pszId) != strlen(lp->name) ||
30✔
2852
            strncasecmp(pszId, lp->name, strlen(lp->name)) != 0) {
29✔
2853
          msSetError(MS_MISCERR,
1✔
2854
                     "Feature id %s not consistent with feature type name %s.",
2855
                     "FLTPreParseFilterForAlias()", pszId, lp->name);
2856
          status = MS_FAILURE;
2857
          break;
2858
        }
2859
      }
2860
    }
2861
    msFreeCharArray(tokens, nTokens);
39✔
2862
  }
2863

2864
  if (psFilterNode->psLeftNode) {
1,267✔
2865
    status = FLTCheckFeatureIdFilters(psFilterNode->psLeftNode, map, i);
472✔
2866
    if (status == MS_SUCCESS) {
472✔
2867
      if (psFilterNode->psRightNode)
472✔
2868
        status = FLTCheckFeatureIdFilters(psFilterNode->psRightNode, map, i);
2869
    }
2870
  }
2871
  return status;
831✔
2872
}
2873

2874
/************************************************************************/
2875
/*                        FLTCheckInvalidOperand                        */
2876
/*                                                                      */
2877
/*      Check that the operand of a comparison operator is valid        */
2878
/*      Currently only detects use of boundedBy in a binary comparison  */
2879
/************************************************************************/
2880
int FLTCheckInvalidOperand(FilterEncodingNode *psFilterNode) {
193✔
2881
  int status = MS_SUCCESS;
2882

2883
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
261✔
2884
      psFilterNode->psLeftNode != NULL &&
35✔
2885
      psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
35✔
2886
    if (strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") == 0 &&
35✔
2887
        strcmp(psFilterNode->pszValue, "PropertyIsNull") != 0 &&
3✔
2888
        strcmp(psFilterNode->pszValue, "PropertyIsNil") != 0) {
2✔
2889
      msSetError(MS_MISCERR, "Operand '%s' is invalid in comparison.",
1✔
2890
                 "FLTCheckInvalidOperand()",
2891
                 psFilterNode->psLeftNode->pszValue);
2892
      return MS_FAILURE;
1✔
2893
    }
2894
  }
2895
  if (psFilterNode->psLeftNode) {
260✔
2896
    status = FLTCheckInvalidOperand(psFilterNode->psLeftNode);
96✔
2897
    if (status == MS_SUCCESS) {
96✔
2898
      if (psFilterNode->psRightNode)
96✔
2899
        status = FLTCheckInvalidOperand(psFilterNode->psRightNode);
2900
    }
2901
  }
2902
  return status;
2903
}
2904

2905
/************************************************************************/
2906
/*                       FLTProcessPropertyIsNull                       */
2907
/*                                                                      */
2908
/*      HACK for PropertyIsNull processing. PostGIS & Spatialite only   */
2909
/*      for now.                                                        */
2910
/************************************************************************/
2911
int FLTProcessPropertyIsNull(FilterEncodingNode *psFilterNode, mapObj *map,
831✔
2912
                             int i) {
2913
  int status = MS_SUCCESS;
2914

2915
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
1,511✔
2916
      psFilterNode->psLeftNode != NULL &&
244✔
2917
      psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
244✔
2918
      strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
1,511✔
2919
      !FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
16✔
2920
    layerObj *lp;
2921
    int layerWasOpened;
2922

2923
    lp = GET_LAYER(map, i);
7✔
2924
    layerWasOpened = msLayerIsOpen(lp);
7✔
2925

2926
    /* Horrible HACK to compensate for the lack of null testing in MapServer */
2927
    if (lp->connectiontype == MS_POSTGIS ||
7✔
2928
        (lp->connectiontype == MS_OGR && msOGRSupportsIsNull(lp))) {
4✔
2929
      msFree(psFilterNode->pszValue);
5✔
2930
      psFilterNode->pszValue = msStrdup("PropertyIsEqualTo");
5✔
2931
      psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
5✔
2932
      psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
5✔
2933
      psFilterNode->psRightNode->pszValue = msStrdup("_MAPSERVER_NULL_");
5✔
2934
    }
2935

2936
    if (!layerWasOpened) /* do not close the layer if it has been opened
7✔
2937
                            somewhere else (paging?) */
UNCOV
2938
      msLayerClose(lp);
×
2939
  }
2940

2941
  if (psFilterNode->psLeftNode) {
1,267✔
2942
    status = FLTProcessPropertyIsNull(psFilterNode->psLeftNode, map, i);
472✔
2943
    if (status == MS_SUCCESS) {
472✔
2944
      if (psFilterNode->psRightNode)
472✔
2945
        status = FLTProcessPropertyIsNull(psFilterNode->psRightNode, map, i);
2946
    }
2947
  }
2948
  return status;
831✔
2949
}
2950

2951
/************************************************************************/
2952
/*                        FLTCheckInvalidProperty                       */
2953
/*                                                                      */
2954
/*      Check that property names are known                             */
2955
/************************************************************************/
2956
int FLTCheckInvalidProperty(FilterEncodingNode *psFilterNode, mapObj *map,
181✔
2957
                            int i) {
2958
  int status = MS_SUCCESS;
2959

2960
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
249✔
2961
      psFilterNode->psLeftNode != NULL &&
34✔
2962
      psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
34✔
2963
    layerObj *lp;
2964
    int layerWasOpened;
2965
    int bFound = MS_FALSE;
2966

2967
    if ((strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
57✔
2968
         strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0) &&
34✔
2969
        FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
16✔
2970
      return MS_SUCCESS;
2971
    }
2972

2973
    lp = GET_LAYER(map, i);
24✔
2974
    layerWasOpened = msLayerIsOpen(lp);
24✔
2975
    if ((layerWasOpened || msLayerOpen(lp) == MS_SUCCESS) &&
48✔
2976
        msLayerGetItems(lp) == MS_SUCCESS) {
24✔
2977
      int i;
2978
      gmlItemListObj *items = msGMLGetItems(lp, "G");
24✔
2979
      for (i = 0; i < items->numitems; i++) {
174✔
2980
        if (!items->items[i].name || strlen(items->items[i].name) == 0 ||
173✔
2981
            !items->items[i].visible)
173✔
2982
          continue;
13✔
2983
        if (strcasecmp(items->items[i].name,
160✔
2984
                       psFilterNode->psLeftNode->pszValue) == 0) {
160✔
2985
          bFound = MS_TRUE;
2986
          break;
2987
        }
2988
      }
2989
      msGMLFreeItems(items);
24✔
2990
    }
2991

2992
    if (!layerWasOpened) /* do not close the layer if it has been opened
24✔
2993
                            somewhere else (paging?) */
UNCOV
2994
      msLayerClose(lp);
×
2995

2996
    if (!bFound) {
24✔
2997
      msSetError(MS_MISCERR, "Property '%s' is unknown.",
1✔
2998
                 "FLTCheckInvalidProperty()",
2999
                 psFilterNode->psLeftNode->pszValue);
1✔
3000
      return MS_FAILURE;
1✔
3001
    }
3002
  }
3003

3004
  if (psFilterNode->psLeftNode) {
238✔
3005
    status = FLTCheckInvalidProperty(psFilterNode->psLeftNode, map, i);
85✔
3006
    if (status == MS_SUCCESS) {
85✔
3007
      if (psFilterNode->psRightNode)
85✔
3008
        status = FLTCheckInvalidProperty(psFilterNode->psRightNode, map, i);
3009
    }
3010
  }
3011
  return status;
3012
}
3013

3014
/************************************************************************/
3015
/*                           FLTSimplify                                */
3016
/*                                                                      */
3017
/*      Simplify the expression by removing parts that evaluate to      */
3018
/*      constants.                                                      */
3019
/*      The passed psFilterNode is potentially consumed by the function */
3020
/*      and replaced by the returned value.                             */
3021
/*      If the function returns NULL, *pnEvaluation = MS_FALSE means    */
3022
/*      that  the filter evaluates to FALSE, or MS_TRUE that it         */
3023
/*      evaluates to TRUE                                               */
3024
/************************************************************************/
3025
FilterEncodingNode *FLTSimplify(FilterEncodingNode *psFilterNode,
131✔
3026
                                int *pnEvaluation) {
3027
  *pnEvaluation = -1;
131✔
3028

3029
  /* There are no nullable or nillable property in WFS currently */
3030
  /* except gml:name or gml:description that are null */
3031
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
131✔
3032
      (strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
33✔
3033
       strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0) &&
23✔
3034
      psFilterNode->psLeftNode != NULL &&
15✔
3035
      psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
15✔
3036
    if (strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
10✔
3037
        FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) &&
10✔
3038
        strcmp(psFilterNode->psLeftNode->pszValue, "@gml:id") != 0 &&
24✔
3039
        strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") != 0)
9✔
3040
      *pnEvaluation = MS_TRUE;
8✔
3041
    else
3042
      *pnEvaluation = MS_FALSE;
7✔
3043
    FLTFreeFilterEncodingNode(psFilterNode);
15✔
3044
    return NULL;
15✔
3045
  }
3046

3047
  if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL &&
116✔
3048
      strcasecmp(psFilterNode->pszValue, "NOT") == 0 &&
24✔
3049
      psFilterNode->psLeftNode != NULL) {
11✔
3050
    int nEvaluation;
3051
    psFilterNode->psLeftNode =
11✔
3052
        FLTSimplify(psFilterNode->psLeftNode, &nEvaluation);
11✔
3053
    if (psFilterNode->psLeftNode == NULL) {
11✔
3054
      *pnEvaluation = 1 - nEvaluation;
5✔
3055
      FLTFreeFilterEncodingNode(psFilterNode);
5✔
3056
      return NULL;
5✔
3057
    }
3058
  }
3059

3060
  if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL &&
111✔
3061
      (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
19✔
3062
       strcasecmp(psFilterNode->pszValue, "OR") == 0) &&
12✔
3063
      psFilterNode->psLeftNode != NULL && psFilterNode->psRightNode != NULL) {
13✔
3064
    FilterEncodingNode *psOtherNode;
3065
    int nEvaluation;
3066
    int nExpectedValForFastExit;
3067
    psFilterNode->psLeftNode =
13✔
3068
        FLTSimplify(psFilterNode->psLeftNode, &nEvaluation);
13✔
3069

3070
    if (strcasecmp(psFilterNode->pszValue, "AND") == 0)
13✔
3071
      nExpectedValForFastExit = MS_FALSE;
3072
    else
3073
      nExpectedValForFastExit = MS_TRUE;
3074

3075
    if (psFilterNode->psLeftNode == NULL) {
13✔
3076
      if (nEvaluation == nExpectedValForFastExit) {
3✔
3077
        *pnEvaluation = nEvaluation;
2✔
3078
        FLTFreeFilterEncodingNode(psFilterNode);
2✔
3079
        return NULL;
8✔
3080
      }
3081
      psOtherNode = psFilterNode->psRightNode;
1✔
3082
      psFilterNode->psRightNode = NULL;
1✔
3083
      FLTFreeFilterEncodingNode(psFilterNode);
1✔
3084
      return FLTSimplify(psOtherNode, pnEvaluation);
1✔
3085
    }
3086

3087
    psFilterNode->psRightNode =
10✔
3088
        FLTSimplify(psFilterNode->psRightNode, &nEvaluation);
10✔
3089
    if (psFilterNode->psRightNode == NULL) {
10✔
3090
      if (nEvaluation == nExpectedValForFastExit) {
3✔
3091
        *pnEvaluation = nEvaluation;
2✔
3092
        FLTFreeFilterEncodingNode(psFilterNode);
2✔
3093
        return NULL;
2✔
3094
      }
3095
      psOtherNode = psFilterNode->psLeftNode;
1✔
3096
      psFilterNode->psLeftNode = NULL;
1✔
3097
      FLTFreeFilterEncodingNode(psFilterNode);
1✔
3098
      return FLTSimplify(psOtherNode, pnEvaluation);
1✔
3099
    }
3100
  }
3101

3102
  return psFilterNode;
3103
}
3104

3105
#ifdef USE_LIBXML2
3106

3107
xmlNodePtr FLTGetCapabilities(xmlNsPtr psNsParent, xmlNsPtr psNsOgc,
24✔
3108
                              int bTemporal) {
3109
  xmlNodePtr psRootNode = NULL, psNode = NULL, psSubNode = NULL,
3110
             psSubSubNode = NULL;
3111

3112
  psRootNode = xmlNewNode(psNsParent, BAD_CAST "Filter_Capabilities");
24✔
3113

3114
  psNode =
3115
      xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Spatial_Capabilities", NULL);
24✔
3116

3117
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "GeometryOperands", NULL);
24✔
3118
  xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
24✔
3119
              BAD_CAST "gml:Point");
3120
  xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
24✔
3121
              BAD_CAST "gml:LineString");
3122
  xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
24✔
3123
              BAD_CAST "gml:Polygon");
3124
  xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
24✔
3125
              BAD_CAST "gml:Envelope");
3126

3127
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "SpatialOperators", NULL);
24✔
3128
#ifdef USE_GEOS
3129
  psSubSubNode =
3130
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3131
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Equals");
24✔
3132
  psSubSubNode =
3133
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3134
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Disjoint");
24✔
3135
  psSubSubNode =
3136
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3137
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Touches");
24✔
3138
  psSubSubNode =
3139
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3140
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Within");
24✔
3141
  psSubSubNode =
3142
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3143
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Overlaps");
24✔
3144
  psSubSubNode =
3145
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3146
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Crosses");
24✔
3147
  psSubSubNode =
3148
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3149
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Intersects");
24✔
3150
  psSubSubNode =
3151
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3152
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Contains");
24✔
3153
  psSubSubNode =
3154
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3155
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "DWithin");
24✔
3156
  psSubSubNode =
3157
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3158
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Beyond");
24✔
3159
#endif
3160
  psSubSubNode =
3161
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
24✔
3162
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "BBOX");
24✔
3163

3164
  if (bTemporal) {
24✔
3165
    psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Temporal_Capabilities",
4✔
3166
                         NULL);
3167
    psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperands", NULL);
4✔
3168
    xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand",
4✔
3169
                BAD_CAST "gml:TimePeriod");
3170
    xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand",
4✔
3171
                BAD_CAST "gml:TimeInstant");
3172

3173
    psSubNode =
3174
        xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperators", NULL);
4✔
3175
    psSubSubNode =
3176
        xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperator", NULL);
4✔
3177
    xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "TM_Equals");
4✔
3178
  }
3179
  psNode =
3180
      xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Scalar_Capabilities", NULL);
24✔
3181
  xmlNewChild(psNode, psNsOgc, BAD_CAST "LogicalOperators", NULL);
24✔
3182
  psNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperators", NULL);
24✔
3183
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
24✔
3184
              BAD_CAST "LessThan");
3185
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
24✔
3186
              BAD_CAST "GreaterThan");
3187
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
24✔
3188
              BAD_CAST "LessThanEqualTo");
3189
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
24✔
3190
              BAD_CAST "GreaterThanEqualTo");
3191
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
24✔
3192
              BAD_CAST "EqualTo");
3193
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
24✔
3194
              BAD_CAST "NotEqualTo");
3195
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Like");
24✔
3196
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
24✔
3197
              BAD_CAST "Between");
3198

3199
  psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Id_Capabilities", NULL);
24✔
3200
  xmlNewChild(psNode, psNsOgc, BAD_CAST "EID", NULL);
24✔
3201
  xmlNewChild(psNode, psNsOgc, BAD_CAST "FID", NULL);
24✔
3202
  return psRootNode;
24✔
3203
}
3204
#endif
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