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 
30 #define _GNU_SOURCE
31 #include "mapserver-config.h"
32 
33 #include "cpl_minixml.h"
34 #include "cpl_string.h"
35 
36 #include "mapogcfilter.h"
37 #include "mapserver.h"
38 #include "mapowscommon.h"
39 #include "maptime.h"
40 #include "mapows.h"
41 #include <ctype.h>
42 
43 #if 0
44 static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode);
45 #endif
46 
47 #if !(defined(_WIN32) && !defined(__CYGWIN__))
IGUR_double(double ignored)48 static inline void IGUR_double(double ignored) { (void)ignored; }  /* Ignore GCC Unused Result */
49 #endif
50 
FLTIsNumeric(const char * pszValue)51 int FLTIsNumeric(const char *pszValue)
52 {
53   if (pszValue != NULL && *pszValue != '\0' && !isspace(*pszValue)) {
54     /*the regex seems to have a problem on windows when mapserver is built using
55       PHP regex*/
56 #if defined(_WIN32) && !defined(__CYGWIN__)
57     int i = 0, nLength=0, bString=0;
58 
59     nLength = strlen(pszValue);
60     for (i=0; i<nLength; i++) {
61       if (i == 0) {
62         if (!isdigit(pszValue[i]) &&  pszValue[i] != '-') {
63           bString = 1;
64           break;
65         }
66       } else if (!isdigit(pszValue[i]) &&  pszValue[i] != '.') {
67         bString = 1;
68         break;
69       }
70     }
71     if (!bString)
72       return MS_TRUE;
73 #else
74     char * p;
75     IGUR_double(strtod(pszValue, &p));
76     if ( p != pszValue && *p == '\0') return MS_TRUE;
77 #endif
78   }
79 
80   return MS_FALSE;
81 }
82 
83 /*
84 ** Apply an expression to the layer's filter element.
85 **
86 */
FLTApplyExpressionToLayer(layerObj * lp,const char * pszExpression)87 int FLTApplyExpressionToLayer(layerObj *lp, const char *pszExpression)
88 {
89   char *pszFinalExpression=NULL, *pszBuffer = NULL;
90   /*char *escapedTextString=NULL;*/
91   int bConcatWhere=0, bHasAWhere=0;
92 
93   if (lp && pszExpression) {
94     if (lp->connectiontype == MS_POSTGIS || lp->connectiontype ==  MS_ORACLESPATIAL ||
95         lp->connectiontype == MS_PLUGIN) {
96       pszFinalExpression = msStrdup("(");
97       pszFinalExpression = msStringConcatenate(pszFinalExpression, pszExpression);
98       pszFinalExpression = msStringConcatenate(pszFinalExpression, ")");
99     } else if (lp->connectiontype == MS_OGR) {
100       pszFinalExpression = msStrdup(pszExpression);
101       if (lp->filter.type != MS_EXPRESSION) {
102         bConcatWhere = 1;
103       } else {
104         if (lp->filter.string && EQUALN(lp->filter.string,"WHERE ",6)) {
105           bHasAWhere = 1;
106           bConcatWhere =1;
107         }
108       }
109 
110     } else
111       pszFinalExpression = msStrdup(pszExpression);
112 
113     if (bConcatWhere)
114       pszBuffer = msStringConcatenate(pszBuffer, "WHERE ");
115     /* if the filter is set and it's an expression type, concatenate it with
116                 this filter. If not just free it */
117     if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
118       pszBuffer = msStringConcatenate(pszBuffer, "((");
119       if (bHasAWhere)
120         pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string+6);
121       else
122         pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
123       pszBuffer = msStringConcatenate(pszBuffer, ") and ");
124     } else if (lp->filter.string)
125       msFreeExpression(&lp->filter);
126 
127     pszBuffer = msStringConcatenate(pszBuffer, pszFinalExpression);
128 
129     if(lp->filter.string && lp->filter.type == MS_EXPRESSION)
130       pszBuffer = msStringConcatenate(pszBuffer, ")");
131 
132     /*assuming that expression was properly escaped
133           escapedTextString = msStringEscape(pszBuffer);
134           msLoadExpressionString(&lp->filter,
135                                  (char*)CPLSPrintf("%s", escapedTextString));
136           msFree(escapedTextString);
137     */
138     msLoadExpressionString(&lp->filter, pszBuffer);
139 
140 
141     msFree(pszFinalExpression);
142 
143     if (pszBuffer)
144       msFree(pszBuffer);
145 
146     return MS_TRUE;
147   }
148 
149   return MS_FALSE;
150 }
151 
FLTGetExpressionForValuesRanges(layerObj * lp,const char * item,const char * value,int forcecharcter)152 char *FLTGetExpressionForValuesRanges(layerObj *lp, const char *item, const char *value,  int forcecharcter)
153 {
154   int bIscharacter, bSqlLayer=MS_FALSE;
155   char *pszExpression = NULL, *pszEscapedStr=NULL, *pszTmpExpression=NULL;
156   char **paszElements = NULL, **papszRangeElements=NULL;
157   int numelements,i,nrangeelements;
158 
159   /* TODO: remove the bSqlLayer checks since we want to write MapServer expressions only. */
160 
161   /* double minval, maxval; */
162   if (lp && item && value) {
163     if (strstr(value, "/") == NULL) {
164       /*value(s)*/
165       paszElements = msStringSplit (value, ',', &numelements);
166       if (paszElements && numelements > 0) {
167         if (forcecharcter)
168           bIscharacter = MS_TRUE;
169         else
170           bIscharacter= !FLTIsNumeric(paszElements[0]);
171 
172         pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
173         for (i=0; i<numelements; i++) {
174           pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
175           if (bSqlLayer)
176             pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
177           else {
178             if (bIscharacter)
179               pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
180             pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
181             pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
182             pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
183             if (bIscharacter)
184               pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
185           }
186           if (bIscharacter) {
187             if (bSqlLayer)
188               pszTmpExpression = msStringConcatenate(pszTmpExpression, " = '");
189             else
190               pszTmpExpression = msStringConcatenate(pszTmpExpression, " = \"");
191           } else
192             pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");
193 
194           pszEscapedStr = msLayerEscapeSQLParam(lp, paszElements[i]);
195           pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
196 
197           if (bIscharacter) {
198             if (bSqlLayer)
199               pszTmpExpression = msStringConcatenate(pszTmpExpression, "'");
200             else
201               pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
202           }
203           pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
204 
205           msFree(pszEscapedStr);
206           pszEscapedStr=NULL;
207 
208           if (pszExpression != NULL)
209             pszExpression = msStringConcatenate(pszExpression, " OR ");
210 
211           pszExpression =  msStringConcatenate(pszExpression, pszTmpExpression);
212 
213           msFree(pszTmpExpression);
214           pszTmpExpression = NULL;
215         }
216         pszExpression = msStringConcatenate(pszExpression, ")");
217       }
218       msFreeCharArray(paszElements, numelements);
219     } else {
220       /*range(s)*/
221       paszElements = msStringSplit (value, ',', &numelements);
222       if (paszElements && numelements > 0) {
223         pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
224         for (i=0; i<numelements; i++) {
225           papszRangeElements = msStringSplit (paszElements[i], '/', &nrangeelements);
226           if (papszRangeElements && nrangeelements > 0) {
227             pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
228             if (nrangeelements == 2 || nrangeelements == 3) {
229               /*
230               minval = atof(papszRangeElements[0]);
231               maxval = atof(papszRangeElements[1]);
232               */
233               if (bSqlLayer)
234                 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
235               else {
236                 pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
237                 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
238                 pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
239               }
240 
241               pszTmpExpression = msStringConcatenate(pszTmpExpression, " >= ");
242 
243               pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[0]);
244               pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
245               msFree(pszEscapedStr);
246               pszEscapedStr=NULL;
247 
248               pszTmpExpression = msStringConcatenate(pszTmpExpression, " AND ");
249 
250               if (bSqlLayer)
251                 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
252               else {
253                 pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
254                 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
255                 pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
256               }
257 
258               pszTmpExpression = msStringConcatenate(pszTmpExpression, " <= ");
259 
260               pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[1]);
261               pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
262               msFree(pszEscapedStr);
263               pszEscapedStr=NULL;
264 
265               pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
266             } else if (nrangeelements == 1) {
267               pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
268               if (bSqlLayer)
269                 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
270               else {
271                 pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
272                 pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
273                 pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
274               }
275 
276               pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");
277 
278               pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[0]);
279               pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
280               msFree(pszEscapedStr);
281               pszEscapedStr=NULL;
282 
283               pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
284             }
285 
286             if (pszExpression != NULL)
287               pszExpression = msStringConcatenate(pszExpression, " OR ");
288 
289             pszExpression =  msStringConcatenate(pszExpression, pszTmpExpression);
290             msFree(pszTmpExpression);
291             pszTmpExpression = NULL;
292 
293           }
294           msFreeCharArray(papszRangeElements, nrangeelements);
295         }
296         pszExpression = msStringConcatenate(pszExpression, ")");
297       }
298       msFreeCharArray(paszElements, numelements);
299     }
300   }
301   msFree(pszTmpExpression);
302   return pszExpression;
303 }
304 
FLTogrConvertGeometry(OGRGeometryH hGeometry,shapeObj * psShape,OGRwkbGeometryType nType)305 int FLTogrConvertGeometry(OGRGeometryH hGeometry, shapeObj *psShape,
306                           OGRwkbGeometryType nType)
307 {
308   return msOGRGeometryToShape(hGeometry, psShape, nType);
309 }
310 
311 static
FLTShapeFromGMLTree(CPLXMLNode * psTree,shapeObj * psShape,char ** ppszSRS)312 int FLTShapeFromGMLTree(CPLXMLNode *psTree, shapeObj *psShape , char **ppszSRS)
313 {
314   const char *pszSRS = NULL;
315   if (psTree && psShape) {
316     CPLXMLNode *psNext = psTree->psNext;
317     OGRGeometryH hGeometry = NULL;
318 
319     psTree->psNext = NULL;
320     hGeometry = OGR_G_CreateFromGMLTree(psTree );
321     psTree->psNext = psNext;
322 
323     if (hGeometry) {
324       OGRwkbGeometryType nType;
325       nType = OGR_G_GetGeometryType(hGeometry);
326       if (nType == wkbPolygon25D || nType == wkbMultiPolygon25D)
327         nType = wkbPolygon;
328       else if (nType == wkbLineString25D || nType == wkbMultiLineString25D)
329         nType = wkbLineString;
330       else if (nType == wkbPoint25D  || nType ==  wkbMultiPoint25D)
331         nType = wkbPoint;
332       FLTogrConvertGeometry(hGeometry, psShape, nType);
333 
334       OGR_G_DestroyGeometry(hGeometry);
335 
336       pszSRS = CPLGetXMLValue(psTree, "srsName", NULL);
337       if (ppszSRS && pszSRS)
338         *ppszSRS = msStrdup(pszSRS);
339 
340       return MS_TRUE;
341     }
342   }
343 
344   return MS_FALSE;
345 }
346 
FLTGetGeosOperator(char * pszValue)347 int FLTGetGeosOperator(char *pszValue)
348 {
349   if (!pszValue)
350     return -1;
351 
352   if (strcasecmp(pszValue, "Equals") == 0)
353     return MS_GEOS_EQUALS;
354   else if (strcasecmp(pszValue, "Intersect") == 0 ||
355            strcasecmp(pszValue, "Intersects") == 0)
356     return MS_GEOS_INTERSECTS;
357   else if (strcasecmp(pszValue, "Disjoint") == 0)
358     return MS_GEOS_DISJOINT;
359   else if (strcasecmp(pszValue, "Touches") == 0)
360     return MS_GEOS_TOUCHES;
361   else if (strcasecmp(pszValue, "Crosses") == 0)
362     return MS_GEOS_CROSSES;
363   else if (strcasecmp(pszValue, "Within") == 0)
364     return MS_GEOS_WITHIN;
365   else if (strcasecmp(pszValue, "Contains") == 0)
366     return MS_GEOS_CONTAINS;
367   else if (strcasecmp(pszValue, "Overlaps") == 0)
368     return MS_GEOS_OVERLAPS;
369   else if (strcasecmp(pszValue, "Beyond") == 0)
370     return MS_GEOS_BEYOND;
371   else if (strcasecmp(pszValue, "DWithin") == 0)
372     return MS_GEOS_DWITHIN;
373 
374   return -1;
375 }
376 
FLTIsGeosNode(char * pszValue)377 int FLTIsGeosNode(char *pszValue)
378 {
379   if (FLTGetGeosOperator(pszValue) == -1)
380     return MS_FALSE;
381 
382   return MS_TRUE;
383 }
384 
385 /************************************************************************/
386 /*                        FLTIsSimpleFilterNoSpatial                    */
387 /*                                                                      */
388 /*      Filter encoding with only attribute queries                     */
389 /************************************************************************/
FLTIsSimpleFilterNoSpatial(FilterEncodingNode * psNode)390 int FLTIsSimpleFilterNoSpatial(FilterEncodingNode *psNode)
391 {
392   if (FLTIsSimpleFilter(psNode) && FLTNumberOfFilterType(psNode, "BBOX") == 0)
393     return MS_TRUE;
394 
395   return MS_FALSE;
396 }
397 
398 /************************************************************************/
399 /*                      FLTApplySimpleSQLFilter()                       */
400 /************************************************************************/
401 
FLTApplySimpleSQLFilter(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)402 int FLTApplySimpleSQLFilter(FilterEncodingNode *psNode, mapObj *map, int iLayerIndex)
403 {
404   layerObj *lp = NULL;
405   char *szExpression = NULL;
406   rectObj sQueryRect = map->extent;
407   const char *szEPSG = NULL;
408   projectionObj sProjTmp;
409   char *pszBuffer = NULL;
410   int bConcatWhere = 0;
411   int bHasAWhere =0;
412   char *pszTmp = NULL, *pszTmp2 = NULL;
413   char *tmpfilename = NULL;
414   const char* pszTimeField = NULL;
415   const char* pszTimeValue = NULL;
416 
417   lp = (GET_LAYER(map, iLayerIndex));
418 
419   /* if there is a bbox use it */
420   szEPSG = FLTGetBBOX(psNode, &sQueryRect);
421   if(szEPSG && map->projection.numargs > 0) {
422     msInitProjection(&sProjTmp);
423     msProjectionInheritContextFrom(&sProjTmp, &map->projection);
424     /* Use the non EPSG variant since axis swapping is done in FLTDoAxisSwappingIfNecessary */
425     if (msLoadProjectionString(&sProjTmp, szEPSG) == 0) {
426       msProjectRect(&sProjTmp, &map->projection, &sQueryRect);
427     }
428     msFreeProjection(&sProjTmp);
429   }
430 
431   if( lp->connectiontype == MS_OGR ) {
432     pszTimeValue = FLTGetDuring(psNode, &pszTimeField);
433   }
434 
435   /* make sure that the layer can be queried*/
436   if (!lp->template) lp->template = msStrdup("ttt.html");
437 
438   /* if there is no class, create at least one, so that query by rect would work */
439   if (lp->numclasses == 0) {
440     if (msGrowLayerClasses(lp) == NULL)
441       return MS_FAILURE;
442     initClass(lp->class[0]);
443   }
444 
445   bConcatWhere = 0;
446   bHasAWhere = 0;
447   if (lp->connectiontype == MS_POSTGIS || lp->connectiontype ==  MS_ORACLESPATIAL ||
448       lp->connectiontype == MS_PLUGIN) {
449     szExpression = FLTGetSQLExpression(psNode, lp);
450     if (szExpression) {
451       pszTmp = msStrdup("(");
452       pszTmp = msStringConcatenate(pszTmp, szExpression);
453       pszTmp = msStringConcatenate(pszTmp, ")");
454       msFree(szExpression);
455       szExpression = pszTmp;
456     }
457   }
458   /* concatenates the WHERE clause for OGR layers. This only applies if
459      the expression was empty or not of an expression string. If there
460      is an sql type expression, it is assumed to have the WHERE clause.
461      If it is an expression and does not have a WHERE it is assumed to be a mapserver
462      type expression*/
463   else if (lp->connectiontype == MS_OGR) {
464     if (lp->filter.type != MS_EXPRESSION) {
465       szExpression = FLTGetSQLExpression(psNode, lp);
466       bConcatWhere = 1;
467     } else {
468       if (lp->filter.string && EQUALN(lp->filter.string,"WHERE ",6)) {
469         szExpression = FLTGetSQLExpression(psNode, lp);
470         bHasAWhere = 1;
471         bConcatWhere =1;
472       } else {
473         szExpression = FLTGetCommonExpression(psNode, lp);
474       }
475     }
476   } else {
477     szExpression = FLTGetCommonExpression(psNode, lp);
478 
479   }
480 
481   if (szExpression) {
482     if (bConcatWhere)
483       pszBuffer = msStringConcatenate(pszBuffer, "WHERE ");
484 
485     /* if the filter is set and it's an expression type, concatenate it with
486                  this filter. If not just free it */
487     if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
488       pszBuffer = msStringConcatenate(pszBuffer, "((");
489       if (bHasAWhere)
490         pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string+6);
491       else
492         pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
493       pszBuffer = msStringConcatenate(pszBuffer, ") and ");
494     } else if (lp->filter.string)
495       msFreeExpression(&lp->filter);
496 
497     pszBuffer = msStringConcatenate(pszBuffer, szExpression);
498 
499     if(lp->filter.string && lp->filter.type == MS_EXPRESSION)
500       pszBuffer = msStringConcatenate(pszBuffer, ")");
501 
502     msLoadExpressionString(&lp->filter, pszBuffer);
503     free(szExpression);
504   }
505 
506   if (pszTimeField && pszTimeValue)
507       msLayerSetTimeFilter(lp, pszTimeValue, pszTimeField);
508 
509   if (pszBuffer)
510     free(pszBuffer);
511 
512   map->query.type = MS_QUERY_BY_RECT;
513   map->query.mode = MS_QUERY_MULTIPLE;
514   map->query.layer = lp->index;
515   map->query.rect = sQueryRect;
516 
517   if(map->debug == MS_DEBUGLEVEL_VVV) {
518     tmpfilename = msTmpFile(map, map->mappath, NULL, "_filter.map");
519     if (tmpfilename == NULL) {
520       tmpfilename = msTmpFile(map, NULL, NULL, "_filter.map" );
521     }
522     if (tmpfilename) {
523       msSaveMap(map,tmpfilename);
524       msDebug("FLTApplySimpleSQLFilter(): Map file after Filter was applied %s\n", tmpfilename);
525       msFree(tmpfilename);
526     }
527   }
528 
529   /*for oracle connection, if we have a simple filter with no spatial constraints
530     we should set the connection function to NONE to have a better performance
531     (#2725)*/
532 
533   if (lp->connectiontype ==  MS_ORACLESPATIAL && FLTIsSimpleFilterNoSpatial(psNode)) {
534     if (strcasestr(lp->data, "USING") == 0)
535       lp->data = msStringConcatenate(lp->data, " USING NONE");
536     else if (strcasestr(lp->data, "NONE") == 0) {
537       /*if one of the functions is used, just replace it with NONE*/
538       if (strcasestr(lp->data, "FILTER"))
539         lp->data = msCaseReplaceSubstring(lp->data, "FILTER", "NONE");
540       else if (strcasestr(lp->data, "GEOMRELATE"))
541         lp->data = msCaseReplaceSubstring(lp->data, "GEOMRELATE", "NONE");
542       else if (strcasestr(lp->data, "RELATE"))
543         lp->data = msCaseReplaceSubstring(lp->data, "RELATE", "NONE");
544       else if (strcasestr(lp->data, "VERSION")) {
545         /*should add NONE just before the VERSION. Cases are:
546           DATA "ORA_GEOMETRY FROM data USING VERSION 10g
547           DATA "ORA_GEOMETRY FROM data  USING UNIQUE FID VERSION 10g"
548          */
549         pszTmp = (char *)strcasestr(lp->data, "VERSION");
550         pszTmp2 = msStringConcatenate(pszTmp2, " NONE ");
551         pszTmp2 = msStringConcatenate(pszTmp2, pszTmp);
552 
553         lp->data = msCaseReplaceSubstring(lp->data, pszTmp, pszTmp2);
554 
555         msFree(pszTmp2);
556 
557       } else if (strcasestr(lp->data, "SRID")) {
558         lp->data = msStringConcatenate(lp->data, " NONE");
559       }
560     }
561   }
562 
563   return msQueryByRect(map);
564 
565   /* return MS_SUCCESS; */
566 }
567 
568 /************************************************************************/
569 /*                            FLTSplitFilters                           */
570 /*                                                                      */
571 /*    Split filters separated by parentheses into an array of strings.  */
572 /************************************************************************/
FLTSplitFilters(const char * pszStr,int * pnTokens)573 char** FLTSplitFilters(const char* pszStr, int* pnTokens)
574 {
575     const char* pszTokenBegin;
576     char** papszRet = NULL;
577     int nTokens = 0;
578     char chStringQuote = '\0';
579     int nXMLIndent = 0;
580     int bInBracket = FALSE;
581 
582     if( *pszStr != '(' )
583     {
584         *pnTokens = 0;
585         return NULL;
586     }
587     pszStr ++;
588     pszTokenBegin = pszStr;
589     while( *pszStr != '\0' )
590     {
591         /* Ignore any character until end of quoted string */
592         if( chStringQuote != '\0' )
593         {
594             if( *pszStr == chStringQuote )
595                 chStringQuote = 0;
596         }
597         /* Detect begin of quoted string only for an XML attribute, i.e. between < and > */
598         else if( bInBracket && (*pszStr == '\'' || *pszStr == '"') )
599         {
600             chStringQuote = *pszStr;
601         }
602         /* Begin of XML element */
603         else if( *pszStr == '<' )
604         {
605             bInBracket = TRUE;
606             if( pszStr[1] == '/' )
607                 nXMLIndent --;
608             else if( pszStr[1] != '!' )
609                 nXMLIndent ++;
610         }
611         /* <something /> case */
612         else if (*pszStr == '/' && pszStr[1] == '>' )
613         {
614             bInBracket = FALSE;
615             nXMLIndent --;
616             pszStr ++;
617         }
618         /* End of XML element */
619         else if( *pszStr == '>' )
620         {
621             bInBracket = FALSE;
622         }
623         /* Only detect and of filter when XML indentation goes back to zero */
624         else if( nXMLIndent == 0 && *pszStr == ')' )
625         {
626             papszRet = (char**) msSmallRealloc(papszRet, sizeof(char*) * (nTokens + 1));
627             papszRet[nTokens] = msStrdup(pszTokenBegin);
628             papszRet[nTokens][pszStr - pszTokenBegin] = '\0';
629             nTokens ++;
630             if( pszStr[1] != '(' )
631             {
632                 break;
633             }
634             pszStr ++;
635             pszTokenBegin = pszStr + 1;
636         }
637         pszStr ++;
638     }
639     *pnTokens = nTokens;
640     return papszRet;
641 }
642 
643 /************************************************************************/
644 /*                            FLTIsSimpleFilter                         */
645 /*                                                                      */
646 /*      Filter encoding with only attribute queries and only one bbox.  */
647 /************************************************************************/
FLTIsSimpleFilter(FilterEncodingNode * psNode)648 int FLTIsSimpleFilter(FilterEncodingNode *psNode)
649 {
650   if (FLTValidForBBoxFilter(psNode)) {
651     if (FLTNumberOfFilterType(psNode, "DWithin") == 0 &&
652         FLTNumberOfFilterType(psNode, "Intersect") == 0 &&
653         FLTNumberOfFilterType(psNode, "Intersects") == 0 &&
654         FLTNumberOfFilterType(psNode, "Equals") == 0 &&
655         FLTNumberOfFilterType(psNode, "Disjoint") == 0 &&
656         FLTNumberOfFilterType(psNode, "Touches") == 0 &&
657         FLTNumberOfFilterType(psNode, "Crosses") == 0 &&
658         FLTNumberOfFilterType(psNode, "Within") == 0 &&
659         FLTNumberOfFilterType(psNode, "Contains") == 0 &&
660         FLTNumberOfFilterType(psNode, "Overlaps") == 0 &&
661         FLTNumberOfFilterType(psNode, "Beyond") == 0)
662       return TRUE;
663   }
664 
665   return FALSE;
666 }
667 
668 /************************************************************************/
669 /*                          FLTApplyFilterToLayer                       */
670 /*                                                                      */
671 /*      Use the filter encoding node to create mapserver expressions    */
672 /*      and apply it to the layer.                                      */
673 /************************************************************************/
FLTApplyFilterToLayer(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)674 int FLTApplyFilterToLayer(FilterEncodingNode *psNode, mapObj *map, int iLayerIndex)
675 {
676   layerObj *layer = GET_LAYER(map, iLayerIndex);
677 
678   if ( ! layer->vtable) {
679     int rv =  msInitializeVirtualTable(layer);
680     if (rv != MS_SUCCESS)
681       return rv;
682   }
683   return layer->vtable->LayerApplyFilterToLayer(psNode, map,  iLayerIndex);
684 }
685 
686 /************************************************************************/
687 /*               FLTLayerApplyCondSQLFilterToLayer                       */
688 /*                                                                      */
689 /* Helper function for layer virtual table architecture                 */
690 /************************************************************************/
FLTLayerApplyCondSQLFilterToLayer(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)691 int FLTLayerApplyCondSQLFilterToLayer(FilterEncodingNode *psNode, mapObj *map, int iLayerIndex)
692 {
693   return FLTLayerApplyPlainFilterToLayer(psNode, map, iLayerIndex);
694 }
695 
696 
697 /************************************************************************/
698 /*                           FLTGetTopBBOX                              */
699 /*                                                                      */
700 /* Return the "top" BBOX if there's a unique one.                       */
701 /************************************************************************/
FLTGetTopBBOXInternal(FilterEncodingNode * psNode,FilterEncodingNode ** ppsTopBBOX,int * pnCount)702 static int FLTGetTopBBOXInternal(FilterEncodingNode *psNode, FilterEncodingNode** ppsTopBBOX, int *pnCount)
703 {
704   if (psNode->pszValue && strcasecmp(psNode->pszValue, "BBOX") == 0) {
705     (*pnCount) ++;
706     if( *pnCount == 1 )
707     {
708       *ppsTopBBOX = psNode;
709       return TRUE;
710     }
711     *ppsTopBBOX = NULL;
712     return FALSE;
713   }
714   else if (psNode->pszValue && strcasecmp(psNode->pszValue, "AND") == 0) {
715     return FLTGetTopBBOXInternal(psNode->psLeftNode, ppsTopBBOX, pnCount) &&
716            FLTGetTopBBOXInternal(psNode->psRightNode, ppsTopBBOX, pnCount);
717   }
718   else
719   {
720     return TRUE;
721   }
722 }
723 
FLTGetTopBBOX(FilterEncodingNode * psNode)724 static FilterEncodingNode* FLTGetTopBBOX(FilterEncodingNode *psNode)
725 {
726   int nCount = 0;
727   FilterEncodingNode* psTopBBOX = NULL;
728   FLTGetTopBBOXInternal(psNode, &psTopBBOX, &nCount);
729   return psTopBBOX;
730 }
731 
732 /************************************************************************/
733 /*                   FLTLayerSetInvalidRectIfSupported                  */
734 /*                                                                      */
735 /*  This function will set in *rect a very huge extent if the layer     */
736 /*  wfs_use_default_extent_for_getfeature metadata item is set to false */
737 /*  and the layer supports such degenerate rectangle, as a hint that    */
738 /*  they should not issue a spatial filter.                             */
739 /************************************************************************/
740 
FLTLayerSetInvalidRectIfSupported(layerObj * lp,rectObj * rect)741 int FLTLayerSetInvalidRectIfSupported(layerObj* lp,
742                                       rectObj* rect)
743 {
744     const char* pszUseDefaultExtent = msOWSLookupMetadata(&(lp->metadata), "F",
745                                               "use_default_extent_for_getfeature");
746     if( pszUseDefaultExtent && !CSLTestBoolean(pszUseDefaultExtent) &&
747         (lp->connectiontype == MS_OGR ||
748         ((lp->connectiontype == MS_PLUGIN) && (strstr(lp->plugin_library,"msplugin_mssql2008") != NULL))) )
749     {
750         const rectObj rectInvalid = MS_INIT_INVALID_RECT;
751         *rect = rectInvalid;
752         return MS_TRUE;
753     }
754     return MS_FALSE;
755 }
756 
757 /************************************************************************/
758 /*                   FLTLayerApplyPlainFilterToLayer                    */
759 /*                                                                      */
760 /* Helper function for layer virtual table architecture                 */
761 /************************************************************************/
FLTLayerApplyPlainFilterToLayer(FilterEncodingNode * psNode,mapObj * map,int iLayerIndex)762 int FLTLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
763                                     int iLayerIndex)
764 {
765   char *pszExpression  =NULL;
766   int status =MS_FALSE;
767   layerObj* lp = GET_LAYER(map, iLayerIndex);
768 
769   pszExpression = FLTGetCommonExpression(psNode,  lp);
770   if (pszExpression) {
771     FilterEncodingNode* psTopBBOX;
772     rectObj rect = map->extent;
773 
774     FLTLayerSetInvalidRectIfSupported(lp, &rect);
775 
776     psTopBBOX = FLTGetTopBBOX(psNode);
777     if( psTopBBOX )
778     {
779       int can_remove_expression = MS_TRUE;
780       const char* pszEPSG = FLTGetBBOX(psNode, &rect);
781       if(pszEPSG && map->projection.numargs > 0) {
782         projectionObj sProjTmp;
783         msInitProjection(&sProjTmp);
784         msProjectionInheritContextFrom(&sProjTmp, &map->projection);
785         /* Use the non EPSG variant since axis swapping is done in FLTDoAxisSwappingIfNecessary */
786         if (msLoadProjectionString(&sProjTmp, pszEPSG) == 0) {
787           rectObj oldRect = rect;
788           msProjectRect(&sProjTmp, &map->projection, &rect);
789           /* If reprojection is involved, do not remove the expression */
790           if( rect.minx != oldRect.minx ||
791               rect.miny != oldRect.miny ||
792               rect.maxx != oldRect.maxx ||
793               rect.maxy != oldRect.maxy )
794           {
795             can_remove_expression = MS_FALSE;
796           }
797         }
798         msFreeProjection(&sProjTmp);
799       }
800 
801       /* Small optimization: if the query is just a BBOX, then do a */
802       /* msQueryByRect() */
803       if( psTopBBOX == psNode && can_remove_expression )
804       {
805         msFree(pszExpression);
806         pszExpression = NULL;
807       }
808     }
809 
810     if(map->debug == MS_DEBUGLEVEL_VVV)
811     {
812       if( pszExpression )
813         msDebug("FLTLayerApplyPlainFilterToLayer(): %s, rect=%.15g,%.15g,%.15g,%.15g\n", pszExpression, rect.minx, rect.miny, rect.maxx, rect.maxy);
814       else
815         msDebug("FLTLayerApplyPlainFilterToLayer(): rect=%.15g,%.15g,%.15g,%.15g\n", rect.minx, rect.miny, rect.maxx, rect.maxy);
816     }
817 
818     status = FLTApplyFilterToLayerCommonExpressionWithRect(map, iLayerIndex,
819                                                            pszExpression, rect);
820     msFree(pszExpression);
821   }
822 
823   return status;
824 }
825 
826 
827 
828 /************************************************************************/
829 /*            FilterNode *FLTPaserFilterEncoding(char *szXMLString)     */
830 /*                                                                      */
831 /*      Parses an Filter Encoding XML string and creates a              */
832 /*      FilterEncodingNodes corresponding to the string.                */
833 /*      Returns a pointer to the first node or NULL if                  */
834 /*      unsuccessfull.                                                  */
835 /*      Calling function should use FreeFilterEncodingNode function     */
836 /*      to free memeory.                                                */
837 /************************************************************************/
FLTParseFilterEncoding(const char * szXMLString)838 FilterEncodingNode *FLTParseFilterEncoding(const char *szXMLString)
839 {
840   CPLXMLNode *psRoot = NULL, *psChild=NULL, *psFilter=NULL;
841   FilterEncodingNode *psFilterNode = NULL;
842 
843   if (szXMLString == NULL || strlen(szXMLString) <= 0 ||
844       (strstr(szXMLString, "Filter") == NULL))
845     return NULL;
846 
847   psRoot = CPLParseXMLString(szXMLString);
848 
849   if( psRoot == NULL)
850     return NULL;
851 
852   /* strip namespaces. We srtip all name spaces (#1350)*/
853   CPLStripXMLNamespace(psRoot, NULL, 1);
854 
855   /* -------------------------------------------------------------------- */
856   /*      get the root element (Filter).                                  */
857   /* -------------------------------------------------------------------- */
858   psFilter = CPLGetXMLNode(psRoot, "=Filter");
859   if (!psFilter)
860   {
861     CPLDestroyXMLNode( psRoot );
862     return NULL;
863   }
864 
865   psChild = psFilter->psChild;
866   while (psChild) {
867     if (FLTIsSupportedFilterType(psChild)) {
868       psFilterNode = FLTCreateFilterEncodingNode();
869       FLTInsertElementInNode(psFilterNode, psChild);
870       break;
871     } else
872       psChild = psChild->psNext;
873   }
874 
875   CPLDestroyXMLNode( psRoot );
876 
877   /* -------------------------------------------------------------------- */
878   /*      validate the node tree to make sure that all the nodes are valid.*/
879   /* -------------------------------------------------------------------- */
880   if (!FLTValidFilterNode(psFilterNode)) {
881     FLTFreeFilterEncodingNode(psFilterNode);
882     return NULL;
883   }
884 
885 
886   return psFilterNode;
887 }
888 
889 
890 /************************************************************************/
891 /*      int FLTValidFilterNode(FilterEncodingNode *psFilterNode)        */
892 /*                                                                      */
893 /*      Validate that all the nodes are filled properly. We could       */
894 /*      have parts of the nodes that are correct and part which         */
895 /*      could be incorrect if the filter string sent is corrupted       */
896 /*      (eg missing a value :<PropertyName><PropertyName>)              */
897 /************************************************************************/
FLTValidFilterNode(FilterEncodingNode * psFilterNode)898 int FLTValidFilterNode(FilterEncodingNode *psFilterNode)
899 {
900   int  bReturn = 0;
901 
902   if (!psFilterNode)
903     return 0;
904 
905   if (psFilterNode->eType == FILTER_NODE_TYPE_UNDEFINED)
906     return 0;
907 
908   if (psFilterNode->psLeftNode) {
909     bReturn = FLTValidFilterNode(psFilterNode->psLeftNode);
910     if (bReturn == 0)
911       return 0;
912     else if (psFilterNode->psRightNode)
913       return FLTValidFilterNode(psFilterNode->psRightNode);
914   }
915 
916   return 1;
917 }
918 
919 /************************************************************************/
920 /*                       FLTIsGeometryFilterNodeType                    */
921 /************************************************************************/
922 
FLTIsGeometryFilterNodeType(int eType)923 static int FLTIsGeometryFilterNodeType(int eType)
924 {
925     return (eType == FILTER_NODE_TYPE_GEOMETRY_POINT ||
926             eType == FILTER_NODE_TYPE_GEOMETRY_LINE ||
927             eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON);
928 }
929 
930 /************************************************************************/
931 /*                          FLTFreeFilterEncodingNode                   */
932 /*                                                                      */
933 /*      recursive freeing of Filter Encoding nodes.                      */
934 /************************************************************************/
FLTFreeFilterEncodingNode(FilterEncodingNode * psFilterNode)935 void FLTFreeFilterEncodingNode(FilterEncodingNode *psFilterNode)
936 {
937   if (psFilterNode) {
938     if (psFilterNode->psLeftNode) {
939       FLTFreeFilterEncodingNode(psFilterNode->psLeftNode);
940       psFilterNode->psLeftNode = NULL;
941     }
942     if (psFilterNode->psRightNode) {
943       FLTFreeFilterEncodingNode(psFilterNode->psRightNode);
944       psFilterNode->psRightNode = NULL;
945     }
946 
947     if (psFilterNode->pszSRS)
948       free( psFilterNode->pszSRS);
949 
950     if( psFilterNode->pOther ) {
951       if (psFilterNode->pszValue != NULL &&
952           strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
953         FEPropertyIsLike* propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
954         if( propIsLike->pszWildCard )
955           free( propIsLike->pszWildCard );
956         if( propIsLike->pszSingleChar )
957           free( propIsLike->pszSingleChar );
958         if( propIsLike->pszEscapeChar )
959           free( propIsLike->pszEscapeChar );
960       } else if (FLTIsGeometryFilterNodeType(psFilterNode->eType)) {
961         msFreeShape((shapeObj *)(psFilterNode->pOther));
962       }
963       /* else */
964       /* TODO free pOther special fields */
965       free( psFilterNode->pOther );
966     }
967 
968     /* Cannot free pszValue before, 'cause we are testing it above */
969     if( psFilterNode->pszValue )
970       free( psFilterNode->pszValue );
971 
972     free(psFilterNode);
973   }
974 }
975 
976 
977 /************************************************************************/
978 /*                         FLTCreateFilterEncodingNode                  */
979 /*                                                                      */
980 /*      return a FilterEncoding node.                                    */
981 /************************************************************************/
FLTCreateFilterEncodingNode(void)982 FilterEncodingNode *FLTCreateFilterEncodingNode(void)
983 {
984   FilterEncodingNode *psFilterNode = NULL;
985 
986   psFilterNode =
987   (FilterEncodingNode *)malloc(sizeof (FilterEncodingNode));
988   psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
989   psFilterNode->pszValue = NULL;
990   psFilterNode->pOther = NULL;
991   psFilterNode->pszSRS = NULL;
992   psFilterNode->psLeftNode = NULL;
993   psFilterNode->psRightNode = NULL;
994 
995   return psFilterNode;
996 }
997 
FLTCreateBinaryCompFilterEncodingNode(void)998 FilterEncodingNode *FLTCreateBinaryCompFilterEncodingNode(void)
999 {
1000   FilterEncodingNode *psFilterNode = NULL;
1001 
1002   psFilterNode = FLTCreateFilterEncodingNode();
1003   /* used to store case sensitivity flag. Default is 0 meaning the
1004      comparing is case sensititive */
1005   psFilterNode->pOther = (int *)malloc(sizeof(int));
1006   (*(int *)(psFilterNode->pOther)) = 0;
1007 
1008   return psFilterNode;
1009 }
1010 
1011 
1012 /************************************************************************/
1013 /*                           FLTFindGeometryNode                        */
1014 /*                                                                      */
1015 /************************************************************************/
1016 
FLTFindGeometryNode(CPLXMLNode * psXMLNode,int * pbPoint,int * pbLine,int * pbPolygon)1017 static CPLXMLNode* FLTFindGeometryNode(CPLXMLNode* psXMLNode,
1018                                        int* pbPoint,
1019                                        int* pbLine,
1020                                        int* pbPolygon)
1021 {
1022     CPLXMLNode *psGMLElement = NULL;
1023 
1024     psGMLElement = CPLGetXMLNode(psXMLNode, "Point");
1025     if (!psGMLElement)
1026         psGMLElement =  CPLGetXMLNode(psXMLNode, "PointType");
1027     if (psGMLElement)
1028         *pbPoint =1;
1029     else {
1030       psGMLElement= CPLGetXMLNode(psXMLNode, "Polygon");
1031       if (psGMLElement)
1032         *pbPolygon = 1;
1033       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiPolygon")))
1034         *pbPolygon = 1;
1035       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Surface")))
1036         *pbPolygon = 1;
1037       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiSurface")))
1038         *pbPolygon = 1;
1039       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Box")))
1040         *pbPolygon = 1;
1041       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Envelope")))
1042         *pbPolygon = 1;
1043       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "LineString")))
1044         *pbLine = 1;
1045       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiLineString")))
1046         *pbLine = 1;
1047       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Curve")))
1048         *pbLine = 1;
1049       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiCurve")))
1050         *pbLine = 1;
1051       else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiPoint")))
1052         *pbPoint = 1;
1053     }
1054     return psGMLElement;
1055 }
1056 
1057 /************************************************************************/
1058 /*                           FLTGetPropertyName                         */
1059 /************************************************************************/
FLTGetPropertyName(CPLXMLNode * psXMLNode)1060 static const char* FLTGetPropertyName(CPLXMLNode* psXMLNode)
1061 {
1062     const char* pszPropertyName;
1063 
1064     pszPropertyName = CPLGetXMLValue(psXMLNode, "PropertyName", NULL);
1065     if( pszPropertyName == NULL ) /* FE 2.0 ? */
1066         pszPropertyName = CPLGetXMLValue(psXMLNode, "ValueReference", NULL);
1067     return pszPropertyName;
1068 }
1069 
1070 /************************************************************************/
1071 /*                          FLTGetFirstChildNode                        */
1072 /************************************************************************/
FLTGetFirstChildNode(CPLXMLNode * psXMLNode)1073 static CPLXMLNode* FLTGetFirstChildNode(CPLXMLNode* psXMLNode)
1074 {
1075     if( psXMLNode == NULL )
1076         return NULL;
1077     psXMLNode = psXMLNode->psChild;
1078     while( psXMLNode != NULL )
1079     {
1080         if( psXMLNode->eType == CXT_Element )
1081             return psXMLNode;
1082         psXMLNode = psXMLNode->psNext;
1083     }
1084     return NULL;
1085 }
1086 
1087 /************************************************************************/
1088 /*                        FLTGetNextSibblingNode                        */
1089 /************************************************************************/
FLTGetNextSibblingNode(CPLXMLNode * psXMLNode)1090 static CPLXMLNode* FLTGetNextSibblingNode(CPLXMLNode* psXMLNode)
1091 {
1092     if( psXMLNode == NULL )
1093         return NULL;
1094     psXMLNode = psXMLNode->psNext;
1095     while( psXMLNode != NULL )
1096     {
1097         if( psXMLNode->eType == CXT_Element )
1098             return psXMLNode;
1099         psXMLNode = psXMLNode->psNext;
1100     }
1101     return NULL;
1102 }
1103 
1104 /************************************************************************/
1105 /*                           FLTInsertElementInNode                     */
1106 /*                                                                      */
1107 /*      Utility function to parse an XML node and transfer the          */
1108 /*      contents into the Filter Encoding node structure.               */
1109 /************************************************************************/
FLTInsertElementInNode(FilterEncodingNode * psFilterNode,CPLXMLNode * psXMLNode)1110 void FLTInsertElementInNode(FilterEncodingNode *psFilterNode,
1111                             CPLXMLNode *psXMLNode)
1112 {
1113   int nStrLength = 0;
1114   char *pszTmp = NULL;
1115   FilterEncodingNode *psCurFilNode= NULL;
1116   CPLXMLNode *psCurXMLNode = NULL;
1117   CPLXMLNode *psTmpNode = NULL;
1118   CPLXMLNode *psFeatureIdNode = NULL;
1119   const char *pszFeatureId=NULL;
1120   char *pszFeatureIdList=NULL;
1121 
1122   if (psFilterNode && psXMLNode && psXMLNode->pszValue) {
1123     psFilterNode->pszValue = msStrdup(psXMLNode->pszValue);
1124     psFilterNode->psLeftNode = NULL;
1125     psFilterNode->psRightNode = NULL;
1126 
1127     /* -------------------------------------------------------------------- */
1128     /*      Logical filter. AND, OR and NOT are supported. Example of       */
1129     /*      filer using logical filters :                                   */
1130     /*      <Filter>                                                        */
1131     /*        <And>                                                         */
1132     /*          <PropertyIsGreaterThan>                                     */
1133     /*            <PropertyName>Person/Age</PropertyName>                   */
1134     /*            <Literal>50</Literal>                                     */
1135     /*          </PropertyIsGreaterThan>                                    */
1136     /*          <PropertyIsEqualTo>                                         */
1137     /*             <PropertyName>Person/Address/City</PropertyName>         */
1138     /*             <Literal>Toronto</Literal>                               */
1139     /*          </PropertyIsEqualTo>                                        */
1140     /*        </And>                                                        */
1141     /*      </Filter>                                                       */
1142     /* -------------------------------------------------------------------- */
1143     if (FLTIsLogicalFilterType(psXMLNode->pszValue)) {
1144       psFilterNode->eType = FILTER_NODE_TYPE_LOGICAL;
1145       if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
1146           strcasecmp(psFilterNode->pszValue, "OR") == 0) {
1147         CPLXMLNode* psFirstNode = FLTGetFirstChildNode(psXMLNode);
1148         CPLXMLNode* psSecondNode = FLTGetNextSibblingNode(psFirstNode);
1149         if (psFirstNode && psSecondNode) {
1150           /*2 operators */
1151           CPLXMLNode* psNextNode = FLTGetNextSibblingNode(psSecondNode);
1152           if (psNextNode == NULL) {
1153             psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1154             FLTInsertElementInNode(psFilterNode->psLeftNode, psFirstNode);
1155             psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1156             FLTInsertElementInNode(psFilterNode->psRightNode, psSecondNode);
1157           } else {
1158             psCurXMLNode = psFirstNode;
1159             psCurFilNode = psFilterNode;
1160             while(psCurXMLNode) {
1161               psNextNode = FLTGetNextSibblingNode(psCurXMLNode);
1162               if (FLTGetNextSibblingNode(psNextNode)) {
1163                 psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
1164                 FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
1165                 psCurFilNode->psRightNode = FLTCreateFilterEncodingNode();
1166                 psCurFilNode->psRightNode->eType = FILTER_NODE_TYPE_LOGICAL;
1167                 psCurFilNode->psRightNode->pszValue = msStrdup(psFilterNode->pszValue);
1168 
1169                 psCurFilNode = psCurFilNode->psRightNode;
1170                 psCurXMLNode = psNextNode;
1171               } else { /*last 2 operators*/
1172                 psCurFilNode->psLeftNode = FLTCreateFilterEncodingNode();
1173                 FLTInsertElementInNode(psCurFilNode->psLeftNode, psCurXMLNode);
1174 
1175                 psCurFilNode->psRightNode = FLTCreateFilterEncodingNode();
1176                 FLTInsertElementInNode(psCurFilNode->psRightNode, psNextNode);
1177                 break;
1178               }
1179             }
1180           }
1181         }
1182         else
1183           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1184       } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
1185         CPLXMLNode* psFirstNode = FLTGetFirstChildNode(psXMLNode);
1186         if (psFirstNode) {
1187           psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1188           FLTInsertElementInNode(psFilterNode->psLeftNode,
1189                                  psFirstNode);
1190         }
1191         else
1192           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1193       } else
1194         psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1195     }/* end if is logical */
1196     /* -------------------------------------------------------------------- */
1197     /*      Spatial Filter.                                                 */
1198     /*      BBOX :                                                          */
1199     /*      <Filter>                                                        */
1200     /*       <BBOX>                                                         */
1201     /*        <PropertyName>Geometry</PropertyName>                         */
1202     /*        <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
1203     /*          <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
1204     /*        </gml:Box>                                                    */
1205     /*       </BBOX>                                                        */
1206     /*      </Filter>                                                       */
1207     /*                                                                      */
1208     /*       DWithin                                                        */
1209     /*                                                                      */
1210     /*      <xsd:element name="DWithin"                                     */
1211     /*      type="ogc:DistanceBufferType"                                   */
1212     /*      substitutionGroup="ogc:spatialOps"/>                            */
1213     /*                                                                      */
1214     /*      <xsd:complexType name="DistanceBufferType">                     */
1215     /*         <xsd:complexContent>                                         */
1216     /*            <xsd:extension base="ogc:SpatialOpsType">                 */
1217     /*               <xsd:sequence>                                         */
1218     /*                  <xsd:element ref="ogc:PropertyName"/>               */
1219     /*                  <xsd:element ref="gml:_Geometry"/>                  */
1220     /*                  <xsd:element name="Distance" type="ogc:DistanceType"/>*/
1221     /*               </xsd:sequence>                                        */
1222     /*            </xsd:extension>                                          */
1223     /*         </xsd:complexContent>                                        */
1224     /*      </xsd:complexType>                                              */
1225     /*                                                                      */
1226     /*                                                                      */
1227     /*       <Filter>                                                       */
1228     /*       <DWithin>                                                      */
1229     /*        <PropertyName>Geometry</PropertyName>                         */
1230     /*        <gml:Point>                                                   */
1231     /*          <gml:coordinates>13.0983,31.5899</gml:coordinates>          */
1232     /*        </gml:Point>                                                  */
1233     /*        <Distance units="url#m">10</Distance>                         */
1234     /*       </DWithin>                                                     */
1235     /*      </Filter>                                                       */
1236     /*                                                                      */
1237     /*       Intersect                                                      */
1238     /*                                                                      */
1239     /*       type="ogc:BinarySpatialOpType" substitutionGroup="ogc:spatialOps"/>*/
1240     /*      <xsd:element name="Intersects"                                  */
1241     /*      type="ogc:BinarySpatialOpType"                                  */
1242     /*      substitutionGroup="ogc:spatialOps"/>                            */
1243     /*                                                                      */
1244     /*      <xsd:complexType name="BinarySpatialOpType">                    */
1245     /*      <xsd:complexContent>                                            */
1246     /*      <xsd:extension base="ogc:SpatialOpsType">                       */
1247     /*      <xsd:sequence>                                                  */
1248     /*      <xsd:element ref="ogc:PropertyName"/>                           */
1249     /*      <xsd:choice>                                                    */
1250     /*      <xsd:element ref="gml:_Geometry"/>                              */
1251     /*      <xsd:element ref="gml:Box"/>                                    */
1252     /*      </xsd:sequence>                                                 */
1253     /*      </xsd:extension>                                                */
1254     /*      </xsd:complexContent>                                           */
1255     /*      </xsd:complexType>                                              */
1256     /* -------------------------------------------------------------------- */
1257     else if (FLTIsSpatialFilterType(psXMLNode->pszValue)) {
1258       psFilterNode->eType = FILTER_NODE_TYPE_SPATIAL;
1259 
1260       if (strcasecmp(psXMLNode->pszValue, "BBOX") == 0) {
1261         char *pszSRS = NULL;
1262         const char* pszPropertyName = NULL;
1263         CPLXMLNode *psBox = NULL, *psEnvelope=NULL;
1264         rectObj sBox = {0};
1265 
1266         int bCoordinatesValid = 0;
1267 
1268         pszPropertyName = FLTGetPropertyName(psXMLNode);
1269         psBox = CPLGetXMLNode(psXMLNode, "Box");
1270         if (!psBox)
1271           psBox = CPLGetXMLNode(psXMLNode, "BoxType");
1272 
1273         /*FE 1.0 used box FE1.1 uses envelop*/
1274         if (psBox)
1275           bCoordinatesValid = FLTParseGMLBox(psBox, &sBox, &pszSRS);
1276         else if ((psEnvelope = CPLGetXMLNode(psXMLNode, "Envelope")))
1277           bCoordinatesValid = FLTParseGMLEnvelope(psEnvelope, &sBox, &pszSRS);
1278 
1279         if (bCoordinatesValid) {
1280           /*set the srs if available*/
1281           if (pszSRS)
1282             psFilterNode->pszSRS = pszSRS;
1283 
1284           psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1285           psFilterNode->psLeftNode->eType =  FILTER_NODE_TYPE_PROPERTYNAME;
1286           /* PropertyName is optional since FE 1.1.0, in which case */
1287           /* the BBOX must apply to all geometry fields. As we support */
1288           /* currently only one geometry field, this doesn't make much */
1289           /* difference to further processing. */
1290           if( pszPropertyName != NULL ) {
1291             psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1292           }
1293 
1294           /* coordinates */
1295           psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1296           psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BBOX;
1297           psFilterNode->psRightNode->pOther =
1298           (rectObj *)msSmallMalloc(sizeof(rectObj));
1299           ((rectObj *)psFilterNode->psRightNode->pOther)->minx = sBox.minx;
1300           ((rectObj *)psFilterNode->psRightNode->pOther)->miny = sBox.miny;
1301           ((rectObj *)psFilterNode->psRightNode->pOther)->maxx = sBox.maxx;
1302           ((rectObj *)psFilterNode->psRightNode->pOther)->maxy =  sBox.maxy;
1303         } else {
1304           msFree(pszSRS);
1305           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1306         }
1307       } else if (strcasecmp(psXMLNode->pszValue, "DWithin") == 0 ||
1308                  strcasecmp(psXMLNode->pszValue, "Beyond") == 0)
1309 
1310       {
1311         shapeObj *psShape = NULL;
1312         int bPoint = 0, bLine = 0, bPolygon = 0;
1313         const char *pszUnits = NULL;
1314         const char* pszDistance = NULL;
1315         const char* pszPropertyName;
1316         char *pszSRS = NULL;
1317 
1318         CPLXMLNode *psGMLElement = NULL, *psDistance=NULL;
1319 
1320         pszPropertyName = FLTGetPropertyName(psXMLNode);
1321 
1322         psGMLElement = FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
1323 
1324         psDistance = CPLGetXMLNode(psXMLNode, "Distance");
1325         if( psDistance != NULL )
1326             pszDistance = CPLGetXMLValue(psDistance, NULL, NULL );
1327         if (pszPropertyName != NULL && psGMLElement && psDistance != NULL ) {
1328           pszUnits = CPLGetXMLValue(psDistance, "units", NULL);
1329           if( pszUnits == NULL ) /* FE 2.0 */
1330               pszUnits = CPLGetXMLValue(psDistance, "uom", NULL);
1331           psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
1332           msInitShape(psShape);
1333           if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS))
1334           {
1335             /*set the srs if available*/
1336             if (pszSRS)
1337               psFilterNode->pszSRS = pszSRS;
1338 
1339             psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1340             psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1341             psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1342 
1343             psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1344             if (bPoint)
1345               psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POINT;
1346             else if (bLine)
1347               psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
1348             else if (bPolygon)
1349               psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POLYGON;
1350             psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
1351             /*the value will be distance;units*/
1352             psFilterNode->psRightNode->pszValue = msStrdup(pszDistance);
1353             if (pszUnits) {
1354               psFilterNode->psRightNode->pszValue= msStringConcatenate(psFilterNode->psRightNode->pszValue, ";");
1355               psFilterNode->psRightNode->pszValue= msStringConcatenate(psFilterNode->psRightNode->pszValue, pszUnits);
1356             }
1357           }
1358           else
1359           {
1360               free(psShape);
1361               msFree(pszSRS);
1362               psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1363           }
1364         } else
1365           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1366       } else if (strcasecmp(psXMLNode->pszValue, "Intersect") == 0 ||
1367                  strcasecmp(psXMLNode->pszValue, "Intersects") == 0 ||
1368                  strcasecmp(psXMLNode->pszValue, "Equals") == 0 ||
1369                  strcasecmp(psXMLNode->pszValue, "Disjoint") == 0 ||
1370                  strcasecmp(psXMLNode->pszValue, "Touches") == 0 ||
1371                  strcasecmp(psXMLNode->pszValue, "Crosses") == 0 ||
1372                  strcasecmp(psXMLNode->pszValue, "Within") == 0 ||
1373                  strcasecmp(psXMLNode->pszValue, "Contains") == 0 ||
1374                  strcasecmp(psXMLNode->pszValue, "Overlaps") == 0) {
1375         shapeObj *psShape = NULL;
1376         int  bLine = 0, bPolygon = 0, bPoint=0;
1377         char *pszSRS = NULL;
1378         const char* pszPropertyName;
1379 
1380         CPLXMLNode *psGMLElement = NULL;
1381 
1382         pszPropertyName = FLTGetPropertyName(psXMLNode);
1383 
1384         psGMLElement = FLTFindGeometryNode(psXMLNode, &bPoint, &bLine, &bPolygon);
1385 
1386         if (pszPropertyName != NULL && psGMLElement) {
1387           psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
1388           msInitShape(psShape);
1389           if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS))
1390           {
1391             /*set the srs if available*/
1392             if (pszSRS)
1393               psFilterNode->pszSRS = pszSRS;
1394 
1395             psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1396             psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1397             psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1398 
1399             psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1400             if (bPoint)
1401               psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POINT;
1402             else if (bLine)
1403               psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
1404             else if (bPolygon)
1405               psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POLYGON;
1406             psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
1407 
1408           }
1409           else
1410           {
1411               free(psShape);
1412               msFree(pszSRS);
1413               psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1414           }
1415         } else
1416           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1417       }
1418 
1419 
1420     }/* end of is spatial */
1421 
1422 
1423     /* -------------------------------------------------------------------- */
1424     /*      Comparison Filter                                               */
1425     /* -------------------------------------------------------------------- */
1426     else if (FLTIsComparisonFilterType(psXMLNode->pszValue)) {
1427       psFilterNode->eType = FILTER_NODE_TYPE_COMPARISON;
1428       /* -------------------------------------------------------------------- */
1429       /*      binary comaparison types. Example :                             */
1430       /*                                                                      */
1431       /*      <Filter>                                                        */
1432       /*        <PropertyIsEqualTo>                                           */
1433       /*          <PropertyName>SomeProperty</PropertyName>                   */
1434       /*          <Literal>100</Literal>                                      */
1435       /*        </PropertyIsEqualTo>                                          */
1436       /*      </Filter>                                                       */
1437       /* -------------------------------------------------------------------- */
1438       if (FLTIsBinaryComparisonFilterType(psXMLNode->pszValue)) {
1439         const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1440         if (pszPropertyName != NULL ) {
1441 
1442           psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1443           psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1444           psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1445 
1446           psTmpNode = CPLSearchXMLNode(psXMLNode,  "Literal");
1447           if (psTmpNode) {
1448             const char* pszLiteral = CPLGetXMLValue(psTmpNode, NULL, NULL);
1449 
1450             psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
1451             psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
1452 
1453             if (pszLiteral != NULL) {
1454               const char* pszMatchCase;
1455 
1456               psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
1457 
1458               pszMatchCase = CPLGetXMLValue(psXMLNode, "matchCase", NULL);
1459 
1460               /*check if the matchCase attribute is set*/
1461               if( pszMatchCase != NULL && strcasecmp( pszMatchCase, "false") == 0) {
1462                 (*(int *)psFilterNode->psRightNode->pOther) = 1;
1463               }
1464 
1465             }
1466             /* special case where the user puts an empty value */
1467             /* for the Literal so it can end up as an empty  */
1468             /* string query in the expression */
1469             else
1470               psFilterNode->psRightNode->pszValue = NULL;
1471           }
1472         }
1473         if (psFilterNode->psLeftNode == NULL || psFilterNode->psRightNode == NULL)
1474           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1475       }
1476 
1477       /* -------------------------------------------------------------------- */
1478       /*      PropertyIsBetween filter : extract property name and boudary    */
1479       /*      values. The boundary  values are stored in the right            */
1480       /*      node. The values are separated by a semi-column (;)             */
1481       /*      Eg of Filter :                                                  */
1482       /*      <PropertyIsBetween>                                             */
1483       /*         <PropertyName>DEPTH</PropertyName>                           */
1484       /*         <LowerBoundary><Literal>400</Literal></LowerBoundary>        */
1485       /*         <UpperBoundary><Literal>800</Literal></UpperBoundary>        */
1486       /*      </PropertyIsBetween>                                            */
1487       /*                                                                      */
1488       /*      Or                                                              */
1489       /*      <PropertyIsBetween>                                             */
1490       /*         <PropertyName>DEPTH</PropertyName>                           */
1491       /*         <LowerBoundary>400</LowerBoundary>                           */
1492       /*         <UpperBoundary>800</UpperBoundary>                           */
1493       /*      </PropertyIsBetween>                                            */
1494       /* -------------------------------------------------------------------- */
1495       else if (strcasecmp(psXMLNode->pszValue, "PropertyIsBetween") == 0) {
1496         const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1497         CPLXMLNode* psLowerBoundary = CPLGetXMLNode(psXMLNode, "LowerBoundary");
1498         CPLXMLNode* psUpperBoundary = CPLGetXMLNode(psXMLNode, "UpperBoundary");
1499         const char* pszLowerNode = NULL;
1500         const char* pszUpperNode = NULL;
1501         if( psLowerBoundary != NULL )
1502         {
1503           /* check if the <Literal> is there */
1504           if (CPLGetXMLNode(psLowerBoundary, "Literal") != NULL)
1505             pszLowerNode = CPLGetXMLValue(psLowerBoundary, "Literal", NULL);
1506           else
1507             pszLowerNode = CPLGetXMLValue(psLowerBoundary, NULL, NULL);
1508         }
1509         if( psUpperBoundary != NULL )
1510         {
1511            if (CPLGetXMLNode(psUpperBoundary, "Literal") != NULL)
1512             pszUpperNode = CPLGetXMLValue(psUpperBoundary, "Literal", NULL);
1513           else
1514             pszUpperNode = CPLGetXMLValue(psUpperBoundary, NULL, NULL);
1515         }
1516         if (pszPropertyName != NULL && pszLowerNode != NULL && pszUpperNode != NULL) {
1517           psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1518 
1519           psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1520           psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1521 
1522           psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1523           psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BOUNDARY;
1524 
1525           /* adding a ; between bounary values */
1526           nStrLength = strlen(pszLowerNode) + strlen(pszUpperNode) + 2;
1527 
1528           psFilterNode->psRightNode->pszValue =
1529               (char *)malloc(sizeof(char)*(nStrLength));
1530           strcpy( psFilterNode->psRightNode->pszValue, pszLowerNode);
1531           strlcat(psFilterNode->psRightNode->pszValue, ";", nStrLength);
1532           strlcat(psFilterNode->psRightNode->pszValue, pszUpperNode, nStrLength);
1533 
1534 
1535         } else
1536           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1537 
1538       }/* end of PropertyIsBetween  */
1539       /* -------------------------------------------------------------------- */
1540       /*      PropertyIsLike                                                  */
1541       /*                                                                      */
1542       /*      <Filter>                                                        */
1543       /*      <PropertyIsLike wildCard="*" singleChar="#" escape="!">         */
1544       /*      <PropertyName>LAST_NAME</PropertyName>                          */
1545       /*      <Literal>JOHN*</Literal>                                        */
1546       /*      </PropertyIsLike>                                               */
1547       /*      </Filter>                                                       */
1548       /* -------------------------------------------------------------------- */
1549       else if (strcasecmp(psXMLNode->pszValue, "PropertyIsLike") == 0) {
1550         const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1551         const char* pszLiteral = CPLGetXMLValue(psXMLNode, "Literal", NULL);
1552         const char* pszWildCard = CPLGetXMLValue(psXMLNode, "wildCard", NULL);
1553         const char* pszSingleChar = CPLGetXMLValue(psXMLNode, "singleChar", NULL);
1554         const char* pszEscapeChar = CPLGetXMLValue(psXMLNode, "escape", NULL);
1555         if( pszEscapeChar == NULL )
1556             pszEscapeChar = CPLGetXMLValue(psXMLNode, "escapeChar", NULL);
1557         if (pszPropertyName != NULL && pszLiteral != NULL &&
1558             pszWildCard != NULL && pszSingleChar != NULL && pszEscapeChar != NULL)
1559         {
1560           FEPropertyIsLike* propIsLike;
1561 
1562           propIsLike = (FEPropertyIsLike *)malloc(sizeof(FEPropertyIsLike));
1563 
1564           psFilterNode->pOther = propIsLike;
1565           propIsLike->bCaseInsensitive = 0;
1566           propIsLike->pszWildCard = msStrdup(pszWildCard);
1567           propIsLike->pszSingleChar = msStrdup(pszSingleChar);
1568           propIsLike->pszEscapeChar = msStrdup(pszEscapeChar);
1569 
1570           pszTmp = (char *)CPLGetXMLValue(psXMLNode, "matchCase", NULL);
1571           if (pszTmp && strcasecmp(pszTmp, "false") == 0) {
1572             propIsLike->bCaseInsensitive =1;
1573           }
1574           /* -------------------------------------------------------------------- */
1575           /*      Create left and right node for the attribute and the value.     */
1576           /* -------------------------------------------------------------------- */
1577           psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1578 
1579           psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1580           psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1581 
1582           psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1583 
1584           psFilterNode->psRightNode->pszValue = msStrdup(pszLiteral);
1585 
1586           psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
1587         } else
1588           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1589 
1590       }
1591 
1592       else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNull") == 0) {
1593         const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1594         if( pszPropertyName != NULL )
1595         {
1596             psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1597             psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1598             psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1599         }  else
1600           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1601       }
1602 
1603       else if (strcasecmp(psXMLNode->pszValue, "PropertyIsNil") == 0) {
1604         const char* pszPropertyName = FLTGetPropertyName(psXMLNode);
1605         if( pszPropertyName != NULL )
1606         {
1607             psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1608             psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1609             psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
1610         }  else
1611           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1612       }
1613     }
1614     /* -------------------------------------------------------------------- */
1615     /*      FeatureId Filter                                                */
1616     /*                                                                      */
1617     /*      <ogc:Filter>                                                    */
1618     /*      <ogc:FeatureId fid="INWATERA_1M.1013"/>                         */
1619     /*      <ogc:FeatureId fid="INWATERA_1M.10"/>                           */
1620     /*      <ogc:FeatureId fid="INWATERA_1M.13"/>                           */
1621     /*      <ogc:FeatureId fid="INWATERA_1M.140"/>                          */
1622     /*      <ogc:FeatureId fid="INWATERA_1M.5001"/>                         */
1623     /*      <ogc:FeatureId fid="INWATERA_1M.2001"/>                         */
1624     /*      </ogc:Filter>                                                   */
1625     /*                                                                      */
1626     /*                                                                      */
1627     /*      Note that for FES1.1.0 the featureid has been depricated in     */
1628     /*      favor of GmlObjectId                                            */
1629     /*      <GmlObjectId gml:id="TREESA_1M.1234"/>                          */
1630     /*                                                                      */
1631     /*      And in FES 2.0, in favor of <fes:ResourceId rid="foo.1234"/>    */
1632     /* -------------------------------------------------------------------- */
1633     else if (FLTIsFeatureIdFilterType(psXMLNode->pszValue)) {
1634       psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
1635       pszFeatureId = CPLGetXMLValue(psXMLNode, "fid", NULL);
1636       /*for FE 1.1.0 GmlObjectId */
1637       if (pszFeatureId == NULL)
1638         pszFeatureId = CPLGetXMLValue(psXMLNode, "id", NULL);
1639       /*for FE 2.0 ResourceId */
1640       if (pszFeatureId == NULL)
1641         pszFeatureId = CPLGetXMLValue(psXMLNode, "rid", NULL);
1642       pszFeatureIdList = NULL;
1643 
1644       psFeatureIdNode = psXMLNode;
1645       while (psFeatureIdNode) {
1646         pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "fid", NULL);
1647         if (!pszFeatureId)
1648           pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "id", NULL);
1649         if (!pszFeatureId)
1650           pszFeatureId = CPLGetXMLValue(psFeatureIdNode, "rid", NULL);
1651 
1652         if (pszFeatureId) {
1653           if (pszFeatureIdList)
1654             pszFeatureIdList = msStringConcatenate(pszFeatureIdList, ",");
1655 
1656           pszFeatureIdList = msStringConcatenate(pszFeatureIdList, pszFeatureId);
1657         }
1658         psFeatureIdNode = psFeatureIdNode->psNext;
1659       }
1660 
1661       if (pszFeatureIdList) {
1662         msFree(psFilterNode->pszValue);
1663         psFilterNode->pszValue =  msStrdup(pszFeatureIdList);
1664         msFree(pszFeatureIdList);
1665       } else
1666         psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1667     }
1668 
1669     /* -------------------------------------------------------------------- */
1670     /*      Temporal Filter.                                                */
1671     /*
1672     <fes:During>
1673     <fes:ValueReference>gml:TimeInstant</fes:ValueReference>
1674     <gml:TimePeriod gml:id="TP1">
1675     <gml:begin>
1676     <gml:TimeInstant gml:id="TI1">
1677     <gml:timePosition>2005-05-17T00:00:00Z</gml:timePosition>
1678     </gml:TimeInstant>
1679     </gml:begin>
1680     <gml:end>
1681     <gml:TimeInstant gml:id="TI2">
1682     <gml:timePosition>2005-05-23T00:00:00Z</gml:timePosition>
1683     </gml:TimeInstant>
1684     </gml:end>
1685     </gml:TimePeriod>
1686     </fes:During>
1687     */
1688     /* -------------------------------------------------------------------- */
1689     else if (FLTIsTemporalFilterType(psXMLNode->pszValue)) {
1690       psFilterNode->eType = FILTER_NODE_TYPE_TEMPORAL;
1691 
1692       if (strcasecmp(psXMLNode->pszValue, "During") == 0) {
1693         const char* pszPropertyName = NULL;
1694         const char* pszBeginTime;
1695         const char* pszEndTime;
1696 
1697         pszPropertyName = FLTGetPropertyName(psXMLNode);
1698         pszBeginTime = CPLGetXMLValue(psXMLNode, "TimePeriod.begin.TimeInstant.timePosition", NULL);
1699         if( pszBeginTime == NULL )
1700             pszBeginTime = CPLGetXMLValue(psXMLNode, "TimePeriod.beginPosition", NULL);
1701         pszEndTime = CPLGetXMLValue(psXMLNode, "TimePeriod.end.TimeInstant.timePosition", NULL);
1702         if( pszEndTime == NULL )
1703             pszEndTime = CPLGetXMLValue(psXMLNode, "TimePeriod.endPosition", NULL);
1704 
1705         if (pszPropertyName && pszBeginTime && pszEndTime &&
1706             strchr(pszBeginTime, '\'') == NULL && strchr(pszBeginTime, '\\') == NULL &&
1707             strchr(pszEndTime, '\'') == NULL && strchr(pszEndTime, '\\') == NULL &&
1708             msTimeGetResolution(pszBeginTime) >= 0 &&
1709             msTimeGetResolution(pszEndTime) >= 0) {
1710 
1711           psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
1712           psFilterNode->psLeftNode->eType =  FILTER_NODE_TYPE_PROPERTYNAME;
1713           psFilterNode->psLeftNode->pszValue = msStrdup(pszPropertyName);
1714 
1715           psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
1716           psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_TIME_PERIOD;
1717           psFilterNode->psRightNode->pszValue = msSmallMalloc( strlen(pszBeginTime) + strlen(pszEndTime) + 2 );
1718           sprintf(psFilterNode->psRightNode->pszValue, "%s/%s", pszBeginTime, pszEndTime);
1719         }
1720         else
1721           psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1722       } else {
1723         psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
1724       }
1725 
1726     }/* end of is temporal */
1727 
1728 
1729 
1730   }
1731 }
1732 
1733 
1734 /************************************************************************/
1735 /*            int FLTIsLogicalFilterType((char *pszValue)                  */
1736 /*                                                                      */
1737 /*      return TRUE if the value of the node is of logical filter       */
1738 /*       encoding type.                                                 */
1739 /************************************************************************/
FLTIsLogicalFilterType(const char * pszValue)1740 int FLTIsLogicalFilterType(const char *pszValue)
1741 {
1742   if (pszValue) {
1743     if (strcasecmp(pszValue, "AND") == 0 ||
1744         strcasecmp(pszValue, "OR") == 0 ||
1745         strcasecmp(pszValue, "NOT") == 0)
1746       return MS_TRUE;
1747   }
1748 
1749   return MS_FALSE;
1750 }
1751 
1752 /************************************************************************/
1753 /*         int FLTIsBinaryComparisonFilterType(char *pszValue)             */
1754 /*                                                                      */
1755 /*      Binary comparison filter type.                                  */
1756 /************************************************************************/
FLTIsBinaryComparisonFilterType(const char * pszValue)1757 int FLTIsBinaryComparisonFilterType(const char *pszValue)
1758 {
1759   if (pszValue) {
1760     if (strcasecmp(pszValue, "PropertyIsEqualTo") == 0 ||
1761         strcasecmp(pszValue, "PropertyIsNotEqualTo") == 0 ||
1762         strcasecmp(pszValue, "PropertyIsLessThan") == 0 ||
1763         strcasecmp(pszValue, "PropertyIsGreaterThan") == 0 ||
1764         strcasecmp(pszValue, "PropertyIsLessThanOrEqualTo") == 0 ||
1765         strcasecmp(pszValue, "PropertyIsGreaterThanOrEqualTo") == 0)
1766       return MS_TRUE;
1767   }
1768 
1769   return MS_FALSE;
1770 }
1771 
1772 /************************************************************************/
1773 /*            int FLTIsComparisonFilterType(char *pszValue)                */
1774 /*                                                                      */
1775 /*      return TRUE if the value of the node is of comparison filter    */
1776 /*      encoding type.                                                  */
1777 /************************************************************************/
FLTIsComparisonFilterType(const char * pszValue)1778 int FLTIsComparisonFilterType(const char *pszValue)
1779 {
1780   if (pszValue) {
1781     if (FLTIsBinaryComparisonFilterType(pszValue) ||
1782         strcasecmp(pszValue, "PropertyIsLike") == 0 ||
1783         strcasecmp(pszValue, "PropertyIsBetween") == 0 ||
1784         strcasecmp(pszValue, "PropertyIsNull") == 0 ||
1785         strcasecmp(pszValue, "PropertyIsNil") == 0)
1786       return MS_TRUE;
1787   }
1788 
1789   return MS_FALSE;
1790 }
1791 
1792 /************************************************************************/
1793 /*            int FLTIsFeatureIdFilterType(char *pszValue)              */
1794 /*                                                                      */
1795 /*      return TRUE if the value of the node is of featureid filter     */
1796 /*      encoding type.                                                  */
1797 /************************************************************************/
FLTIsFeatureIdFilterType(const char * pszValue)1798 int FLTIsFeatureIdFilterType(const char *pszValue)
1799 {
1800   if (pszValue && (strcasecmp(pszValue, "FeatureId") == 0 ||
1801                    strcasecmp(pszValue, "GmlObjectId") == 0 ||
1802                    strcasecmp(pszValue, "ResourceId") == 0))
1803 
1804     return MS_TRUE;
1805 
1806   return MS_FALSE;
1807 }
1808 
1809 /************************************************************************/
1810 /*            int FLTIsSpatialFilterType(char *pszValue)                */
1811 /*                                                                      */
1812 /*      return TRUE if the value of the node is of spatial filter       */
1813 /*      encoding type.                                                  */
1814 /************************************************************************/
FLTIsSpatialFilterType(const char * pszValue)1815 int FLTIsSpatialFilterType(const char *pszValue)
1816 {
1817   if (pszValue) {
1818     if ( strcasecmp(pszValue, "BBOX") == 0 ||
1819          strcasecmp(pszValue, "DWithin") == 0 ||
1820          strcasecmp(pszValue, "Intersect") == 0 ||
1821          strcasecmp(pszValue, "Intersects") == 0 ||
1822          strcasecmp(pszValue, "Equals") == 0 ||
1823          strcasecmp(pszValue, "Disjoint") == 0 ||
1824          strcasecmp(pszValue, "Touches") == 0 ||
1825          strcasecmp(pszValue, "Crosses") == 0 ||
1826          strcasecmp(pszValue, "Within") == 0 ||
1827          strcasecmp(pszValue, "Contains") == 0 ||
1828          strcasecmp(pszValue, "Overlaps") == 0 ||
1829          strcasecmp(pszValue, "Beyond") == 0)
1830       return MS_TRUE;
1831   }
1832 
1833   return MS_FALSE;
1834 }
1835 
1836 /************************************************************************/
1837 /*            int FLTIsTemportalFilterType(char *pszValue)              */
1838 /*                                                                      */
1839 /*      return TRUE if the value of the node is of temporal filter      */
1840 /*      encoding type.                                                  */
1841 /************************************************************************/
FLTIsTemporalFilterType(const char * pszValue)1842 int FLTIsTemporalFilterType(const char *pszValue)
1843 {
1844   if (pszValue) {
1845     if ( strcasecmp(pszValue, "During") == 0 )
1846       return MS_TRUE;
1847   }
1848 
1849   return MS_FALSE;
1850 }
1851 
1852 /************************************************************************/
1853 /*           int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode)           */
1854 /*                                                                      */
1855 /*      Verfify if the value of the node is one of the supported        */
1856 /*      filter type.                                                    */
1857 /************************************************************************/
FLTIsSupportedFilterType(CPLXMLNode * psXMLNode)1858 int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode)
1859 {
1860   if (psXMLNode) {
1861     if (FLTIsLogicalFilterType(psXMLNode->pszValue) ||
1862         FLTIsSpatialFilterType(psXMLNode->pszValue) ||
1863         FLTIsComparisonFilterType(psXMLNode->pszValue) ||
1864         FLTIsFeatureIdFilterType(psXMLNode->pszValue) ||
1865         FLTIsTemporalFilterType(psXMLNode->pszValue))
1866       return MS_TRUE;
1867   }
1868 
1869   return MS_FALSE;
1870 }
1871 
1872 /************************************************************************/
1873 /*                          FLTNumberOfFilterType                       */
1874 /*                                                                      */
1875 /*      Loop trhough the nodes and return the number of nodes of        */
1876 /*      specified value.                                                */
1877 /************************************************************************/
FLTNumberOfFilterType(FilterEncodingNode * psFilterNode,const char * szType)1878 int FLTNumberOfFilterType(FilterEncodingNode *psFilterNode, const char *szType)
1879 {
1880   int nCount = 0;
1881   int nLeftNode=0 , nRightNode = 0;
1882 
1883   if (!psFilterNode || !szType || !psFilterNode->pszValue)
1884     return 0;
1885 
1886   if (strcasecmp(psFilterNode->pszValue, (char*)szType) == 0)
1887     nCount++;
1888 
1889   if (psFilterNode->psLeftNode)
1890     nLeftNode = FLTNumberOfFilterType(psFilterNode->psLeftNode, szType);
1891 
1892   nCount += nLeftNode;
1893 
1894   if (psFilterNode->psRightNode)
1895     nRightNode = FLTNumberOfFilterType(psFilterNode->psRightNode, szType);
1896   nCount += nRightNode;
1897 
1898   return nCount;
1899 }
1900 
1901 
1902 
1903 
1904 /************************************************************************/
1905 /*                          FLTValidForBBoxFilter                       */
1906 /*                                                                      */
1907 /*      Validate if there is only one BBOX filter node. Here is waht    */
1908 /*      is supported (is valid) :                                       */
1909 /*        - one node which is a BBOX                                    */
1910 /*        - a logical AND with a valid BBOX                             */
1911 /*                                                                      */
1912 /*      eg 1: <Filter>                                                  */
1913 /*            <BBOX>                                                    */
1914 /*              <PropertyName>Geometry</PropertyName>                   */
1915 /*              <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
1916 /*                <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
1917 /*              </gml:Box>                                              */
1918 /*            </BBOX>                                                   */
1919 /*          </Filter>                                                   */
1920 /*                                                                      */
1921 /*      eg 2 :<Filter>                                                  */
1922 /*              <AND>                                                   */
1923 /*               <BBOX>                                                 */
1924 /*                <PropertyName>Geometry</PropertyName>                  */
1925 /*                <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
1926 /*                  <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
1927 /*                </gml:Box>                                            */
1928 /*               </BBOX>                                                */
1929 /*               <PropertyIsEqualTo>                                    */
1930 /*               <PropertyName>SomeProperty</PropertyName>              */
1931 /*                <Literal>100</Literal>                                */
1932 /*              </PropertyIsEqualTo>                                    */
1933 /*             </AND>                                                   */
1934 /*           </Filter>                                                  */
1935 /*                                                                      */
1936 /************************************************************************/
FLTValidForBBoxFilter(FilterEncodingNode * psFilterNode)1937 int FLTValidForBBoxFilter(FilterEncodingNode *psFilterNode)
1938 {
1939   int nCount = 0;
1940 
1941   if (!psFilterNode || !psFilterNode->pszValue)
1942     return 1;
1943 
1944   nCount = FLTNumberOfFilterType(psFilterNode, "BBOX");
1945 
1946   if (nCount > 1)
1947     return 0;
1948   else if (nCount == 0)
1949     return 1;
1950 
1951   /* nCount ==1  */
1952   if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
1953     return 1;
1954 
1955   if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
1956     return FLTValidForBBoxFilter(psFilterNode->psLeftNode) &&
1957            FLTValidForBBoxFilter(psFilterNode->psRightNode);
1958   }
1959 
1960   return 0;
1961 }
1962 
1963 #if 0
1964 static int FLTHasUniqueTopLevelDuringFilter(FilterEncodingNode *psFilterNode)
1965 {
1966   int nCount = 0;
1967 
1968   if (!psFilterNode || !psFilterNode->pszValue)
1969     return 1;
1970 
1971   nCount = FLTNumberOfFilterType(psFilterNode, "During");
1972 
1973   if (nCount > 1)
1974     return 0;
1975   else if (nCount == 0)
1976     return 1;
1977 
1978   /* nCount ==1  */
1979   if (strcasecmp(psFilterNode->pszValue, "During") == 0)
1980     return 1;
1981 
1982   if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
1983     return FLTHasUniqueTopLevelDuringFilter(psFilterNode->psLeftNode) &&
1984            FLTHasUniqueTopLevelDuringFilter(psFilterNode->psRightNode);
1985   }
1986 
1987   return 0;
1988 }
1989 #endif
1990 
FLTIsLineFilter(FilterEncodingNode * psFilterNode)1991 int FLTIsLineFilter(FilterEncodingNode *psFilterNode)
1992 {
1993   if (!psFilterNode || !psFilterNode->pszValue)
1994     return 0;
1995 
1996   if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
1997       psFilterNode->psRightNode &&
1998       psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_LINE)
1999     return 1;
2000 
2001   return 0;
2002 }
2003 
FLTIsPolygonFilter(FilterEncodingNode * psFilterNode)2004 int FLTIsPolygonFilter(FilterEncodingNode *psFilterNode)
2005 {
2006   if (!psFilterNode || !psFilterNode->pszValue)
2007     return 0;
2008 
2009   if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
2010       psFilterNode->psRightNode &&
2011       psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON)
2012     return 1;
2013 
2014   return 0;
2015 }
2016 
FLTIsPointFilter(FilterEncodingNode * psFilterNode)2017 int FLTIsPointFilter(FilterEncodingNode *psFilterNode)
2018 {
2019   if (!psFilterNode || !psFilterNode->pszValue)
2020     return 0;
2021 
2022   if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
2023       psFilterNode->psRightNode &&
2024       psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POINT)
2025     return 1;
2026 
2027   return 0;
2028 }
2029 
FLTIsBBoxFilter(FilterEncodingNode * psFilterNode)2030 int FLTIsBBoxFilter(FilterEncodingNode *psFilterNode)
2031 {
2032   if (!psFilterNode || !psFilterNode->pszValue)
2033     return 0;
2034 
2035   if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
2036     return 1;
2037 
2038   return 0;
2039 }
2040 
FLTGetShape(FilterEncodingNode * psFilterNode,double * pdfDistance,int * pnUnit)2041 shapeObj *FLTGetShape(FilterEncodingNode *psFilterNode, double *pdfDistance,
2042                       int *pnUnit)
2043 {
2044   char **tokens = NULL;
2045   int nTokens = 0;
2046   FilterEncodingNode *psNode = psFilterNode;
2047   char *szUnitStr = NULL;
2048   char *szUnit = NULL;
2049 
2050   if (psNode) {
2051     if (psNode->eType == FILTER_NODE_TYPE_SPATIAL && psNode->psRightNode)
2052       psNode = psNode->psRightNode;
2053 
2054     if (FLTIsGeometryFilterNodeType(psNode->eType)) {
2055 
2056       if (psNode->pszValue && pdfDistance) {
2057         /*
2058         sytnax expected is "distance;unit" or just "distance"
2059         if unit is there syntax is "URI#unit" (eg http://..../#m)
2060         or just "unit"
2061         */
2062         tokens = msStringSplit(psNode->pszValue,';', &nTokens);
2063         if (tokens && nTokens >= 1) {
2064           *pdfDistance = atof(tokens[0]);
2065 
2066           if (nTokens == 2 && pnUnit) {
2067             szUnitStr = msStrdup(tokens[1]);
2068             msFreeCharArray(tokens, nTokens);
2069             nTokens = 0;
2070             tokens = msStringSplit(szUnitStr,'#', &nTokens);
2071             msFree(szUnitStr);
2072             if (tokens && nTokens >= 1) {
2073               if (nTokens ==1)
2074                 szUnit = tokens[0];
2075               else
2076                 szUnit = tokens[1];
2077 
2078               if (strcasecmp(szUnit,"m") == 0 ||
2079                   strcasecmp(szUnit,"meters") == 0 )
2080                 *pnUnit = MS_METERS;
2081               else if (strcasecmp(szUnit,"km") == 0 ||
2082                        strcasecmp(szUnit,"kilometers") == 0)
2083                 *pnUnit = MS_KILOMETERS;
2084               else if (strcasecmp(szUnit,"NM") == 0 ||
2085                        strcasecmp(szUnit,"nauticalmiles") == 0)
2086                 *pnUnit = MS_NAUTICALMILES;
2087               else if (strcasecmp(szUnit,"mi") == 0 ||
2088                        strcasecmp(szUnit,"miles") == 0)
2089                 *pnUnit = MS_MILES;
2090               else if (strcasecmp(szUnit,"in") == 0 ||
2091                        strcasecmp(szUnit,"inches") == 0)
2092                 *pnUnit = MS_INCHES;
2093               else if (strcasecmp(szUnit,"ft") == 0 ||
2094                        strcasecmp(szUnit,"feet") == 0)
2095                 *pnUnit = MS_FEET;
2096               else if (strcasecmp(szUnit,"deg") == 0 ||
2097                        strcasecmp(szUnit,"dd") == 0)
2098                 *pnUnit = MS_DD;
2099               else if (strcasecmp(szUnit,"px") == 0)
2100                 *pnUnit = MS_PIXELS;
2101 
2102             }
2103           }
2104         }
2105         msFreeCharArray(tokens, nTokens);
2106       }
2107 
2108       return (shapeObj *)psNode->pOther;
2109     }
2110   }
2111   return NULL;
2112 }
2113 
2114 /************************************************************************/
2115 /*                                FLTGetBBOX                            */
2116 /*                                                                      */
2117 /*      Loop through the nodes are return the coordinates of the        */
2118 /*      first bbox node found. The retrun value is the epsg code of     */
2119 /*      the bbox.                                                       */
2120 /************************************************************************/
FLTGetBBOX(FilterEncodingNode * psFilterNode,rectObj * psRect)2121 const char *FLTGetBBOX(FilterEncodingNode *psFilterNode, rectObj *psRect)
2122 {
2123   const char *pszReturn = NULL;
2124 
2125   if (!psFilterNode || !psRect)
2126     return NULL;
2127 
2128   if (psFilterNode->pszValue && strcasecmp(psFilterNode->pszValue, "BBOX") == 0) {
2129     if (psFilterNode->psRightNode && psFilterNode->psRightNode->pOther) {
2130       rectObj* pRect= (rectObj *)psFilterNode->psRightNode->pOther;
2131       psRect->minx = pRect->minx;
2132       psRect->miny = pRect->miny;
2133       psRect->maxx = pRect->maxx;
2134       psRect->maxy = pRect->maxy;
2135 
2136       return psFilterNode->pszSRS;
2137 
2138     }
2139   } else {
2140     pszReturn = FLTGetBBOX(psFilterNode->psLeftNode, psRect);
2141     if (pszReturn)
2142       return pszReturn;
2143     else
2144       return  FLTGetBBOX(psFilterNode->psRightNode, psRect);
2145   }
2146 
2147   return pszReturn;
2148 }
2149 
FLTGetDuring(FilterEncodingNode * psFilterNode,const char ** ppszTimeField)2150 const char* FLTGetDuring(FilterEncodingNode *psFilterNode, const char** ppszTimeField)
2151 {
2152   const char *pszReturn = NULL;
2153 
2154   if (!psFilterNode || !ppszTimeField)
2155     return NULL;
2156 
2157   if (psFilterNode->pszValue && strcasecmp(psFilterNode->pszValue, "During") == 0) {
2158     *ppszTimeField = psFilterNode->psLeftNode->pszValue;
2159     return psFilterNode->psRightNode->pszValue;
2160   } else {
2161     pszReturn = FLTGetDuring(psFilterNode->psLeftNode, ppszTimeField);
2162     if (pszReturn)
2163       return pszReturn;
2164     else
2165       return  FLTGetDuring(psFilterNode->psRightNode, ppszTimeField);
2166   }
2167 
2168   return pszReturn;
2169 }
2170 
2171 /************************************************************************/
2172 /*                           FLTGetSQLExpression                        */
2173 /*                                                                      */
2174 /*      Build SQL expressions from the mapserver nodes.                 */
2175 /************************************************************************/
FLTGetSQLExpression(FilterEncodingNode * psFilterNode,layerObj * lp)2176 char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
2177 {
2178   char *pszExpression = NULL;
2179   const char *pszAttribute = NULL;
2180   char szTmp[256];
2181   char **tokens = NULL;
2182   int nTokens = 0, i=0, bString=0;
2183 
2184   if (psFilterNode == NULL || lp == NULL)
2185     return NULL;
2186 
2187   if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
2188     if ( psFilterNode->psLeftNode && psFilterNode->psRightNode) {
2189       if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue)) {
2190         pszExpression =
2191           FLTGetBinaryComparisonSQLExpresssion(psFilterNode, lp);
2192       } else if (strcasecmp(psFilterNode->pszValue,
2193                             "PropertyIsBetween") == 0) {
2194         pszExpression =
2195           FLTGetIsBetweenComparisonSQLExpresssion(psFilterNode, lp);
2196       } else if (strcasecmp(psFilterNode->pszValue,
2197                             "PropertyIsLike") == 0) {
2198         pszExpression =
2199           FLTGetIsLikeComparisonSQLExpression(psFilterNode, lp);
2200 
2201       }
2202     }
2203   } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
2204     if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
2205         strcasecmp(psFilterNode->pszValue, "OR") == 0) {
2206       pszExpression =
2207         FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
2208 
2209     } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
2210       pszExpression =
2211         FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);
2212 
2213     }
2214   }
2215 
2216   else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
2217     /* TODO */
2218   } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
2219 #if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR)
2220     if (psFilterNode->pszValue) {
2221       pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
2222       if (pszAttribute) {
2223         tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens);
2224         bString = 0;
2225         if (tokens && nTokens > 0) {
2226           for (i=0; i<nTokens; i++) {
2227             char *pszEscapedStr = NULL;
2228             const char* pszId = tokens[i];
2229             const char* pszDot = strchr(pszId, '.');
2230             if( pszDot )
2231                 pszId = pszDot + 1;
2232 
2233             if (strlen(pszId) <= 0)
2234               continue;
2235 
2236             if (FLTIsNumeric(pszId) == MS_FALSE)
2237               bString = 1;
2238 
2239             pszEscapedStr = msLayerEscapeSQLParam(lp, pszId);
2240             if (bString)
2241             {
2242               if( lp->connectiontype == MS_OGR || lp->connectiontype == MS_POSTGIS )
2243                 snprintf(szTmp, sizeof(szTmp), "(CAST(%s AS CHARACTER(255)) = '%s')" , pszAttribute, pszEscapedStr);
2244               else
2245                 snprintf(szTmp, sizeof(szTmp), "(%s = '%s')" , pszAttribute, pszEscapedStr);
2246             }
2247             else
2248               snprintf(szTmp, sizeof(szTmp), "(%s = %s)" , pszAttribute, pszEscapedStr);
2249 
2250             msFree(pszEscapedStr);
2251             pszEscapedStr=NULL;
2252 
2253             if (pszExpression != NULL)
2254               pszExpression = msStringConcatenate(pszExpression, " OR ");
2255             else
2256               /*opening and closing brackets*/
2257               pszExpression = msStringConcatenate(pszExpression, "(");
2258 
2259             pszExpression = msStringConcatenate(pszExpression, szTmp);
2260           }
2261 
2262           msFreeCharArray(tokens, nTokens);
2263         }
2264       }
2265       /*opening and closing brackets*/
2266       if (pszExpression)
2267         pszExpression = msStringConcatenate(pszExpression, ")");
2268     }
2269 #else
2270     msSetError(MS_MISCERR, "OWS support is not available.",
2271                "FLTGetSQLExpression()");
2272     return NULL;
2273 #endif
2274 
2275   }
2276   else if ( lp->connectiontype != MS_OGR &&
2277             psFilterNode->eType == FILTER_NODE_TYPE_TEMPORAL )
2278     pszExpression = FLTGetTimeExpression(psFilterNode, lp);
2279 
2280   return pszExpression;
2281 }
2282 
2283 /************************************************************************/
2284 /*                     FLTGetLogicalComparisonSQLExpresssion            */
2285 /*                                                                      */
2286 /*      Return the expression for logical comparison expression.        */
2287 /************************************************************************/
FLTGetLogicalComparisonSQLExpresssion(FilterEncodingNode * psFilterNode,layerObj * lp)2288 char *FLTGetLogicalComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
2289     layerObj *lp)
2290 {
2291   char *pszBuffer = NULL;
2292   char *pszTmp = NULL;
2293   int nTmp = 0;
2294 
2295   if (lp == NULL)
2296     return NULL;
2297 
2298   /* ==================================================================== */
2299   /*      special case for BBOX node.                                     */
2300   /* ==================================================================== */
2301   if (psFilterNode->psLeftNode && psFilterNode->psRightNode &&
2302       ((strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") == 0) ||
2303        (strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") == 0))) {
2304     if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") != 0)
2305       pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
2306     else
2307       pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
2308 
2309     if (!pszTmp)
2310       return NULL;
2311 
2312     pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 1));
2313     sprintf(pszBuffer, "%s", pszTmp);
2314   }
2315 
2316   /* ==================================================================== */
2317   /*      special case for temporal filter node (OGR layer only)          */
2318   /* ==================================================================== */
2319   else if (lp->connectiontype == MS_OGR &&
2320       psFilterNode->psLeftNode && psFilterNode->psRightNode &&
2321       (psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_TEMPORAL ||
2322        psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_TEMPORAL) ) {
2323     if (psFilterNode->psLeftNode->eType != FILTER_NODE_TYPE_TEMPORAL)
2324       pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
2325     else
2326       pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
2327 
2328     if (!pszTmp)
2329       return NULL;
2330 
2331     pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 1));
2332     sprintf(pszBuffer, "%s", pszTmp);
2333   }
2334 
2335   /* -------------------------------------------------------------------- */
2336   /*      OR and AND                                                      */
2337   /* -------------------------------------------------------------------- */
2338   else if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
2339     pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
2340     if (!pszTmp)
2341       return NULL;
2342 
2343     pszBuffer = (char *)malloc(sizeof(char) *
2344                                (strlen(pszTmp) +
2345                                 strlen(psFilterNode->pszValue) + 5));
2346     pszBuffer[0] = '\0';
2347     strcat(pszBuffer, " (");
2348     strcat(pszBuffer, pszTmp);
2349     strcat(pszBuffer, " ");
2350     strcat(pszBuffer, psFilterNode->pszValue);
2351     strcat(pszBuffer, " ");
2352 
2353     free( pszTmp );
2354 
2355     nTmp = strlen(pszBuffer);
2356     pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
2357     if (!pszTmp) {
2358       free(pszBuffer);
2359       return NULL;
2360     }
2361 
2362     pszBuffer = (char *)realloc(pszBuffer,
2363                                 sizeof(char) * (strlen(pszTmp) + nTmp +3));
2364     strcat(pszBuffer, pszTmp);
2365     strcat(pszBuffer, ") ");
2366   }
2367   /* -------------------------------------------------------------------- */
2368   /*      NOT                                                             */
2369   /* -------------------------------------------------------------------- */
2370   else if (psFilterNode->psLeftNode &&
2371            strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
2372     pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
2373     if (!pszTmp)
2374       return NULL;
2375 
2376     pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) +  9));
2377     pszBuffer[0] = '\0';
2378 
2379     strcat(pszBuffer, " (NOT ");
2380     strcat(pszBuffer, pszTmp);
2381     strcat(pszBuffer, ") ");
2382   } else
2383     return NULL;
2384 
2385   /* -------------------------------------------------------------------- */
2386   /*      Cleanup.                                                        */
2387   /* -------------------------------------------------------------------- */
2388   if( pszTmp != NULL )
2389     free( pszTmp );
2390   return pszBuffer;
2391 
2392 }
2393 
2394 
2395 /************************************************************************/
2396 /*                      FLTGetBinaryComparisonSQLExpresssion            */
2397 /*                                                                      */
2398 /*      Return the expression for a binary comparison filter node.      */
2399 /************************************************************************/
FLTGetBinaryComparisonSQLExpresssion(FilterEncodingNode * psFilterNode,layerObj * lp)2400 char *FLTGetBinaryComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
2401     layerObj *lp)
2402 {
2403   const size_t bufferSize = 1024;
2404   char szBuffer[1024];
2405   int bString=0;
2406   char szTmp[256];
2407   char* pszEscapedStr = NULL;
2408 
2409   szBuffer[0] = '\0';
2410   if (!psFilterNode || !
2411       FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
2412     return NULL;
2413 
2414   /* -------------------------------------------------------------------- */
2415   /*      check if the value is a numeric value or alphanumeric. If it    */
2416   /*      is alphanumeric, add quotes around attribute and values.        */
2417   /* -------------------------------------------------------------------- */
2418   bString = 0;
2419   if (psFilterNode->psRightNode->pszValue) {
2420     const char* pszOFGType;
2421     snprintf(szTmp, sizeof(szTmp), "%s_type",  psFilterNode->psLeftNode->pszValue);
2422     pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
2423     if (pszOFGType!= NULL && strcasecmp(pszOFGType, "Character") == 0)
2424       bString = 1;
2425 
2426     else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
2427       bString = 1;
2428   }
2429 
2430   /* specical case to be able to have empty strings in the expression. */
2431   if (psFilterNode->psRightNode->pszValue == NULL)
2432     bString = 1;
2433 
2434 
2435   /*opening bracket*/
2436   strlcat(szBuffer, " (", bufferSize);
2437 
2438   pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
2439 
2440 
2441   /* attribute */
2442   /*case insensitive set ? */
2443   if (bString &&
2444       strcasecmp(psFilterNode->pszValue,
2445                  "PropertyIsEqualTo") == 0 &&
2446       psFilterNode->psRightNode->pOther &&
2447       (*(int *)psFilterNode->psRightNode->pOther) == 1) {
2448     snprintf(szTmp, sizeof(szTmp), "lower(%s) ",  pszEscapedStr);
2449     strlcat(szBuffer, szTmp, bufferSize);
2450   } else
2451     strlcat(szBuffer, pszEscapedStr, bufferSize);
2452 
2453   msFree(pszEscapedStr);
2454   pszEscapedStr = NULL;
2455 
2456 
2457   /* logical operator */
2458   if (strcasecmp(psFilterNode->pszValue,
2459                  "PropertyIsEqualTo") == 0)
2460     strlcat(szBuffer, "=", bufferSize);
2461   else if (strcasecmp(psFilterNode->pszValue,
2462                       "PropertyIsNotEqualTo") == 0)
2463     strlcat(szBuffer, "<>", bufferSize);
2464   else if (strcasecmp(psFilterNode->pszValue,
2465                       "PropertyIsLessThan") == 0)
2466     strlcat(szBuffer, "<", bufferSize);
2467   else if (strcasecmp(psFilterNode->pszValue,
2468                       "PropertyIsGreaterThan") == 0)
2469     strlcat(szBuffer, ">", bufferSize);
2470   else if (strcasecmp(psFilterNode->pszValue,
2471                       "PropertyIsLessThanOrEqualTo") == 0)
2472     strlcat(szBuffer, "<=", bufferSize);
2473   else if (strcasecmp(psFilterNode->pszValue,
2474                       "PropertyIsGreaterThanOrEqualTo") == 0)
2475     strlcat(szBuffer, ">=", bufferSize);
2476 
2477   strlcat(szBuffer, " ", bufferSize);
2478 
2479   /* value */
2480 
2481   if (bString &&
2482       psFilterNode->psRightNode->pszValue &&
2483       strcasecmp(psFilterNode->pszValue,
2484                  "PropertyIsEqualTo") == 0 &&
2485       psFilterNode->psRightNode->pOther &&
2486       (*(int *)psFilterNode->psRightNode->pOther) == 1) {
2487     char* pszEscapedStr;
2488     pszEscapedStr = msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
2489     snprintf(szTmp, sizeof(szTmp), "lower('%s') ", pszEscapedStr);
2490     msFree(pszEscapedStr);
2491     strlcat(szBuffer, szTmp, bufferSize);
2492   } else {
2493     if (bString)
2494       strlcat(szBuffer, "'", bufferSize);
2495 
2496     if (psFilterNode->psRightNode->pszValue) {
2497       if (bString) {
2498         char* pszEscapedStr;
2499         pszEscapedStr = msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
2500         strlcat(szBuffer, pszEscapedStr, bufferSize);
2501         msFree(pszEscapedStr);
2502         pszEscapedStr=NULL;
2503       } else
2504         strlcat(szBuffer, psFilterNode->psRightNode->pszValue, bufferSize);
2505     }
2506 
2507     if (bString)
2508       strlcat(szBuffer, "'", bufferSize);
2509 
2510   }
2511   /*closing bracket*/
2512   strlcat(szBuffer, ") ", bufferSize);
2513 
2514   return msStrdup(szBuffer);
2515 }
2516 
2517 
2518 /************************************************************************/
2519 /*                    FLTGetIsBetweenComparisonSQLExpresssion           */
2520 /*                                                                      */
2521 /*      Build an SQL expresssion for IsBteween Filter.                  */
2522 /************************************************************************/
FLTGetIsBetweenComparisonSQLExpresssion(FilterEncodingNode * psFilterNode,layerObj * lp)2523 char *FLTGetIsBetweenComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
2524     layerObj *lp)
2525 {
2526   const size_t bufferSize = 1024;
2527   char szBuffer[1024];
2528   char **aszBounds = NULL;
2529   int nBounds = 0;
2530   int bString=0;
2531   char szTmp[256];
2532   char* pszEscapedStr;
2533 
2534   szBuffer[0] = '\0';
2535   if (!psFilterNode ||
2536       !(strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0))
2537     return NULL;
2538 
2539   if (!psFilterNode->psLeftNode || !psFilterNode->psRightNode )
2540     return NULL;
2541 
2542   /* -------------------------------------------------------------------- */
2543   /*      Get the bounds value which are stored like boundmin;boundmax    */
2544   /* -------------------------------------------------------------------- */
2545   aszBounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';', &nBounds);
2546   if (nBounds != 2) {
2547     msFreeCharArray(aszBounds, nBounds);
2548     return NULL;
2549   }
2550   /* -------------------------------------------------------------------- */
2551   /*      check if the value is a numeric value or alphanumeric. If it    */
2552   /*      is alphanumeric, add quotes around attribute and values.        */
2553   /* -------------------------------------------------------------------- */
2554   bString = 0;
2555   if (aszBounds[0]) {
2556     const char* pszOFGType;
2557     snprintf(szTmp, sizeof(szTmp), "%s_type",  psFilterNode->psLeftNode->pszValue);
2558     pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
2559     if (pszOFGType!= NULL && strcasecmp(pszOFGType, "Character") == 0)
2560       bString = 1;
2561     else if (FLTIsNumeric(aszBounds[0]) == MS_FALSE)
2562       bString = 1;
2563   }
2564   if (!bString) {
2565     if (aszBounds[1]) {
2566       if (FLTIsNumeric(aszBounds[1]) == MS_FALSE)
2567         bString = 1;
2568     }
2569   }
2570 
2571 
2572   /* -------------------------------------------------------------------- */
2573   /*      build expresssion.                                              */
2574   /* -------------------------------------------------------------------- */
2575   /*opening paranthesis */
2576   strlcat(szBuffer, " (", bufferSize);
2577 
2578   /* attribute */
2579   pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
2580 
2581   strlcat(szBuffer, pszEscapedStr, bufferSize);
2582   msFree(pszEscapedStr);
2583   pszEscapedStr = NULL;
2584 
2585   /*between*/
2586   strlcat(szBuffer, " BETWEEN ", bufferSize);
2587 
2588   /*bound 1*/
2589   if (bString)
2590     strlcat(szBuffer,"'", bufferSize);
2591   pszEscapedStr = msLayerEscapeSQLParam( lp, aszBounds[0]);
2592   strlcat(szBuffer, pszEscapedStr, bufferSize);
2593   msFree(pszEscapedStr);
2594   pszEscapedStr=NULL;
2595 
2596   if (bString)
2597     strlcat(szBuffer,"'", bufferSize);
2598 
2599   strlcat(szBuffer, " AND ", bufferSize);
2600 
2601   /*bound 2*/
2602   if (bString)
2603     strlcat(szBuffer, "'", bufferSize);
2604   pszEscapedStr = msLayerEscapeSQLParam( lp, aszBounds[1]);
2605   strlcat(szBuffer, pszEscapedStr, bufferSize);
2606   msFree(pszEscapedStr);
2607   pszEscapedStr=NULL;
2608 
2609   if (bString)
2610     strlcat(szBuffer,"'", bufferSize);
2611 
2612   /*closing paranthesis*/
2613   strlcat(szBuffer, ")", bufferSize);
2614 
2615   msFreeCharArray(aszBounds, nBounds);
2616 
2617   return msStrdup(szBuffer);
2618 }
2619 
2620 /************************************************************************/
2621 /*                      FLTGetIsLikeComparisonSQLExpression             */
2622 /*                                                                      */
2623 /*      Build an sql expression for IsLike filter.                      */
2624 /************************************************************************/
FLTGetIsLikeComparisonSQLExpression(FilterEncodingNode * psFilterNode,layerObj * lp)2625 char *FLTGetIsLikeComparisonSQLExpression(FilterEncodingNode *psFilterNode,
2626     layerObj *lp)
2627 {
2628   const size_t bufferSize = 1024;
2629   char szBuffer[1024];
2630   char *pszValue = NULL;
2631 
2632   const char *pszWild = NULL;
2633   const char *pszSingle = NULL;
2634   const char *pszEscape = NULL;
2635   char szTmp[4];
2636 
2637   int nLength=0, i=0, j=0;
2638   int  bCaseInsensitive = 0;
2639 
2640   char *pszEscapedStr = NULL;
2641   FEPropertyIsLike* propIsLike;
2642 
2643   if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
2644       !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
2645     return NULL;
2646 
2647   propIsLike = (FEPropertyIsLike *)psFilterNode->pOther;
2648   pszWild = propIsLike->pszWildCard;
2649   pszSingle = propIsLike->pszSingleChar;
2650   pszEscape = propIsLike->pszEscapeChar;
2651   bCaseInsensitive = propIsLike->bCaseInsensitive;
2652 
2653   if (!pszWild || strlen(pszWild) == 0 ||
2654       !pszSingle || strlen(pszSingle) == 0 ||
2655       !pszEscape || strlen(pszEscape) == 0)
2656     return NULL;
2657 
2658   if (pszEscape[0] == '\'') {
2659     /* This might be valid, but the risk of SQL injection is too high */
2660     /* and the below code is not ready for that */
2661     /* Someone who does this has clearly suspect intentions ! */
2662     msSetError(MS_MISCERR, "Single quote character is not allowed as an escaping character.",
2663                "FLTGetIsLikeComparisonSQLExpression()");
2664     return NULL;
2665   }
2666 
2667 
2668   szBuffer[0] = '\0';
2669   /*opening bracket*/
2670   strlcat(szBuffer, " (", bufferSize);
2671 
2672   /* attribute name */
2673   pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);
2674 
2675   strlcat(szBuffer, pszEscapedStr, bufferSize);
2676   msFree(pszEscapedStr);
2677   pszEscapedStr = NULL;
2678 
2679   if (lp->connectiontype == MS_POSTGIS) {
2680     if (bCaseInsensitive == 1)
2681       strlcat(szBuffer, "::text ilike '", bufferSize);
2682     else
2683       strlcat(szBuffer, "::text like '", bufferSize);
2684   } else
2685     strlcat(szBuffer, " like '", bufferSize);
2686 
2687   pszValue = psFilterNode->psRightNode->pszValue;
2688   nLength = strlen(pszValue);
2689 
2690   pszEscapedStr = (char*) msSmallMalloc( 3 * nLength + 1);
2691 
2692   for (i=0, j=0; i<nLength; i++) {
2693     char c = pszValue[i];
2694     if (c != pszWild[0] &&
2695         c != pszSingle[0] &&
2696         c != pszEscape[0]) {
2697       if (c == '\'') {
2698         pszEscapedStr[j++] = '\'';
2699         pszEscapedStr[j++] = '\'';
2700       } else if (c == '\\') {
2701         pszEscapedStr[j++] = '\\';
2702         pszEscapedStr[j++] = '\\';
2703       } else
2704         pszEscapedStr[j++] = c;
2705     } else if  (c == pszSingle[0]) {
2706       pszEscapedStr[j++] = '_';
2707     } else if  (c == pszEscape[0]) {
2708       pszEscapedStr[j++] = pszEscape[0];
2709       if (i+1<nLength) {
2710         char nextC = pszValue[i+1];
2711         i++;
2712         if (nextC == '\'') {
2713           pszEscapedStr[j++] = '\'';
2714           pszEscapedStr[j++] = '\'';
2715         } else
2716           pszEscapedStr[j++] = nextC;
2717       }
2718     } else if (c == pszWild[0]) {
2719       pszEscapedStr[j++] = '%';
2720     }
2721   }
2722   pszEscapedStr[j++] = 0;
2723   strlcat(szBuffer, pszEscapedStr, bufferSize);
2724   msFree(pszEscapedStr);
2725 
2726   strlcat(szBuffer, "'", bufferSize);
2727   if (lp->connectiontype != MS_OGR) {
2728     if (lp->connectiontype == MS_POSTGIS && pszEscape[0] == '\\')
2729         strlcat(szBuffer, " escape E'", bufferSize);
2730     else
2731         strlcat(szBuffer, " escape '", bufferSize);
2732     szTmp[0] = pszEscape[0];
2733     if (pszEscape[0] == '\\') {
2734       szTmp[1] = '\\';
2735       szTmp[2] = '\'';
2736       szTmp[3] = '\0';
2737     } else {
2738       szTmp[1] = '\'';
2739       szTmp[2] = '\0';
2740     }
2741 
2742     strlcat(szBuffer,  szTmp, bufferSize);
2743   }
2744   strlcat(szBuffer,  ") ", bufferSize);
2745 
2746   return msStrdup(szBuffer);
2747 }
2748 
2749 /************************************************************************/
2750 /*                           FLTHasSpatialFilter                        */
2751 /*                                                                      */
2752 /*      Utility function to see if a spatial filter is included in      */
2753 /*      the node.                                                       */
2754 /************************************************************************/
FLTHasSpatialFilter(FilterEncodingNode * psNode)2755 int FLTHasSpatialFilter(FilterEncodingNode *psNode)
2756 {
2757   int bResult = MS_FALSE;
2758 
2759   if (!psNode)
2760     return MS_FALSE;
2761 
2762   if (psNode->eType == FILTER_NODE_TYPE_LOGICAL) {
2763     if (psNode->psLeftNode)
2764       bResult = FLTHasSpatialFilter(psNode->psLeftNode);
2765 
2766     if (bResult)
2767       return MS_TRUE;
2768 
2769     if (psNode->psRightNode)
2770       bResult = FLTHasSpatialFilter(psNode->psRightNode);
2771 
2772     if (bResult)
2773       return MS_TRUE;
2774   } else if (FLTIsBBoxFilter(psNode) || FLTIsPointFilter(psNode) ||
2775              FLTIsLineFilter(psNode) || FLTIsPolygonFilter(psNode))
2776     return MS_TRUE;
2777 
2778 
2779   return MS_FALSE;
2780 }
2781 
2782 
2783 /************************************************************************/
2784 /*                     FLTCreateFeatureIdFilterEncoding                 */
2785 /*                                                                      */
2786 /*      Utility function to create a filter node of FeatureId type.     */
2787 /************************************************************************/
FLTCreateFeatureIdFilterEncoding(const char * pszString)2788 FilterEncodingNode *FLTCreateFeatureIdFilterEncoding(const char *pszString)
2789 {
2790   FilterEncodingNode *psFilterNode = NULL;
2791 
2792   if (pszString) {
2793     psFilterNode = FLTCreateFilterEncodingNode();
2794     psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
2795     psFilterNode->pszValue =  msStrdup(pszString);
2796     return psFilterNode;
2797   }
2798   return NULL;
2799 }
2800 
2801 
2802 /************************************************************************/
2803 /*                              FLTParseGMLBox                          */
2804 /*                                                                      */
2805 /*      Parse gml box. Used for FE 1.0                                  */
2806 /************************************************************************/
FLTParseGMLBox(CPLXMLNode * psBox,rectObj * psBbox,char ** ppszSRS)2807 int FLTParseGMLBox(CPLXMLNode *psBox, rectObj *psBbox, char **ppszSRS)
2808 {
2809   int bCoordinatesValid = 0;
2810   CPLXMLNode *psCoordinates = NULL;
2811   CPLXMLNode *psCoord1 = NULL, *psCoord2 = NULL;
2812   char **papszCoords=NULL, **papszMin=NULL, **papszMax = NULL;
2813   int nCoords = 0, nCoordsMin = 0, nCoordsMax = 0;
2814   const char *pszTmpCoord = NULL;
2815   const char *pszSRS = NULL;
2816   const char *pszTS = NULL;
2817   const char *pszCS = NULL;
2818   double minx = 0.0, miny = 0.0, maxx = 0.0, maxy = 0.0;
2819 
2820   if (psBox) {
2821     pszSRS = CPLGetXMLValue(psBox, "srsName", NULL);
2822     if (ppszSRS && pszSRS)
2823       *ppszSRS = msStrdup(pszSRS);
2824 
2825     psCoordinates = CPLGetXMLNode(psBox, "coordinates");
2826     pszTS = CPLGetXMLValue(psCoordinates, "ts", NULL);
2827     if( pszTS == NULL )
2828         pszTS = " ";
2829     pszCS = CPLGetXMLValue(psCoordinates, "cs", NULL);
2830     if( pszCS == NULL )
2831         pszCS = ",";
2832     pszTmpCoord = CPLGetXMLValue(psCoordinates, NULL, NULL);
2833 
2834     if (pszTmpCoord) {
2835       papszCoords = msStringSplit(pszTmpCoord, pszTS[0], &nCoords);
2836       if (papszCoords && nCoords == 2) {
2837         papszMin = msStringSplit(papszCoords[0], pszCS[0], &nCoordsMin);
2838         if (papszMin && nCoordsMin == 2) {
2839           papszMax = msStringSplit(papszCoords[1], pszCS[0], &nCoordsMax);
2840         }
2841         if (papszMax && nCoordsMax == 2) {
2842           bCoordinatesValid =1;
2843           minx =  atof(papszMin[0]);
2844           miny =  atof(papszMin[1]);
2845           maxx =  atof(papszMax[0]);
2846           maxy =  atof(papszMax[1]);
2847         }
2848 
2849         msFreeCharArray(papszMin, nCoordsMin);
2850         msFreeCharArray(papszMax, nCoordsMax);
2851       }
2852 
2853       msFreeCharArray(papszCoords, nCoords);
2854     } else {
2855       psCoord1 = CPLGetXMLNode(psBox, "coord");
2856       psCoord2 = FLTGetNextSibblingNode(psCoord1);
2857       if (psCoord1 && psCoord2 && strcmp(psCoord2->pszValue, "coord") == 0) {
2858         const char* pszX = CPLGetXMLValue(psCoord1, "X", NULL);
2859         const char* pszY = CPLGetXMLValue(psCoord1, "Y", NULL);
2860         if (pszX && pszY) {
2861           minx = atof(pszX);
2862           miny = atof(pszY);
2863 
2864           pszX = CPLGetXMLValue(psCoord2, "X", NULL);
2865           pszY = CPLGetXMLValue(psCoord2, "Y", NULL);
2866           if (pszX && pszY) {
2867             maxx = atof(pszX);
2868             maxy = atof(pszY);
2869             bCoordinatesValid = 1;
2870           }
2871         }
2872       }
2873 
2874     }
2875   }
2876 
2877   if (bCoordinatesValid) {
2878     psBbox->minx =  minx;
2879     psBbox->miny =  miny;
2880 
2881     psBbox->maxx =  maxx;
2882     psBbox->maxy =  maxy;
2883   }
2884 
2885   return bCoordinatesValid;
2886 }
2887 /************************************************************************/
2888 /*                           FLTParseGMLEnvelope                        */
2889 /*                                                                      */
2890 /*      Utility function to parse a gml:Envelope (used for SOS and FE1.1)*/
2891 /************************************************************************/
FLTParseGMLEnvelope(CPLXMLNode * psRoot,rectObj * psBbox,char ** ppszSRS)2892 int FLTParseGMLEnvelope(CPLXMLNode *psRoot, rectObj *psBbox, char **ppszSRS)
2893 {
2894   CPLXMLNode *psUpperCorner=NULL, *psLowerCorner=NULL;
2895   const char *pszLowerCorner=NULL, *pszUpperCorner=NULL;
2896   int bValid = 0;
2897   char **tokens;
2898   int n;
2899 
2900   if (psRoot && psBbox && psRoot->eType == CXT_Element &&
2901       EQUAL(psRoot->pszValue,"Envelope")) {
2902     /*Get the srs if available*/
2903     if (ppszSRS) {
2904       const char* pszSRS = CPLGetXMLValue(psRoot, "srsName", NULL);
2905       if( pszSRS != NULL )
2906           *ppszSRS = msStrdup(pszSRS);
2907     }
2908     psLowerCorner = CPLSearchXMLNode(psRoot, "lowerCorner");
2909     psUpperCorner = CPLSearchXMLNode(psRoot, "upperCorner");
2910 
2911     if (psLowerCorner && psUpperCorner) {
2912       pszLowerCorner = CPLGetXMLValue(psLowerCorner, NULL, NULL);
2913       pszUpperCorner = CPLGetXMLValue(psUpperCorner, NULL, NULL);
2914 
2915       if (pszLowerCorner && pszUpperCorner) {
2916         tokens = msStringSplit(pszLowerCorner, ' ', &n);
2917         if (tokens && n >= 2) {
2918           psBbox->minx = atof(tokens[0]);
2919           psBbox->miny = atof(tokens[1]);
2920 
2921           msFreeCharArray(tokens, n);
2922 
2923           tokens = msStringSplit(pszUpperCorner, ' ', &n);
2924           if (tokens && n >= 2) {
2925             psBbox->maxx = atof(tokens[0]);
2926             psBbox->maxy = atof(tokens[1]);
2927             bValid = 1;
2928           }
2929         }
2930         msFreeCharArray(tokens, n);
2931       }
2932     }
2933   }
2934 
2935   return bValid;
2936 }
2937 
2938 /************************************************************************/
2939 /*                        FLTNeedSRSSwapping                            */
2940 /************************************************************************/
2941 
FLTNeedSRSSwapping(mapObj * map,const char * pszSRS)2942 static int FLTNeedSRSSwapping( mapObj *map, const char* pszSRS )
2943 {
2944     int bNeedSwapping = MS_FALSE;
2945     projectionObj sProjTmp;
2946     msInitProjection(&sProjTmp);
2947     if( map )
2948     {
2949         msProjectionInheritContextFrom(&sProjTmp, &map->projection);
2950     }
2951     if (msLoadProjectionStringEPSG(&sProjTmp, pszSRS) == 0) {
2952         bNeedSwapping = msIsAxisInvertedProj(&sProjTmp);
2953     }
2954     msFreeProjection(&sProjTmp);
2955     return bNeedSwapping;
2956 }
2957 
2958 /************************************************************************/
2959 /*                      FLTDoAxisSwappingIfNecessary                    */
2960 /*                                                                      */
2961 /*      Explore all geometries and BBOX to do axis swapping when the    */
2962 /*      SRS requires it. If no explicit SRS is attached to the geometry */
2963 /*      the bDefaultSRSNeedsAxisSwapping is taken into account. The     */
2964 /*      caller will have to determine its value from a more general     */
2965 /*      context.                                                        */
2966 /************************************************************************/
FLTDoAxisSwappingIfNecessary(mapObj * map,FilterEncodingNode * psFilterNode,int bDefaultSRSNeedsAxisSwapping)2967 void FLTDoAxisSwappingIfNecessary(mapObj *map,
2968                                   FilterEncodingNode *psFilterNode,
2969                                   int bDefaultSRSNeedsAxisSwapping)
2970 {
2971     if( psFilterNode == NULL )
2972         return;
2973 
2974     if( psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
2975         psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_BBOX )
2976     {
2977         rectObj* rect = (rectObj *)psFilterNode->psRightNode->pOther;
2978         const char* pszSRS = psFilterNode->pszSRS;
2979         if( (pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
2980             (pszSRS == NULL && bDefaultSRSNeedsAxisSwapping) )
2981         {
2982             double tmp;
2983 
2984             tmp = rect->minx;
2985             rect->minx = rect->miny;
2986             rect->miny = tmp;
2987 
2988             tmp = rect->maxx;
2989             rect->maxx = rect->maxy;
2990             rect->maxy = tmp;
2991         }
2992     }
2993     else if( psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
2994              FLTIsGeometryFilterNodeType(psFilterNode->psRightNode->eType) )
2995     {
2996         shapeObj* shape = (shapeObj *)(psFilterNode->psRightNode->pOther);
2997         const char* pszSRS = psFilterNode->pszSRS;
2998         if( (pszSRS != NULL && FLTNeedSRSSwapping(map, pszSRS)) ||
2999             (pszSRS == NULL && bDefaultSRSNeedsAxisSwapping) )
3000         {
3001             msAxisSwapShape(shape);
3002         }
3003     }
3004     else
3005     {
3006         FLTDoAxisSwappingIfNecessary(map, psFilterNode->psLeftNode, bDefaultSRSNeedsAxisSwapping);
3007         FLTDoAxisSwappingIfNecessary(map, psFilterNode->psRightNode, bDefaultSRSNeedsAxisSwapping);
3008     }
3009 }
3010 
3011 
FLTReplacePropertyName(FilterEncodingNode * psFilterNode,const char * pszOldName,const char * pszNewName)3012 static void FLTReplacePropertyName(FilterEncodingNode *psFilterNode,
3013                                    const char *pszOldName,
3014                                    const char *pszNewName)
3015 {
3016   if (psFilterNode && pszOldName && pszNewName) {
3017     if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
3018       if (psFilterNode->pszValue &&
3019           strcasecmp(psFilterNode->pszValue, pszOldName) == 0) {
3020         msFree(psFilterNode->pszValue);
3021         psFilterNode->pszValue = msStrdup(pszNewName);
3022       }
3023     }
3024     if (psFilterNode->psLeftNode)
3025       FLTReplacePropertyName(psFilterNode->psLeftNode, pszOldName,
3026                              pszNewName);
3027     if (psFilterNode->psRightNode)
3028       FLTReplacePropertyName(psFilterNode->psRightNode, pszOldName,
3029                              pszNewName);
3030   }
3031 }
3032 
3033 
FLTIsGMLDefaultProperty(const char * pszName)3034 static int FLTIsGMLDefaultProperty(const char* pszName)
3035 {
3036     return (strcmp(pszName, "gml:name") == 0 ||
3037             strcmp(pszName, "gml:description") == 0 ||
3038             strcmp(pszName, "gml:descriptionReference") == 0 ||
3039             strcmp(pszName, "gml:identifier") == 0 ||
3040             strcmp(pszName, "gml:boundedBy") == 0 ||
3041             strcmp(pszName, "@gml:id") == 0);
3042 }
3043 
FLTStripNameSpacesFromPropertyName(FilterEncodingNode * psFilterNode)3044 static void FLTStripNameSpacesFromPropertyName(FilterEncodingNode *psFilterNode)
3045 {
3046   char **tokens=NULL;
3047   int n=0;
3048 
3049   if (psFilterNode) {
3050 
3051     if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
3052         psFilterNode->psLeftNode != NULL &&
3053         psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
3054         FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) )
3055     {
3056         return;
3057     }
3058 
3059     if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
3060       if (psFilterNode->pszValue &&
3061           strstr(psFilterNode->pszValue, ":")) {
3062         tokens = msStringSplit(psFilterNode->pszValue, ':', &n);
3063         if (tokens && n==2) {
3064           msFree(psFilterNode->pszValue);
3065           psFilterNode->pszValue = msStrdup(tokens[1]);
3066         }
3067         msFreeCharArray(tokens, n);
3068       }
3069     }
3070     if (psFilterNode->psLeftNode)
3071       FLTStripNameSpacesFromPropertyName(psFilterNode->psLeftNode);
3072     if (psFilterNode->psRightNode)
3073       FLTStripNameSpacesFromPropertyName(psFilterNode->psRightNode);
3074   }
3075 
3076 }
3077 
FLTRemoveGroupName(FilterEncodingNode * psFilterNode,gmlGroupListObj * groupList)3078 static void FLTRemoveGroupName(FilterEncodingNode *psFilterNode,
3079                                gmlGroupListObj* groupList)
3080 {
3081   int i;
3082 
3083   if (psFilterNode) {
3084 
3085     if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
3086       if( psFilterNode->pszValue != NULL )
3087       {
3088         const char* pszPropertyName = psFilterNode->pszValue;
3089         const char* pszSlash = strchr(pszPropertyName, '/');
3090         if( pszSlash != NULL ) {
3091           const char* pszColon = strchr(pszPropertyName, ':');
3092           if( pszColon != NULL && pszColon < pszSlash )
3093               pszPropertyName = pszColon + 1;
3094           for(i=0;i<groupList->numgroups;i++) {
3095             const char* pszGroupName = groupList->groups[i].name;
3096             size_t nGroupNameLen = strlen(pszGroupName);
3097             if(strncasecmp(pszPropertyName, pszGroupName, nGroupNameLen) == 0 &&
3098                             pszPropertyName[nGroupNameLen] == '/') {
3099               char* pszTmp;
3100               pszPropertyName = pszPropertyName + nGroupNameLen + 1;
3101               pszColon = strchr(pszPropertyName, ':');
3102               if( pszColon != NULL )
3103                 pszPropertyName = pszColon + 1;
3104               pszTmp = msStrdup(pszPropertyName);
3105               msFree(psFilterNode->pszValue);
3106               psFilterNode->pszValue = pszTmp;
3107               break;
3108             }
3109           }
3110         }
3111       }
3112     }
3113 
3114     if (psFilterNode->psLeftNode)
3115       FLTRemoveGroupName(psFilterNode->psLeftNode, groupList);
3116     if (psFilterNode->psRightNode)
3117       FLTRemoveGroupName(psFilterNode->psRightNode, groupList);
3118   }
3119 
3120 }
3121 
3122 /************************************************************************/
3123 /*                    FLTPreParseFilterForAliasAndGroup                 */
3124 /*                                                                      */
3125 /*      Utility function to replace aliased' and grouped attributes     */
3126 /*      with their internal name.                                       */
3127 /************************************************************************/
FLTPreParseFilterForAliasAndGroup(FilterEncodingNode * psFilterNode,mapObj * map,int i,const char * namespaces)3128 void FLTPreParseFilterForAliasAndGroup(FilterEncodingNode *psFilterNode,
3129                                mapObj *map, int i, const char *namespaces)
3130 {
3131   layerObj *lp=NULL;
3132   char szTmp[256];
3133   const char *pszFullName = NULL;
3134   int layerWasOpened =  MS_FALSE;
3135 
3136 #if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR)
3137 
3138   if (psFilterNode && map && i>=0 && i<map->numlayers) {
3139     /*strip name spaces before hand*/
3140     FLTStripNameSpacesFromPropertyName(psFilterNode);
3141 
3142     lp = GET_LAYER(map, i);
3143     layerWasOpened = msLayerIsOpen(lp);
3144     if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
3145 
3146       /* Remove group names from property names if using groupname/itemname syntax */
3147       gmlGroupListObj* groupList = msGMLGetGroups(lp, namespaces);
3148       if( groupList && groupList->numgroups > 0 )
3149         FLTRemoveGroupName(psFilterNode, groupList);
3150       msGMLFreeGroups(groupList);
3151 
3152       for(i=0; i<lp->numitems; i++) {
3153         if (!lp->items[i] || strlen(lp->items[i]) <= 0)
3154           continue;
3155         snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[i]);
3156         pszFullName = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
3157         if (pszFullName) {
3158           FLTReplacePropertyName(psFilterNode, pszFullName,
3159                                  lp->items[i]);
3160         }
3161       }
3162       if (!layerWasOpened) /* do not close the layer if it has been opened somewhere else (paging?) */
3163         msLayerClose(lp);
3164     }
3165   }
3166 #else
3167   msSetError(MS_MISCERR, "OWS support is not available.",
3168              "FLTPreParseFilterForAlias()");
3169 
3170 #endif
3171 }
3172 
3173 /************************************************************************/
3174 /*                        FLTCheckFeatureIdFilters                      */
3175 /*                                                                      */
3176 /*      Check that FeatureId filters match features in the active       */
3177 /*      layer.                                                          */
3178 /************************************************************************/
FLTCheckFeatureIdFilters(FilterEncodingNode * psFilterNode,mapObj * map,int i)3179 int FLTCheckFeatureIdFilters(FilterEncodingNode *psFilterNode,
3180                              mapObj *map, int i)
3181 {
3182     int status = MS_SUCCESS;
3183 
3184     if (psFilterNode->eType ==  FILTER_NODE_TYPE_FEATUREID)
3185     {
3186         char** tokens;
3187         int nTokens = 0;
3188         layerObj* lp;
3189         int j;
3190 
3191         lp = GET_LAYER(map, i);
3192         tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens);
3193         for (j=0; j<nTokens; j++) {
3194             const char* pszId = tokens[j];
3195             const char* pszDot = strrchr(pszId, '.');
3196             if( pszDot )
3197             {
3198                 if( pszDot - pszId != strlen(lp->name) ||
3199                     strncasecmp(pszId, lp->name, strlen(lp->name)) != 0 )
3200                 {
3201                     msSetError(MS_MISCERR, "Feature id %s not consistent with feature type name %s.",
3202                                "FLTPreParseFilterForAlias()", pszId, lp->name);
3203                     status = MS_FAILURE;
3204                     break;
3205                 }
3206             }
3207         }
3208         msFreeCharArray(tokens, nTokens);
3209     }
3210 
3211     if (psFilterNode->psLeftNode)
3212     {
3213       status = FLTCheckFeatureIdFilters(psFilterNode->psLeftNode, map, i);
3214       if( status == MS_SUCCESS )
3215       {
3216         if (psFilterNode->psRightNode)
3217             status = FLTCheckFeatureIdFilters(psFilterNode->psRightNode, map, i);
3218       }
3219     }
3220     return status;
3221 }
3222 
3223 /************************************************************************/
3224 /*                        FLTCheckInvalidOperand                        */
3225 /*                                                                      */
3226 /*      Check that the operand of a comparison operator is valid        */
3227 /*      Currently only detects use of boundedBy in a binary comparison  */
3228 /************************************************************************/
FLTCheckInvalidOperand(FilterEncodingNode * psFilterNode)3229 int FLTCheckInvalidOperand(FilterEncodingNode *psFilterNode)
3230 {
3231     int status = MS_SUCCESS;
3232 
3233     if (psFilterNode->eType ==  FILTER_NODE_TYPE_COMPARISON &&
3234         psFilterNode->psLeftNode != NULL &&
3235         psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME)
3236     {
3237         if( strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") == 0 &&
3238             strcmp(psFilterNode->pszValue, "PropertyIsNull") != 0 &&
3239             strcmp(psFilterNode->pszValue, "PropertyIsNil") != 0 )
3240         {
3241             msSetError(MS_MISCERR, "Operand '%s' is invalid in comparison.",
3242                        "FLTCheckInvalidOperand()", psFilterNode->psLeftNode->pszValue);
3243             return MS_FAILURE;
3244         }
3245     }
3246     if (psFilterNode->psLeftNode)
3247     {
3248       status = FLTCheckInvalidOperand(psFilterNode->psLeftNode);
3249       if( status == MS_SUCCESS )
3250       {
3251         if (psFilterNode->psRightNode)
3252             status = FLTCheckInvalidOperand(psFilterNode->psRightNode);
3253       }
3254     }
3255     return status;
3256 }
3257 
3258 /************************************************************************/
3259 /*                       FLTProcessPropertyIsNull                       */
3260 /*                                                                      */
3261 /*      HACK for PropertyIsNull processing. PostGIS & Spatialite only   */
3262 /*      for now.                                                        */
3263 /************************************************************************/
FLTProcessPropertyIsNull(FilterEncodingNode * psFilterNode,mapObj * map,int i)3264 int FLTProcessPropertyIsNull(FilterEncodingNode *psFilterNode,
3265                             mapObj *map, int i)
3266 {
3267     int status = MS_SUCCESS;
3268 
3269     if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON &&
3270         psFilterNode->psLeftNode != NULL &&
3271         psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME &&
3272         strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
3273         !FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) )
3274     {
3275         layerObj* lp;
3276         int layerWasOpened;
3277 
3278         lp = GET_LAYER(map, i);
3279         layerWasOpened = msLayerIsOpen(lp);
3280 
3281         /* Horrible HACK to compensate for the lack of null testing in MapServer */
3282         if( (lp->connectiontype == MS_POSTGIS ||
3283              (lp->connectiontype == MS_OGR && msOGRIsSpatialite(lp))) &&
3284             strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 )
3285         {
3286             msFree(psFilterNode->pszValue);
3287             psFilterNode->pszValue = msStrdup("PropertyIsEqualTo");
3288             psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
3289             psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
3290             psFilterNode->psRightNode->pszValue = msStrdup("_MAPSERVER_NULL_");
3291         }
3292 
3293         if (!layerWasOpened) /* do not close the layer if it has been opened somewhere else (paging?) */
3294           msLayerClose(lp);
3295     }
3296 
3297     if (psFilterNode->psLeftNode)
3298     {
3299       status = FLTProcessPropertyIsNull(psFilterNode->psLeftNode, map, i);
3300       if( status == MS_SUCCESS )
3301       {
3302         if (psFilterNode->psRightNode)
3303             status = FLTProcessPropertyIsNull(psFilterNode->psRightNode, map, i);
3304       }
3305     }
3306     return status;
3307 }
3308 
3309 /************************************************************************/
3310 /*                        FLTCheckInvalidProperty                       */
3311 /*                                                                      */
3312 /*      Check that property names are known                             */
3313 /************************************************************************/
FLTCheckInvalidProperty(FilterEncodingNode * psFilterNode,mapObj * map,int i)3314 int FLTCheckInvalidProperty(FilterEncodingNode *psFilterNode,
3315                             mapObj *map, int i)
3316 {
3317     int status = MS_SUCCESS;
3318 
3319     if (psFilterNode->eType ==  FILTER_NODE_TYPE_COMPARISON &&
3320         psFilterNode->psLeftNode != NULL &&
3321         psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME)
3322     {
3323         layerObj* lp;
3324         int layerWasOpened;
3325         int bFound = MS_FALSE;
3326 
3327         if ((strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
3328              strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0) &&
3329              FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) )
3330         {
3331             return MS_SUCCESS;
3332         }
3333 
3334         lp = GET_LAYER(map, i);
3335         layerWasOpened = msLayerIsOpen(lp);
3336         if ((layerWasOpened || msLayerOpen(lp) == MS_SUCCESS)
3337              && msLayerGetItems(lp) == MS_SUCCESS) {
3338           int i;
3339           gmlItemListObj* items = msGMLGetItems(lp, "G");
3340           for(i=0; i<items->numitems; i++) {
3341             if (!items->items[i].name || strlen(items->items[i].name) <= 0 ||
3342                 !items->items[i].visible)
3343               continue;
3344             if (strcasecmp(items->items[i].name, psFilterNode->psLeftNode->pszValue) == 0) {
3345                 bFound = MS_TRUE;
3346                 break;
3347             }
3348           }
3349           msGMLFreeItems(items);
3350         }
3351 
3352         if (!layerWasOpened) /* do not close the layer if it has been opened somewhere else (paging?) */
3353           msLayerClose(lp);
3354 
3355         if( !bFound )
3356         {
3357             msSetError(MS_MISCERR, "Property '%s' is unknown.",
3358                        "FLTCheckInvalidProperty()", psFilterNode->psLeftNode->pszValue);
3359             return MS_FAILURE;
3360         }
3361     }
3362 
3363     if (psFilterNode->psLeftNode)
3364     {
3365       status = FLTCheckInvalidProperty(psFilterNode->psLeftNode, map, i);
3366       if( status == MS_SUCCESS )
3367       {
3368         if (psFilterNode->psRightNode)
3369             status = FLTCheckInvalidProperty(psFilterNode->psRightNode, map, i);
3370       }
3371     }
3372     return status;
3373 }
3374 
3375 /************************************************************************/
3376 /*                           FLTSimplify                                */
3377 /*                                                                      */
3378 /*      Simplify the expression by removing parts that evaluate to      */
3379 /*      constants.                                                      */
3380 /*      The passed psFilterNode is potentially consumed by the function */
3381 /*      and replaced by the returned value.                             */
3382 /*      If the function returns NULL, *pnEvaluation = MS_FALSE means    */
3383 /*      that  the filter evaluates to FALSE, or MS_TRUE that it         */
3384 /*      evaluates to TRUE                                               */
3385 /************************************************************************/
FLTSimplify(FilterEncodingNode * psFilterNode,int * pnEvaluation)3386 FilterEncodingNode* FLTSimplify(FilterEncodingNode *psFilterNode,
3387                                 int* pnEvaluation)
3388 {
3389     *pnEvaluation = -1;
3390 
3391     /* There are no nullable or nillable property in WFS currently */
3392     /* except gml:name or gml:description that are null */
3393     if( psFilterNode->eType ==  FILTER_NODE_TYPE_COMPARISON &&
3394         (strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 ||
3395          strcmp(psFilterNode->pszValue, "PropertyIsNil") == 0 ) &&
3396         psFilterNode->psLeftNode != NULL &&
3397         psFilterNode->psLeftNode->eType == FILTER_NODE_TYPE_PROPERTYNAME )
3398     {
3399         if( strcmp(psFilterNode->pszValue, "PropertyIsNull") == 0 &&
3400             FLTIsGMLDefaultProperty(psFilterNode->psLeftNode->pszValue) &&
3401             strcmp(psFilterNode->psLeftNode->pszValue, "@gml:id") != 0  &&
3402             strcmp(psFilterNode->psLeftNode->pszValue, "gml:boundedBy") != 0)
3403             *pnEvaluation = MS_TRUE;
3404         else
3405             *pnEvaluation = MS_FALSE;
3406         FLTFreeFilterEncodingNode(psFilterNode);
3407         return NULL;
3408     }
3409 
3410     if( psFilterNode->eType ==  FILTER_NODE_TYPE_LOGICAL &&
3411         strcasecmp(psFilterNode->pszValue, "NOT") == 0 &&
3412         psFilterNode->psLeftNode != NULL )
3413     {
3414         int nEvaluation;
3415         psFilterNode->psLeftNode = FLTSimplify(psFilterNode->psLeftNode,
3416                                                &nEvaluation);
3417         if( psFilterNode->psLeftNode == NULL )
3418         {
3419             *pnEvaluation = 1 - nEvaluation;
3420             FLTFreeFilterEncodingNode(psFilterNode);
3421             return NULL;
3422         }
3423     }
3424 
3425     if( psFilterNode->eType ==  FILTER_NODE_TYPE_LOGICAL &&
3426         (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
3427          strcasecmp(psFilterNode->pszValue, "OR") == 0) &&
3428         psFilterNode->psLeftNode != NULL &&
3429         psFilterNode->psRightNode != NULL )
3430     {
3431         FilterEncodingNode* psOtherNode;
3432         int nEvaluation;
3433         int nExpectedValForFastExit;
3434         psFilterNode->psLeftNode = FLTSimplify(psFilterNode->psLeftNode,
3435                                                &nEvaluation);
3436 
3437         if( strcasecmp(psFilterNode->pszValue, "AND") == 0 )
3438             nExpectedValForFastExit = MS_FALSE;
3439         else
3440             nExpectedValForFastExit = MS_TRUE;
3441 
3442         if( psFilterNode->psLeftNode == NULL )
3443         {
3444             if( nEvaluation == nExpectedValForFastExit )
3445             {
3446                 *pnEvaluation = nEvaluation;
3447                 FLTFreeFilterEncodingNode(psFilterNode);
3448                 return NULL;
3449             }
3450             psOtherNode = psFilterNode->psRightNode;
3451             psFilterNode->psRightNode = NULL;
3452             FLTFreeFilterEncodingNode(psFilterNode);
3453             return FLTSimplify(psOtherNode, pnEvaluation);
3454         }
3455 
3456         psFilterNode->psRightNode = FLTSimplify(psFilterNode->psRightNode,
3457                                                 &nEvaluation);
3458         if( psFilterNode->psRightNode == NULL )
3459         {
3460             if( nEvaluation == nExpectedValForFastExit )
3461             {
3462                 *pnEvaluation = nEvaluation;
3463                 FLTFreeFilterEncodingNode(psFilterNode);
3464                 return NULL;
3465             }
3466             psOtherNode = psFilterNode->psLeftNode;
3467             psFilterNode->psLeftNode = NULL;
3468             FLTFreeFilterEncodingNode(psFilterNode);
3469             return FLTSimplify(psOtherNode, pnEvaluation);
3470         }
3471     }
3472 
3473     return psFilterNode;
3474 }
3475 
3476 
3477 #ifdef USE_LIBXML2
3478 
FLTGetCapabilities(xmlNsPtr psNsParent,xmlNsPtr psNsOgc,int bTemporal)3479 xmlNodePtr FLTGetCapabilities(xmlNsPtr psNsParent, xmlNsPtr psNsOgc, int bTemporal)
3480 {
3481   xmlNodePtr psRootNode = NULL, psNode = NULL, psSubNode = NULL, psSubSubNode = NULL;
3482 
3483   psRootNode = xmlNewNode(psNsParent, BAD_CAST "Filter_Capabilities");
3484 
3485   psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Spatial_Capabilities", NULL);
3486 
3487   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "GeometryOperands", NULL);
3488   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Point");
3489   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:LineString");
3490   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Polygon");
3491   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Envelope");
3492 
3493   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "SpatialOperators", NULL);
3494 #ifdef USE_GEOS
3495   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3496   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Equals");
3497   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3498   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Disjoint");
3499   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3500   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Touches");
3501   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3502   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Within");
3503   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3504   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Overlaps");
3505   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3506   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Crosses");
3507   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3508   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Intersects");
3509   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3510   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Contains");
3511   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3512   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "DWithin");
3513   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3514   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Beyond");
3515 #endif
3516   psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
3517   xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "BBOX");
3518 
3519   if (bTemporal) {
3520     psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Temporal_Capabilities", NULL);
3521     psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperands", NULL);
3522     psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand", BAD_CAST "gml:TimePeriod");
3523     psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand", BAD_CAST "gml:TimeInstant");
3524 
3525     psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperators", NULL);
3526     psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperator", NULL);
3527     xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "TM_Equals");
3528   }
3529   psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Scalar_Capabilities", NULL);
3530   xmlNewChild(psNode, psNsOgc, BAD_CAST "LogicalOperators", NULL);
3531   psNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperators", NULL);
3532   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "LessThan");
3533   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "GreaterThan");
3534   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "LessThanEqualTo");
3535   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "GreaterThanEqualTo");
3536   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "EqualTo");
3537   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "NotEqualTo");
3538   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Like");
3539   psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Between");
3540 
3541   psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Id_Capabilities", NULL);
3542   xmlNewChild(psNode, psNsOgc, BAD_CAST "EID", NULL);
3543   xmlNewChild(psNode, psNsOgc, BAD_CAST "FID", NULL);
3544   return psRootNode;
3545 }
3546 #endif
3547