Skip to content

Commit d50893f

Browse files
committed
Improving tile URI processing logic
1 parent fddb0a9 commit d50893f

1 file changed

Lines changed: 104 additions & 19 deletions

File tree

src/mod_tile.c

Lines changed: 104 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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-
10097
static 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+
11081141
static 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

Comments
 (0)