Skip to content

Commit c5162c0

Browse files
committed
Improving tile URI processing logic
1 parent dec0fc0 commit c5162c0

File tree

1 file changed

+104
-19
lines changed

1 file changed

+104
-19
lines changed

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;
@@ -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+
10911124
static 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

Comments
 (0)