auto-response-header: rewrite arh_parse
authorSébastien Marie <semarie@users.noreply.github.com>
Wed, 5 Nov 2014 05:50:26 +0000 (06:50 +0100)
committerSébastien Marie <semarie@users.noreply.github.com>
Wed, 5 Nov 2014 05:52:24 +0000 (06:52 +0100)
`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
src/arh.c

index 9c7255f..ff1c778 100644 (file)
@@ -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
index 82a3614..abc4aa0 100644 (file)
--- a/src/arh.c
+++ b/src/arh.c
 #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