[Git][debian-gis-team/mapserver][master] 5 commits: New upstream version 8.6.5

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Fri Jul 10 18:59:17 BST 2026



Bas Couwenberg pushed to branch master at Debian GIS Project / mapserver


Commits:
709e83ca by Bas Couwenberg at 2026-07-10T19:46:42+02:00
New upstream version 8.6.5
- - - - -
269d5dde by Bas Couwenberg at 2026-07-10T19:46:56+02:00
Update upstream source from tag 'upstream/8.6.5'

Update to upstream version '8.6.5'
with Debian dir cc3cfa24bcae37cb9bd6b8748e98be31d653e365
- - - - -
dda2f08b by Bas Couwenberg at 2026-07-10T19:47:44+02:00
New upstream release.

- - - - -
afebc8d5 by Bas Couwenberg at 2026-07-10T19:54:30+02:00
Update symbols for 8.6.5.

- - - - -
8fe0b00d by Bas Couwenberg at 2026-07-10T19:54:30+02:00
Set distribution to unstable.

- - - - -


20 changed files:

- CITATION.cff
- CMakeLists.txt
- HISTORY.md
- debian/changelog
- debian/libmapserver2t64.symbols
- src/flatgeobuf/flatgeobuf_c.cpp
- src/mapimageio.c
- src/mapjoin.c
- src/maplayer.c
- src/mapogcapi.cpp
- src/mapogcfilter.cpp
- src/mapogcfilter.h
- src/mapogcsld.cpp
- src/mappostgresql.c
- src/mapscript/python/pyextend.i
- src/mapserver.h
- src/maptemplate.c
- src/maptree.c
- src/mapwcs.cpp
- src/mapwms.cpp


Changes:

=====================================
CITATION.cff
=====================================
@@ -1,8 +1,8 @@
 cff-version: 1.2.0
 title: MapServer
 message: If you use this software, please cite it using the metadata from this file.
-version: 8.6.4
-date-released: 2026-06-01
+version: 8.6.5
+date-released: 2026-07-10
 abstract: MapServer is an Open Source platform for publishing spatial data and interactive mapping applications to the web.
 type: software
 authors:


=====================================
CMakeLists.txt
=====================================
@@ -17,7 +17,7 @@ include(CheckCSourceCompiles)
 
 set (MapServer_VERSION_MAJOR 8)
 set (MapServer_VERSION_MINOR 6)
-set (MapServer_VERSION_REVISION 4)
+set (MapServer_VERSION_REVISION 5)
 set (MapServer_VERSION_SUFFIX "")
 
 # Set C++ version


=====================================
HISTORY.md
=====================================
@@ -13,6 +13,21 @@ https://mapserver.org/development/changelog/
 
 The online Migration Guide can be found at https://mapserver.org/MIGRATION_GUIDE.html
 
+8.6.5 release (2026-07-10)
+--------------------------
+
+- security: OGC API Features: validate offset before rendering to fix reflected XSS (#7573)
+
+- security: WCS POST: NULL-Dereference Denial of Service When `BoundingBox` Omits `crs` (#7556)
+
+- security: Reflected XSS in WMS OpenLayers output via the `X-Forwarded-Host` header (#7544)
+
+- security: Heap buffer overflow in WMS GetLegendGraphic via repeated LAYER parameters (#7555)
+
+- security: msMySQLJoinNext(): prevent SQL injection (#7547)
+
+- security: msPOSTGRESQLJoinNext(): avoid potential SQL injection (#7546)
+
 8.6.4 release (2026-06-01)
 --------------------------
 


=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+mapserver (8.6.5-1) unstable; urgency=high
+
+  * New upstream release.
+  * Update symbols for 8.6.5.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Fri, 10 Jul 2026 19:48:35 +0200
+
 mapserver (8.6.4-1) unstable; urgency=high
 
   * New upstream release.


=====================================
debian/libmapserver2t64.symbols
=====================================
@@ -1,4 +1,4 @@
-# SymbolsHelper-Confirmed: 8.6.2 amd64
+# SymbolsHelper-Confirmed: 8.6.5 amd64
 libmapserver.so.2 #PACKAGE# #MINVER#
 * Build-Depends-Package: libmapserver-dev
  CompareEllipseRegion at Base 6.2.1
@@ -64,7 +64,6 @@ libmapserver.so.2 #PACKAGE# #MINVER#
  LayerDefaultClose at Base 6.2.1
  LayerDefaultCloseConnection at Base 6.2.1
  LayerDefaultCreateItems at Base 6.2.1
- LayerDefaultEscapePropertyName at Base 6.2.1
  LayerDefaultEscapeSQLParam at Base 6.2.1
  LayerDefaultFreeItemInfo at Base 6.2.1
  LayerDefaultGetAutoStyle at Base 6.2.1
@@ -86,8 +85,7 @@ libmapserver.so.2 #PACKAGE# #MINVER#
  Pix2LayerGeoref at Base 6.2.1
  RebuildClusters at Base 6.2.1
  _Z11agg2CleanupPv at Base 6.2.1
- (optional=templinst)_Z11parse_valueIhEvPhPPctRjb at Base 8.0.0
- (optional=templinst|arch=!armhf !i386 !powerpc)_Z11parse_valueIlEvPhPPctRjb at Base 8.4.0
+ (optional=templinst)_Z11parse_valueIhEbPhjPPctRjb at Base 8.6.5
  (optional=templinst|arch=armel armhf i386 m68k mipsel powerpc)_Z11parse_valueIxEvPhPPctRjb at Base 8.0.0
  (optional=templinst|arch=mipsel)_Z11parse_valueIyEvPhPPctRjb at Base 8.0.0
  _Z12msPrintShapeP8shapeObj at Base 8.6.0
@@ -1462,8 +1460,6 @@ libmapserver.so.2 #PACKAGE# #MINVER#
  (optional=templinst)_ZNSt8__detail18__to_chars_10_implIjEEvPcjT_ at Base 8.0.0
  (optional=templinst|arch=!armel !armhf !i386 !m68k !mipsel !powerpc)_ZNSt8__detail18__to_chars_10_implImEEvPcjT_ at Base 8.0.0
  (optional=templinst|arch=armel armhf i386 m68k mipsel powerpc)_ZNSt8__detail18__to_chars_10_implIyEEvPcjT_ at Base 8.0.0
- (optional=templinst|arch=amd64 arm64 riscv64 s390x sparc64)_ZNSt8functionIFN11ms_nlohmann10basic_jsonISt3mapSt6vectorNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEblmdSaNS0_14adl_serializerES3_IhSaIhEEEERS3_IPKSD_SaISF_EEEEC1ERKSK_ at Base 8.6.0
- (optional=templinst|arch=amd64 arm64 riscv64 s390x sparc64)_ZNSt8functionIFN11ms_nlohmann10basic_jsonISt3mapSt6vectorNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEblmdSaNS0_14adl_serializerES3_IhSaIhEEEERS3_IPKSD_SaISF_EEEEC2ERKSK_ at Base 8.6.0
  (optional=templinst|arch=!armel !armhf !i386 !m68k !mipsel !powerpc)_ZSt13__adjust_heapIN11ms_nlohmann6detail9iter_implINS0_10basic_jsonISt3mapSt6vectorNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEblmdSaNS0_14adl_serializerES5_IhSaIhEEEEEElSF_N9__gnu_cxx5__ops15_Iter_less_iterEEvT_T0_SL_T1_T2_ at Base 8.0.0
  (optional=templinst|arch=armel armhf i386 m68k mipsel powerpc)_ZSt13__adjust_heapIN11ms_nlohmann6detail9iter_implINS0_10basic_jsonISt3mapSt6vectorNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEbxydSaNS0_14adl_serializerES5_IhSaIhEEEEEEiSF_N9__gnu_cxx5__ops15_Iter_less_iterEEvT_T0_SL_T1_T2_ at Base 8.0.0
  (optional=templinst|arch=!alpha !amd64 !arm64 !ia64 !kfreebsd-amd64 !loong64 !mips64el !ppc64 !ppc64el !riscv64 !s390x !sparc64)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPPN10ClipperLib6OutRecESt6vectorIS4_SaIS4_EEEEiS4_NS0_5__ops15_Iter_comp_iterIPFbS4_S4_EEEEvT_T0_SG_T1_T2_ at Base 6.4.1
@@ -2001,6 +1997,7 @@ libmapserver.so.2 #PACKAGE# #MINVER#
  msCalculateScale at Base 6.2.1
  msCaseEvalRegex at Base 7.6.3
  msCaseReplaceSubstring at Base 6.2.1
+ msCheckDepthLessThan at Base 8.6.5
  msCheckLabelMinDistance at Base 7.0.0
  msCheckNumber at Base 8.0.0
  msCheckParentPointer at Base 6.2.1
@@ -2134,6 +2131,7 @@ libmapserver.so.2 #PACKAGE# #MINVER#
  msDecodeHTMLEntities at Base 6.2.1
  msDecryptStringTokens at Base 6.2.1
  msDecryptStringWithKey at Base 6.2.1
+ msDefaultEscapePropertyName at Base 8.6.5
  msDeleteLabelStyle at Base 6.2.1
  msDeleteStyle at Base 6.2.1
  msDensify at Base 8.0.0


=====================================
src/flatgeobuf/flatgeobuf_c.cpp
=====================================
@@ -15,15 +15,21 @@ uint8_t FLATGEOBUF_MAGICBYTES_SIZE = sizeof(flatgeobuf_magicbytes);
 uint32_t INIT_BUFFER_SIZE = 1024 * 4;
 
 template <typename T>
-void parse_value(uint8_t *data, char **values, uint16_t i, uint32_t &offset, bool found)
+bool parse_value(uint8_t *data, uint32_t size, char **values, uint16_t i, uint32_t &offset, bool found)
 {
     using std::to_string;
+    if (sizeof(T) > size - offset)
+    {
+        msSetError(MS_FGBERR, "Invalid size for property value", "flatgeobuf_decode_properties");
+        return false;
+    }
     if (found)
     {
         msFree(values[i]);
         values[i] = msStrdup(to_string(*((T*) (data + offset))).c_str());
     }
     offset += sizeof(T);
+    return true;
 }
 
 ctx *flatgeobuf_init_ctx()
@@ -139,7 +145,8 @@ int flatgeobuf_decode_feature(ctx *ctx, layerObj *layer, shapeObj *shape)
     if (properties && properties->size() != 0) {
         ctx->properties = (uint8_t *) properties->data();
         ctx->properties_size = properties->size();
-        flatgeobuf_decode_properties(ctx, layer, shape);
+        if (flatgeobuf_decode_properties(ctx, layer, shape) == -1)
+            return -1;
     } else {
         ctx->properties_size = 0;
     }
@@ -183,47 +190,51 @@ int flatgeobuf_decode_properties(ctx *ctx, layerObj *layer, shapeObj *shape)
 		type = column.type;
 		switch (type) {
 		case flatgeobuf_column_type_bool:
-            parse_value<uint8_t>(data, values, ii, offset, found);
+            if (!parse_value<uint8_t>(data, size, values, ii, offset, found)) return -1;
 			break;
 		case flatgeobuf_column_type_byte:
-            parse_value<int8_t>(data, values, ii, offset, found);
+            if (!parse_value<int8_t>(data, size, values, ii, offset, found)) return -1;
 			break;
 		case flatgeobuf_column_type_ubyte:
-            parse_value<uint8_t>(data, values, ii, offset, found);
+            if (!parse_value<uint8_t>(data, size, values, ii, offset, found)) return -1;
 			break;
 		case flatgeobuf_column_type_short:
-			parse_value<int16_t>(data, values, ii, offset, found);
+			if (!parse_value<int16_t>(data, size, values, ii, offset, found)) return -1;
 			break;
 		case flatgeobuf_column_type_ushort:
-            parse_value<uint16_t>(data, values, ii, offset, found);
+            if (!parse_value<uint16_t>(data, size, values, ii, offset, found)) return -1;
 			break;
 		case flatgeobuf_column_type_int:
-            parse_value<int32_t>(data, values, ii, offset, found);
+            if (!parse_value<int32_t>(data, size, values, ii, offset, found)) return -1;
             break;
 		case flatgeobuf_column_type_uint:
-			parse_value<uint32_t>(data, values, ii, offset, found);
+			if (!parse_value<uint32_t>(data, size, values, ii, offset, found)) return -1;
 			break;
 		case flatgeobuf_column_type_long:
-            parse_value<int64_t>(data, values, ii, offset, found);
+            if (!parse_value<int64_t>(data, size, values, ii, offset, found)) return -1;
             break;
 		case flatgeobuf_column_type_ulong:
-			parse_value<uint64_t>(data, values, ii, offset, found);
+			if (!parse_value<uint64_t>(data, size, values, ii, offset, found)) return -1;
 			break;
 		case flatgeobuf_column_type_float:
-            parse_value<float>(data, values, ii, offset, found);
+            if (!parse_value<float>(data, size, values, ii, offset, found)) return -1;
 			break;
 		case flatgeobuf_column_type_double:
-			parse_value<double>(data, values, ii, offset, found);
+			if (!parse_value<double>(data, size, values, ii, offset, found)) return -1;
 			break;
         case flatgeobuf_column_type_datetime:
 		case flatgeobuf_column_type_string: {
 			uint32_t len;
-			if (offset + sizeof(len) > size){
+			if (sizeof(len) > size - offset){
                 msSetError(MS_FGBERR, "Invalid size for string value", "flatgeobuf_decode_properties");
                 return -1;
             }
 			memcpy(&len, data + offset, sizeof(uint32_t));
 			offset += sizeof(len);
+			if (len > size - offset){
+                msSetError(MS_FGBERR, "Invalid size for string value", "flatgeobuf_decode_properties");
+                return -1;
+            }
 			if (found) {
                 char *str = (char *) msSmallMalloc(len + 1);
                 memcpy(str, data + offset, len);


=====================================
src/mapimageio.c
=====================================
@@ -860,6 +860,17 @@ static char const *gif_error_msg()
   }
 }
 
+/* release the giflib handle, ignoring the close status */
+static void closeGIF(GifFileType *image) {
+#if defined GIFLIB_MAJOR && GIFLIB_MINOR &&                                    \
+    ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || (GIFLIB_MAJOR > 5))
+  int errcode;
+  DGifCloseFile(image, &errcode);
+#else
+  DGifCloseFile(image);
+#endif
+}
+
 /* not fully implemented and tested */
 /* missing: set the first pointer to a,r,g,b */
 int readGIF(char *path, rasterBufferObj *rb) {
@@ -905,11 +916,24 @@ int readGIF(char *path, rasterBufferObj *rb) {
   a = rb->data.rgba.a = &rb->data.rgba.pixels[3];
 
   cmap = (image->Image.ColorMap) ? image->Image.ColorMap : image->SColorMap;
+  if (cmap == NULL) {
+    msSetError(MS_MISCERR, "gif image has no color map", "readGIF()");
+    free(rb->data.rgba.pixels);
+    rb->data.rgba.pixels = NULL;
+    closeGIF(image);
+    return MS_FAILURE;
+  }
+  /* the background index is read straight from the file and may point past
+     the end of the color table */
+  const int bgIndex = (image->SBackGroundColor >= 0 &&
+                       image->SBackGroundColor < cmap->ColorCount)
+                          ? image->SBackGroundColor
+                          : 0;
   for (unsigned i = 0; i < rb->width * rb->height; i++) {
     *a = 255;
-    *r = cmap->Colors[image->SBackGroundColor].Red;
-    *g = cmap->Colors[image->SBackGroundColor].Green;
-    *b = cmap->Colors[image->SBackGroundColor].Blue;
+    *r = cmap->Colors[bgIndex].Red;
+    *g = cmap->Colors[bgIndex].Green;
+    *b = cmap->Colors[bgIndex].Blue;
     a += rb->data.rgba.pixel_step;
     r += rb->data.rgba.pixel_step;
     g += rb->data.rgba.pixel_step;
@@ -955,6 +979,9 @@ int readGIF(char *path, rasterBufferObj *rb) {
           msSetError(MS_MISCERR,
                      "corrupted gif image, img size exceeds screen size",
                      "readGIF()");
+          free(rb->data.rgba.pixels);
+          rb->data.rgba.pixels = NULL;
+          closeGIF(image);
           return MS_FAILURE;
         }
 
@@ -983,7 +1010,8 @@ int readGIF(char *path, rasterBufferObj *rb) {
 
               for (unsigned j = 0; j < width; j++) {
                 GifPixelType pix = line[j];
-                if (transIdx == -1 || pix != transIdx) {
+                if ((transIdx == -1 || pix != transIdx) &&
+                    pix < cmap->ColorCount) {
                   *a = 255;
                   *r = cmap->Colors[pix].Red;
                   *g = cmap->Colors[pix].Green;
@@ -1018,7 +1046,8 @@ int readGIF(char *path, rasterBufferObj *rb) {
             }
             for (unsigned j = 0; j < width; j++) {
               GifPixelType pix = line[j];
-              if (transIdx == -1 || pix != transIdx) {
+              if ((transIdx == -1 || pix != transIdx) &&
+                  pix < cmap->ColorCount) {
                 *a = 255;
                 *r = cmap->Colors[pix].Red;
                 *g = cmap->Colors[pix].Green;
@@ -1102,25 +1131,7 @@ int readGIF(char *path, rasterBufferObj *rb) {
 
   } while (recordType != TERMINATE_RECORD_TYPE);
 
-#if defined GIFLIB_MAJOR && GIFLIB_MINOR &&                                    \
-    ((GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1) || (GIFLIB_MAJOR > 5))
-  if (DGifCloseFile(image, &errcode) == GIF_ERROR) {
-    msSetError(MS_MISCERR, "failed to close gif after loading: %s", "readGIF()",
-               gif_error_msg(errcode));
-    return MS_FAILURE;
-  }
-#else
-  if (DGifCloseFile(image) == GIF_ERROR) {
-#if defined GIFLIB_MAJOR && GIFLIB_MAJOR >= 5
-    msSetError(MS_MISCERR, "failed to close gif after loading: %s", "readGIF()",
-               gif_error_msg(image->Error));
-#else
-    msSetError(MS_MISCERR, "failed to close gif after loading: %s", "readGIF()",
-               gif_error_msg());
-#endif
-    return MS_FAILURE;
-  }
-#endif
+  closeGIF(image);
 
   return MS_SUCCESS;
 }


=====================================
src/mapjoin.c
=====================================
@@ -808,8 +808,22 @@ int msMySQLJoinNext(joinObj *join) {
   /* if(strcmp(joininfo->target, msMySQLReadStringAttribute(joininfo->conn, i,
    * joininfo->toindex)) == 0) break; */
   /* }   */
-  snprintf(qbuf, sizeof(qbuf), "SELECT * FROM %s WHERE %s = %s", join->table,
-           joininfo->tocolumn, joininfo->target);
+
+  char *endptr = NULL;
+  (void)strtoll(joininfo->target, &endptr, 10);
+  if (endptr != joininfo->target + strlen(joininfo->target)) {
+    msSetError(MS_JOINERR,
+               "Non-numeric value in MySQL JOIN colunn not supported.",
+               "msMySQLJoinNext()");
+    return (MS_FAILURE);
+  }
+
+  char *escapedTableName = msReplaceSubstring(join->table, "`", "``");
+  char *escapedToColumn = msReplaceSubstring(joininfo->tocolumn, "`", "``");
+  snprintf(qbuf, sizeof(qbuf), "SELECT * FROM `%s` WHERE `%s` = %s",
+           escapedTableName, escapedToColumn, joininfo->target);
+  msFree(escapedTableName);
+  msFree(escapedToColumn);
   MYDEBUG printf("%s<BR>\n", qbuf);
   if ((joininfo->qresult =
            msMySQLQuery(qbuf, joininfo->conn))) { /* There were some rows found,


=====================================
src/maplayer.c
=====================================
@@ -2254,33 +2254,41 @@ char *LayerDefaultEscapeSQLParam(layerObj *layer, const char *pszString) {
 /*                                                                      */
 /*      Return the property name in a properly escaped and quoted form. */
 /************************************************************************/
-char *LayerDefaultEscapePropertyName(layerObj *layer, const char *pszString) {
+
+char *msDefaultEscapePropertyName(const char *pszString) {
   char *pszEscapedStr = NULL;
   int i, j = 0;
 
-  if (layer && pszString && strlen(pszString) > 0) {
-    int nLength = strlen(pszString);
+  int nLength = strlen(pszString);
 
-    pszEscapedStr = (char *)msSmallMalloc(1 + 2 * nLength + 1 + 1);
-    pszEscapedStr[j++] = '"';
+  pszEscapedStr = (char *)msSmallMalloc(1 + 2 * nLength + 1 + 1);
+  pszEscapedStr[j++] = '"';
 
-    for (i = 0; i < nLength; i++) {
-      char c = pszString[i];
-      if (c == '"') {
-        pszEscapedStr[j++] = '"';
-        pszEscapedStr[j++] = '"';
-      } else if (c == '\\') {
-        pszEscapedStr[j++] = '\\';
-        pszEscapedStr[j++] = '\\';
-      } else
-        pszEscapedStr[j++] = c;
-    }
-    pszEscapedStr[j++] = '"';
-    pszEscapedStr[j++] = 0;
+  for (i = 0; i < nLength; i++) {
+    char c = pszString[i];
+    if (c == '"') {
+      pszEscapedStr[j++] = '"';
+      pszEscapedStr[j++] = '"';
+    } else if (c == '\\') {
+      pszEscapedStr[j++] = '\\';
+      pszEscapedStr[j++] = '\\';
+    } else
+      pszEscapedStr[j++] = c;
   }
+  pszEscapedStr[j++] = '"';
+  pszEscapedStr[j++] = 0;
+
   return pszEscapedStr;
 }
 
+static char *LayerDefaultEscapePropertyName(layerObj *layer,
+                                            const char *pszString) {
+  if (layer && pszString && strlen(pszString) > 0) {
+    return msDefaultEscapePropertyName(pszString);
+  }
+  return NULL;
+}
+
 /*
  * msConnectLayer
  *


=====================================
src/mapogcapi.cpp
=====================================
@@ -1351,6 +1351,18 @@ static int processCollectionItemsRequest(mapObj *map, cgiRequestObj *request,
       return MS_SUCCESS;
     }
   } else { // bbox query
+    const char *offsetStr = getRequestParameter(request, "offset");
+    if (offsetStr) {
+      if (msStringToInt(offsetStr, &offset, 10) != MS_SUCCESS) {
+        msOGCAPIOutputError(OGCAPI_PARAM_ERROR, "Bad value for offset.");
+        return MS_SUCCESS;
+      }
+      if (offset < 0) {
+        msOGCAPIOutputError(OGCAPI_PARAM_ERROR, "Offset out of range.");
+        return MS_SUCCESS;
+      }
+    }
+
     map->query.type = MS_QUERY_BY_RECT;
     map->query.mode = MS_QUERY_MULTIPLE;
     map->query.layer = i;
@@ -1376,14 +1388,8 @@ static int processCollectionItemsRequest(mapObj *map, cgiRequestObj *request,
       map->query.only_cache_result_count = MS_FALSE;
       map->query.maxfeatures = limit;
 
-      const char *offsetStr = getRequestParameter(request, "offset");
       if (offsetStr) {
-        if (msStringToInt(offsetStr, &offset, 10) != MS_SUCCESS) {
-          msOGCAPIOutputError(OGCAPI_PARAM_ERROR, "Bad value for offset.");
-          return MS_SUCCESS;
-        }
-
-        if (offset < 0 || offset >= numberMatched) {
+        if (offset >= numberMatched) {
           msOGCAPIOutputError(OGCAPI_PARAM_ERROR, "Offset out of range.");
           return MS_SUCCESS;
         }


=====================================
src/mapogcfilter.cpp
=====================================
@@ -806,6 +806,26 @@ int FLTLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
   return status;
 }
 
+static bool msCheckDepthLessThanInternal(const CPLXMLNode *psNode,
+                                         int nMaxDepth) {
+  if (nMaxDepth <= 0)
+    return false;
+  for (const CPLXMLNode *psIter = psNode->psChild; psIter;
+       psIter = psIter->psNext) {
+    if (!msCheckDepthLessThanInternal(psIter, nMaxDepth - 1))
+      return false;
+  }
+  return true;
+}
+
+bool msCheckDepthLessThan(const CPLXMLNode *psNode, int nMaxDepth) {
+  for (const CPLXMLNode *psIter = psNode; psIter; psIter = psIter->psNext) {
+    if (!msCheckDepthLessThanInternal(psIter, nMaxDepth))
+      return false;
+  }
+  return true;
+}
+
 /************************************************************************/
 /*            FilterNode *FLTPaserFilterEncoding(char *szXMLString)     */
 /*                                                                      */
@@ -828,6 +848,11 @@ FilterEncodingNode *FLTParseFilterEncoding(const char *szXMLString) {
 
   if (psRoot == NULL)
     return NULL;
+  if (!msCheckDepthLessThan(psRoot, 256)) {
+    msDebug("FLTParseFilterEncoding(): %s", "Too deep nesting in filter");
+    CPLDestroyXMLNode(psRoot);
+    return NULL;
+  }
 
   /* strip namespaces. We srtip all name spaces (#1350)*/
   CPLStripXMLNamespace(psRoot, NULL, 1);


=====================================
src/mapogcfilter.h
=====================================
@@ -54,6 +54,9 @@ typedef struct {
 /* -------------------------------------------------------------------- */
 /*      prototypes.                                                     */
 /* -------------------------------------------------------------------- */
+
+bool msCheckDepthLessThan(const CPLXMLNode *psNode, int nMaxDepth);
+
 MS_DLL_EXPORT int FLTIsNumeric(const char *pszValue);
 MS_DLL_EXPORT int FLTApplyExpressionToLayer(layerObj *lp,
                                             const char *pszExpression);


=====================================
src/mapogcsld.cpp
=====================================
@@ -644,6 +644,11 @@ layerObj *msSLDParseSLD(mapObj *map, const char *psSLDXML, int *pnLayers) {
     msSetError(MS_WMSERR, "Invalid SLD document : %s", "", psSLDXML);
     return NULL;
   }
+  if (!msCheckDepthLessThan(psRoot, 256)) {
+    msSetError(MS_WMSERR, "Invalid SLD document : too deep nesting", "");
+    CPLDestroyXMLNode(psRoot);
+    return NULL;
+  }
 
   /* strip namespaces ogc and sld and gml */
   CPLStripXMLNamespace(psRoot, "ogc", 1);
@@ -4981,9 +4986,11 @@ static char *msSLDGetAttributeNameOrValue(const char *pszExpression,
     iValue = 0;
     for (int i = 0; i < nLength; i++) {
       // Check for comparison operator
-      if ((i + 1 < nLength && EQUALN(&pszExpression[i], szCompare, 2)) ||
-          (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare2, 2)) ||
-          (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare3, 2))) {
+      if ((i + 1 < nLength && EQUALN(&pszExpression[i], szCompare2, 2)) ||
+          (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare3, 2)) ||
+          (i + 1 < nLength && EQUALN(&pszExpression[i], szCompare, 2) &&
+           (i == 0 || pszExpression[i - 1] == ' ') &&
+           (i + 2 >= nLength || pszExpression[i + 2] == ' '))) {
         // Extract value after the operator
         int offset = 2; // All operators are 2 characters long
         pszAttributeValue = msStrdup(&pszExpression[i + offset]);


=====================================
src/mappostgresql.c
=====================================
@@ -266,6 +266,19 @@ int msPOSTGRESQLJoinPrepare(joinObj *join, shapeObj *shape) {
   return MS_SUCCESS;
 }
 
+static char *msPOSTGRESQLEscapeSQLParam(PGconn *conn, const char *pszString) {
+
+  size_t nSrcLen = strlen(pszString);
+  char *pszEscapedStr = (char *)msSmallMalloc(2 * nSrcLen + 1);
+  int nError = 0;
+  PQescapeStringConn(conn, pszEscapedStr, pszString, nSrcLen, &nError);
+  if (nError != 0) {
+    free(pszEscapedStr);
+    pszEscapedStr = NULL;
+  }
+  return pszEscapedStr;
+}
+
 /************************************************************************/
 /*                       msPOSTGRESQLJoinNext()                         */
 /*                                                                      */
@@ -306,7 +319,8 @@ int msPOSTGRESQLJoinNext(joinObj *join) {
     /* Write the list of column names. */
     length = 0;
     for (i = 0; i < join->numitems; i++) {
-      length += 8 + strlen(join->items[i]) + 2;
+      length += strlen("::text") + strlen("\"\"") + strlen(join->items[i]) * 2 +
+                strlen(", ");
     }
     if (length > 1024 * 1024) {
       msSetError(MS_MEMERR, "Too many joins.\n", "msPOSTGRESQLJoinNext()");
@@ -321,24 +335,33 @@ int msPOSTGRESQLJoinNext(joinObj *join) {
 
     columns[0] = 0;
     for (i = 0; i < join->numitems; i++) {
-      strcat(columns, "\"");
-      strcat(columns, join->items[i]);
-      strcat(columns, "\"::text");
+      char *stresc = msDefaultEscapePropertyName(join->items[i]);
+      strcat(columns, stresc);
+      msFree(stresc);
+      strcat(columns, "::text");
       if (i != join->numitems - 1) {
         strcat(columns, ", ");
       }
     }
 
     /* Create the query string. */
-    const size_t nSize = 26 + strlen(columns) + strlen(join->table) +
-                         strlen(join->to) + strlen(joininfo->from_value);
-    sql = (char *)malloc(nSize);
-    if (!sql) {
-      msSetError(MS_MEMERR, "Failure to malloc.\n", "msPOSTGRESQLJoinNext()");
+    char *pszEscapedFrom =
+        msPOSTGRESQLEscapeSQLParam(joininfo->conn, joininfo->from_value);
+    if (!pszEscapedFrom) {
+      free(columns);
       return MS_FAILURE;
     }
+    char *pszEscapedTable = msDefaultEscapePropertyName(join->table);
+    char *pszEscapedTo = msDefaultEscapePropertyName(join->to);
+    const size_t nSize = strlen("SELECT  FROM  WHERE  = ''") + strlen(columns) +
+                         strlen(pszEscapedTable) + strlen(pszEscapedTo) +
+                         strlen(pszEscapedFrom) + 1;
+    sql = (char *)msSmallMalloc(nSize);
     snprintf(sql, nSize, "SELECT %s FROM %s WHERE %s = '%s'", columns,
-             join->table, join->to, joininfo->from_value);
+             pszEscapedTable, pszEscapedTo, pszEscapedFrom);
+    msFree(pszEscapedTable);
+    msFree(pszEscapedTo);
+    msFree(pszEscapedFrom);
     if (joininfo->layer_debug) {
       msDebug("msPOSTGRESQLJoinNext(): executing %s.\n", sql);
     }


=====================================
src/mapscript/python/pyextend.i
=====================================
@@ -257,7 +257,7 @@ def fromstring(data, mappath=None, configpath=None):
         PyObject *order;
         order = PyTuple_New(self->numlayers);
         for (i = 0; i < self->numlayers; i++) {
-            PyTuple_SetItem(order,i,PyInt_FromLong((long)self->layerorder[i]));
+            PyTuple_SetItem(order,i,PyLong_FromLong((long)self->layerorder[i]));
         }
         return order;
     } 
@@ -269,7 +269,7 @@ def fromstring(data, mappath=None, configpath=None):
         int i;
         Py_ssize_t size = PyTuple_Size(order);
         for (i = 0; i < size; i++) {
-            self->layerorder[i] = (int)PyInt_AsLong(PyTuple_GetItem(order, i));
+            self->layerorder[i] = (int)PyLong_AsLong(PyTuple_GetItem(order, i));
         }
         return MS_SUCCESS;
     }
@@ -281,8 +281,8 @@ def fromstring(data, mappath=None, configpath=None):
     {
         PyObject* output ;
         output = PyTuple_New(2);
-        PyTuple_SetItem(output,0,PyInt_FromLong((long)self->width));
-        PyTuple_SetItem(output,1,PyInt_FromLong((long)self->height));
+        PyTuple_SetItem(output,0,PyLong_FromLong((long)self->width));
+        PyTuple_SetItem(output,1,PyLong_FromLong((long)self->height));
         return output;
     }
 


=====================================
src/mapserver.h
=====================================
@@ -2889,6 +2889,7 @@ MS_DLL_EXPORT void msStringFirstCap(char *string);
 MS_DLL_EXPORT int msEncodeChar(const char);
 MS_DLL_EXPORT char *msEncodeUrlExcept(const char *, const char);
 MS_DLL_EXPORT char *msEncodeUrl(const char *);
+char *msDefaultEscapePropertyName(const char *pszString);
 MS_DLL_EXPORT char *msEscapeJSonString(const char *pszString);
 MS_DLL_EXPORT char *msEscapeJSonLikeString(const char *pszString, char chQuote);
 MS_DLL_EXPORT char *msEncodeHTMLEntities(const char *string);


=====================================
src/maptemplate.c
=====================================
@@ -43,6 +43,7 @@
 
 #include <assert.h>
 #include <ctype.h>
+#include <math.h>
 
 static inline void IGUR_sizet(size_t ignored) {
   (void)ignored;
@@ -82,11 +83,19 @@ static const char *const olTemplate =
     "    <script "
     "src=\"[openlayers_js_url]\"></script>\n"
     "    <script>\n"
+    "        if (!ol.proj.get('[openlayers_projection]')) {\n"
+    "            ol.proj.addProjection(new ol.proj.Projection({ code: "
+    "'[openlayers_projection]' }));\n"
+    "        }\n"
     "        [openlayers_layer]\n"
     "        const map = new ol.Map({\n"
     "            layers: [mslayer],\n"
     "            target: 'map',\n"
-    "            view: new ol.View()\n"
+    "            view: new ol.View({\n"
+    "                projection: '[openlayers_projection]',\n"
+    "                minResolution: [ol_minresolution],\n"
+    "                maxResolution: [ol_maxresolution]\n"
+    "            })\n"
     "        });\n"
     "        map.getView().fit([[minx], [miny], [maxx], [maxy]], { size: "
     "map.getSize() });\n"
@@ -108,11 +117,7 @@ static const char *const olLayerMapServerTag =
     "        });";
 
 static const char *const olLayerWMSTag =
-    "if (!ol.proj.get('[openlayers_projection]')) {\n"
-    "            ol.proj.addProjection(new ol.proj.Projection({ code : "
-    "'[openlayers_projection]' }));\n"
-    "        }\n"
-    "        const mslayer = new ol.layer.Image({\n"
+    "const mslayer = new ol.layer.Image({\n"
     "            source: new ol.source.Image({\n"
     "                loader: ol.source.wms.createLoader({\n"
     "                    url: '[mapserv_onlineresource]',\n"
@@ -4101,7 +4106,13 @@ static char *processLine(mapservObj *mapserv, const char *instr, FILE *stream,
     if (strstr(outstr, "'[mapserv_onlineresource]'")) {
       // Fix
       // https://github.com/MapServer/MapServer/security/advisories/GHSA-xqj6-vjqr-33vv
+      // The value is inside a single-quoted JS string in a <script> element.
+      // Escaping the quote is not enough: a literal "</script>" in the value
+      // still closes the element. Encode '<' and '>' as JS unicode escapes so
+      // they cannot break out of the script block.
       char *pszEscaped = msEscapeJSonLikeString(ol, '\'');
+      pszEscaped = msReplaceSubstring(pszEscaped, "<", "\\u003c");
+      pszEscaped = msReplaceSubstring(pszEscaped, ">", "\\u003e");
       const size_t nLen = 1 + strlen(pszEscaped) + 1 + 1;
       char *pszEscapedWithSingleQuotes = (char *)msSmallMalloc(nLen);
       snprintf(pszEscapedWithSingleQuotes, nLen, "'%s'", pszEscaped);
@@ -5045,6 +5056,16 @@ int msReturnOpenLayersPage(mapservObj *mapserv) {
     }
   }
 
+  if (mapserv->Mode == BROWSE) {
+    char *epsgCode = NULL;
+    msOWSGetEPSGProj(&(mapserv->map->projection), NULL, NULL, MS_TRUE,
+                     &epsgCode);
+    if (epsgCode) {
+      // Transfers ownership of epsgCode to projection
+      projection = epsgCode;
+    }
+  }
+
   if (mapserv->map->outputformat->mimetype &&
       *mapserv->map->outputformat->mimetype) {
     format = mapserv->map->outputformat->mimetype;
@@ -5075,12 +5096,36 @@ int msReturnOpenLayersPage(mapservObj *mapserv) {
   buffer = msReplaceSubstring(buffer, "[openlayers_js_url]", openlayersUrl);
   buffer = msReplaceSubstring(buffer, "[openlayers_css_url]", openlayersCssUrl);
   buffer = msReplaceSubstring(buffer, "[openlayers_layer]", layer);
-  if (projection)
-    buffer = msReplaceSubstring(buffer, "[openlayers_projection]", projection);
-  if (format)
+  if (!projection) {
+    // if no projection can be found then use the default OL projection
+    projection = msStrdup("EPSG:3857");
+  }
+  buffer = msReplaceSubstring(buffer, "[openlayers_projection]", projection);
+  if (format) {
     buffer = msReplaceSubstring(buffer, "[openlayers_format]", format);
-  else
+  } else {
     buffer = msReplaceSubstring(buffer, "[openlayers_format]", "image/png");
+  }
+
+  double extentWidth = mapserv->map->extent.maxx - mapserv->map->extent.minx;
+  double extentHeight = mapserv->map->extent.maxy - mapserv->map->extent.miny;
+  // assume default tile size and zoom-levels
+  int zoomLevels = 20;
+  double maxResolution = MS_MAX(extentWidth, extentHeight) / 256.0;
+
+  if (maxResolution <= 0) {
+    maxResolution = 1; // fallback to avoid any divide by zero
+  }
+
+  double minResolution = maxResolution / pow(2.0, zoomLevels);
+  char olMinRes[64], olMaxRes[64];
+
+  snprintf(olMaxRes, sizeof(olMaxRes), "%g", maxResolution);
+  snprintf(olMinRes, sizeof(olMinRes), "%g", minResolution);
+
+  buffer = msReplaceSubstring(buffer, "[ol_maxresolution]", olMaxRes);
+  buffer = msReplaceSubstring(buffer, "[ol_minresolution]", olMinRes);
+
   msIO_fwrite(buffer, strlen(buffer), 1, stdout);
   free(layer);
   free(buffer);


=====================================
src/maptree.c
=====================================
@@ -380,7 +380,7 @@ static int treeAddShapeId(treeObj *tree, int id, rectObj rect) {
   return (treeNodeAddShapeId(tree->root, id, rect, tree->maxdepth));
 }
 
-static void treeCollectShapeIds(treeNodeObj *node, rectObj aoi,
+static void treeCollectShapeIds(treeNodeObj *node, rectObj aoi, int numshapes,
                                 ms_bitarray status) {
   int i;
 
@@ -395,14 +395,15 @@ static void treeCollectShapeIds(treeNodeObj *node, rectObj aoi,
   /*      Add the local nodes shapeids to the list.                       */
   /* -------------------------------------------------------------------- */
   for (i = 0; i < node->numshapes; i++)
-    msSetBit(status, node->ids[i], 1);
+    if (node->ids[i] >= 0 && node->ids[i] < numshapes)
+      msSetBit(status, node->ids[i], 1);
 
   /* -------------------------------------------------------------------- */
   /*      Recurse to subnodes if they exist.                              */
   /* -------------------------------------------------------------------- */
   for (i = 0; i < node->numsubnodes; i++) {
     if (node->subnode[i])
-      treeCollectShapeIds(node->subnode[i], aoi, status);
+      treeCollectShapeIds(node->subnode[i], aoi, numshapes, status);
   }
 }
 
@@ -415,7 +416,7 @@ ms_bitarray msSearchTree(const treeObj *tree, rectObj aoi) {
     return (NULL);
   }
 
-  treeCollectShapeIds(tree->root, aoi, status);
+  treeCollectShapeIds(tree->root, aoi, tree->numshapes, status);
 
   return (status);
 }
@@ -507,11 +508,13 @@ static void searchDiskTreeNode(SHPTreeHandle disktree, rectObj aoi,
     if (disktree->needswap) {
       for (i = 0; i < numshapes; i++) {
         SwapWord(4, &ids[i]);
-        msSetBit(status, ids[i], 1);
+        if (ids[i] >= 0 && ids[i] < disktree->nShapes)
+          msSetBit(status, ids[i], 1);
       }
     } else {
       for (i = 0; i < numshapes; i++)
-        msSetBit(status, ids[i], 1);
+        if (ids[i] >= 0 && ids[i] < disktree->nShapes)
+          msSetBit(status, ids[i], 1);
     }
     free(ids);
     ids = NULL;


=====================================
src/mapwcs.cpp
=====================================
@@ -356,6 +356,23 @@ void msWCSSetDefaultBandsRangeSetInfo(wcsParamsObj *params,
   free(bandlist);
 }
 
+/************************************************************************/
+/*                             msWCSSetParam                            */
+/************************************************************************/
+
+static int msWCSSetParam(char **ppszOut, cgiRequestObj *request, int i,
+                         const char *pszExpectedParamName) {
+  if (strcasecmp(request->ParamNames[i], pszExpectedParamName) == 0) {
+    /* Keep the first value supplied and ignore later duplicates. This avoids
+       leaking the previously allocated value when the same parameter appears
+       multiple times in a request (consistent with msWFSSetParam()). */
+    if (*ppszOut == NULL)
+      *ppszOut = msStrdup(request->ParamValues[i]);
+    return 1;
+  }
+  return 0;
+}
+
 /************************************************************************/
 /*                         msWCSParseRequest()                          */
 /************************************************************************/
@@ -434,6 +451,15 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
           if (EQUAL((char *)tmpNode->name, "BoundingBox")) {
             xmlNodePtr cornerNode = NULL;
             params->crs = (char *)xmlGetProp(tmpNode, BAD_CAST "crs");
+            if (!params->crs) {
+              msSetError(MS_WCSERR, "Required parameter CRS was not supplied.",
+                         "msWCSGetCoverage()");
+              msWCSException(map, "MissingParameterValue", "crs",
+                             params->version);
+              xmlFreeDoc(doc);
+              xmlCleanupParser();
+              return MS_DONE;
+            }
             if (strncasecmp(params->crs, "urn:ogc:def:crs:", 16) == 0 &&
                 strncasecmp(params->crs + strlen(params->crs) - 8, "imageCRS",
                             8) == 0)
@@ -447,8 +473,13 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
                   msSetError(MS_WCSERR,
                              "Wrong number of arguments for LowerCorner",
                              "msWCSParseRequest()");
-                  return msWCSException(map, "InvalidParameterValue",
-                                        "LowerCorner", params->version);
+                  msWCSException(map, "InvalidParameterValue", "LowerCorner",
+                                 params->version);
+                  msFreeCharArray(tokens, n);
+                  xmlFree(value);
+                  xmlFreeDoc(doc);
+                  xmlCleanupParser();
+                  return MS_DONE;
                 }
                 params->bbox.minx = atof(tokens[0]);
                 params->bbox.miny = atof(tokens[1]);
@@ -462,8 +493,13 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
                   msSetError(MS_WCSERR,
                              "Wrong number of arguments for UpperCorner",
                              "msWCSParseRequest()");
-                  return msWCSException(map, "InvalidParameterValue",
-                                        "UpperCorner", params->version);
+                  msWCSException(map, "InvalidParameterValue", "UpperCorner",
+                                 params->version);
+                  msFreeCharArray(tokens, n);
+                  xmlFree(value);
+                  xmlFreeDoc(doc);
+                  xmlCleanupParser();
+                  return MS_DONE;
                 }
                 params->bbox.maxx = atof(tokens[0]);
                 params->bbox.maxy = atof(tokens[1]);
@@ -493,8 +529,13 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
                   msSetError(MS_WCSERR,
                              "Wrong number of arguments for GridOrigin",
                              "msWCSParseRequest()");
-                  return msWCSException(map, "InvalidParameterValue",
-                                        "GridOffsets", params->version);
+                  msWCSException(map, "InvalidParameterValue", "GridOffsets",
+                                 params->version);
+                  msFreeCharArray(tokens, n);
+                  xmlFree(value);
+                  xmlFreeDoc(doc);
+                  xmlCleanupParser();
+                  return MS_DONE;
                 }
                 params->originx = atof(tokens[0]);
                 params->originy = atof(tokens[1]);
@@ -507,8 +548,13 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
                   msSetError(MS_WCSERR,
                              "Wrong number of arguments for GridOffsets",
                              "msWCSParseRequest()");
-                  return msWCSException(map, "InvalidParameterValue",
-                                        "GridOffsets", params->version);
+                  msWCSException(map, "InvalidParameterValue", "GridOffsets",
+                                 params->version);
+                  msFreeCharArray(tokens, n);
+                  xmlFree(value);
+                  xmlFreeDoc(doc);
+                  xmlCleanupParser();
+                  return MS_DONE;
                 }
                 /* take absolute values to convert to positive RESX/RESY style
                 WCS 1.0 behavior.  *but* this does break some possibilities! */
@@ -540,22 +586,20 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
   if (request->NumParams > 0) {
     for (i = 0; i < request->NumParams; i++) {
 
-      if (strcasecmp(request->ParamNames[i], "VERSION") == 0)
-        params->version = msStrdup(request->ParamValues[i]);
-      if (strcasecmp(request->ParamNames[i], "UPDATESEQUENCE") == 0)
-        params->updatesequence = msStrdup(request->ParamValues[i]);
-      else if (strcasecmp(request->ParamNames[i], "REQUEST") == 0)
-        params->request = msStrdup(request->ParamValues[i]);
-      else if (strcasecmp(request->ParamNames[i], "INTERPOLATION") == 0)
-        params->interpolation = msStrdup(request->ParamValues[i]);
-      else if (strcasecmp(request->ParamNames[i], "SERVICE") == 0)
-        params->service = msStrdup(request->ParamValues[i]);
-      else if (strcasecmp(request->ParamNames[i], "SECTION") == 0) /* 1.0 */
-        params->section =
-            msStrdup(request->ParamValues[i]); /* TODO: validate value here */
-      else if (strcasecmp(request->ParamNames[i], "SECTIONS") == 0) /* 1.1 */
-        params->section =
-            msStrdup(request->ParamValues[i]); /* TODO: validate value here */
+      if (msWCSSetParam(&(params->version), request, i, "VERSION")) {
+      } else if (msWCSSetParam(&(params->updatesequence), request, i,
+                               "UPDATESEQUENCE")) {
+      } else if (msWCSSetParam(&(params->request), request, i, "REQUEST")) {
+      } else if (msWCSSetParam(&(params->interpolation), request, i,
+                               "INTERPOLATION")) {
+      } else if (msWCSSetParam(&(params->service), request, i, "SERVICE")) {
+      } else if (msWCSSetParam(&(params->section), request, i,
+                               "SECTION")) { /* 1.0 */
+        /* TODO: validate value here */
+      } else if (msWCSSetParam(&(params->section), request, i,
+                               "SECTIONS")) { /* 1.1 */
+        /* TODO: validate value here */
+      }
 
       /* GetCoverage parameters. */
       else if (strcasecmp(request->ParamNames[i], "BBOX") == 0) {
@@ -583,14 +627,12 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
       else if (strcasecmp(request->ParamNames[i], "COVERAGE") == 0)
         params->coverages =
             CSLAddString(params->coverages, request->ParamValues[i]);
-      else if (strcasecmp(request->ParamNames[i], "TIME") == 0)
-        params->time = msStrdup(request->ParamValues[i]);
-      else if (strcasecmp(request->ParamNames[i], "FORMAT") == 0)
-        params->format = msStrdup(request->ParamValues[i]);
-      else if (strcasecmp(request->ParamNames[i], "CRS") == 0)
-        params->crs = msStrdup(request->ParamValues[i]);
-      else if (strcasecmp(request->ParamNames[i], "RESPONSE_CRS") == 0)
-        params->response_crs = msStrdup(request->ParamValues[i]);
+      else if (msWCSSetParam(&(params->time), request, i, "TIME")) {
+      } else if (msWCSSetParam(&(params->format), request, i, "FORMAT")) {
+      } else if (msWCSSetParam(&(params->crs), request, i, "CRS")) {
+      } else if (msWCSSetParam(&(params->response_crs), request, i,
+                               "RESPONSE_CRS")) {
+      }
 
       /* WCS 1.1 DescribeCoverage and GetCoverage ... */
       else if (strcasecmp(request->ParamNames[i], "IDENTIFIER") == 0 ||
@@ -606,8 +648,10 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
         if (tokens == NULL || n < 5) {
           msSetError(MS_WCSERR, "Wrong number of arguments for BOUNDINGBOX.",
                      "msWCSParseRequest()");
-          return msWCSException(map, "InvalidParameterValue", "boundingbox",
-                                params->version);
+          msWCSException(map, "InvalidParameterValue", "boundingbox",
+                         params->version);
+          msFreeCharArray(tokens, n);
+          return MS_DONE;
         }
 
         /* NOTE: WCS 1.1 boundingbox is center of pixel oriented, not edge
@@ -618,6 +662,7 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
         params->bbox.maxx = atof(tokens[2]);
         params->bbox.maxy = atof(tokens[3]);
 
+        msFree(params->crs);
         params->crs = msStrdup(tokens[4]);
         msFreeCharArray(tokens, n);
         /* normalize imageCRS urns to simply "imageCRS" */
@@ -630,8 +675,10 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
         if (tokens == NULL || n < 2) {
           msSetError(MS_WCSERR, "Wrong number of arguments for GridOffsets",
                      "msWCSParseRequest()");
-          return msWCSException(map, "InvalidParameterValue", "GridOffsets",
-                                params->version);
+          msWCSException(map, "InvalidParameterValue", "GridOffsets",
+                         params->version);
+          msFreeCharArray(tokens, n);
+          return MS_DONE;
         }
         /* take absolute values to convert to positive RESX/RESY style
            WCS 1.0 behavior.  *but* this does break some possibilities! */
@@ -643,8 +690,10 @@ static int msWCSParseRequest(cgiRequestObj *request, wcsParamsObj *params,
         if (tokens == NULL || n < 2) {
           msSetError(MS_WCSERR, "Wrong number of arguments for GridOrigin",
                      "msWCSParseRequest()");
-          return msWCSException(map, "InvalidParameterValue", "GridOffsets",
-                                params->version);
+          msWCSException(map, "InvalidParameterValue", "GridOffsets",
+                         params->version);
+          msFreeCharArray(tokens, n);
+          return MS_DONE;
         }
         params->originx = atof(tokens[0]);
         params->originy = atof(tokens[1]);
@@ -3163,6 +3212,11 @@ int msWCSDispatch(mapObj *map, cgiRequestObj *request,
       strcmp(ows_request->version, "1.1.2") == 0) {
     auto paramsTmp = msWCSCreateParams();
     status = msWCSParseRequest(request, paramsTmp, map);
+    if (status == MS_DONE) {
+      msWCSFreeParams(paramsTmp);
+      free(paramsTmp);
+      return MS_FAILURE;
+    }
     if (status == MS_FAILURE) {
       msWCSFreeParams(paramsTmp);
       free(paramsTmp);


=====================================
src/mapwms.cpp
=====================================
@@ -991,9 +991,12 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion, char **names,
     non mandatory
    */
   for (int i = 0; i < numentries; i++) {
+    if (strcasecmp(names[i], "REQUEST") == 0) {
+      request = values[i];
+    }
     /* check if SLD is passed.  If yes, check for OGR support */
-    if (strcasecmp(names[i], "SLD") == 0 ||
-        strcasecmp(names[i], "SLD_BODY") == 0) {
+    else if (strcasecmp(names[i], "SLD") == 0 ||
+             strcasecmp(names[i], "SLD_BODY") == 0) {
       sldenabled =
           msOWSLookupMetadata(&(map->web.metadata), "MO", "sld_enabled");
 
@@ -1012,15 +1015,15 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion, char **names,
     }
   }
 
+  const char *layerKey = request && strcasecmp(request, "GetLegendGraphic") == 0
+                             ? "LAYER"
+                             : "LAYERS";
+
   std::vector<std::string> wmslayers;
   for (int i = 0; i < numentries; i++) {
     /* getMap parameters */
 
-    if (strcasecmp(names[i], "REQUEST") == 0) {
-      request = values[i];
-    }
-
-    if (strcasecmp(names[i], "LAYERS") == 0) {
+    if (strcasecmp(names[i], layerKey) == 0) {
       std::vector<int> layerOrder(map->numlayers);
 
       wmslayers = msStringSplit(values[i], ',');
@@ -5983,16 +5986,6 @@ int msWMSDispatch(mapObj *map, cgiRequestObj *req, owsRequestObj *ows_request,
     }
     if (found) {
       isContentDependentLegend = true;
-      /* getLegendGraphic uses LAYER= , we need to create a LAYERS= value that
-       * is identical we'll suppose that the client is conformat and hasn't
-       * included a LAYERS= parameter in its request */
-      for (int i = 0; i < req->NumParams; i++) {
-        if (strcasecmp(req->ParamNames[i], "LAYER") == 0) {
-          req->ParamNames[req->NumParams] = msStrdup("LAYERS");
-          req->ParamValues[req->NumParams] = msStrdup(req->ParamValues[i]);
-          req->NumParams++;
-        }
-      }
     } else {
       return msWMSLegendGraphic(map, nVersion, req->ParamNames,
                                 req->ParamValues, req->NumParams,



View it on GitLab: https://salsa.debian.org/debian-gis-team/mapserver/-/compare/9c49374f98a2640deefb354ce87701210bb336e6...8fe0b00dc6998af03c6010c0eb812568301db5d1

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/mapserver/-/compare/9c49374f98a2640deefb354ce87701210bb336e6...8fe0b00dc6998af03c6010c0eb812568301db5d1
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260710/76914a7c/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list