@@ -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 ;
@@ -1105,9 +1102,45 @@ static int tile_storage_hook(request_rec *r)
11051102 return HTTP_NOT_FOUND ;
11061103}
11071104
1105+ /**
1106+ * Safely converts a string to an integer.
1107+ * Returns APR_SUCCESS if the whole string was a valid number.
1108+ */
1109+ static apr_status_t safe_parse_int (const char * str , int * result )
1110+ {
1111+ char * endptr ;
1112+
1113+ if (!str || * str == '\0' ) {
1114+ return APR_EINVAL ;
1115+ }
1116+
1117+ // We use base 10 for coordinates
1118+ apr_int64_t val = apr_strtoi64 (str , & endptr , 10 );
1119+
1120+ // Check if we reached the end of the string.
1121+ // If *endptr isn't '\0', it means there was non-numeric garbage.
1122+ if (* endptr != '\0' ) {
1123+ return APR_EINVAL ;
1124+ }
1125+
1126+ * result = (int )val ;
1127+ return APR_SUCCESS ;
1128+ }
1129+
1130+ // helper to split the extension from the filename (e.g., "12.png" -> "png")
1131+ static void get_extension (char * filename , char * ext_dest , size_t dest_len )
1132+ {
1133+ char * dot = strrchr (filename , '.' );
1134+
1135+ if (dot ) {
1136+ apr_cpystrn (ext_dest , dot + 1 , dest_len );
1137+ * dot = '\0' ; // Truncate the filename at the dot to leave just the number
1138+ }
1139+ }
1140+
11081141static int tile_translate (request_rec * r )
11091142{
1110- int i , n , limit , oob ;
1143+ int basuri_len , i , limit , oob ;
11111144 char option [11 ];
11121145 char extension [256 ];
11131146
@@ -1141,18 +1174,19 @@ static int tile_translate(request_rec *r)
11411174
11421175 for (i = 0 ; i < scfg -> configs -> nelts ; ++ i ) {
11431176 tile_config_rec * tile_config = & tile_configs [i ];
1177+ basuri_len = strlen (tile_config -> baseuri );
11441178
11451179 ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: testing baseuri(%s) name(%s) extension(%s)" ,
11461180 tile_config -> baseuri , tile_config -> xmlname , tile_config -> fileExtension );
11471181
1148- if (!strncmp (tile_config -> baseuri , r -> uri , strlen ( tile_config -> baseuri ) )) {
1182+ if (!strncmp (tile_config -> baseuri , r -> uri , basuri_len )) {
11491183
11501184 struct tile_request_data * rdata = (struct tile_request_data * )apr_pcalloc (r -> pool , sizeof (struct tile_request_data ));
11511185 struct protocol * cmd = (struct protocol * )apr_pcalloc (r -> pool , sizeof (struct protocol ));
11521186 bzero (cmd , sizeof (struct protocol ));
11531187 bzero (rdata , sizeof (struct tile_request_data ));
11541188
1155- if (!strncmp (r -> uri + strlen ( tile_config -> baseuri ) , "tile-layer.json" , strlen ("tile-layer.json" ))) {
1189+ if (!strncmp (r -> uri + basuri_len , "tile-layer.json" , strlen ("tile-layer.json" ))) {
11561190 ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Requesting tileJSON for tilelayer %s" , tile_config -> xmlname );
11571191 r -> handler = "tile_json" ;
11581192 rdata -> layerNumber = i ;
@@ -1162,24 +1196,75 @@ static int tile_translate(request_rec *r)
11621196
11631197 char parameters [XMLCONFIG_MAX ];
11641198
1165- if (tile_config -> enableOptions ) {
1199+ // 1. Get the part of the URI after the base
1200+ char * path_info = apr_pstrdup (r -> pool , r -> uri + basuri_len );
1201+
1202+ // 2. Tokenize into an array
1203+ // We split by "/" to get parts like ["layername", "12", "123", "456.png", "option"]
1204+ apr_array_header_t * parts = apr_array_make (r -> pool , 6 , sizeof (char * ));
1205+ char * last ;
1206+ char * token = apr_strtok (path_info , "/" , & last );
1207+
1208+ while (token ) {
1209+ * (char * * )apr_array_push (parts ) = token ;
1210+ token = apr_strtok (NULL , "/" , & last );
1211+ }
1212+
1213+ char * * elts = (char * * )parts -> elts ;
1214+ int n_parts = parts -> nelts ;
1215+
1216+ // 3. Path processing logic
1217+ if (tile_config -> enableOptions && n_parts >= 4 ) {
1218+ // Expected: /parameter_options/z/x/y.ext/option
11661219 cmd -> ver = PROTO_VER ;
1167- 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 );
1220+ apr_cpystrn ( parameters , elts [ 0 ], 41 );
11681221
1169- if (n < 5 ) {
1170- ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Invalid URL for tilelayer %s with options" , tile_config -> xmlname );
1222+ // Parse Z, X, Y safely
1223+ if (safe_parse_int (elts [1 ], & (cmd -> z )) != APR_SUCCESS ||
1224+ safe_parse_int (elts [2 ], & (cmd -> x )) != APR_SUCCESS ) {
1225+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Z/X coordinates: %s/%s" , elts [1 ], elts [2 ]);
11711226 return DECLINED ;
11721227 }
1173- } else {
1228+
1229+ // Special handling for elts[3] which is "y.ext"
1230+ char * y_str = apr_pstrdup (r -> pool , elts [3 ]);
1231+ get_extension (y_str , extension , 256 ); // This null-terminates y_str at the dot
1232+
1233+ if (safe_parse_int (y_str , & (cmd -> y )) != APR_SUCCESS ) {
1234+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Y coordinate: %s" , y_str );
1235+ return DECLINED ;
1236+ }
1237+
1238+ if (n_parts >= 5 ) {
1239+ apr_cpystrn (option , elts [4 ], 11 );
1240+ }
1241+ } else if (!tile_config -> enableOptions && n_parts >= 3 ) {
1242+ // Expected: /z/x/y.ext/option
11741243 cmd -> ver = 2 ;
1175- n = sscanf ( r -> uri + strlen ( tile_config -> baseuri ), "%d/%d/%d.%255[a-z]/%10s" , & ( cmd -> z ), & ( cmd -> x ), & ( cmd -> y ), extension , option ) ;
1244+ parameters [ 0 ] = '\0' ;
11761245
1177- if (n < 4 ) {
1178- ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Invalid URL for tilelayer %s without options" , tile_config -> xmlname );
1246+ // Parse Z, X, Y safely
1247+ if (safe_parse_int (elts [0 ], & (cmd -> z )) != APR_SUCCESS ||
1248+ safe_parse_int (elts [1 ], & (cmd -> x )) != APR_SUCCESS ) {
1249+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Z/X coordinates: %s/%s" , elts [0 ], elts [1 ]);
11791250 return DECLINED ;
11801251 }
11811252
1182- parameters [0 ] = 0 ;
1253+ // Special handling for elts[2] which is "y.ext"
1254+ char * y_str = apr_pstrdup (r -> pool , elts [2 ]);
1255+ get_extension (y_str , extension , 256 ); // This null-terminates y_str at the dot
1256+
1257+ if (safe_parse_int (y_str , & (cmd -> y )) != APR_SUCCESS ) {
1258+ ap_log_rerror (APLOG_MARK , APLOG_ERR , 0 , r , "tile_translate: Invalid Y coordinate: %s" , y_str );
1259+ return DECLINED ;
1260+ }
1261+
1262+ if (n_parts >= 4 ) {
1263+ apr_cpystrn (option , elts [3 ], 11 );
1264+ }
1265+ } else {
1266+ ap_log_rerror (APLOG_MARK , APLOG_DEBUG , 0 , r , "tile_translate: Invalid tile path structure" );
1267+ return DECLINED ;
11831268 }
11841269
11851270 if (strcmp (extension , tile_config -> fileExtension ) != 0 ) {
@@ -1205,9 +1290,9 @@ static int tile_translate(request_rec *r)
12051290 return HTTP_NOT_FOUND ;
12061291 }
12071292
1208- strcpy (cmd -> xmlname , tile_config -> xmlname );
1209- strcpy (cmd -> mimetype , tile_config -> mimeType );
1210- strcpy (cmd -> options , parameters );
1293+ apr_cpystrn (cmd -> xmlname , tile_config -> xmlname , XMLCONFIG_MAX );
1294+ apr_cpystrn (cmd -> mimetype , tile_config -> mimeType , XMLCONFIG_MAX );
1295+ apr_cpystrn (cmd -> options , parameters , XMLCONFIG_MAX );
12111296
12121297 // Store a copy for later
12131298 rdata -> cmd = cmd ;
@@ -1228,7 +1313,7 @@ static int tile_translate(request_rec *r)
12281313
12291314 r -> filename = NULL ;
12301315
1231- if ((tile_config -> enableOptions && (n == 6 )) || (!tile_config -> enableOptions && (n == 5 ))) {
1316+ if ((tile_config -> enableOptions && (n_parts == 5 )) || (!tile_config -> enableOptions && (n_parts == 4 ))) {
12321317 if (!strcmp (option , "status" )) {
12331318 r -> handler = "tile_status" ;
12341319 } else if (!strcmp (option , "dirty" )) {
0 commit comments