@@ -1088,9 +1088,45 @@ static int tile_storage_hook(request_rec *r)
10881088 return HTTP_NOT_FOUND ;
10891089}
10901090
1091+ /**
1092+ * Safely converts a string to an integer.
1093+ * Returns APR_SUCCESS if the whole string was a valid number.
1094+ */
1095+ static apr_status_t safe_parse_int (const char * str , int * result )
1096+ {
1097+ char * endptr ;
1098+
1099+ if (!str || * str == '\0' ) {
1100+ return APR_EINVAL ;
1101+ }
1102+
1103+ // We use base 10 for coordinates
1104+ apr_int64_t val = apr_strtoi64 (str , & endptr , 10 );
1105+
1106+ // Check if we reached the end of the string.
1107+ // If *endptr isn't '\0', it means there was non-numeric garbage.
1108+ if (* endptr != '\0' ) {
1109+ return APR_EINVAL ;
1110+ }
1111+
1112+ * result = (int )val ;
1113+ return APR_SUCCESS ;
1114+ }
1115+
1116+ // helper to split the extension from the filename (e.g., "12.png" -> "png")
1117+ static void get_extension (char * filename , char * ext_dest , size_t dest_len )
1118+ {
1119+ char * dot = strrchr (filename , '.' );
1120+
1121+ if (dot ) {
1122+ apr_cpystrn (ext_dest , dot + 1 , dest_len );
1123+ * dot = '\0' ; // Truncate the filename at the dot to leave just the number
1124+ }
1125+ }
1126+
10911127static int tile_translate (request_rec * r )
10921128{
1093- int i , n , limit , oob ;
1129+ int basuri_len , i , limit , oob ;
10941130 char option [11 ];
10951131 char extension [256 ];
10961132
@@ -1124,18 +1160,19 @@ static int tile_translate(request_rec *r)
11241160
11251161 for (i = 0 ; i < scfg -> configs -> nelts ; ++ i ) {
11261162 tile_config_rec * tile_config = & tile_configs [i ];
1163+ basuri_len = strlen (tile_config -> baseuri );
11271164
11281165 ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: testing baseuri(%s) name(%s) extension(%s)" ,
11291166 tile_config -> baseuri , tile_config -> xmlname , tile_config -> fileExtension );
11301167
1131- if (!strncmp (tile_config -> baseuri , r -> uri , strlen ( tile_config -> baseuri ) )) {
1168+ if (!strncmp (tile_config -> baseuri , r -> uri , basuri_len )) {
11321169
11331170 struct tile_request_data * rdata = (struct tile_request_data * )apr_pcalloc (r -> pool , sizeof (struct tile_request_data ));
11341171 struct protocol * cmd = (struct protocol * )apr_pcalloc (r -> pool , sizeof (struct protocol ));
11351172 bzero (cmd , sizeof (struct protocol ));
11361173 bzero (rdata , sizeof (struct tile_request_data ));
11371174
1138- if (!strncmp (r -> uri + strlen ( tile_config -> baseuri ) , "tile-layer.json" , strlen ("tile-layer.json" ))) {
1175+ if (!strncmp (r -> uri + basuri_len , "tile-layer.json" , strlen ("tile-layer.json" ))) {
11391176 ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Requesting tileJSON for tilelayer %s" , tile_config -> xmlname );
11401177 r -> handler = "tile_json" ;
11411178 rdata -> layerNumber = i ;
@@ -1145,24 +1182,80 @@ static int tile_translate(request_rec *r)
11451182
11461183 char parameters [XMLCONFIG_MAX ];
11471184
1148- if (tile_config -> enableOptions ) {
1185+ // 1. Safety Check: Does the URI actually start with our baseuri?
1186+ if (strncmp (r -> uri , tile_config -> baseuri , basuri_len ) != 0 ) {
1187+ return DECLINED ;
1188+ }
1189+
1190+ // 2. Get the part of the URI after the base
1191+ char * path_info = apr_pstrdup (r -> pool , r -> uri + basuri_len );
1192+
1193+ // 3. Tokenize into an array
1194+ // We split by "/" to get parts like ["layername", "12", "123", "456.png", "option"]
1195+ apr_array_header_t * parts = apr_array_make (r -> pool , 6 , sizeof (char * ));
1196+ char * last ;
1197+ char * token = apr_strtok (path_info , "/" , & last );
1198+
1199+ while (token ) {
1200+ * (char * * )apr_array_push (parts ) = token ;
1201+ token = apr_strtok (NULL , "/" , & last );
1202+ }
1203+
1204+ char * * elts = (char * * )parts -> elts ;
1205+ int n_parts = parts -> nelts ;
1206+
1207+ // 4. Logic based on your options
1208+ if (tile_config -> enableOptions && n_parts >= 4 ) {
1209+ // Expected: /parameter_options/z/x/y.ext/option
11491210 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 );
1211+ apr_cpystrn ( parameters , elts [ 0 ], 41 );
11511212
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 );
1213+ // Parse Z, X, Y safely
1214+ if (safe_parse_int (elts [1 ], & (cmd -> z )) != APR_SUCCESS ||
1215+ safe_parse_int (elts [2 ], & (cmd -> x )) != APR_SUCCESS ) {
1216+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Z/X coordinates: %s/%s" , elts [1 ], elts [2 ]);
11541217 return DECLINED ;
11551218 }
1156- } else {
1219+
1220+ // Special handling for elts[3] which is "y.ext"
1221+ char * y_str = apr_pstrdup (r -> pool , elts [3 ]);
1222+ get_extension (y_str , extension , 256 ); // This null-terminates y_str at the dot
1223+
1224+ if (safe_parse_int (y_str , & (cmd -> y )) != APR_SUCCESS ) {
1225+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Y coordinate: %s" , y_str );
1226+ return DECLINED ;
1227+ }
1228+
1229+ if (n_parts >= 5 ) {
1230+ apr_cpystrn (option , elts [4 ], 11 );
1231+ }
1232+ } else if (!tile_config -> enableOptions && n_parts >= 3 ) {
1233+ // Expected: /z/x/y.ext/option
11571234 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 ) ;
1235+ parameters [ 0 ] = '\0' ;
11591236
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 );
1237+ // Parse Z, X, Y safely
1238+ if (safe_parse_int (elts [0 ], & (cmd -> z )) != APR_SUCCESS ||
1239+ safe_parse_int (elts [1 ], & (cmd -> x )) != APR_SUCCESS ) {
1240+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Z/X coordinates: %s/%s" , elts [0 ], elts [1 ]);
11621241 return DECLINED ;
11631242 }
11641243
1165- parameters [0 ] = 0 ;
1244+ // Special handling for elts[2] which is "y.ext"
1245+ char * y_str = apr_pstrdup (r -> pool , elts [2 ]);
1246+ get_extension (y_str , extension , 256 ); // This null-terminates y_str at the dot
1247+
1248+ if (safe_parse_int (y_str , & (cmd -> y )) != APR_SUCCESS ) {
1249+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Y coordinate: %s" , y_str );
1250+ return DECLINED ;
1251+ }
1252+
1253+ if (n_parts >= 4 ) {
1254+ apr_cpystrn (option , elts [3 ], 11 );
1255+ }
1256+ } else {
1257+ ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Invalid tile path structure" );
1258+ return DECLINED ;
11661259 }
11671260
11681261 if (strcmp (extension , tile_config -> fileExtension ) != 0 ) {
@@ -1211,7 +1304,7 @@ static int tile_translate(request_rec *r)
12111304
12121305 r -> filename = NULL ;
12131306
1214- if ((tile_config -> enableOptions && (n == 6 )) || (!tile_config -> enableOptions && (n == 5 ))) {
1307+ if ((tile_config -> enableOptions && (n_parts == 5 )) || (!tile_config -> enableOptions && (n_parts == 4 ))) {
12151308 if (!strcmp (option , "status" )) {
12161309 r -> handler = "tile_status" ;
12171310 } else if (!strcmp (option , "dirty" )) {
0 commit comments