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

MapServer / MapServer / 22113288002

17 Feb 2026 07:49PM UTC coverage: 41.892% (+0.09%) from 41.804%
22113288002

push

github

web-flow
Merge pull request #7433 from rouault/ogcapi_part3_queryables

OGCAPI Features Part 3 Filtering: add support for 'Queryables' and 'Queryables as query parameter'

224 of 245 new or added lines in 4 files covered. (91.43%)

328 existing lines in 4 files now uncovered.

63165 of 150782 relevant lines covered (41.89%)

25378.38 hits per line

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

75.71
/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) {
355✔
52
  if (pszValue != NULL && *pszValue != '\0' && !isspace(*pszValue)) {
355✔
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));
355✔
75
    if (p != pszValue && *p == '\0')
355✔
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;
×
UNCOV
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) {
×
UNCOV
408
  if (FLTValidForBBoxFilter(psNode)) {
×
UNCOV
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 &&
×
UNCOV
414
        FLTNumberOfFilterType(psNode, "Touches") == 0 &&
×
UNCOV
415
        FLTNumberOfFilterType(psNode, "Crosses") == 0 &&
×
416
        FLTNumberOfFilterType(psNode, "Within") == 0 &&
×
417
        FLTNumberOfFilterType(psNode, "Contains") == 0 &&
×
UNCOV
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
    }
UNCOV
460
    *ppsTopBBOX = NULL;
×
UNCOV
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(layerObj *lp, rectObj *rect,
659✔
487
                                      const char *metadata_namespaces) {
488
  const char *pszUseDefaultExtent =
489
      msOWSLookupMetadata(&(lp->metadata), metadata_namespaces,
659✔
490
                          "use_default_extent_for_getfeature");
491
  if (pszUseDefaultExtent && !CSLTestBoolean(pszUseDefaultExtent) &&
659✔
492
      (lp->connectiontype == MS_OGR || lp->connectiontype == MS_POSTGIS ||
11✔
493
       ((lp->connectiontype == MS_PLUGIN) &&
×
UNCOV
494
        (strstr(lp->plugin_library, "msplugin_mssql2008") != NULL)))) {
×
495
    const rectObj rectInvalid = MS_INIT_INVALID_RECT;
496
    *rect = rectInvalid;
6✔
497
    return MS_TRUE;
498
  }
499
  return MS_FALSE;
500
}
501

502
/************************************************************************/
503
/*                   FLTLayerApplyPlainFilterToLayer                    */
504
/*                                                                      */
505
/* Helper function for layer virtual table architecture                 */
506
/************************************************************************/
507
int FLTLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
390✔
508
                                    int iLayerIndex) {
509
  char *pszExpression = NULL;
510
  int status = MS_FALSE;
511
  layerObj *lp = GET_LAYER(map, iLayerIndex);
390✔
512

513
  pszExpression = FLTGetCommonExpression(psNode, lp);
390✔
514
  if (pszExpression) {
390✔
515
    FilterEncodingNode *psTopBBOX;
516
    rectObj rect = map->extent;
390✔
517

518
    FLTLayerSetInvalidRectIfSupported(lp, &rect, "OF");
390✔
519

520
    psTopBBOX = FLTGetTopBBOX(psNode);
521
    if (psTopBBOX) {
390✔
522
      int can_remove_expression = MS_TRUE;
523
      const char *pszEPSG = FLTGetBBOX(psNode, &rect);
38✔
524
      if (pszEPSG && map->projection.numargs > 0) {
38✔
525
        projectionObj sProjTmp;
526
        msInitProjection(&sProjTmp);
37✔
527
        msProjectionInheritContextFrom(&sProjTmp, &map->projection);
37✔
528
        /* Use the non EPSG variant since axis swapping is done in
529
         * FLTDoAxisSwappingIfNecessary */
530
        if (msLoadProjectionString(&sProjTmp, pszEPSG) == 0) {
37✔
531
          rectObj oldRect = rect;
37✔
532
          msProjectRect(&sProjTmp, &map->projection, &rect);
37✔
533
          /* If reprojection is involved, do not remove the expression */
534
          if (rect.minx != oldRect.minx || rect.miny != oldRect.miny ||
37✔
535
              rect.maxx != oldRect.maxx || rect.maxy != oldRect.maxy) {
32✔
536
            can_remove_expression = MS_FALSE;
537
          }
538
        }
539
        msFreeProjection(&sProjTmp);
37✔
540
      }
541

542
      /* Small optimization: if the query is just a BBOX, then do a */
543
      /* msQueryByRect() */
544
      if (psTopBBOX == psNode && can_remove_expression) {
38✔
545
        msFree(pszExpression);
25✔
546
        pszExpression = NULL;
547
      }
548
    }
549

550
    if (map->debug == MS_DEBUGLEVEL_VVV) {
390✔
UNCOV
551
      if (pszExpression)
×
552
        msDebug("FLTLayerApplyPlainFilterToLayer(): %s, "
×
553
                "rect=%.15g,%.15g,%.15g,%.15g\n",
554
                pszExpression, rect.minx, rect.miny, rect.maxx, rect.maxy);
555
      else
UNCOV
556
        msDebug(
×
557
            "FLTLayerApplyPlainFilterToLayer(): rect=%.15g,%.15g,%.15g,%.15g\n",
558
            rect.minx, rect.miny, rect.maxx, rect.maxy);
559
    }
560

561
    status = FLTApplyFilterToLayerCommonExpressionWithRect(map, iLayerIndex,
390✔
562
                                                           pszExpression, rect);
563
    msFree(pszExpression);
390✔
564
  }
565

566
  return status;
390✔
567
}
568

569
/************************************************************************/
570
/*            FilterNode *FLTPaserFilterEncoding(char *szXMLString)     */
571
/*                                                                      */
572
/*      Parses an Filter Encoding XML string and creates a              */
573
/*      FilterEncodingNodes corresponding to the string.                */
574
/*      Returns a pointer to the first node or NULL if                  */
575
/*      unsuccessful.                                                  */
576
/*      Calling function should use FreeFilterEncodingNode function     */
577
/*      to free memory.                                                */
578
/************************************************************************/
579
FilterEncodingNode *FLTParseFilterEncoding(const char *szXMLString) {
460✔
580
  CPLXMLNode *psRoot = NULL, *psChild = NULL, *psFilter = NULL;
581
  FilterEncodingNode *psFilterNode = NULL;
582

583
  if (szXMLString == NULL || strlen(szXMLString) == 0 ||
460✔
584
      (strstr(szXMLString, "Filter") == NULL))
585
    return NULL;
586

587
  psRoot = CPLParseXMLString(szXMLString);
459✔
588

589
  if (psRoot == NULL)
459✔
590
    return NULL;
591

592
  /* strip namespaces. We srtip all name spaces (#1350)*/
593
  CPLStripXMLNamespace(psRoot, NULL, 1);
456✔
594

595
  /* -------------------------------------------------------------------- */
596
  /*      get the root element (Filter).                                  */
597
  /* -------------------------------------------------------------------- */
598
  psFilter = CPLGetXMLNode(psRoot, "=Filter");
456✔
599
  if (!psFilter) {
456✔
600
    CPLDestroyXMLNode(psRoot);
1✔
601
    return NULL;
1✔
602
  }
603

604
  psChild = psFilter->psChild;
455✔
605
  while (psChild) {
467✔
606
    if (FLTIsSupportedFilterType(psChild)) {
465✔
607
      psFilterNode = FLTCreateFilterEncodingNode();
453✔
608
      FLTInsertElementInNode(psFilterNode, psChild);
453✔
609
      break;
453✔
610
    } else
611
      psChild = psChild->psNext;
12✔
612
  }
613

614
  CPLDestroyXMLNode(psRoot);
455✔
615

616
  /* -------------------------------------------------------------------- */
617
  /*      validate the node tree to make sure that all the nodes are valid.*/
618
  /* -------------------------------------------------------------------- */
619
  if (!FLTValidFilterNode(psFilterNode)) {
455✔
620
    FLTFreeFilterEncodingNode(psFilterNode);
14✔
621
    return NULL;
14✔
622
  }
623

624
  return psFilterNode;
625
}
626

627
/************************************************************************/
628
/*      int FLTValidFilterNode(FilterEncodingNode *psFilterNode)        */
629
/*                                                                      */
630
/*      Validate that all the nodes are filled properly. We could       */
631
/*      have parts of the nodes that are correct and part which         */
632
/*      could be incorrect if the filter string sent is corrupted       */
633
/*      (eg missing a value :<PropertyName><PropertyName>)              */
634
/************************************************************************/
635
int FLTValidFilterNode(FilterEncodingNode *psFilterNode) {
1,043✔
636
  if (!psFilterNode)
1,590✔
637
    return 0;
638

639
  if (psFilterNode->eType == FILTER_NODE_TYPE_UNDEFINED)
1,588✔
640
    return 0;
641

642
  if (psFilterNode->psLeftNode) {
1,576✔
643
    const int bReturn = FLTValidFilterNode(psFilterNode->psLeftNode);
588✔
644
    if (bReturn == 0)
588✔
645
      return 0;
646
    else if (psFilterNode->psRightNode)
588✔
647
      return FLTValidFilterNode(psFilterNode->psRightNode);
648
  }
649

650
  return 1;
651
}
652

653
/************************************************************************/
654
/*                       FLTIsGeometryFilterNodeType                    */
655
/************************************************************************/
656

657
static int FLTIsGeometryFilterNodeType(int eType) {
658
  return (eType == FILTER_NODE_TYPE_GEOMETRY_POINT ||
659
          eType == FILTER_NODE_TYPE_GEOMETRY_LINE ||
504✔
660
          eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON);
661
}
662

663
/************************************************************************/
664
/*                          FLTFreeFilterEncodingNode                   */
665
/*                                                                      */
666
/*      recursive freeing of Filter Encoding nodes.                      */
667
/************************************************************************/
668
void FLTFreeFilterEncodingNode(FilterEncodingNode *psFilterNode) {
1,804✔
669
  if (psFilterNode) {
1,804✔
670
    if (psFilterNode->psLeftNode) {
1,789✔
671
      FLTFreeFilterEncodingNode(psFilterNode->psLeftNode);
643✔
672
      psFilterNode->psLeftNode = NULL;
643✔
673
    }
674
    if (psFilterNode->psRightNode) {
1,789✔
675
      FLTFreeFilterEncodingNode(psFilterNode->psRightNode);
616✔
676
      psFilterNode->psRightNode = NULL;
616✔
677
    }
678

679
    if (psFilterNode->pszSRS)
1,789✔
680
      free(psFilterNode->pszSRS);
63✔
681

682
    if (psFilterNode->pOther) {
1,789✔
683
      if (psFilterNode->pszValue != NULL &&
433✔
684
          strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
320✔
685
        FEPropertyIsLike *propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
686
        if (propIsLike->pszWildCard)
30✔
687
          free(propIsLike->pszWildCard);
30✔
688
        if (propIsLike->pszSingleChar)
30✔
689
          free(propIsLike->pszSingleChar);
30✔
690
        if (propIsLike->pszEscapeChar)
30✔
691
          free(propIsLike->pszEscapeChar);
30✔
692
      } else if (FLTIsGeometryFilterNodeType(psFilterNode->eType)) {
403✔
693
        msFreeShape((shapeObj *)(psFilterNode->pOther));
90✔
694
      }
695
      /* else */
696
      /* TODO free pOther special fields */
697
      free(psFilterNode->pOther);
433✔
698
    }
699

700
    /* Cannot free pszValue before, 'cause we are testing it above */
701
    if (psFilterNode->pszValue)
1,789✔
702
      free(psFilterNode->pszValue);
1,671✔
703

704
    free(psFilterNode);
1,789✔
705
  }
706
}
1,804✔
707

708
/************************************************************************/
709
/*                         FLTCreateFilterEncodingNode                  */
710
/*                                                                      */
711
/*      return a FilterEncoding node.                                    */
712
/************************************************************************/
713
FilterEncodingNode *FLTCreateFilterEncodingNode(void) {
1,789✔
714
  FilterEncodingNode *psFilterNode = NULL;
715

716
  psFilterNode = (FilterEncodingNode *)malloc(sizeof(FilterEncodingNode));
1,789✔
717
  psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1,789✔
718
  psFilterNode->pszValue = NULL;
1,789✔
719
  psFilterNode->pOther = NULL;
1,789✔
720
  psFilterNode->pszSRS = NULL;
1,789✔
721
  psFilterNode->psLeftNode = NULL;
1,789✔
722
  psFilterNode->psRightNode = NULL;
1,789✔
723

724
  return psFilterNode;
1,789✔
725
}
726

727
FilterEncodingNode *FLTCreateBinaryCompFilterEncodingNode(void) {
271✔
728
  FilterEncodingNode *psFilterNode = NULL;
729

730
  psFilterNode = FLTCreateFilterEncodingNode();
271✔
731
  /* used to store case sensitivity flag. Default is 0 meaning the
732
     comparing is case sensititive */
733
  psFilterNode->pOther = (int *)malloc(sizeof(int));
271✔
734
  (*(int *)(psFilterNode->pOther)) = 0;
271✔
735

736
  return psFilterNode;
271✔
737
}
738

739
/************************************************************************/
740
/*                           FLTFindGeometryNode                        */
741
/*                                                                      */
742
/************************************************************************/
743

744
static CPLXMLNode *FLTFindGeometryNode(CPLXMLNode *psXMLNode, int *pbPoint,
94✔
745
                                       int *pbLine, int *pbPolygon) {
746
  CPLXMLNode *psGMLElement = NULL;
747

748
  psGMLElement = CPLGetXMLNode(psXMLNode, "Point");
94✔
749
  if (!psGMLElement)
94✔
750
    psGMLElement = CPLGetXMLNode(psXMLNode, "PointType");
66✔
751
  if (psGMLElement)
66✔
752
    *pbPoint = 1;
28✔
753
  else {
754
    psGMLElement = CPLGetXMLNode(psXMLNode, "Polygon");
66✔
755
    if (psGMLElement)
66✔
756
      *pbPolygon = 1;
54✔
757
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiPolygon")))
12✔
758
      *pbPolygon = 1;
3✔
759
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Surface")))
9✔
UNCOV
760
      *pbPolygon = 1;
×
761
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiSurface")))
9✔
UNCOV
762
      *pbPolygon = 1;
×
763
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Box")))
9✔
UNCOV
764
      *pbPolygon = 1;
×
765
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Envelope")))
9✔
766
      *pbPolygon = 1;
1✔
767
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "LineString")))
8✔
768
      *pbLine = 1;
4✔
769
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiLineString")))
4✔
UNCOV
770
      *pbLine = 1;
×
771
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Curve")))
4✔
UNCOV
772
      *pbLine = 1;
×
773
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiCurve")))
4✔
UNCOV
774
      *pbLine = 1;
×
775
    else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "MultiPoint")))
4✔
776
      *pbPoint = 1;
2✔
777
  }
778
  return psGMLElement;
94✔
779
}
780

781
/************************************************************************/
782
/*                           FLTGetPropertyName                         */
783
/************************************************************************/
784
static const char *FLTGetPropertyName(CPLXMLNode *psXMLNode) {
494✔
785
  const char *pszPropertyName;
786

787
  pszPropertyName = CPLGetXMLValue(psXMLNode, "PropertyName", NULL);
494✔
788
  if (pszPropertyName == NULL) /* FE 2.0 ? */
494✔
789
    pszPropertyName = CPLGetXMLValue(psXMLNode, "ValueReference", NULL);
60✔
790
  return pszPropertyName;
494✔
791
}
792

793
/************************************************************************/
794
/*                          FLTGetFirstChildNode                        */
795
/************************************************************************/
796
static CPLXMLNode *FLTGetFirstChildNode(CPLXMLNode *psXMLNode) {
797
  if (psXMLNode == NULL)
798
    return NULL;
799
  psXMLNode = psXMLNode->psChild;
104✔
800
  while (psXMLNode != NULL) {
104✔
801
    if (psXMLNode->eType == CXT_Element)
102✔
802
      return psXMLNode;
UNCOV
803
    psXMLNode = psXMLNode->psNext;
×
804
  }
805
  return NULL;
806
}
807

808
/************************************************************************/
809
/*                        FLTGetNextSibblingNode                        */
810
/************************************************************************/
811
static CPLXMLNode *FLTGetNextSibblingNode(CPLXMLNode *psXMLNode) {
812
  if (psXMLNode == NULL)
86✔
813
    return NULL;
814
  psXMLNode = psXMLNode->psNext;
169✔
815
  while (psXMLNode != NULL) {
169✔
816
    if (psXMLNode->eType == CXT_Element)
87✔
817
      return psXMLNode;
UNCOV
818
    psXMLNode = psXMLNode->psNext;
×
819
  }
820
  return NULL;
821
}
822

823
/************************************************************************/
824
/*                           FLTInsertElementInNode                     */
825
/*                                                                      */
826
/*      Utility function to parse an XML node and transfer the          */
827
/*      contents into the Filter Encoding node structure.               */
828
/************************************************************************/
829
void FLTInsertElementInNode(FilterEncodingNode *psFilterNode,
536✔
830
                            CPLXMLNode *psXMLNode) {
831
  char *pszTmp = NULL;
832
  FilterEncodingNode *psCurFilNode = NULL;
833
  CPLXMLNode *psCurXMLNode = NULL;
834
  CPLXMLNode *psTmpNode = NULL;
835
  CPLXMLNode *psFeatureIdNode = NULL;
836
  const char *pszFeatureId = NULL;
837
  char *pszFeatureIdList = NULL;
838

839
  if (psFilterNode && psXMLNode && psXMLNode->pszValue) {
638✔
840
    psFilterNode->pszValue = msStrdup(psXMLNode->pszValue);
638✔
841
    psFilterNode->psLeftNode = NULL;
638✔
842
    psFilterNode->psRightNode = NULL;
638✔
843

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

887
                psCurFilNode = psCurFilNode->psRightNode;
1✔
888
                psCurXMLNode = psNextNode;
889
              } else { /*last 2 operators*/
890
                psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
1✔
891
                FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
1✔
892

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

976
      if (strcasecmp(psXMLNode->pszValue, "BBOX") == 0) {
137✔
977
        char *pszSRS = NULL;
43✔
978
        const char *pszPropertyName = NULL;
979
        CPLXMLNode *psBox = NULL, *psEnvelope = NULL;
980
        rectObj sBox = {0, 0, 0, 0};
43✔
981

982
        int bCoordinatesValid = 0;
983

984
        pszPropertyName = FLTGetPropertyName(psXMLNode);
43✔
985
        psBox = CPLGetXMLNode(psXMLNode, "Box");
43✔
986
        if (!psBox)
43✔
987
          psBox = CPLGetXMLNode(psXMLNode, "BoxType");
13✔
988

989
        /*FE 1.0 used box FE1.1 uses envelop*/
990
        if (psBox)
13✔
991
          bCoordinatesValid = FLTParseGMLBox(psBox, &sBox, &pszSRS);
30✔
992
        else if ((psEnvelope = CPLGetXMLNode(psXMLNode, "Envelope")))
13✔
993
          bCoordinatesValid = FLTParseGMLEnvelope(psEnvelope, &sBox, &pszSRS);
12✔
994

995
        if (bCoordinatesValid) {
42✔
996
          /*set the srs if available*/
997
          if (pszSRS)
42✔
998
            psFilterNode->pszSRS = pszSRS;
41✔
999

1000
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
42✔
1001
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
42✔
1002
          /* PropertyName is optional since FE 1.1.0, in which case */
1003
          /* the BBOX must apply to all geometry fields. As we support */
1004
          /* currently only one geometry field, this doesn't make much */
1005
          /* difference to further processing. */
1006
          if (pszPropertyName != NULL) {
42✔
1007
            psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
37✔
1008
          }
1009

1010
          /* coordinates */
1011
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
42✔
1012
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BBOX;
42✔
1013
          psFilterNode->psRightNode->pOther =
42✔
1014
              (rectObj *)msSmallMalloc(sizeof(rectObj));
42✔
1015
          ((rectObj *)psFilterNode->psRightNode->pOther)->minx = sBox.minx;
42✔
1016
          ((rectObj *)psFilterNode->psRightNode->pOther)->miny = sBox.miny;
42✔
1017
          ((rectObj *)psFilterNode->psRightNode->pOther)->maxx = sBox.maxx;
42✔
1018
          ((rectObj *)psFilterNode->psRightNode->pOther)->maxy = sBox.maxy;
42✔
1019
        } else {
1020
          msFree(pszSRS);
1✔
1021
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1022
        }
1023
      } else if (strcasecmp(psXMLNode->pszValue, "DWithin") == 0 ||
94✔
1024
                 strcasecmp(psXMLNode->pszValue, "Beyond") == 0)
75✔
1025

1026
      {
1027
        shapeObj *psShape = NULL;
1028
        int bPoint = 0, bLine = 0, bPolygon = 0;
25✔
1029
        const char *pszUnits = NULL;
1030
        const char *pszDistance = NULL;
1031
        const char *pszPropertyName;
1032
        char *pszSRS = NULL;
25✔
1033

1034
        CPLXMLNode *psGMLElement = NULL, *psDistance = NULL;
1035

1036
        pszPropertyName = FLTGetPropertyName(psXMLNode);
25✔
1037

1038
        psGMLElement =
1039
            FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
25✔
1040

1041
        psDistance = CPLGetXMLNode(psXMLNode, "Distance");
25✔
1042
        if (psDistance != NULL)
25✔
1043
          pszDistance = CPLGetXMLValue(psDistance, NULL, NULL);
24✔
1044
        if (pszPropertyName != NULL && psGMLElement && psDistance != NULL) {
25✔
1045
          pszUnits = CPLGetXMLValue(psDistance, "units", NULL);
24✔
1046
          if (pszUnits == NULL) /* FE 2.0 */
24✔
1047
            pszUnits = CPLGetXMLValue(psDistance, "uom", NULL);
5✔
1048
          psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
24✔
1049
          msInitShape(psShape);
24✔
1050
          if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS)) {
24✔
1051
            /*set the srs if available*/
1052
            if (pszSRS)
23✔
1053
              psFilterNode->pszSRS = pszSRS;
1✔
1054

1055
            psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
23✔
1056
            psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
23✔
1057
            psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
23✔
1058

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

1098
        CPLXMLNode *psGMLElement = NULL;
1099

1100
        pszPropertyName = FLTGetPropertyName(psXMLNode);
69✔
1101

1102
        psGMLElement =
1103
            FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
69✔
1104

1105
        if (pszPropertyName != NULL && psGMLElement) {
69✔
1106
          psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
68✔
1107
          msInitShape(psShape);
68✔
1108
          if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS)) {
68✔
1109
            /*set the srs if available*/
1110
            if (pszSRS)
67✔
1111
              psFilterNode->pszSRS = pszSRS;
21✔
1112

1113
            psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
67✔
1114
            psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
67✔
1115
            psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
67✔
1116

1117
            psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
67✔
1118
            if (bPoint)
67✔
1119
              psFilterNode->psRightNode->eType =
11✔
1120
                  FILTER_NODE_TYPE_GEOMETRY_POINT;
1121
            else if (bLine)
56✔
1122
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
4✔
1123
            else if (bPolygon)
52✔
1124
              psFilterNode->psRightNode->eType =
52✔
1125
                  FILTER_NODE_TYPE_GEOMETRY_POLYGON;
1126
            psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
67✔
1127

1128
          } else {
1129
            free(psShape);
1✔
1130
            msFree(pszSRS);
1✔
1131
            psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1132
          }
1133
        } else
1134
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1135
      }
1136

1137
    } /* end of is spatial */
1138

1139
    /* -------------------------------------------------------------------- */
1140
    /*      Comparison Filter                                               */
1141
    /* -------------------------------------------------------------------- */
1142
    else if (FLTIsComparisonFilterType(psXMLNode->pszValue)) {
397✔
1143
      psFilterNode->eType = FILTER_NODE_TYPE_COMPARISON;
338✔
1144
      /* -------------------------------------------------------------------- */
1145
      /*      binary comaparison types. Example :                             */
1146
      /*                                                                      */
1147
      /*      <Filter>                                                        */
1148
      /*        <PropertyIsEqualTo>                                           */
1149
      /*          <PropertyName>SomeProperty</PropertyName>                   */
1150
      /*          <Literal>100</Literal>                                      */
1151
      /*        </PropertyIsEqualTo>                                          */
1152
      /*      </Filter>                                                       */
1153
      /* -------------------------------------------------------------------- */
1154
      if (FLTIsBinaryComparisonFilterType(psXMLNode->pszValue)) {
338✔
1155
        const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
268✔
1156
        if (pszPropertyName != NULL) {
268✔
1157

1158
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
266✔
1159
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
266✔
1160
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
266✔
1161

1162
          psTmpNode = CPLSearchXMLNode(psXMLNode, "Literal");
266✔
1163
          if (psTmpNode) {
266✔
1164
            const char *pszLiteral = CPLGetXMLValue(psTmpNode, NULL, NULL);
266✔
1165

1166
            psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
266✔
1167
            psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
266✔
1168

1169
            if (pszLiteral != NULL) {
266✔
1170
              const char *pszMatchCase;
1171

1172
              psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
262✔
1173

1174
              pszMatchCase = CPLGetXMLValue(psXMLNode, "matchCase", NULL);
262✔
1175

1176
              /*check if the matchCase attribute is set*/
1177
              if (pszMatchCase != NULL &&
262✔
1178
                  strcasecmp(pszMatchCase, "false") == 0) {
11✔
1179
                (*(int *)psFilterNode->psRightNode->pOther) = 1;
11✔
1180
              }
1181

1182
            }
1183
            /* special case where the user puts an empty value */
1184
            /* for the Literal so it can end up as an empty  */
1185
            /* string query in the expression */
1186
            else
1187
              psFilterNode->psRightNode->pszValue = NULL;
4✔
1188
          }
1189
        }
1190
        if (psFilterNode->psLeftNode == NULL ||
268✔
1191
            psFilterNode->psRightNode == NULL)
266✔
1192
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
2✔
1193
      }
1194

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

1236
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
17✔
1237
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
17✔
1238

1239
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
17✔
1240
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BOUNDARY;
17✔
1241

1242
          /* adding a ; between boundary values */
1243
          const int nStrLength =
17✔
1244
              strlen(pszLowerNode) + strlen(pszUpperNode) + 2;
17✔
1245

1246
          psFilterNode->psRightNode->pszValue =
17✔
1247
              (char *)malloc(sizeof(char) * (nStrLength));
17✔
1248
          strcpy(psFilterNode->psRightNode->pszValue, pszLowerNode);
1249
          strlcat(psFilterNode->psRightNode->pszValue, ";", nStrLength);
17✔
1250
          strlcat(psFilterNode->psRightNode->pszValue, pszUpperNode,
17✔
1251
                  nStrLength);
1252

1253
        } else
1254
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1255

1256
      } /* end of PropertyIsBetween  */
1257
      /* -------------------------------------------------------------------- */
1258
      /*      PropertyIsLike                                                  */
1259
      /*                                                                      */
1260
      /*      <Filter>                                                        */
1261
      /*      <PropertyIsLike wildCard="*" singleChar="#" escape="!">         */
1262
      /*      <PropertyName>LAST_NAME</PropertyName>                          */
1263
      /*      <Literal>JOHN*</Literal>                                        */
1264
      /*      </PropertyIsLike>                                               */
1265
      /*      </Filter>                                                       */
1266
      /* -------------------------------------------------------------------- */
1267
      else if (strcasecmp(psXMLNode->pszValue, "PropertyIsLike") == 0) {
52✔
1268
        const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
31✔
1269
        const char *pszLiteral = CPLGetXMLValue(psXMLNode, "Literal", NULL);
31✔
1270
        const char *pszWildCard = CPLGetXMLValue(psXMLNode, "wildCard", NULL);
31✔
1271
        const char *pszSingleChar =
1272
            CPLGetXMLValue(psXMLNode, "singleChar", NULL);
31✔
1273
        const char *pszEscapeChar = CPLGetXMLValue(psXMLNode, "escape", NULL);
31✔
1274
        if (pszEscapeChar == NULL)
31✔
1275
          pszEscapeChar = CPLGetXMLValue(psXMLNode, "escapeChar", NULL);
4✔
1276
        if (pszPropertyName != NULL && pszLiteral != NULL &&
31✔
1277
            pszWildCard != NULL && pszSingleChar != NULL &&
30✔
1278
            pszEscapeChar != NULL) {
1279
          FEPropertyIsLike *propIsLike;
1280

1281
          propIsLike = (FEPropertyIsLike *)malloc(sizeof(FEPropertyIsLike));
30✔
1282

1283
          psFilterNode->pOther = propIsLike;
30✔
1284
          propIsLike->bCaseInsensitive = 0;
30✔
1285
          propIsLike->pszWildCard = msStrdup(pszWildCard);
30✔
1286
          propIsLike->pszSingleChar = msStrdup(pszSingleChar);
30✔
1287
          propIsLike->pszEscapeChar = msStrdup(pszEscapeChar);
30✔
1288

1289
          pszTmp = (char *)CPLGetXMLValue(psXMLNode, "matchCase", NULL);
30✔
1290
          if (pszTmp && strcasecmp(pszTmp, "false") == 0) {
30✔
1291
            propIsLike->bCaseInsensitive = 1;
4✔
1292
          }
1293
          /* --------------------------------------------------------------------
1294
           */
1295
          /*      Create left and right node for the attribute and the value. */
1296
          /* --------------------------------------------------------------------
1297
           */
1298
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
30✔
1299

1300
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
30✔
1301
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
30✔
1302

1303
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
30✔
1304

1305
          psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
30✔
1306

1307
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
30✔
1308
        } else
30✔
1309
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1310

1311
      }
1312

1313
      else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNull") == 0) {
21✔
1314
        const char *pszPropertyName = FLTGetPropertyName(psXMLNode);
16✔
1315
        if (pszPropertyName != NULL) {
16✔
1316
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
16✔
1317
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
16✔
1318
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
16✔
1319
        } else
UNCOV
1320
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
×
1321
      }
1322

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

1356
      psFeatureIdNode = psXMLNode;
1357
      while (psFeatureIdNode) {
82✔
1358
        pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "fid", NULL);
42✔
1359
        if (!pszFeatureId)
42✔
1360
          pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "id", NULL);
42✔
1361
        if (!pszFeatureId)
42✔
1362
          pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "rid", NULL);
42✔
1363

1364
        if (pszFeatureId) {
42✔
1365
          if (pszFeatureIdList)
41✔
1366
            pszFeatureIdList = msStringConcatenate(pszFeatureIdList, ",");
2✔
1367

1368
          pszFeatureIdList =
1369
              msStringConcatenate(pszFeatureIdList, pszFeatureId);
41✔
1370
        }
1371
        psFeatureIdNode = psFeatureIdNode->psNext;
42✔
1372
      }
1373

1374
      if (pszFeatureIdList) {
40✔
1375
        msFree(psFilterNode->pszValue);
39✔
1376
        psFilterNode->pszValue = msStrdup(pszFeatureIdList);
39✔
1377
        msFree(pszFeatureIdList);
39✔
1378
      } else
1379
        psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1✔
1380
    }
1381

1382
    /* -------------------------------------------------------------------- */
1383
    /*      Temporal Filter.                                                */
1384
    /*
1385
    <fes:During>
1386
    <fes:ValueReference>gml:TimeInstant</fes:ValueReference>
1387
    <gml:TimePeriod gml:id="TP1">
1388
    <gml:begin>
1389
    <gml:TimeInstant gml:id="TI1">
1390
    <gml:timePosition>2005-05-17T00:00:00Z</gml:timePosition>
1391
    </gml:TimeInstant>
1392
    </gml:begin>
1393
    <gml:end>
1394
    <gml:TimeInstant gml:id="TI2">
1395
    <gml:timePosition>2005-05-23T00:00:00Z</gml:timePosition>
1396
    </gml:TimeInstant>
1397
    </gml:end>
1398
    </gml:TimePeriod>
1399
    </fes:During>
1400
    */
1401
    /* -------------------------------------------------------------------- */
1402
    else if (FLTIsTemporalFilterType(psXMLNode->pszValue)) {
19✔
1403
      psFilterNode->eType = FILTER_NODE_TYPE_TEMPORAL;
19✔
1404

1405
      if (strcasecmp(psXMLNode->pszValue, "During") == 0) {
19✔
1406
        const char *pszPropertyName = NULL;
1407
        const char *pszBeginTime;
1408
        const char *pszEndTime;
1409

1410
        pszPropertyName = FLTGetPropertyName(psXMLNode);
19✔
1411
        pszBeginTime = CPLGetXMLValue(
19✔
1412
            psXMLNode, "TimePeriod.begin.TimeInstant.timePosition", NULL);
1413
        if (pszBeginTime == NULL)
19✔
1414
          pszBeginTime =
1415
              CPLGetXMLValue(psXMLNode, "TimePeriod.beginPosition", NULL);
1✔
1416
        pszEndTime = CPLGetXMLValue(
19✔
1417
            psXMLNode, "TimePeriod.end.TimeInstant.timePosition", NULL);
1418
        if (pszEndTime == NULL)
19✔
1419
          pszEndTime =
1420
              CPLGetXMLValue(psXMLNode, "TimePeriod.endPosition", NULL);
1✔
1421

1422
        if (pszPropertyName && pszBeginTime && pszEndTime &&
19✔
1423
            strchr(pszBeginTime, '\'') == NULL &&
19✔
1424
            strchr(pszBeginTime, '\\') == NULL &&
19✔
1425
            strchr(pszEndTime, '\'') == NULL &&
19✔
1426
            strchr(pszEndTime, '\\') == NULL &&
19✔
1427
            msTimeGetResolution(pszBeginTime) >= 0 &&
57✔
1428
            msTimeGetResolution(pszEndTime) >= 0) {
19✔
1429

1430
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
19✔
1431
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
19✔
1432
          psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
19✔
1433

1434
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
19✔
1435
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_TIME_PERIOD;
19✔
1436
          const size_t nSize = strlen(pszBeginTime) + strlen(pszEndTime) + 2;
19✔
1437
          psFilterNode->psRightNode->pszValue =
19✔
1438
              static_cast<char *>(msSmallMalloc(nSize));
19✔
1439
          snprintf(psFilterNode->psRightNode->pszValue, nSize, "%s/%s",
19✔
1440
                   pszBeginTime, pszEndTime);
1441
        } else
UNCOV
1442
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
×
1443
      } else {
UNCOV
1444
        psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
×
1445
      }
1446

1447
    } /* end of is temporal */
1448
  }
1449
}
536✔
1450

1451
/************************************************************************/
1452
/*            int FLTIsLogicalFilterType((char *pszValue)                  */
1453
/*                                                                      */
1454
/*      return TRUE if the value of the node is of logical filter       */
1455
/*       encoding type.                                                 */
1456
/************************************************************************/
1457
int FLTIsLogicalFilterType(const char *pszValue) {
1,103✔
1458
  if (pszValue) {
1,103✔
1459
    if (strcasecmp(pszValue, "AND") == 0 || strcasecmp(pszValue, "OR") == 0 ||
1,103✔
1460
        strcasecmp(pszValue, "NOT") == 0)
938✔
1461
      return MS_TRUE;
194✔
1462
  }
1463

1464
  return MS_FALSE;
1465
}
1466

1467
/************************************************************************/
1468
/*         int FLTIsBinaryComparisonFilterType(char *pszValue)             */
1469
/*                                                                      */
1470
/*      Binary comparison filter type.                                  */
1471
/************************************************************************/
1472
int FLTIsBinaryComparisonFilterType(const char *pszValue) {
1,301✔
1473
  if (pszValue) {
1,301✔
1474
    if (strcasecmp(pszValue, "PropertyIsEqualTo") == 0 ||
1,301✔
1475
        strcasecmp(pszValue, "PropertyIsNotEqualTo") == 0 ||
639✔
1476
        strcasecmp(pszValue, "PropertyIsLessThan") == 0 ||
591✔
1477
        strcasecmp(pszValue, "PropertyIsGreaterThan") == 0 ||
483✔
1478
        strcasecmp(pszValue, "PropertyIsLessThanOrEqualTo") == 0 ||
439✔
1479
        strcasecmp(pszValue, "PropertyIsGreaterThanOrEqualTo") == 0)
405✔
1480
      return MS_TRUE;
960✔
1481
  }
1482

1483
  return MS_FALSE;
1484
}
1485

1486
/************************************************************************/
1487
/*            int FLTIsComparisonFilterType(char *pszValue)                */
1488
/*                                                                      */
1489
/*      return TRUE if the value of the node is of comparison filter    */
1490
/*      encoding type.                                                  */
1491
/************************************************************************/
1492
int FLTIsComparisonFilterType(const char *pszValue) {
646✔
1493
  if (pszValue) {
646✔
1494
    if (FLTIsBinaryComparisonFilterType(pszValue) ||
646✔
1495
        strcasecmp(pszValue, "PropertyIsLike") == 0 ||
224✔
1496
        strcasecmp(pszValue, "PropertyIsBetween") == 0 ||
172✔
1497
        strcasecmp(pszValue, "PropertyIsNull") == 0 ||
791✔
1498
        strcasecmp(pszValue, "PropertyIsNil") == 0)
121✔
1499
      return MS_TRUE;
1500
  }
1501

1502
  return MS_FALSE;
1503
}
1504

1505
/************************************************************************/
1506
/*            int FLTIsFeatureIdFilterType(char *pszValue)              */
1507
/*                                                                      */
1508
/*      return TRUE if the value of the node is of featureid filter     */
1509
/*      encoding type.                                                  */
1510
/************************************************************************/
1511
int FLTIsFeatureIdFilterType(const char *pszValue) {
113✔
1512
  if (pszValue && (strcasecmp(pszValue, "FeatureId") == 0 ||
113✔
1513
                   strcasecmp(pszValue, "GmlObjectId") == 0 ||
109✔
1514
                   strcasecmp(pszValue, "ResourceId") == 0))
109✔
1515

1516
    return MS_TRUE;
72✔
1517

1518
  return MS_FALSE;
1519
}
1520

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

1544
  return MS_FALSE;
1545
}
1546

1547
/************************************************************************/
1548
/*            int FLTIsTemportalFilterType(char *pszValue)              */
1549
/*                                                                      */
1550
/*      return TRUE if the value of the node is of temporal filter      */
1551
/*      encoding type.                                                  */
1552
/************************************************************************/
1553
int FLTIsTemporalFilterType(const char *pszValue) {
41✔
1554
  if (pszValue) {
41✔
1555
    if (strcasecmp(pszValue, "During") == 0)
41✔
1556
      return MS_TRUE;
29✔
1557
  }
1558

1559
  return MS_FALSE;
1560
}
1561

1562
/************************************************************************/
1563
/*           int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode)           */
1564
/*                                                                      */
1565
/*      Verify if the value of the node is one of the supported        */
1566
/*      filter type.                                                    */
1567
/************************************************************************/
1568
int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode) {
465✔
1569
  if (psXMLNode) {
465✔
1570
    if (FLTIsLogicalFilterType(psXMLNode->pszValue) ||
840✔
1571
        FLTIsSpatialFilterType(psXMLNode->pszValue) ||
624✔
1572
        FLTIsComparisonFilterType(psXMLNode->pszValue) ||
303✔
1573
        FLTIsFeatureIdFilterType(psXMLNode->pszValue) ||
541✔
1574
        FLTIsTemporalFilterType(psXMLNode->pszValue))
22✔
1575
      return MS_TRUE;
453✔
1576
  }
1577

1578
  return MS_FALSE;
1579
}
1580

1581
/************************************************************************/
1582
/*                          FLTNumberOfFilterType                       */
1583
/*                                                                      */
1584
/*      Loop trhough the nodes and return the number of nodes of        */
1585
/*      specified value.                                                */
1586
/************************************************************************/
UNCOV
1587
int FLTNumberOfFilterType(FilterEncodingNode *psFilterNode,
×
1588
                          const char *szType) {
1589
  int nCount = 0;
1590
  int nLeftNode = 0, nRightNode = 0;
1591

UNCOV
1592
  if (!psFilterNode || !szType || !psFilterNode->pszValue)
×
1593
    return 0;
1594

UNCOV
1595
  if (strcasecmp(psFilterNode->pszValue, (char *)szType) == 0)
×
1596
    nCount++;
1597

UNCOV
1598
  if (psFilterNode->psLeftNode)
×
UNCOV
1599
    nLeftNode = FLTNumberOfFilterType(psFilterNode->psLeftNode, szType);
×
1600

UNCOV
1601
  nCount += nLeftNode;
×
1602

UNCOV
1603
  if (psFilterNode->psRightNode)
×
1604
    nRightNode = FLTNumberOfFilterType(psFilterNode->psRightNode, szType);
UNCOV
1605
  nCount += nRightNode;
×
1606

UNCOV
1607
  return nCount;
×
1608
}
1609

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

UNCOV
1648
  if (!psFilterNode || !psFilterNode->pszValue)
×
1649
    return 1;
1650

UNCOV
1651
  nCount = FLTNumberOfFilterType(psFilterNode, "BBOX");
×
1652

UNCOV
1653
  if (nCount > 1)
×
1654
    return 0;
UNCOV
1655
  else if (nCount == 0)
×
1656
    return 1;
1657

1658
  /* nCount ==1  */
UNCOV
1659
  if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
×
1660
    return 1;
1661

UNCOV
1662
  if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
×
UNCOV
1663
    return FLTValidForBBoxFilter(psFilterNode->psLeftNode) &&
×
UNCOV
1664
           FLTValidForBBoxFilter(psFilterNode->psRightNode);
×
1665
  }
1666

1667
  return 0;
1668
}
1669

1670
#if 0
1671
static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode)
1672
{
1673
  int nCount = 0;
1674

1675
  if (!psFilterNode || !psFilterNode->pszValue)
1676
    return 1;
1677

1678
  nCount = FLTNumberOfFilterType(psFilterNode, "During");
1679

1680
  if (nCount > 1)
1681
    return 0;
1682
  else if (nCount == 0)
1683
    return 1;
1684

1685
  /* nCount ==1  */
1686
  if (strcasecmp(psFilterNode->pszValue, "During") == 0)
1687
    return 1;
1688

1689
  if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
1690
    return FLTHasUniqueTopLevelDuringFilter(psFilterNode->psLeftNode) &&
1691
           FLTHasUniqueTopLevelDuringFilter(psFilterNode->psRightNode);
1692
  }
1693

1694
  return 0;
1695
}
1696
#endif
1697

UNCOV
1698
int FLTIsLineFilter(FilterEncodingNode *psFilterNode) {
×
UNCOV
1699
  if (!psFilterNode || !psFilterNode->pszValue)
×
1700
    return 0;
1701

UNCOV
1702
  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
×
UNCOV
1703
      psFilterNode->psRightNode &&
×
UNCOV
1704
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_LINE)
×
UNCOV
1705
    return 1;
×
1706

1707
  return 0;
1708
}
1709

UNCOV
1710
int FLTIsPolygonFilter(FilterEncodingNode *psFilterNode) {
×
UNCOV
1711
  if (!psFilterNode || !psFilterNode->pszValue)
×
1712
    return 0;
1713

UNCOV
1714
  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
×
UNCOV
1715
      psFilterNode->psRightNode &&
×
UNCOV
1716
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON)
×
UNCOV
1717
    return 1;
×
1718

1719
  return 0;
1720
}
1721

UNCOV
1722
int FLTIsPointFilter(FilterEncodingNode *psFilterNode) {
×
UNCOV
1723
  if (!psFilterNode || !psFilterNode->pszValue)
×
1724
    return 0;
1725

UNCOV
1726
  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
×
UNCOV
1727
      psFilterNode->psRightNode &&
×
UNCOV
1728
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POINT)
×
UNCOV
1729
    return 1;
×
1730

1731
  return 0;
1732
}
1733

1734
int FLTIsBBoxFilter(FilterEncodingNode *psFilterNode) {
132✔
1735
  if (!psFilterNode || !psFilterNode->pszValue)
132✔
1736
    return 0;
1737

1738
  if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
132✔
1739
    return 1;
42✔
1740

1741
  return 0;
1742
}
1743

1744
shapeObj *FLTGetShape(FilterEncodingNode *psFilterNode, double *pdfDistance,
90✔
1745
                      int *pnUnit) {
1746
  char **tokens = NULL;
1747
  int nTokens = 0;
90✔
1748
  FilterEncodingNode *psNode = psFilterNode;
1749
  char *szUnitStr = NULL;
1750
  char *szUnit = NULL;
1751

1752
  if (psNode) {
90✔
1753
    if (psNode->eType == FILTER_NODE_TYPE_SPATIAL && psNode->psRightNode)
90✔
1754
      psNode = psNode->psRightNode;
1755

1756
    if (FLTIsGeometryFilterNodeType(psNode->eType)) {
90✔
1757

1758
      if (psNode->pszValue && pdfDistance) {
90✔
1759
        /*
1760
        syntax expected is "distance;unit" or just "distance"
1761
        if unit is there syntax is "URI#unit" (eg http://..../#m)
1762
        or just "unit"
1763
        */
1764
        tokens = msStringSplit(psNode->pszValue, ';', &nTokens);
23✔
1765
        if (tokens && nTokens >= 1) {
23✔
1766
          *pdfDistance = atof(tokens[0]);
23✔
1767

1768
          if (nTokens == 2 && pnUnit) {
23✔
1769
            szUnitStr = msStrdup(tokens[1]);
19✔
1770
            msFreeCharArray(tokens, nTokens);
19✔
1771
            nTokens = 0;
19✔
1772
            tokens = msStringSplit(szUnitStr, '#', &nTokens);
19✔
1773
            msFree(szUnitStr);
19✔
1774
            if (tokens && nTokens >= 1) {
19✔
1775
              if (nTokens == 1)
19✔
1776
                szUnit = tokens[0];
19✔
1777
              else
UNCOV
1778
                szUnit = tokens[1];
×
1779

1780
              if (strcasecmp(szUnit, "m") == 0 ||
19✔
1781
                  strcasecmp(szUnit, "meters") == 0)
18✔
1782
                *pnUnit = MS_METERS;
1✔
1783
              else if (strcasecmp(szUnit, "km") == 0 ||
18✔
1784
                       strcasecmp(szUnit, "kilometers") == 0)
18✔
1785
                *pnUnit = MS_KILOMETERS;
3✔
1786
              else if (strcasecmp(szUnit, "NM") == 0 ||
15✔
1787
                       strcasecmp(szUnit, "nauticalmiles") == 0)
15✔
UNCOV
1788
                *pnUnit = MS_NAUTICALMILES;
×
1789
              else if (strcasecmp(szUnit, "mi") == 0 ||
15✔
1790
                       strcasecmp(szUnit, "miles") == 0)
15✔
UNCOV
1791
                *pnUnit = MS_MILES;
×
1792
              else if (strcasecmp(szUnit, "in") == 0 ||
15✔
1793
                       strcasecmp(szUnit, "inches") == 0)
15✔
UNCOV
1794
                *pnUnit = MS_INCHES;
×
1795
              else if (strcasecmp(szUnit, "ft") == 0 ||
15✔
1796
                       strcasecmp(szUnit, "feet") == 0)
15✔
UNCOV
1797
                *pnUnit = MS_FEET;
×
1798
              else if (strcasecmp(szUnit, "deg") == 0 ||
15✔
1799
                       strcasecmp(szUnit, "dd") == 0)
15✔
1800
                *pnUnit = MS_DD;
15✔
UNCOV
1801
              else if (strcasecmp(szUnit, "px") == 0)
×
UNCOV
1802
                *pnUnit = MS_PIXELS;
×
1803
            }
1804
          }
1805
        }
1806
        msFreeCharArray(tokens, nTokens);
23✔
1807
      }
1808

1809
      return (shapeObj *)psNode->pOther;
90✔
1810
    }
1811
  }
1812
  return NULL;
1813
}
1814

1815
/************************************************************************/
1816
/*                                FLTGetBBOX                            */
1817
/*                                                                      */
1818
/*      Loop through the nodes are return the coordinates of the        */
1819
/*      first bbox node found. The return value is the epsg code of     */
1820
/*      the bbox.                                                       */
1821
/************************************************************************/
1822
const char *FLTGetBBOX(FilterEncodingNode *psFilterNode, rectObj *psRect) {
88✔
1823
  const char *pszReturn = NULL;
1824

1825
  if (!psFilterNode || !psRect)
88✔
1826
    return NULL;
1827

1828
  if (psFilterNode->pszValue &&
88✔
1829
      strcasecmp(psFilterNode->pszValue, "BBOX") == 0) {
88✔
1830
    if (psFilterNode->psRightNode && psFilterNode->psRightNode->pOther) {
80✔
1831
      rectObj *pRect = (rectObj *)psFilterNode->psRightNode->pOther;
1832
      psRect->minx = pRect->minx;
80✔
1833
      psRect->miny = pRect->miny;
80✔
1834
      psRect->maxx = pRect->maxx;
80✔
1835
      psRect->maxy = pRect->maxy;
80✔
1836

1837
      return psFilterNode->pszSRS;
80✔
1838
    }
1839
  } else {
1840
    pszReturn = FLTGetBBOX(psFilterNode->psLeftNode, psRect);
8✔
1841
    if (pszReturn)
8✔
1842
      return pszReturn;
1843
    else
UNCOV
1844
      return FLTGetBBOX(psFilterNode->psRightNode, psRect);
×
1845
  }
1846

1847
  return pszReturn;
1848
}
1849

1850
const char *FLTGetDuring(FilterEncodingNode *psFilterNode,
19✔
1851
                         const char **ppszTimeField) {
1852
  const char *pszReturn = NULL;
1853

1854
  if (!psFilterNode || !ppszTimeField)
19✔
1855
    return NULL;
1856

1857
  if (psFilterNode->pszValue &&
19✔
1858
      strcasecmp(psFilterNode->pszValue, "During") == 0) {
19✔
1859
    *ppszTimeField = psFilterNode->psLeftNode->pszValue;
19✔
1860
    return psFilterNode->psRightNode->pszValue;
19✔
1861
  } else {
UNCOV
1862
    pszReturn = FLTGetDuring(psFilterNode->psLeftNode, ppszTimeField);
×
UNCOV
1863
    if (pszReturn)
×
1864
      return pszReturn;
1865
    else
UNCOV
1866
      return FLTGetDuring(psFilterNode->psRightNode, ppszTimeField);
×
1867
  }
1868

1869
  return pszReturn;
1870
}
1871

1872
/************************************************************************/
1873
/*                           FLTGetSQLExpression                        */
1874
/*                                                                      */
1875
/*      Build SQL expressions from the mapserver nodes.                 */
1876
/************************************************************************/
UNCOV
1877
char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp) {
×
1878
  char *pszExpression = NULL;
1879

UNCOV
1880
  if (psFilterNode == NULL || lp == NULL)
×
1881
    return NULL;
1882

UNCOV
1883
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
×
UNCOV
1884
    if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
×
1885
      if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue)) {
×
UNCOV
1886
        pszExpression = FLTGetBinaryComparisonSQLExpresssion(psFilterNode, lp);
×
UNCOV
1887
      } else if (strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0) {
×
1888
        pszExpression =
UNCOV
1889
            FLTGetIsBetweenComparisonSQLExpresssion(psFilterNode, lp);
×
UNCOV
1890
      } else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
×
1891
        pszExpression = FLTGetIsLikeComparisonSQLExpression(psFilterNode, lp);
×
1892
      }
1893
    }
UNCOV
1894
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
×
1895
    if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
×
UNCOV
1896
        strcasecmp(psFilterNode->pszValue, "OR") == 0) {
×
UNCOV
1897
      pszExpression = FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
×
1898

1899
    } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
×
UNCOV
1900
      pszExpression = FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
×
1901
    }
1902
  }
1903

1904
  else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
×
1905
    /* TODO */
UNCOV
1906
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
×
1907
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
1908
    defined(USE_SOS_SVR)
UNCOV
1909
    if (psFilterNode->pszValue) {
×
1910
      const char *pszAttribute =
UNCOV
1911
          msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
×
UNCOV
1912
      if (pszAttribute) {
×
UNCOV
1913
        int nTokens = 0;
×
UNCOV
1914
        char **tokens = msStringSplit(psFilterNode->pszValue, ',', &nTokens);
×
1915
        int bString = 0;
UNCOV
1916
        if (tokens && nTokens > 0) {
×
UNCOV
1917
          for (int i = 0; i < nTokens; i++) {
×
1918
            char *pszEscapedStr = NULL;
UNCOV
1919
            const char *pszId = tokens[i];
×
1920
            const char *pszDot = strchr(pszId, '.');
UNCOV
1921
            if (pszDot)
×
UNCOV
1922
              pszId = pszDot + 1;
×
1923

UNCOV
1924
            if (strlen(pszId) <= 0)
×
UNCOV
1925
              continue;
×
1926

UNCOV
1927
            if (FLTIsNumeric(pszId) == MS_FALSE)
×
1928
              bString = 1;
1929

UNCOV
1930
            pszEscapedStr = msLayerEscapeSQLParam(lp, pszId);
×
1931
            char szTmp[256];
UNCOV
1932
            if (bString) {
×
UNCOV
1933
              if (lp->connectiontype == MS_OGR ||
×
1934
                  lp->connectiontype == MS_POSTGIS)
1935
                snprintf(szTmp, sizeof(szTmp),
1936
                         "(CAST(%s AS CHARACTER(255)) = '%s')", pszAttribute,
1937
                         pszEscapedStr);
1938
              else
1939
                snprintf(szTmp, sizeof(szTmp), "(%s = '%s')", pszAttribute,
1940
                         pszEscapedStr);
1941
            } else
1942
              snprintf(szTmp, sizeof(szTmp), "(%s = %s)", pszAttribute,
1943
                       pszEscapedStr);
1944

1945
            msFree(pszEscapedStr);
×
1946
            pszEscapedStr = NULL;
1947

UNCOV
1948
            if (pszExpression != NULL)
×
UNCOV
1949
              pszExpression = msStringConcatenate(pszExpression, " OR ");
×
1950
            else
1951
              /*opening and closing brackets*/
UNCOV
1952
              pszExpression = msStringConcatenate(pszExpression, "(");
×
1953

1954
            pszExpression = msStringConcatenate(pszExpression, szTmp);
×
1955
          }
1956
        }
1957
        msFreeCharArray(tokens, nTokens);
×
1958
      }
1959
      /*opening and closing brackets*/
UNCOV
1960
      if (pszExpression)
×
UNCOV
1961
        pszExpression = msStringConcatenate(pszExpression, ")");
×
1962
    }
1963
#else
1964
    msSetError(MS_MISCERR, "OWS support is not available.",
1965
               "FLTGetSQLExpression()");
1966
    return NULL;
1967
#endif
1968

1969
  } else if (lp->connectiontype != MS_OGR &&
×
1970
             psFilterNode->eType == FILTER_NODE_TYPE_TEMPORAL)
UNCOV
1971
    pszExpression = msStrdup(FLTGetTimeExpression(psFilterNode, lp).c_str());
×
1972

1973
  return pszExpression;
1974
}
1975

1976
/************************************************************************/
1977
/*                     FLTGetLogicalComparisonSQLExpresssion            */
1978
/*                                                                      */
1979
/*      Return the expression for logical comparison expression.        */
1980
/************************************************************************/
UNCOV
1981
char *FLTGetLogicalComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
×
1982
                                            layerObj *lp) {
1983
  char *pszBuffer = NULL;
1984
  char *pszTmp = NULL;
1985

UNCOV
1986
  if (lp == NULL)
×
1987
    return NULL;
1988

1989
  /* ==================================================================== */
1990
  /*      special case for BBOX node.                                     */
1991
  /* ==================================================================== */
UNCOV
1992
  if (psFilterNode->psLeftNode && psFilterNode->psRightNode &&
×
UNCOV
1993
      ((strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") == 0) ||
×
UNCOV
1994
       (strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") == 0))) {
×
UNCOV
1995
    if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") != 0)
×
UNCOV
1996
      pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
×
1997
    else
UNCOV
1998
      pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
×
1999

UNCOV
2000
    if (!pszTmp)
×
2001
      return NULL;
2002

UNCOV
2003
    const size_t nSize = strlen(pszTmp) + 1;
×
UNCOV
2004
    pszBuffer = (char *)malloc(nSize);
×
2005
    snprintf(pszBuffer, nSize, "%s", pszTmp);
2006
  }
2007

2008
  /* ==================================================================== */
2009
  /*      special case for temporal filter node (OGR layer only)          */
2010
  /* ==================================================================== */
UNCOV
2011
  else if (lp->connectiontype == MS_OGR && psFilterNode->psLeftNode &&
×
UNCOV
2012
           psFilterNode->psRightNode &&
×
UNCOV
2013
           (psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_TEMPORAL ||
×
UNCOV
2014
            psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_TEMPORAL)) {
×
UNCOV
2015
    if (psFilterNode->psLeftNode->eType != FILTER_NODE_TYPE_TEMPORAL)
×
UNCOV
2016
      pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
×
2017
    else
2018
      pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
×
2019

UNCOV
2020
    if (!pszTmp)
×
2021
      return NULL;
2022

UNCOV
2023
    const size_t nSize = strlen(pszTmp) + 1;
×
UNCOV
2024
    pszBuffer = (char *)malloc(nSize);
×
2025
    snprintf(pszBuffer, nSize, "%s", pszTmp);
2026
  }
2027

2028
  /* -------------------------------------------------------------------- */
2029
  /*      OR and AND                                                      */
2030
  /* -------------------------------------------------------------------- */
2031
  else if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
×
UNCOV
2032
    pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
×
UNCOV
2033
    if (!pszTmp)
×
2034
      return NULL;
2035

UNCOV
2036
    pszBuffer = (char *)malloc(
×
2037
        sizeof(char) * (strlen(pszTmp) + strlen(psFilterNode->pszValue) + 5));
×
UNCOV
2038
    pszBuffer[0] = '\0';
×
2039
    strcat(pszBuffer, " (");
2040
    strcat(pszBuffer, pszTmp);
2041
    strcat(pszBuffer, " ");
2042
    strcat(pszBuffer, psFilterNode->pszValue);
2043
    strcat(pszBuffer, " ");
2044

UNCOV
2045
    free(pszTmp);
×
2046

UNCOV
2047
    const size_t nTmp = strlen(pszBuffer);
×
UNCOV
2048
    pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
×
UNCOV
2049
    if (!pszTmp) {
×
UNCOV
2050
      free(pszBuffer);
×
UNCOV
2051
      return NULL;
×
2052
    }
2053

UNCOV
2054
    pszBuffer = (char *)msSmallRealloc(
×
UNCOV
2055
        pszBuffer, sizeof(char) * (strlen(pszTmp) + nTmp + 3));
×
2056
    strcat(pszBuffer, pszTmp);
2057
    strcat(pszBuffer, ") ");
2058
  }
2059
  /* -------------------------------------------------------------------- */
2060
  /*      NOT                                                             */
2061
  /* -------------------------------------------------------------------- */
UNCOV
2062
  else if (psFilterNode->psLeftNode &&
×
UNCOV
2063
           strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
×
UNCOV
2064
    pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
×
UNCOV
2065
    if (!pszTmp)
×
2066
      return NULL;
2067

UNCOV
2068
    pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 9));
×
UNCOV
2069
    pszBuffer[0] = '\0';
×
2070

2071
    strcat(pszBuffer, " (NOT ");
2072
    strcat(pszBuffer, pszTmp);
2073
    strcat(pszBuffer, ") ");
2074
  } else
2075
    return NULL;
2076

2077
  /* -------------------------------------------------------------------- */
2078
  /*      Cleanup.                                                        */
2079
  /* -------------------------------------------------------------------- */
2080
  if (pszTmp != NULL)
UNCOV
2081
    free(pszTmp);
×
UNCOV
2082
  return pszBuffer;
×
2083
}
2084

2085
/************************************************************************/
2086
/*                      FLTGetBinaryComparisonSQLExpresssion            */
2087
/*                                                                      */
2088
/*      Return the expression for a binary comparison filter node.      */
2089
/************************************************************************/
UNCOV
2090
char *FLTGetBinaryComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
×
2091
                                           layerObj *lp) {
2092
  const size_t bufferSize = 1024;
2093
  char szBuffer[1024];
2094
  int bString = 0;
2095
  char szTmp[256];
2096
  char *pszEscapedStr = NULL;
2097

UNCOV
2098
  szBuffer[0] = '\0';
×
UNCOV
2099
  if (!psFilterNode || !FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
×
UNCOV
2100
    return NULL;
×
2101

2102
  /* -------------------------------------------------------------------- */
2103
  /*      check if the value is a numeric value or alphanumeric. If it    */
2104
  /*      is alphanumeric, add quotes around attribute and values.        */
2105
  /* -------------------------------------------------------------------- */
2106
  bString = 0;
UNCOV
2107
  if (psFilterNode->psRightNode->pszValue) {
×
2108
    const char *pszOFGType;
UNCOV
2109
    snprintf(szTmp, sizeof(szTmp), "%s_type",
×
UNCOV
2110
             psFilterNode->psLeftNode->pszValue);
×
UNCOV
2111
    pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
×
UNCOV
2112
    if (pszOFGType != NULL && strcasecmp(pszOFGType, "Character") == 0)
×
2113
      bString = 1;
2114

UNCOV
2115
    else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
×
2116
      bString = 1;
2117
  }
2118

2119
  /* special case to be able to have empty strings in the expression. */
2120
  if (psFilterNode->psRightNode->pszValue == NULL)
×
2121
    bString = 1;
2122

2123
  /*opening bracket*/
2124
  strlcat(szBuffer, " (", bufferSize);
2125

2126
  pszEscapedStr =
2127
      msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
×
2128

2129
  /* attribute */
2130
  /*case insensitive set ? */
2131
  if (bString && strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0 &&
×
UNCOV
2132
      psFilterNode->psRightNode->pOther &&
×
UNCOV
2133
      (*(int *)psFilterNode->psRightNode->pOther) == 1) {
×
2134
    snprintf(szTmp, sizeof(szTmp), "lower(%s) ", pszEscapedStr);
2135
    strlcat(szBuffer, szTmp, bufferSize);
2136
  } else
2137
    strlcat(szBuffer, pszEscapedStr, bufferSize);
2138

2139
  msFree(pszEscapedStr);
×
2140
  pszEscapedStr = NULL;
2141

2142
  /* logical operator */
UNCOV
2143
  if (strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0)
×
2144
    strlcat(szBuffer, "=", bufferSize);
UNCOV
2145
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsNotEqualTo") == 0)
×
2146
    strlcat(szBuffer, "<>", bufferSize);
UNCOV
2147
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThan") == 0)
×
2148
    strlcat(szBuffer, "<", bufferSize);
2149
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsGreaterThan") == 0)
×
2150
    strlcat(szBuffer, ">", bufferSize);
2151
  else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLessThanOrEqualTo") ==
×
2152
           0)
2153
    strlcat(szBuffer, "<=", bufferSize);
2154
  else if (strcasecmp(psFilterNode->pszValue,
×
2155
                      "PropertyIsGreaterThanOrEqualTo") == 0)
2156
    strlcat(szBuffer, ">=", bufferSize);
2157

2158
  strlcat(szBuffer, " ", bufferSize);
2159

2160
  /* value */
2161

2162
  if (bString && psFilterNode->psRightNode->pszValue &&
×
UNCOV
2163
      strcasecmp(psFilterNode->pszValue, "PropertyIsEqualTo") == 0 &&
×
2164
      psFilterNode->psRightNode->pOther &&
×
2165
      (*(int *)psFilterNode->psRightNode->pOther) == 1) {
×
2166
    char *pszEscapedStr;
2167
    pszEscapedStr =
UNCOV
2168
        msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
×
2169
    snprintf(szTmp, sizeof(szTmp), "lower('%s') ", pszEscapedStr);
2170
    msFree(pszEscapedStr);
×
2171
    strlcat(szBuffer, szTmp, bufferSize);
2172
  } else {
2173
    if (bString)
2174
      strlcat(szBuffer, "'", bufferSize);
2175

UNCOV
2176
    if (psFilterNode->psRightNode->pszValue) {
×
UNCOV
2177
      if (bString) {
×
2178
        char *pszEscapedStr;
2179
        pszEscapedStr =
UNCOV
2180
            msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
×
2181
        strlcat(szBuffer, pszEscapedStr, bufferSize);
UNCOV
2182
        msFree(pszEscapedStr);
×
2183
        pszEscapedStr = NULL;
2184
      } else
2185
        strlcat(szBuffer, psFilterNode->psRightNode->pszValue, bufferSize);
2186
    }
2187

2188
    if (bString)
×
2189
      strlcat(szBuffer, "'", bufferSize);
2190
  }
2191
  /*closing bracket*/
2192
  strlcat(szBuffer, ") ", bufferSize);
2193

2194
  return msStrdup(szBuffer);
×
2195
}
2196

2197
/************************************************************************/
2198
/*                    FLTGetIsBetweenComparisonSQLExpresssion           */
2199
/*                                                                      */
2200
/*      Build an SQL expression for IsBteween Filter.                  */
2201
/************************************************************************/
UNCOV
2202
char *FLTGetIsBetweenComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
×
2203
                                              layerObj *lp) {
2204
  const size_t bufferSize = 1024;
2205
  char szBuffer[1024];
2206
  char **aszBounds = NULL;
UNCOV
2207
  int nBounds = 0;
×
2208
  int bString = 0;
2209
  char szTmp[256];
2210
  char *pszEscapedStr;
2211

UNCOV
2212
  szBuffer[0] = '\0';
×
UNCOV
2213
  if (!psFilterNode ||
×
UNCOV
2214
      !(strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0))
×
2215
    return NULL;
2216

UNCOV
2217
  if (!psFilterNode->psLeftNode || !psFilterNode->psRightNode)
×
2218
    return NULL;
2219

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

2250
  /* -------------------------------------------------------------------- */
2251
  /*      build expression.                                              */
2252
  /* -------------------------------------------------------------------- */
2253
  /*opening parenthesis */
2254
  strlcat(szBuffer, " (", bufferSize);
2255

2256
  /* attribute */
2257
  pszEscapedStr =
2258
      msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
×
2259

2260
  strlcat(szBuffer, pszEscapedStr, bufferSize);
UNCOV
2261
  msFree(pszEscapedStr);
×
2262
  pszEscapedStr = NULL;
2263

2264
  /*between*/
2265
  strlcat(szBuffer, " BETWEEN ", bufferSize);
2266

2267
  /*bound 1*/
UNCOV
2268
  if (bString)
×
2269
    strlcat(szBuffer, "'", bufferSize);
UNCOV
2270
  pszEscapedStr = msLayerEscapeSQLParam(lp, aszBounds[0]);
×
2271
  strlcat(szBuffer, pszEscapedStr, bufferSize);
2272
  msFree(pszEscapedStr);
×
2273
  pszEscapedStr = NULL;
2274

UNCOV
2275
  if (bString)
×
2276
    strlcat(szBuffer, "'", bufferSize);
2277

2278
  strlcat(szBuffer, " AND ", bufferSize);
2279

2280
  /*bound 2*/
UNCOV
2281
  if (bString)
×
2282
    strlcat(szBuffer, "'", bufferSize);
UNCOV
2283
  pszEscapedStr = msLayerEscapeSQLParam(lp, aszBounds[1]);
×
2284
  strlcat(szBuffer, pszEscapedStr, bufferSize);
2285
  msFree(pszEscapedStr);
×
2286
  pszEscapedStr = NULL;
2287

2288
  if (bString)
×
2289
    strlcat(szBuffer, "'", bufferSize);
2290

2291
  /*closing parenthesis*/
2292
  strlcat(szBuffer, ")", bufferSize);
2293

2294
  msFreeCharArray(aszBounds, nBounds);
×
2295

UNCOV
2296
  return msStrdup(szBuffer);
×
2297
}
2298

2299
/************************************************************************/
2300
/*                      FLTGetIsLikeComparisonSQLExpression             */
2301
/*                                                                      */
2302
/*      Build an sql expression for IsLike filter.                      */
2303
/************************************************************************/
2304
char *FLTGetIsLikeComparisonSQLExpression(FilterEncodingNode *psFilterNode,
×
2305
                                          layerObj *lp) {
2306
  const size_t bufferSize = 1024;
2307
  char szBuffer[1024];
2308
  char *pszValue = NULL;
2309

2310
  const char *pszWild = NULL;
2311
  const char *pszSingle = NULL;
2312
  const char *pszEscape = NULL;
2313
  char szTmp[4];
2314

2315
  int nLength = 0, i = 0, j = 0;
2316
  int bCaseInsensitive = 0;
2317

2318
  char *pszEscapedStr = NULL;
2319
  FEPropertyIsLike *propIsLike;
2320

2321
  if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
×
2322
      !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
×
2323
    return NULL;
2324

2325
  propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
UNCOV
2326
  pszWild = propIsLike->pszWildCard;
×
UNCOV
2327
  pszSingle = propIsLike->pszSingleChar;
×
UNCOV
2328
  pszEscape = propIsLike->pszEscapeChar;
×
UNCOV
2329
  bCaseInsensitive = propIsLike->bCaseInsensitive;
×
2330

UNCOV
2331
  if (!pszWild || strlen(pszWild) == 0 || !pszSingle ||
×
UNCOV
2332
      strlen(pszSingle) == 0 || !pszEscape || strlen(pszEscape) == 0)
×
2333
    return NULL;
2334

UNCOV
2335
  if (pszEscape[0] == '\'') {
×
2336
    /* This might be valid, but the risk of SQL injection is too high */
2337
    /* and the below code is not ready for that */
2338
    /* Someone who does this has clearly suspect intentions ! */
2339
    msSetError(
×
2340
        MS_MISCERR,
2341
        "Single quote character is not allowed as an escaping character.",
2342
        "FLTGetIsLikeComparisonSQLExpression()");
UNCOV
2343
    return NULL;
×
2344
  }
2345

UNCOV
2346
  szBuffer[0] = '\0';
×
2347
  /*opening bracket*/
2348
  strlcat(szBuffer, " (", bufferSize);
2349

2350
  /* attribute name */
2351
  pszEscapedStr =
2352
      msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
×
2353

2354
  strlcat(szBuffer, pszEscapedStr, bufferSize);
2355
  msFree(pszEscapedStr);
×
2356
  pszEscapedStr = NULL;
2357

UNCOV
2358
  if (lp->connectiontype == MS_POSTGIS) {
×
UNCOV
2359
    if (bCaseInsensitive == 1)
×
2360
      strlcat(szBuffer, "::text ilike '", bufferSize);
2361
    else
2362
      strlcat(szBuffer, "::text like '", bufferSize);
2363
  } else
2364
    strlcat(szBuffer, " like '", bufferSize);
2365

UNCOV
2366
  pszValue = psFilterNode->psRightNode->pszValue;
×
2367
  nLength = strlen(pszValue);
×
2368

UNCOV
2369
  pszEscapedStr = (char *)msSmallMalloc(3 * nLength + 1);
×
2370

2371
  for (i = 0, j = 0; i < nLength; i++) {
×
2372
    char c = pszValue[i];
×
2373
    if (c != pszWild[0] && c != pszSingle[0] && c != pszEscape[0]) {
×
UNCOV
2374
      if (c == '\'') {
×
UNCOV
2375
        pszEscapedStr[j++] = '\'';
×
UNCOV
2376
        pszEscapedStr[j++] = '\'';
×
UNCOV
2377
      } else if (c == '\\') {
×
UNCOV
2378
        pszEscapedStr[j++] = '\\';
×
2379
        pszEscapedStr[j++] = '\\';
×
2380
      } else
UNCOV
2381
        pszEscapedStr[j++] = c;
×
UNCOV
2382
    } else if (c == pszSingle[0]) {
×
2383
      pszEscapedStr[j++] = '_';
×
UNCOV
2384
    } else if (c == pszEscape[0]) {
×
2385
      pszEscapedStr[j++] = pszEscape[0];
×
UNCOV
2386
      if (i + 1 < nLength) {
×
2387
        char nextC = pszValue[i + 1];
×
2388
        i++;
2389
        if (nextC == '\'') {
×
UNCOV
2390
          pszEscapedStr[j++] = '\'';
×
2391
          pszEscapedStr[j++] = '\'';
×
2392
        } else
UNCOV
2393
          pszEscapedStr[j++] = nextC;
×
2394
      }
UNCOV
2395
    } else if (c == pszWild[0]) {
×
UNCOV
2396
      pszEscapedStr[j++] = '%';
×
2397
    }
2398
  }
UNCOV
2399
  pszEscapedStr[j++] = 0;
×
2400
  strlcat(szBuffer, pszEscapedStr, bufferSize);
UNCOV
2401
  msFree(pszEscapedStr);
×
2402

2403
  strlcat(szBuffer, "'", bufferSize);
2404
  if (lp->connectiontype != MS_OGR) {
×
2405
    if (lp->connectiontype == MS_POSTGIS && pszEscape[0] == '\\')
×
2406
      strlcat(szBuffer, " escape E'", bufferSize);
2407
    else
2408
      strlcat(szBuffer, " escape '", bufferSize);
UNCOV
2409
    szTmp[0] = pszEscape[0];
×
2410
    if (pszEscape[0] == '\\') {
×
UNCOV
2411
      szTmp[1] = '\\';
×
UNCOV
2412
      szTmp[2] = '\'';
×
UNCOV
2413
      szTmp[3] = '\0';
×
2414
    } else {
UNCOV
2415
      szTmp[1] = '\'';
×
2416
      szTmp[2] = '\0';
×
2417
    }
2418

2419
    strlcat(szBuffer, szTmp, bufferSize);
2420
  }
2421
  strlcat(szBuffer, ") ", bufferSize);
2422

UNCOV
2423
  return msStrdup(szBuffer);
×
2424
}
2425

2426
/************************************************************************/
2427
/*                           FLTHasSpatialFilter                        */
2428
/*                                                                      */
2429
/*      Utility function to see if a spatial filter is included in      */
2430
/*      the node.                                                       */
2431
/************************************************************************/
UNCOV
2432
int FLTHasSpatialFilter(FilterEncodingNode *psNode) {
×
2433
  int bResult = MS_FALSE;
2434

UNCOV
2435
  if (!psNode)
×
2436
    return MS_FALSE;
2437

UNCOV
2438
  if (psNode->eType == FILTER_NODE_TYPE_LOGICAL) {
×
UNCOV
2439
    if (psNode->psLeftNode)
×
UNCOV
2440
      bResult = FLTHasSpatialFilter(psNode->psLeftNode);
×
2441

2442
    if (bResult)
×
2443
      return MS_TRUE;
2444

UNCOV
2445
    if (psNode->psRightNode)
×
UNCOV
2446
      bResult = FLTHasSpatialFilter(psNode->psRightNode);
×
2447

UNCOV
2448
    if (bResult)
×
2449
      return MS_TRUE;
UNCOV
2450
  } else if (FLTIsBBoxFilter(psNode) || FLTIsPointFilter(psNode) ||
×
UNCOV
2451
             FLTIsLineFilter(psNode) || FLTIsPolygonFilter(psNode))
×
2452
    return MS_TRUE;
×
2453

2454
  return MS_FALSE;
2455
}
2456

2457
/************************************************************************/
2458
/*                     FLTCreateFeatureIdFilterEncoding                 */
2459
/*                                                                      */
2460
/*      Utility function to create a filter node of FeatureId type.     */
2461
/************************************************************************/
2462
FilterEncodingNode *FLTCreateFeatureIdFilterEncoding(const char *pszString) {
30✔
2463
  FilterEncodingNode *psFilterNode = NULL;
2464

2465
  if (pszString) {
30✔
2466
    psFilterNode = FLTCreateFilterEncodingNode();
30✔
2467
    psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
30✔
2468
    psFilterNode->pszValue = msStrdup(pszString);
30✔
2469
    return psFilterNode;
30✔
2470
  }
2471
  return NULL;
2472
}
2473

2474
/************************************************************************/
2475
/*                              FLTParseGMLBox                          */
2476
/*                                                                      */
2477
/*      Parse gml box. Used for FE 1.0                                  */
2478
/************************************************************************/
2479
int FLTParseGMLBox(CPLXMLNode *psBox, rectObj *psBbox, char **ppszSRS) {
30✔
2480
  int bCoordinatesValid = 0;
2481
  CPLXMLNode *psCoordinates = NULL;
2482
  CPLXMLNode *psCoord1 = NULL, *psCoord2 = NULL;
2483
  char **papszCoords = NULL, **papszMin = NULL, **papszMax = NULL;
2484
  int nCoords = 0, nCoordsMin = 0, nCoordsMax = 0;
30✔
2485
  const char *pszTmpCoord = NULL;
2486
  const char *pszSRS = NULL;
2487
  const char *pszTS = NULL;
2488
  const char *pszCS = NULL;
2489
  double minx = 0.0, miny = 0.0, maxx = 0.0, maxy = 0.0;
2490

2491
  if (psBox) {
30✔
2492
    pszSRS = CPLGetXMLValue(psBox, "srsName", NULL);
30✔
2493
    if (ppszSRS && pszSRS)
30✔
2494
      *ppszSRS = msStrdup(pszSRS);
30✔
2495

2496
    psCoordinates = CPLGetXMLNode(psBox, "coordinates");
30✔
2497
    pszTS = CPLGetXMLValue(psCoordinates, "ts", NULL);
30✔
2498
    if (pszTS == NULL)
30✔
2499
      pszTS = " ";
2500
    pszCS = CPLGetXMLValue(psCoordinates, "cs", NULL);
30✔
2501
    if (pszCS == NULL)
30✔
2502
      pszCS = ",";
2503
    pszTmpCoord = CPLGetXMLValue(psCoordinates, NULL, NULL);
30✔
2504

2505
    if (pszTmpCoord) {
30✔
2506
      papszCoords = msStringSplit(pszTmpCoord, pszTS[0], &nCoords);
29✔
2507
      if (papszCoords && nCoords == 2) {
29✔
2508
        papszMin = msStringSplit(papszCoords[0], pszCS[0], &nCoordsMin);
29✔
2509
        if (papszMin && nCoordsMin == 2) {
29✔
2510
          papszMax = msStringSplit(papszCoords[1], pszCS[0], &nCoordsMax);
29✔
2511
        }
2512
        if (papszMax && nCoordsMax == 2) {
29✔
2513
          bCoordinatesValid = 1;
2514
          minx = atof(papszMin[0]);
29✔
2515
          miny = atof(papszMin[1]);
29✔
2516
          maxx = atof(papszMax[0]);
29✔
2517
          maxy = atof(papszMax[1]);
29✔
2518
        }
2519

2520
        msFreeCharArray(papszMin, nCoordsMin);
29✔
2521
        msFreeCharArray(papszMax, nCoordsMax);
29✔
2522
      }
2523

2524
      msFreeCharArray(papszCoords, nCoords);
29✔
2525
    } else {
2526
      psCoord1 = CPLGetXMLNode(psBox, "coord");
1✔
2527
      psCoord2 = FLTGetNextSibblingNode(psCoord1);
2528
      if (psCoord1 && psCoord2 && strcmp(psCoord2->pszValue, "coord") == 0) {
1✔
2529
        const char *pszX = CPLGetXMLValue(psCoord1, "X", NULL);
1✔
2530
        const char *pszY = CPLGetXMLValue(psCoord1, "Y", NULL);
1✔
2531
        if (pszX && pszY) {
1✔
2532
          minx = atof(pszX);
2533
          miny = atof(pszY);
2534

2535
          pszX = CPLGetXMLValue(psCoord2, "X", NULL);
1✔
2536
          pszY = CPLGetXMLValue(psCoord2, "Y", NULL);
1✔
2537
          if (pszX && pszY) {
1✔
2538
            maxx = atof(pszX);
2539
            maxy = atof(pszY);
2540
            bCoordinatesValid = 1;
2541
          }
2542
        }
2543
      }
2544
    }
2545
  }
2546

2547
  if (bCoordinatesValid) {
30✔
2548
    psBbox->minx = minx;
30✔
2549
    psBbox->miny = miny;
30✔
2550

2551
    psBbox->maxx = maxx;
30✔
2552
    psBbox->maxy = maxy;
30✔
2553
  }
2554

2555
  return bCoordinatesValid;
30✔
2556
}
2557
/************************************************************************/
2558
/*                           FLTParseGMLEnvelope                        */
2559
/*                                                                      */
2560
/*      Utility function to parse a gml:Envelope (used for SOS and FE1.1)*/
2561
/************************************************************************/
2562
int FLTParseGMLEnvelope(CPLXMLNode *psRoot, rectObj *psBbox, char **ppszSRS) {
14✔
2563
  CPLXMLNode *psUpperCorner = NULL, *psLowerCorner = NULL;
2564
  const char *pszLowerCorner = NULL, *pszUpperCorner = NULL;
2565
  int bValid = 0;
2566
  char **tokens;
2567
  int n;
2568

2569
  if (psRoot && psBbox && psRoot->eType == CXT_Element &&
14✔
2570
      EQUAL(psRoot->pszValue, "Envelope")) {
14✔
2571
    /*Get the srs if available*/
2572
    if (ppszSRS) {
14✔
2573
      const char *pszSRS = CPLGetXMLValue(psRoot, "srsName", NULL);
14✔
2574
      if (pszSRS != NULL)
14✔
2575
        *ppszSRS = msStrdup(pszSRS);
11✔
2576
    }
2577
    psLowerCorner = CPLSearchXMLNode(psRoot, "lowerCorner");
14✔
2578
    psUpperCorner = CPLSearchXMLNode(psRoot, "upperCorner");
14✔
2579

2580
    if (psLowerCorner && psUpperCorner) {
14✔
2581
      pszLowerCorner = CPLGetXMLValue(psLowerCorner, NULL, NULL);
14✔
2582
      pszUpperCorner = CPLGetXMLValue(psUpperCorner, NULL, NULL);
14✔
2583

2584
      if (pszLowerCorner && pszUpperCorner) {
14✔
2585
        tokens = msStringSplit(pszLowerCorner, ' ', &n);
14✔
2586
        if (tokens && n >= 2) {
14✔
2587
          psBbox->minx = atof(tokens[0]);
14✔
2588
          psBbox->miny = atof(tokens[1]);
14✔
2589

2590
          msFreeCharArray(tokens, n);
14✔
2591

2592
          tokens = msStringSplit(pszUpperCorner, ' ', &n);
14✔
2593
          if (tokens && n >= 2) {
14✔
2594
            psBbox->maxx = atof(tokens[0]);
14✔
2595
            psBbox->maxy = atof(tokens[1]);
14✔
2596
            bValid = 1;
2597
          }
2598
        }
2599
        msFreeCharArray(tokens, n);
14✔
2600
      }
2601
    }
2602
  }
2603

2604
  return bValid;
14✔
2605
}
2606

2607
/************************************************************************/
2608
/*                        FLTNeedSRSSwapping                            */
2609
/************************************************************************/
2610

2611
static int FLTNeedSRSSwapping(mapObj *map, const char *pszSRS) {
20✔
2612
  int bNeedSwapping = MS_FALSE;
2613
  projectionObj sProjTmp;
2614
  msInitProjection(&sProjTmp);
20✔
2615
  if (map) {
20✔
2616
    msProjectionInheritContextFrom(&sProjTmp, &map->projection);
20✔
2617
  }
2618
  if (msLoadProjectionStringEPSG(&sProjTmp, pszSRS) == 0) {
20✔
2619
    bNeedSwapping = msIsAxisInvertedProj(&sProjTmp);
20✔
2620
  }
2621
  msFreeProjection(&sProjTmp);
20✔
2622
  return bNeedSwapping;
20✔
2623
}
2624

2625
/************************************************************************/
2626
/*                      FLTDoAxisSwappingIfNecessary                    */
2627
/*                                                                      */
2628
/*      Explore all geometries and BBOX to do axis swapping when the    */
2629
/*      SRS requires it. If no explicit SRS is attached to the geometry */
2630
/*      the bDefaultSRSNeedsAxisSwapping is taken into account. The     */
2631
/*      caller will have to determine its value from a more general     */
2632
/*      context.                                                        */
2633
/************************************************************************/
2634
void FLTDoAxisSwappingIfNecessary(mapObj *map, FilterEncodingNode *psFilterNode,
374✔
2635
                                  int bDefaultSRSNeedsAxisSwapping) {
2636
  if (psFilterNode == NULL)
625✔
2637
    return;
2638

2639
  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
279✔
2640
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_BBOX) {
28✔
2641
    rectObj *rect = (rectObj *)psFilterNode->psRightNode->pOther;
17✔
2642
    const char *pszSRS = psFilterNode->pszSRS;
17✔
2643
    if ((pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
17✔
2644
        (pszSRS == NULL && bDefaultSRSNeedsAxisSwapping)) {
5✔
2645
      double tmp;
2646

2647
      tmp = rect->minx;
13✔
2648
      rect->minx = rect->miny;
13✔
2649
      rect->miny = tmp;
13✔
2650

2651
      tmp = rect->maxx;
13✔
2652
      rect->maxx = rect->maxy;
13✔
2653
      rect->maxy = tmp;
13✔
2654
    }
2655
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
262✔
2656
             FLTIsGeometryFilterNodeType(psFilterNode->psRightNode->eType)) {
11✔
2657
    shapeObj *shape = (shapeObj *)(psFilterNode->psRightNode->pOther);
11✔
2658
    const char *pszSRS = psFilterNode->pszSRS;
11✔
2659
    if ((pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
11✔
2660
        (pszSRS == NULL && bDefaultSRSNeedsAxisSwapping)) {
10✔
2661
      msAxisSwapShape(shape);
8✔
2662
    }
2663
  } else {
2664
    FLTDoAxisSwappingIfNecessary(map, psFilterNode->psLeftNode,
251✔
2665
                                 bDefaultSRSNeedsAxisSwapping);
2666
    FLTDoAxisSwappingIfNecessary(map, psFilterNode->psRightNode,
251✔
2667
                                 bDefaultSRSNeedsAxisSwapping);
2668
  }
2669
}
2670

2671
static void FLTReplacePropertyName(FilterEncodingNode *psFilterNode,
99✔
2672
                                   const char *pszOldName,
2673
                                   const char *pszNewName) {
2674
  if (psFilterNode && pszOldName && pszNewName) {
128✔
2675
    if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
128✔
2676
      if (psFilterNode->pszValue &&
38✔
2677
          strcasecmp(psFilterNode->pszValue, pszOldName) == 0) {
38✔
2678
        msFree(psFilterNode->pszValue);
11✔
2679
        psFilterNode->pszValue = msStrdup(pszNewName);
11✔
2680
      }
2681
    }
2682
    if (psFilterNode->psLeftNode)
128✔
2683
      FLTReplacePropertyName(psFilterNode->psLeftNode, pszOldName, pszNewName);
57✔
2684
    if (psFilterNode->psRightNode)
128✔
2685
      FLTReplacePropertyName(psFilterNode->psRightNode, pszOldName, pszNewName);
2686
  }
2687
}
99✔
2688

2689
static int FLTIsGMLDefaultProperty(const char *pszName) {
367✔
2690
  return (strcmp(pszName, "gml:name") == 0 ||
702✔
2691
          strcmp(pszName, "gml:description") == 0 ||
335✔
2692
          strcmp(pszName, "gml:descriptionReference") == 0 ||
335✔
2693
          strcmp(pszName, "gml:identifier") == 0 ||
335✔
2694
          strcmp(pszName, "gml:boundedBy") == 0 ||
702✔
2695
          strcmp(pszName, "@gml:id") == 0);
328✔
2696
}
2697

2698
static void
2699
FLTStripNameSpacesFromPropertyName(FilterEncodingNode *psFilterNode) {
1,524✔
2700
  char **tokens = NULL;
2701
  int n = 0;
1,524✔
2702

2703
  if (psFilterNode) {
1,524✔
2704

2705
    if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
1,849✔
2706
        psFilterNode->psLeftNode != NULL &&
325✔
2707
        psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
1,849✔
2708
        FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
325✔
2709
      return;
11✔
2710
    }
2711

2712
    if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
1,513✔
2713
      if (psFilterNode->pszValue && strstr(psFilterNode->pszValue, ":")) {
459✔
2714
        tokens = msStringSplit(psFilterNode->pszValue, ':', &n);
2✔
2715
        if (tokens && n == 2) {
2✔
2716
          msFree(psFilterNode->pszValue);
1✔
2717
          psFilterNode->pszValue = msStrdup(tokens[1]);
1✔
2718
        }
2719
        msFreeCharArray(tokens, n);
2✔
2720
      }
2721
    }
2722
    if (psFilterNode->psLeftNode)
1,513✔
2723
      FLTStripNameSpacesFromPropertyName(psFilterNode->psLeftNode);
562✔
2724
    if (psFilterNode->psRightNode)
1,513✔
2725
      FLTStripNameSpacesFromPropertyName(psFilterNode->psRightNode);
536✔
2726
  }
2727
}
2728

2729
static void FLTRemoveGroupName(FilterEncodingNode *psFilterNode,
4✔
2730
                               gmlGroupListObj *groupList) {
2731
  int i;
2732

2733
  if (psFilterNode) {
6✔
2734

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

2764
    if (psFilterNode->psLeftNode)
6✔
2765
      FLTRemoveGroupName(psFilterNode->psLeftNode, groupList);
2✔
2766
    if (psFilterNode->psRightNode)
6✔
2767
      FLTRemoveGroupName(psFilterNode->psRightNode, groupList);
2768
  }
2769
}
4✔
2770

2771
/************************************************************************/
2772
/*                    FLTPreParseFilterForAliasAndGroup                 */
2773
/*                                                                      */
2774
/*      Utility function to replace aliased' and grouped attributes     */
2775
/*      with their internal name.                                       */
2776
/************************************************************************/
2777
void FLTPreParseFilterForAliasAndGroup(FilterEncodingNode *psFilterNode,
426✔
2778
                                       mapObj *map, int i,
2779
                                       const char *namespaces) {
2780
#if defined(USE_WMS_SVR) || defined(USE_WFS_SVR) || defined(USE_WCS_SVR) ||    \
2781
    defined(USE_SOS_SVR)
2782
  if (psFilterNode && map && i >= 0 && i < map->numlayers) {
426✔
2783
    /*strip name spaces before hand*/
2784
    FLTStripNameSpacesFromPropertyName(psFilterNode);
426✔
2785

2786
    layerObj *lp = GET_LAYER(map, i);
426✔
2787
    int layerWasOpened = msLayerIsOpen(lp);
426✔
2788
    if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
426✔
2789

2790
      /* Remove group names from property names if using groupname/itemname
2791
       * syntax */
2792
      gmlGroupListObj *groupList = msGMLGetGroups(lp, namespaces);
426✔
2793
      if (groupList && groupList->numgroups > 0)
426✔
2794
        FLTRemoveGroupName(psFilterNode, groupList);
2✔
2795
      msGMLFreeGroups(groupList);
426✔
2796

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

2817
#endif
2818
}
426✔
2819

2820
/************************************************************************/
2821
/*                        FLTCheckFeatureIdFilters                      */
2822
/*                                                                      */
2823
/*      Check that FeatureId filters match features in the active       */
2824
/*      layer.                                                          */
2825
/************************************************************************/
2826
int FLTCheckFeatureIdFilters(FilterEncodingNode *psFilterNode, mapObj *map,
831✔
2827
                             int i) {
2828
  int status = MS_SUCCESS;
2829

2830
  if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
1,267✔
2831
    char **tokens;
2832
    int nTokens = 0;
39✔
2833
    layerObj *lp;
2834
    int j;
2835

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

2855
  if (psFilterNode->psLeftNode) {
1,267✔
2856
    status = FLTCheckFeatureIdFilters(psFilterNode->psLeftNode, map, i);
472✔
2857
    if (status == MS_SUCCESS) {
472✔
2858
      if (psFilterNode->psRightNode)
472✔
2859
        status = FLTCheckFeatureIdFilters(psFilterNode->psRightNode, map, i);
2860
    }
2861
  }
2862
  return status;
831✔
2863
}
2864

2865
/************************************************************************/
2866
/*                        FLTCheckInvalidOperand                        */
2867
/*                                                                      */
2868
/*      Check that the operand of a comparison operator is valid        */
2869
/*      Currently only detects use of boundedBy in a binary comparison  */
2870
/************************************************************************/
2871
int FLTCheckInvalidOperand(FilterEncodingNode *psFilterNode) {
193✔
2872
  int status = MS_SUCCESS;
2873

2874
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
261✔
2875
      psFilterNode->psLeftNode != NULL &&
35✔
2876
      psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
35✔
2877
    if (strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") == 0 &&
35✔
2878
        strcmp(psFilterNode->pszValue, "PropertyIsNull") != 0 &&
3✔
2879
        strcmp(psFilterNode->pszValue, "PropertyIsNil") != 0) {
2✔
2880
      msSetError(MS_MISCERR, "Operand '%s' is invalid in comparison.",
1✔
2881
                 "FLTCheckInvalidOperand()",
2882
                 psFilterNode->psLeftNode->pszValue);
2883
      return MS_FAILURE;
1✔
2884
    }
2885
  }
2886
  if (psFilterNode->psLeftNode) {
260✔
2887
    status = FLTCheckInvalidOperand(psFilterNode->psLeftNode);
96✔
2888
    if (status == MS_SUCCESS) {
96✔
2889
      if (psFilterNode->psRightNode)
96✔
2890
        status = FLTCheckInvalidOperand(psFilterNode->psRightNode);
2891
    }
2892
  }
2893
  return status;
2894
}
2895

2896
/************************************************************************/
2897
/*                       FLTProcessPropertyIsNull                       */
2898
/*                                                                      */
2899
/*      HACK for PropertyIsNull processing. PostGIS & Spatialite only   */
2900
/*      for now.                                                        */
2901
/************************************************************************/
2902
int FLTProcessPropertyIsNull(FilterEncodingNode *psFilterNode, mapObj *map,
831✔
2903
                             int i) {
2904
  int status = MS_SUCCESS;
2905

2906
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
1,511✔
2907
      psFilterNode->psLeftNode != NULL &&
244✔
2908
      psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
244✔
2909
      strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
1,511✔
2910
      !FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
16✔
2911
    layerObj *lp;
2912
    int layerWasOpened;
2913

2914
    lp = GET_LAYER(map, i);
7✔
2915
    layerWasOpened = msLayerIsOpen(lp);
7✔
2916

2917
    /* Horrible HACK to compensate for the lack of null testing in MapServer */
2918
    if (lp->connectiontype == MS_POSTGIS ||
7✔
2919
        (lp->connectiontype == MS_OGR && msOGRSupportsIsNull(lp))) {
4✔
2920
      msFree(psFilterNode->pszValue);
5✔
2921
      psFilterNode->pszValue = msStrdup("PropertyIsEqualTo");
5✔
2922
      psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
5✔
2923
      psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
5✔
2924
      psFilterNode->psRightNode->pszValue = msStrdup("_MAPSERVER_NULL_");
5✔
2925
    }
2926

2927
    if (!layerWasOpened) /* do not close the layer if it has been opened
7✔
2928
                            somewhere else (paging?) */
UNCOV
2929
      msLayerClose(lp);
×
2930
  }
2931

2932
  if (psFilterNode->psLeftNode) {
1,267✔
2933
    status = FLTProcessPropertyIsNull(psFilterNode->psLeftNode, map, i);
472✔
2934
    if (status == MS_SUCCESS) {
472✔
2935
      if (psFilterNode->psRightNode)
472✔
2936
        status = FLTProcessPropertyIsNull(psFilterNode->psRightNode, map, i);
2937
    }
2938
  }
2939
  return status;
831✔
2940
}
2941

2942
/************************************************************************/
2943
/*                        FLTCheckInvalidProperty                       */
2944
/*                                                                      */
2945
/*      Check that property names are known                             */
2946
/************************************************************************/
2947
int FLTCheckInvalidProperty(FilterEncodingNode *psFilterNode, mapObj *map,
181✔
2948
                            int i) {
2949
  int status = MS_SUCCESS;
2950

2951
  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
249✔
2952
      psFilterNode->psLeftNode != NULL &&
34✔
2953
      psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
34✔
2954
    layerObj *lp;
2955
    int layerWasOpened;
2956
    int bFound = MS_FALSE;
2957

2958
    if ((strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
57✔
2959
         strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0) &&
34✔
2960
        FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue)) {
16✔
2961
      return MS_SUCCESS;
2962
    }
2963

2964
    lp = GET_LAYER(map, i);
24✔
2965
    layerWasOpened = msLayerIsOpen(lp);
24✔
2966
    if ((layerWasOpened || msLayerOpen(lp) == MS_SUCCESS) &&
48✔
2967
        msLayerGetItems(lp) == MS_SUCCESS) {
24✔
2968
      int i;
2969
      gmlItemListObj *items = msGMLGetItems(lp, "G");
24✔
2970
      for (i = 0; i < items->numitems; i++) {
174✔
2971
        if (!items->items[i].name || strlen(items->items[i].name) == 0 ||
173✔
2972
            !items->items[i].visible)
173✔
2973
          continue;
13✔
2974
        if (strcasecmp(items->items[i].name,
160✔
2975
                       psFilterNode->psLeftNode->pszValue) == 0) {
160✔
2976
          bFound = MS_TRUE;
2977
          break;
2978
        }
2979
      }
2980
      msGMLFreeItems(items);
24✔
2981
    }
2982

2983
    if (!layerWasOpened) /* do not close the layer if it has been opened
24✔
2984
                            somewhere else (paging?) */
UNCOV
2985
      msLayerClose(lp);
×
2986

2987
    if (!bFound) {
24✔
2988
      msSetError(MS_MISCERR, "Property '%s' is unknown.",
1✔
2989
                 "FLTCheckInvalidProperty()",
2990
                 psFilterNode->psLeftNode->pszValue);
1✔
2991
      return MS_FAILURE;
1✔
2992
    }
2993
  }
2994

2995
  if (psFilterNode->psLeftNode) {
238✔
2996
    status = FLTCheckInvalidProperty(psFilterNode->psLeftNode, map, i);
85✔
2997
    if (status == MS_SUCCESS) {
85✔
2998
      if (psFilterNode->psRightNode)
85✔
2999
        status = FLTCheckInvalidProperty(psFilterNode->psRightNode, map, i);
3000
    }
3001
  }
3002
  return status;
3003
}
3004

3005
/************************************************************************/
3006
/*                           FLTSimplify                                */
3007
/*                                                                      */
3008
/*      Simplify the expression by removing parts that evaluate to      */
3009
/*      constants.                                                      */
3010
/*      The passed psFilterNode is potentially consumed by the function */
3011
/*      and replaced by the returned value.                             */
3012
/*      If the function returns NULL, *pnEvaluation = MS_FALSE means    */
3013
/*      that  the filter evaluates to FALSE, or MS_TRUE that it         */
3014
/*      evaluates to TRUE                                               */
3015
/************************************************************************/
3016
FilterEncodingNode *FLTSimplify(FilterEncodingNode *psFilterNode,
131✔
3017
                                int *pnEvaluation) {
3018
  *pnEvaluation = -1;
131✔
3019

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

3038
  if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL &&
116✔
3039
      strcasecmp(psFilterNode->pszValue, "NOT") == 0 &&
24✔
3040
      psFilterNode->psLeftNode != NULL) {
11✔
3041
    int nEvaluation;
3042
    psFilterNode->psLeftNode =
11✔
3043
        FLTSimplify(psFilterNode->psLeftNode, &nEvaluation);
11✔
3044
    if (psFilterNode->psLeftNode == NULL) {
11✔
3045
      *pnEvaluation = 1 - nEvaluation;
5✔
3046
      FLTFreeFilterEncodingNode(psFilterNode);
5✔
3047
      return NULL;
5✔
3048
    }
3049
  }
3050

3051
  if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL &&
111✔
3052
      (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
19✔
3053
       strcasecmp(psFilterNode->pszValue, "OR") == 0) &&
12✔
3054
      psFilterNode->psLeftNode != NULL && psFilterNode->psRightNode != NULL) {
13✔
3055
    FilterEncodingNode *psOtherNode;
3056
    int nEvaluation;
3057
    int nExpectedValForFastExit;
3058
    psFilterNode->psLeftNode =
13✔
3059
        FLTSimplify(psFilterNode->psLeftNode, &nEvaluation);
13✔
3060

3061
    if (strcasecmp(psFilterNode->pszValue, "AND") == 0)
13✔
3062
      nExpectedValForFastExit = MS_FALSE;
3063
    else
3064
      nExpectedValForFastExit = MS_TRUE;
3065

3066
    if (psFilterNode->psLeftNode == NULL) {
13✔
3067
      if (nEvaluation == nExpectedValForFastExit) {
3✔
3068
        *pnEvaluation = nEvaluation;
2✔
3069
        FLTFreeFilterEncodingNode(psFilterNode);
2✔
3070
        return NULL;
8✔
3071
      }
3072
      psOtherNode = psFilterNode->psRightNode;
1✔
3073
      psFilterNode->psRightNode = NULL;
1✔
3074
      FLTFreeFilterEncodingNode(psFilterNode);
1✔
3075
      return FLTSimplify(psOtherNode, pnEvaluation);
1✔
3076
    }
3077

3078
    psFilterNode->psRightNode =
10✔
3079
        FLTSimplify(psFilterNode->psRightNode, &nEvaluation);
10✔
3080
    if (psFilterNode->psRightNode == NULL) {
10✔
3081
      if (nEvaluation == nExpectedValForFastExit) {
3✔
3082
        *pnEvaluation = nEvaluation;
2✔
3083
        FLTFreeFilterEncodingNode(psFilterNode);
2✔
3084
        return NULL;
2✔
3085
      }
3086
      psOtherNode = psFilterNode->psLeftNode;
1✔
3087
      psFilterNode->psLeftNode = NULL;
1✔
3088
      FLTFreeFilterEncodingNode(psFilterNode);
1✔
3089
      return FLTSimplify(psOtherNode, pnEvaluation);
1✔
3090
    }
3091
  }
3092

3093
  return psFilterNode;
3094
}
3095

3096
#ifdef USE_LIBXML2
3097

3098
xmlNodePtr FLTGetCapabilities(xmlNsPtr psNsParent, xmlNsPtr psNsOgc,
23✔
3099
                              int bTemporal) {
3100
  xmlNodePtr psRootNode = NULL, psNode = NULL, psSubNode = NULL,
3101
             psSubSubNode = NULL;
3102

3103
  psRootNode = xmlNewNode(psNsParent, BAD_CAST "Filter_Capabilities");
23✔
3104

3105
  psNode =
3106
      xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Spatial_Capabilities", NULL);
23✔
3107

3108
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "GeometryOperands", NULL);
23✔
3109
  xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
23✔
3110
              BAD_CAST "gml:Point");
3111
  xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
23✔
3112
              BAD_CAST "gml:LineString");
3113
  xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
23✔
3114
              BAD_CAST "gml:Polygon");
3115
  xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand",
23✔
3116
              BAD_CAST "gml:Envelope");
3117

3118
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "SpatialOperators", NULL);
23✔
3119
#ifdef USE_GEOS
3120
  psSubSubNode =
3121
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3122
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Equals");
23✔
3123
  psSubSubNode =
3124
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3125
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Disjoint");
23✔
3126
  psSubSubNode =
3127
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3128
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Touches");
23✔
3129
  psSubSubNode =
3130
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3131
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Within");
23✔
3132
  psSubSubNode =
3133
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3134
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Overlaps");
23✔
3135
  psSubSubNode =
3136
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3137
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Crosses");
23✔
3138
  psSubSubNode =
3139
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3140
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Intersects");
23✔
3141
  psSubSubNode =
3142
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3143
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Contains");
23✔
3144
  psSubSubNode =
3145
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3146
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "DWithin");
23✔
3147
  psSubSubNode =
3148
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3149
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Beyond");
23✔
3150
#endif
3151
  psSubSubNode =
3152
      xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
23✔
3153
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "BBOX");
23✔
3154

3155
  if (bTemporal) {
23✔
3156
    psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Temporal_Capabilities",
4✔
3157
                         NULL);
3158
    psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperands", NULL);
4✔
3159
    xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand",
4✔
3160
                BAD_CAST "gml:TimePeriod");
3161
    xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand",
4✔
3162
                BAD_CAST "gml:TimeInstant");
3163

3164
    psSubNode =
3165
        xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperators", NULL);
4✔
3166
    psSubSubNode =
3167
        xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperator", NULL);
4✔
3168
    xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "TM_Equals");
4✔
3169
  }
3170
  psNode =
3171
      xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Scalar_Capabilities", NULL);
23✔
3172
  xmlNewChild(psNode, psNsOgc, BAD_CAST "LogicalOperators", NULL);
23✔
3173
  psNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperators", NULL);
23✔
3174
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
23✔
3175
              BAD_CAST "LessThan");
3176
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
23✔
3177
              BAD_CAST "GreaterThan");
3178
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
23✔
3179
              BAD_CAST "LessThanEqualTo");
3180
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
23✔
3181
              BAD_CAST "GreaterThanEqualTo");
3182
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
23✔
3183
              BAD_CAST "EqualTo");
3184
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
23✔
3185
              BAD_CAST "NotEqualTo");
3186
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Like");
23✔
3187
  xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator",
23✔
3188
              BAD_CAST "Between");
3189

3190
  psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Id_Capabilities", NULL);
23✔
3191
  xmlNewChild(psNode, psNsOgc, BAD_CAST "EID", NULL);
23✔
3192
  xmlNewChild(psNode, psNsOgc, BAD_CAST "FID", NULL);
23✔
3193
  return psRootNode;
23✔
3194
}
3195
#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