From 511521fd8780bb10cb5e42dddafe4c43e2945f13 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Marie?= Date: Wed, 5 Nov 2014 06:50:26 +0100 Subject: [PATCH] auto-response-header: rewrite arh_parse `auto-response-header` setting is a list. Each element has format: "pattern header-list". If an element contains COMMA, the element must by enclosed by QUOTE. The header-list format is like `header` setting. --- doc/vimb.1 | 7 +-- src/arh.c | 155 ++++++++++++++++++++++------------------------------- 2 files changed, 67 insertions(+), 95 deletions(-) diff --git a/doc/vimb.1 b/doc/vimb.1 index 9c7255f..ff1c778 100644 --- a/doc/vimb.1 +++ b/doc/vimb.1 @@ -1204,9 +1204,9 @@ Note that this setting will not remplace existing headers, but add a new one. If multiple patterns match a request uri, the last matched rule will be applied. You could also specified differents headers for same pattern. -The format is: `pattern name=value`. For each request matching `pattern`, an -HTTP header "name: value" will be added to the response. If value is empty, a -previously added header could be removed. +The format is a list of `pattern header-list`. If `header-list` has not than +one element, enclosing with QUOTE is mandatory: `"pattern header-list"`. The +header-list format is the same as `header` setting. .RS .PP Example: @@ -1214,6 +1214,7 @@ Example: .IP ":set auto-response-header=* Content-security-policy=default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'none'" .IP ":set auto-response-header+=https://example.com/* Content-security-policy=default-src 'self' https://*.example.com/" .IP ":set auto-response-header+=https://example.com/* Strict-Transport-Security=max-age=31536000" +.IP ":set auto-response-header+=""https://*.example.org/sub/* Content-security-policy,X-Test=ok""" .PD .RE .TP diff --git a/src/arh.c b/src/arh.c index 82a3614..abc4aa0 100644 --- a/src/arh.c +++ b/src/arh.c @@ -24,16 +24,12 @@ #include "util.h" typedef struct { - char *pattern; /* pattern the uri is matched against */ - char *name; /* header name */ - char *value; /* header value */ + char *pattern; /* pattern the uri is matched against */ + GHashTable *headers; /* header list */ } MatchARH; static void marh_free(MatchARH *); -static char *read_pattern(char **parse); -static char *read_name(char **parse); -static char *read_value(char **parse); - +static char *read_pattern(char **); /** * parse the data string to a list of MatchARH @@ -43,25 +39,53 @@ static char *read_value(char **parse); GSList *arh_parse(const char *data, const char **error) { GSList *parsed = NULL; - char *data_dup = g_strdup(data); - char *parse = data_dup; + GSList *data_list = NULL; + + /* parse data as comma separated list + * of "pattern1 HEADERS-GROUP1","pattern2 HEADERS-GROUP2",... */ + data_list = soup_header_parse_list(data); + + /* iter the list for parsing each elements */ + for (GSList *l = data_list; l; l = g_slist_next(l)) { + char *one_data = (char *)l->data; + char *pattern; + GHashTable *headers; + + /* remove QUOTE around one_data if any */ + if (one_data && *one_data == '"') { + int last = strlen(one_data) - 1; + if (last >= 0 && one_data[last] == '"') { + one_data[last] = '\0'; + one_data++; + } + } - while (*parse) { - char *pattern = read_pattern(&parse); - char *name = read_name(&parse); - char *value = read_value(&parse); + /* read pattern and parse headers */ + pattern = read_pattern(&one_data); + headers = soup_header_parse_param_list(one_data); - if (pattern && name) { + /* check result (need a pattern and at least one header) */ + if (pattern && g_hash_table_size(headers)) { MatchARH *marh = g_slice_new0(MatchARH); - marh->pattern = g_strdup(pattern); - marh->name = g_strdup(name); - marh->value = g_strdup(value); + marh->headers = headers; parsed = g_slist_append(parsed, marh); +#ifdef DEBUG + PRINT_DEBUG("append pattern='%s' headers[%d]='0x%lx'", + marh->pattern, g_hash_table_size(marh->headers), (long)marh->headers); + GHashTableIter iterHeaders; + const char *name, *value; + g_hash_table_iter_init(&iterHeaders, headers); + while (g_hash_table_iter_next(&iterHeaders, (gpointer)&name, (gpointer)&value)) { + PRINT_DEBUG(" %s=%s", name, value); + } +#endif } else { - /* one error occurs: free already parsed list */ + /* an error occurs: cleanup */ + soup_header_free_param_list(headers); + soup_header_free_list(data_list); arh_free(parsed); /* set error if asked */ @@ -69,12 +93,12 @@ GSList *arh_parse(const char *data, const char **error) *error = "syntax error"; } - g_free(data_dup); return NULL; } } - g_free(data_dup); + soup_header_free_list(data_list); + return parsed; } @@ -101,20 +125,25 @@ void arh_run(GSList *list, const char *uri, SoupMessage *msg) MatchARH *marh = (MatchARH *)l->data; if (util_wildmatch(marh->pattern, uri)) { - if (marh->value) { - /* the pattern match, so replace headers - * as side-effect, for one header the last matched will be keeped */ - soup_message_headers_replace(msg->response_headers, - marh->name, marh->value); - - PRINT_DEBUG(" header added: %s: %s", marh->name, marh->value); - } else { - /* remove a previously setted auto-reponse-header */ - soup_message_headers_remove(msg->response_headers, marh->name); - - PRINT_DEBUG(" header removed: %s", marh->name); + GHashTableIter iter; + const char *name = NULL; + const char *value = NULL; + + g_hash_table_iter_init(&iter, marh->headers); + while (g_hash_table_iter_next(&iter, (gpointer)&name, (gpointer)&value)) { + if (value) { + /* the pattern match, so replace headers + * as side-effect, for one header the last matched will be keeped */ + soup_message_headers_replace(msg->response_headers, name, value); + + PRINT_DEBUG(" header added: %s: %s", name, value); + } else { + /* remove a previously setted auto-reponse-header */ + soup_message_headers_remove(msg->response_headers, name); + + PRINT_DEBUG(" header removed: %s", name); + } } - } } } @@ -127,8 +156,7 @@ static void marh_free(MatchARH *marh) { if (marh) { g_free(marh->pattern); - g_free(marh->name); - g_free(marh->value); + soup_header_free_param_list(marh->headers); g_slice_free(MatchARH, marh); } @@ -169,61 +197,4 @@ static char *read_pattern(char **line) return NULL; } } - -/** - * read until '=' - */ -static char *read_name(char **line) -{ - char *name; - - if (!*line || !**line) { - return NULL; - } - - /* remember where the name starts */ - name = *line; - - /* move pointer to the end of the name (or end-of-line) */ - while (**line && **line != '=') { - (*line)++; - } - - if (**line) { - /* end the name */ - *(*line)++ = '\0'; - return name; - - } else { - /* end-of-line was encounter */ - return NULL; - } -} - -/** - * read until ',' or end-of-line - */ -static char *read_value(char **line) -{ - char *value; - - if (!*line || !**line) { - return NULL; - } - - /* remember where the value starts */ - value = *line; - - /* move pointer to the end of the value or of the line */ - while (**line && **line != ',') { - (*line)++; - } - - /* end the value */ - if (**line) { - *(*line)++ = '\0'; - } - - return value; -} #endif -- 2.20.1