#include "config.h" #ifdef HAVE_REWRITE #include #include #include #include #include "rewrite.h" #include "libstr.h" #include "alternative.h" #define MAX_RULE_LOOP 20 #define MAX_MATCH_LOOP 20 t_url_rewrite *find_url_rewrite(char *rewrite_id, t_url_rewrite *url_rewrite) { while (url_rewrite != NULL) { if (strcmp(url_rewrite->rewrite_id, rewrite_id) == 0) { return url_rewrite; } url_rewrite = url_rewrite->next; } return NULL; } static int replace(char *src, int ofs, int len, char *rep, char **dst) { int len_rep; len_rep = strlen(rep); if ((*dst = (char*)malloc(strlen(src) - len + len_rep + 1)) == NULL) { return -1; } memcpy(*dst, src, ofs); memcpy(*dst + ofs, rep, len_rep); strcpy(*dst + ofs + len_rep, src + ofs + len); return 0; } bool url_rewrite_setting(char *key, char *value, t_url_rewrite *rewrite) { t_rewrite_rule *new_rule, *rule; char *rest; int loop; if (strcmp(key, "rewriteid") == 0) { if ((rewrite->rewrite_id = strdup(value)) != NULL) { return true; } } else { if ((new_rule = (t_rewrite_rule*)malloc(sizeof(t_rewrite_rule))) == NULL) { return false; } else if (rewrite->rewrite_rule == NULL) { rewrite->rewrite_rule = new_rule; } else { rule = rewrite->rewrite_rule; while (rule->next != NULL) { rule = rule->next; } rule->next = new_rule; } new_rule->parameter = NULL; new_rule->next = NULL; new_rule->match_loop = 1; new_rule->after_rewrite = am_nothing; if (strcmp(key, "match") == 0) { if (split_string(value, &value, &rest, ' ') == -1) { return false; } else if (regcomp(&(new_rule->pattern), value, REG_EXTENDED) != 0) { return false; } split_string(rest, &value, &rest, ' '); if (strcasecmp(value, "rewrite") == 0) { split_string(rest, &value, &rest, ' '); new_rule->action = rw_match_rewrite; if (strlen(value) == 0) { return false; } else if ((new_rule->parameter = strdup(value)) == NULL) { return false; } while (rest != NULL) { split_string(rest, &value, &rest, ' '); if ((loop = str2int(value)) > 0) { if (loop > MAX_MATCH_LOOP) { return false; } if (new_rule->match_loop != 1) { return false; } new_rule->match_loop = loop; } else if (strcasecmp(value, "return") == 0) { if (new_rule->after_rewrite != am_nothing) { return false; } new_rule->after_rewrite = am_return; } else if (strcasecmp(value, "exit") == 0) { if (new_rule->after_rewrite != am_nothing) { return false; } new_rule->after_rewrite = am_exit; } else { return false; } } } else if (strcasecmp(value, "goto") == 0) { new_rule->action = rw_match_goto; if (strlen(rest) == 0) { return false; } else if ((new_rule->parameter = strdup(rest)) == NULL) { return false; } } else if (strcasecmp(value, "call") == 0) { new_rule->action = rw_match_call; if (strlen(rest) == 0) { return false; } else if ((new_rule->parameter = strdup(rest)) == NULL) { return false; } } else if (strcasecmp(value, "return") == 0) { new_rule->action = rw_match_return; } else if (strcasecmp(value, "exit") == 0) { new_rule->action = rw_match_exit; } else if (strcasecmp(value, "denyaccess") == 0) { new_rule->action = rw_match_deny_access; } else { return false; } } else if (strcmp(key, "call") == 0) { new_rule->action = rw_call; if (strlen(value) == 0) { return false; } else if ((new_rule->parameter = strdup(value)) == NULL) { return false; } } else { return false; } return true; } return false; } bool rewrite_rules_oke(t_url_rewrite *url_rewrite) { t_url_rewrite *rewrite; t_rewrite_rule *rule; rewrite = url_rewrite; while (rewrite != NULL) { if (rewrite->rewrite_id == NULL) { fprintf(stderr, "A RewriteId is missing in an UrlRewrite section.\n"); return false; } rule = rewrite->rewrite_rule; while (rule != NULL) { if ((rule->action == rw_match_goto) || (rule->action == rw_match_call) || (rule->action == rw_call)) { if (rule->parameter == NULL) { fprintf(stderr, "Missing parameter in rewrite rule '%s'.\n", rewrite->rewrite_id); return false; } else if (find_url_rewrite(rule->parameter, url_rewrite) == NULL) { fprintf(stderr, "Unknown RewriteId in Goto/Call in rewrite rule '%s'.\n", rewrite->rewrite_id); return false; } } rule = rule->next; } rewrite = rewrite->next; } return true; } static int do_rewrite(char *url, regex_t *regexp, char *rep, char **new_url, int loop) { int ofs, len, len_rep, i, n; regmatch_t pmatch[10]; char *repl, *c, *sub, *tmp; bool replaced; *new_url = NULL; while (loop-- > 0) { if (regexec(regexp, url, 10, pmatch, 0) == REG_NOMATCH) { break; } if ((ofs = pmatch[0].rm_so) == -1) { return RW_ERROR; } if ((repl = strdup(rep)) == NULL) { return RW_ERROR; } /* Replace '\x' in replacement string with substring. */ c = repl; replaced = false; while (*c != '\0') { if (*c == '$') { if ((*(c+1) >= '0') && (*(c+1) <= '9')) { i = *(c+1) - 48; if (pmatch[i].rm_so != -1) { len = pmatch[i].rm_eo - pmatch[i].rm_so; if ((sub = strdup(url + pmatch[i].rm_so)) == NULL) { free(repl); return RW_ERROR; } sub[len] = '\0'; } else { sub = strdup(""); } n = c - repl; if (replace(repl, n, 2, sub, &tmp) == -1) { free(repl); return RW_ERROR; } if (replaced) { free(repl); } repl = tmp; c = repl + n + strlen(sub) - 1; replaced = true; free(sub); } } c++; } /* Replace pattern with replacement string. */ len = pmatch[0].rm_eo - ofs; len_rep = strlen(repl); if (replace(url, ofs, len, repl, new_url) == -1) { free(repl); return RW_ERROR; } url = *new_url; free(repl); } return 0; } int rewrite_url(char *url, char *rewrite_id, t_url_rewrite *url_rewrite, char **new_url) { t_url_rewrite *rewrite; t_rewrite_rule *rule; bool replaced = false; int loop = 0, result; *new_url = NULL; if ((rewrite = find_url_rewrite(rewrite_id, url_rewrite)) == NULL) { return RW_ERROR; } rule = rewrite->rewrite_rule; while (rule != NULL) { switch (rule->action) { case rw_match_rewrite: if (do_rewrite(url, &(rule->pattern), rule->parameter, new_url, rule->match_loop) == RW_ERROR) { if (*new_url != NULL) { free(*new_url); *new_url = NULL; } return RW_ERROR; } if (*new_url != NULL) { if (replaced) { free(url); } url = *new_url; replaced = true; switch (rule->after_rewrite) { case am_return: return RW_RETURN; case am_exit: return RW_EXIT; case am_nothing: break; } } else if (replaced) { *new_url = url; } break; case rw_match_goto: case rw_match_call: case rw_call: if (rule->action != rw_call) { if (regexec(&(rule->pattern), url, 0, NULL, 0) == REG_NOMATCH) { break; } } if (++loop == MAX_RULE_LOOP) { return RW_ERROR; } if ((result = rewrite_url(url, rule->parameter, url_rewrite, new_url)) == RW_ERROR) { if (*new_url != NULL) { free(*new_url); *new_url = NULL; } return RW_ERROR; } if (*new_url != NULL) { if (replaced) { free(url); } url = *new_url; replaced = true; } if (result == RW_EXIT) { return RW_EXIT; } else if (rule->action == rw_match_goto) { return RW_RETURN; } break; case rw_match_return: if (regexec(&(rule->pattern), url, 0, NULL, 0) == 0) { return RW_RETURN; } break; case rw_match_exit: if (regexec(&(rule->pattern), url, 0, NULL, 0) == 0) { return RW_EXIT; } break; case rw_match_deny_access: if (regexec(&(rule->pattern), url, 0, NULL, 0) == 0) { return RW_DENY_ACCESS; } break; } rule = rule->next; } return RW_RETURN; } #endif