@@ -94,9 +94,6 @@ struct storage_backends {
9494 int noBackends ;
9595};
9696
97- static int error_message (request_rec * r , const char * format , ...)
98- __attribute__((format (printf , 2 , 3 )));
99-
10097static int error_message (request_rec * r , const char * format , ...)
10198{
10299 va_list ap ;
@@ -1088,9 +1085,45 @@ static int tile_storage_hook(request_rec *r)
10881085 return HTTP_NOT_FOUND ;
10891086}
10901087
1088+ /**
1089+ * Safely converts a string to an integer.
1090+ * Returns APR_SUCCESS if the whole string was a valid number.
1091+ */
1092+ static apr_status_t safe_parse_int (const char * str , int * result )
1093+ {
1094+ char * endptr ;
1095+
1096+ if (!str || * str == '\0' ) {
1097+ return APR_EINVAL ;
1098+ }
1099+
1100+ // We use base 10 for coordinates
1101+ apr_int64_t val = apr_strtoi64 (str , & endptr , 10 );
1102+
1103+ // Check if we reached the end of the string.
1104+ // If *endptr isn't '\0', it means there was non-numeric garbage.
1105+ if (* endptr != '\0' ) {
1106+ return APR_EINVAL ;
1107+ }
1108+
1109+ * result = (int )val ;
1110+ return APR_SUCCESS ;
1111+ }
1112+
1113+ // helper to split the extension from the filename (e.g., "12.png" -> "png")
1114+ static void get_extension (char * filename , char * ext_dest , size_t dest_len )
1115+ {
1116+ char * dot = strrchr (filename , '.' );
1117+
1118+ if (dot ) {
1119+ apr_cpystrn (ext_dest , dot + 1 , dest_len );
1120+ * dot = '\0' ; // Truncate the filename at the dot to leave just the number
1121+ }
1122+ }
1123+
10911124static int tile_translate (request_rec * r )
10921125{
1093- int i , n , limit , oob ;
1126+ int basuri_len , i , limit , oob ;
10941127 char option [11 ];
10951128 char extension [256 ];
10961129
@@ -1124,18 +1157,19 @@ static int tile_translate(request_rec *r)
11241157
11251158 for (i = 0 ; i < scfg -> configs -> nelts ; ++ i ) {
11261159 tile_config_rec * tile_config = & tile_configs [i ];
1160+ basuri_len = strlen (tile_config -> baseuri );
11271161
11281162 ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: testing baseuri(%s) name(%s) extension(%s)" ,
11291163 tile_config -> baseuri , tile_config -> xmlname , tile_config -> fileExtension );
11301164
1131- if (!strncmp (tile_config -> baseuri , r -> uri , strlen ( tile_config -> baseuri ) )) {
1165+ if (!strncmp (tile_config -> baseuri , r -> uri , basuri_len )) {
11321166
11331167 struct tile_request_data * rdata = (struct tile_request_data * )apr_pcalloc (r -> pool , sizeof (struct tile_request_data ));
11341168 struct protocol * cmd = (struct protocol * )apr_pcalloc (r -> pool , sizeof (struct protocol ));
11351169 bzero (cmd , sizeof (struct protocol ));
11361170 bzero (rdata , sizeof (struct tile_request_data ));
11371171
1138- if (!strncmp (r -> uri + strlen ( tile_config -> baseuri ) , "tile-layer.json" , strlen ("tile-layer.json" ))) {
1172+ if (!strncmp (r -> uri + basuri_len , "tile-layer.json" , strlen ("tile-layer.json" ))) {
11391173 ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Requesting tileJSON for tilelayer %s" , tile_config -> xmlname );
11401174 r -> handler = "tile_json" ;
11411175 rdata -> layerNumber = i ;
@@ -1145,24 +1179,75 @@ static int tile_translate(request_rec *r)
11451179
11461180 char parameters [XMLCONFIG_MAX ];
11471181
1148- if (tile_config -> enableOptions ) {
1182+ // 1. Get the part of the URI after the base
1183+ char * path_info = apr_pstrdup (r -> pool , r -> uri + basuri_len );
1184+
1185+ // 2. Tokenize into an array
1186+ // We split by "/" to get parts like ["layername", "12", "123", "456.png", "option"]
1187+ apr_array_header_t * parts = apr_array_make (r -> pool , 6 , sizeof (char * ));
1188+ char * last ;
1189+ char * token = apr_strtok (path_info , "/" , & last );
1190+
1191+ while (token ) {
1192+ * (char * * )apr_array_push (parts ) = token ;
1193+ token = apr_strtok (NULL , "/" , & last );
1194+ }
1195+
1196+ char * * elts = (char * * )parts -> elts ;
1197+ int n_parts = parts -> nelts ;
1198+
1199+ // 3. Path processing logic
1200+ if (tile_config -> enableOptions && n_parts >= 4 ) {
1201+ // Expected: /parameter_options/z/x/y.ext/option
11491202 cmd -> ver = PROTO_VER ;
1150- n = sscanf ( r -> uri + strlen ( tile_config -> baseuri ), "%40[^/]/%d/%d/%d.%255[a-z]/%10s" , parameters , & ( cmd -> z ), & ( cmd -> x ), & ( cmd -> y ), extension , option );
1203+ apr_cpystrn ( parameters , elts [ 0 ], 41 );
11511204
1152- if (n < 5 ) {
1153- ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Invalid URL for tilelayer %s with options" , tile_config -> xmlname );
1205+ // Parse Z, X, Y safely
1206+ if (safe_parse_int (elts [1 ], & (cmd -> z )) != APR_SUCCESS ||
1207+ safe_parse_int (elts [2 ], & (cmd -> x )) != APR_SUCCESS ) {
1208+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Z/X coordinates: %s/%s" , elts [1 ], elts [2 ]);
11541209 return DECLINED ;
11551210 }
1156- } else {
1211+
1212+ // Special handling for elts[3] which is "y.ext"
1213+ char * y_str = apr_pstrdup (r -> pool , elts [3 ]);
1214+ get_extension (y_str , extension , 256 ); // This null-terminates y_str at the dot
1215+
1216+ if (safe_parse_int (y_str , & (cmd -> y )) != APR_SUCCESS ) {
1217+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Y coordinate: %s" , y_str );
1218+ return DECLINED ;
1219+ }
1220+
1221+ if (n_parts >= 5 ) {
1222+ apr_cpystrn (option , elts [4 ], 11 );
1223+ }
1224+ } else if (!tile_config -> enableOptions && n_parts >= 3 ) {
1225+ // Expected: /z/x/y.ext/option
11571226 cmd -> ver = 2 ;
1158- n = sscanf ( r -> uri + strlen ( tile_config -> baseuri ), "%d/%d/%d.%255[a-z]/%10s" , & ( cmd -> z ), & ( cmd -> x ), & ( cmd -> y ), extension , option ) ;
1227+ parameters [ 0 ] = '\0' ;
11591228
1160- if (n < 4 ) {
1161- ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Invalid URL for tilelayer %s without options" , tile_config -> xmlname );
1229+ // Parse Z, X, Y safely
1230+ if (safe_parse_int (elts [0 ], & (cmd -> z )) != APR_SUCCESS ||
1231+ safe_parse_int (elts [1 ], & (cmd -> x )) != APR_SUCCESS ) {
1232+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Z/X coordinates: %s/%s" , elts [0 ], elts [1 ]);
11621233 return DECLINED ;
11631234 }
11641235
1165- parameters [0 ] = 0 ;
1236+ // Special handling for elts[2] which is "y.ext"
1237+ char * y_str = apr_pstrdup (r -> pool , elts [2 ]);
1238+ get_extension (y_str , extension , 256 ); // This null-terminates y_str at the dot
1239+
1240+ if (safe_parse_int (y_str , & (cmd -> y )) != APR_SUCCESS ) {
1241+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Y coordinate: %s" , y_str );
1242+ return DECLINED ;
1243+ }
1244+
1245+ if (n_parts >= 4 ) {
1246+ apr_cpystrn (option , elts [3 ], 11 );
1247+ }
1248+ } else {
1249+ ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Invalid tile path structure" );
1250+ return DECLINED ;
11661251 }
11671252
11681253 if (strcmp (extension , tile_config -> fileExtension ) != 0 ) {
@@ -1188,9 +1273,9 @@ static int tile_translate(request_rec *r)
11881273 return HTTP_NOT_FOUND ;
11891274 }
11901275
1191- strcpy (cmd -> xmlname , tile_config -> xmlname );
1192- strcpy (cmd -> mimetype , tile_config -> mimeType );
1193- strcpy (cmd -> options , parameters );
1276+ apr_cpystrn (cmd -> xmlname , tile_config -> xmlname , XMLCONFIG_MAX );
1277+ apr_cpystrn (cmd -> mimetype , tile_config -> mimeType , XMLCONFIG_MAX );
1278+ apr_cpystrn (cmd -> options , parameters , XMLCONFIG_MAX );
11941279
11951280 // Store a copy for later
11961281 rdata -> cmd = cmd ;
@@ -1211,7 +1296,7 @@ static int tile_translate(request_rec *r)
12111296
12121297 r -> filename = NULL ;
12131298
1214- if ((tile_config -> enableOptions && (n == 6 )) || (!tile_config -> enableOptions && (n == 5 ))) {
1299+ if ((tile_config -> enableOptions && (n_parts == 5 )) || (!tile_config -> enableOptions && (n_parts == 4 ))) {
12151300 if (!strcmp (option , "status" )) {
12161301 r -> handler = "tile_status" ;
12171302 } else if (!strcmp (option , "dirty" )) {
0 commit comments