From 1935269d70a8e71b9684fb6db9e588f42b1dd8e9 Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Sat, 8 Mar 2008 00:28:34 +0100
Subject: [PATCH] Variables: First work towards match value support.

---
 src/lib-sieve/ext-envelope.c                  |   2 +-
 src/lib-sieve/plugins/body/tst-body.c         |   2 +-
 src/lib-sieve/plugins/imapflags/tst-hasflag.c |   2 +-
 .../plugins/variables/ext-variables-common.c  | 109 +++++++++-
 .../plugins/variables/ext-variables-common.h  |   3 +
 .../plugins/variables/ext-variables.c         |   2 +
 src/lib-sieve/plugins/variables/tst-string.c  |   2 +-
 .../plugins/variables/variables-match.sieve   |  23 ++
 src/lib-sieve/sieve-match-types.c             | 198 ++++++++++++++++--
 src/lib-sieve/sieve-match-types.h             |  30 ++-
 src/lib-sieve/tst-address.c                   |   2 +-
 src/lib-sieve/tst-header.c                    |   2 +-
 12 files changed, 349 insertions(+), 28 deletions(-)
 create mode 100644 src/lib-sieve/plugins/variables/variables-match.sieve

diff --git a/src/lib-sieve/ext-envelope.c b/src/lib-sieve/ext-envelope.c
index 818f26037..1e5bdf2ae 100644
--- a/src/lib-sieve/ext-envelope.c
+++ b/src/lib-sieve/ext-envelope.c
@@ -241,7 +241,7 @@ static bool ext_envelope_operation_execute
 	}
 	
 	/* Initialize match context */
-	mctx = sieve_match_begin(mtch, cmp, key_list);
+	mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list);
 	
 	/* Iterate through all requested headers to match */
 	hdr_item = NULL;
diff --git a/src/lib-sieve/plugins/body/tst-body.c b/src/lib-sieve/plugins/body/tst-body.c
index b6f3409b7..03fa540d1 100644
--- a/src/lib-sieve/plugins/body/tst-body.c
+++ b/src/lib-sieve/plugins/body/tst-body.c
@@ -364,7 +364,7 @@ static bool ext_body_operation_execute
 		return FALSE;
 	}
 
-	mctx = sieve_match_begin(mtch, cmp, key_list); 	
+	mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); 	
 
 	/* Iterate through all requested body parts to match */
 	matched = FALSE;
diff --git a/src/lib-sieve/plugins/imapflags/tst-hasflag.c b/src/lib-sieve/plugins/imapflags/tst-hasflag.c
index 2df3d6b3b..152e9ae74 100644
--- a/src/lib-sieve/plugins/imapflags/tst-hasflag.c
+++ b/src/lib-sieve/plugins/imapflags/tst-hasflag.c
@@ -246,7 +246,7 @@ static bool tst_hasflag_operation_execute
 	}
 
 	matched = FALSE;
-	mctx = sieve_match_begin(mtch, cmp, key_list); 	
+	mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); 	
 
 	ext_imapflags_get_flags_init(&iter, renv);
 	
diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.c b/src/lib-sieve/plugins/variables/ext-variables-common.c
index 39704ccd0..99da7ad76 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-common.c
+++ b/src/lib-sieve/plugins/variables/ext-variables-common.c
@@ -8,6 +8,7 @@
 #include "sieve-ast.h"
 #include "sieve-binary.h"
 #include "sieve-code.h"
+#include "sieve-match-types.h"
 
 #include "sieve-commands.h"
 #include "sieve-validator.h"
@@ -230,6 +231,9 @@ void ext_variables_interpreter_initialize(struct sieve_interpreter *interp)
 	
 	/* Create our context */
 	ctx = ext_variables_interpreter_context_create(interp);
+	
+	/* Enable support for match values */
+	(void) sieve_match_values_set_enabled(interp, TRUE);
 }
 
 static inline struct ext_variables_interpreter_context *
@@ -347,6 +351,41 @@ static bool arg_variable_generate
 	return TRUE;
 }
 
+/* Match value argument */
+
+static bool arg_match_value_generate
+(struct sieve_generator *generator, struct sieve_ast_argument *arg, 
+	struct sieve_command_context *context ATTR_UNUSED);
+
+const struct sieve_argument match_value_argument = 
+	{ "@match_value", NULL, NULL, NULL, arg_match_value_generate };
+
+static struct sieve_ast_argument *ext_variables_match_value_argument_create
+(struct sieve_validator *validator ATTR_UNUSED, struct sieve_ast *ast, 
+	unsigned int source_line,	unsigned int index)
+{
+	struct sieve_ast_argument *arg;
+	
+	arg = sieve_ast_argument_create(ast, source_line);
+	arg->type = SAAT_STRING;
+	arg->argument = &match_value_argument;
+	arg->context = (void *) index;
+	
+	return arg;
+}
+
+static bool arg_match_value_generate
+(struct sieve_generator *generator, struct sieve_ast_argument *arg, 
+	struct sieve_command_context *context ATTR_UNUSED)
+{
+	unsigned int index = (unsigned int) arg->context;
+	
+	ext_variables_opr_match_value_emit
+		(sieve_generator_get_binary(generator), index);
+
+	return TRUE;
+}
+
 /* Variable string argument */
 
 static bool arg_variable_string_validate
@@ -464,6 +503,7 @@ static bool arg_variable_string_validate
 						array_idx(&substitution, 0);
 						
 					if ( cur_element->num_variable == -1 ) {
+						/* Add variable argument '${identifier}' */
 						string_t *cur_ident = cur_element->identifier; 
 						
 						strarg = ext_variables_variable_argument_create
@@ -471,7 +511,12 @@ static bool arg_variable_string_validate
 						if ( strarg != NULL )
 							sieve_ast_arg_list_add(arglist, strarg);
 					} else {
-						/* FIXME: Match substitutions are not supported */
+						/* Add match value argument '${000}' */
+						strarg = ext_variables_match_value_argument_create
+							(validator, (*arg)->ast, (*arg)->source_line, 
+							cur_element->num_variable);
+						if ( strarg != NULL )
+							sieve_ast_arg_list_add(arglist, strarg);
 					}
 				} else {
 					int i;
@@ -654,6 +699,68 @@ bool ext_variables_opr_variable_read
 	return FALSE;
 }
 
+/* Match value operand */
+
+static bool opr_match_value_read
+	(const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str);
+static bool opr_match_value_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address);
+
+const struct sieve_opr_string_interface match_value_interface = { 
+	opr_match_value_dump,
+	opr_match_value_read
+};
+		
+const struct sieve_operand match_value_operand = { 
+	"match-value", 
+	&variables_extension, 
+	EXT_VARIABLES_OPERAND_MATCH_VALUE,
+	&string_class,
+	&match_value_interface
+};	
+
+void ext_variables_opr_match_value_emit
+	(struct sieve_binary *sbin, unsigned int index) 
+{
+	(void) sieve_operand_emit_code
+		(sbin, &match_value_operand, ext_variables_my_id);
+	(void) sieve_binary_emit_integer(sbin, index);
+}
+
+static bool opr_match_value_dump
+	(const struct sieve_dumptime_env *denv, sieve_size_t *address) 
+{
+	sieve_size_t index = 0;
+	
+	if (sieve_binary_read_integer(denv->sbin, address, &index) ) {
+		sieve_code_dumpf(denv, "MVALUE: %ld", (long) index);
+
+		return TRUE;
+	}
+	
+	return FALSE;
+}
+
+static bool opr_match_value_read
+  (const struct sieve_runtime_env *renv, sieve_size_t *address, string_t **str)
+{ 
+	sieve_size_t index = 0;
+			
+	if (sieve_binary_read_integer(renv->sbin, address, &index) ) {
+		/* Parameter str can be NULL if we are requested to only skip and not 
+		 * actually read the argument.
+		 	*/
+		if ( str != NULL ) {
+			sieve_match_values_get(renv->interp, (unsigned int) index, str);
+		
+			if ( *str == NULL ) *str = t_str_new(0);
+		}
+		return TRUE;
+	}
+	
+	return FALSE;
+}
+
 /* Variable string operand */
 
 static bool opr_variable_string_read
diff --git a/src/lib-sieve/plugins/variables/ext-variables-common.h b/src/lib-sieve/plugins/variables/ext-variables-common.h
index c00a96104..83eb4c99f 100644
--- a/src/lib-sieve/plugins/variables/ext-variables-common.h
+++ b/src/lib-sieve/plugins/variables/ext-variables-common.h
@@ -16,6 +16,7 @@ enum ext_variables_opcode {
 
 enum ext_variables_operand {
 	EXT_VARIABLES_OPERAND_VARIABLE,
+	EXT_VARIABLES_OPERAND_MATCH_VALUE,
 	EXT_VARIABLES_OPERAND_VARIABLE_STRING
 };
 
@@ -57,6 +58,8 @@ extern const struct sieve_argument variable_string_argument;
 
 void ext_variables_opr_variable_emit
 	(struct sieve_binary *sbin, struct sieve_variable *var);
+void ext_variables_opr_match_value_emit
+	(struct sieve_binary *sbin, unsigned int index);
 bool ext_variables_opr_variable_read
 	(const struct sieve_runtime_env *renv, sieve_size_t *address, 
 		struct sieve_variable_storage **storage, unsigned int *var_index);
diff --git a/src/lib-sieve/plugins/variables/ext-variables.c b/src/lib-sieve/plugins/variables/ext-variables.c
index 82884b4ad..6012e368c 100644
--- a/src/lib-sieve/plugins/variables/ext-variables.c
+++ b/src/lib-sieve/plugins/variables/ext-variables.c
@@ -53,10 +53,12 @@ const struct sieve_operation *ext_variables_operations[] = {
 /* Operands */
 
 extern const struct sieve_operand variable_operand;
+extern const struct sieve_operand match_value_operand;
 extern const struct sieve_operand variable_string_operand;
 
 const struct sieve_operand *ext_variables_operands[] = {
 	&variable_operand, 
+	&match_value_operand,
 	&variable_string_operand
 };
 
diff --git a/src/lib-sieve/plugins/variables/tst-string.c b/src/lib-sieve/plugins/variables/tst-string.c
index 425cab6b3..44f0020af 100644
--- a/src/lib-sieve/plugins/variables/tst-string.c
+++ b/src/lib-sieve/plugins/variables/tst-string.c
@@ -208,7 +208,7 @@ static bool tst_string_operation_execute
 		return FALSE;
 	}
 
-	mctx = sieve_match_begin(mtch, cmp, key_list); 	
+	mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); 	
 
 	/* Iterate through all requested strings to match */
 	src_item = NULL;
diff --git a/src/lib-sieve/plugins/variables/variables-match.sieve b/src/lib-sieve/plugins/variables/variables-match.sieve
new file mode 100644
index 000000000..9920fa64f
--- /dev/null
+++ b/src/lib-sieve/plugins/variables/variables-match.sieve
@@ -0,0 +1,23 @@
+require "variables";
+require "fileinto";
+
+set "match1" "Test of general stupidity";
+
+# Test 1
+if string :matches "${match1}" "Test of *" {
+	fileinto "TEST 1: ${1}";
+}
+
+# Test 2
+if string :matches "${match1}" "of *" {
+	fileinto "FAILED 2: ${1}";
+} else {
+	fileinto "TEST 2 NOT MATCHED: ${match1}";
+}
+
+set "match2" "toptoptop";
+
+# Test 3
+if string :matches "${match2}" "*top" {
+	fileinto "TEST 3: ${1}";
+} 
diff --git a/src/lib-sieve/sieve-match-types.c b/src/lib-sieve/sieve-match-types.c
index 71f8ad188..4d23ded4f 100644
--- a/src/lib-sieve/sieve-match-types.c
+++ b/src/lib-sieve/sieve-match-types.c
@@ -189,6 +189,143 @@ static bool mtch_binary_load(struct sieve_binary *sbin)
 	return TRUE;
 }
 
+/* 
+ * Interpreter context
+ */
+
+struct mtch_interpreter_context {
+	struct sieve_match_values *match_values;
+	bool match_values_enabled;
+};
+
+static inline struct mtch_interpreter_context *
+get_interpreter_context(struct sieve_interpreter *interp)
+{
+	return (struct mtch_interpreter_context *)
+		sieve_interpreter_extension_get_context(interp, ext_my_id);
+}
+
+static struct mtch_interpreter_context *
+mtch_interpreter_context_init(struct sieve_interpreter *interp)
+{		
+	pool_t pool = sieve_interpreter_pool(interp);
+	struct mtch_interpreter_context *ctx;
+	
+	ctx = p_new(pool, struct mtch_interpreter_context, 1);
+
+	sieve_interpreter_extension_set_context
+		(interp, ext_my_id, (void *) ctx);
+
+	return ctx;
+}
+
+/*
+ * Match values
+ */
+ 
+struct sieve_match_values {
+	pool_t pool;
+	ARRAY_DEFINE(values, string_t *);
+	unsigned count;
+};
+
+bool sieve_match_values_set_enabled
+(struct sieve_interpreter *interp, bool enable)
+{
+	bool previous;
+	struct mtch_interpreter_context *ctx = get_interpreter_context(interp);
+	
+	if ( ctx == NULL && enable ) 
+		ctx = mtch_interpreter_context_init(interp);
+	
+	previous = ctx->match_values_enabled;
+	ctx->match_values_enabled = enable;
+	
+	return previous;
+}
+
+struct sieve_match_values *sieve_match_values_start(struct sieve_interpreter *interp)
+{
+	struct mtch_interpreter_context *ctx = get_interpreter_context(interp);
+	
+	if ( ctx == NULL || !ctx->match_values_enabled )
+		return NULL;
+		
+	if ( ctx->match_values == NULL ) {
+		pool_t pool = sieve_interpreter_pool(interp);
+		
+		ctx->match_values = p_new(pool, struct sieve_match_values, 1);
+		ctx->match_values->pool = pool;
+		p_array_init(&ctx->match_values->values, pool, 4);
+	}
+	
+	ctx->match_values->count = 0;
+	
+	return ctx->match_values;
+}
+
+void sieve_match_values_add
+	(struct sieve_match_values *mvalues, string_t *value) 
+{
+	string_t *entry;
+	
+	if ( mvalues == NULL ) return;
+	
+	if ( mvalues->count >= array_count(&mvalues->values) ) {
+		entry = str_new(mvalues->pool, 64);
+		array_append(&mvalues->values, &entry, 1);
+	} else {
+		string_t * const *ep = array_idx(&mvalues->values, mvalues->count);
+		entry = *ep;
+		str_truncate(entry, 0);
+	}
+	
+	if ( value != NULL )
+		str_append_str(entry, value);
+	
+	mvalues->count++;
+}
+
+void sieve_match_values_set_first
+	(struct sieve_match_values *mvalues, string_t *value) 
+{
+	string_t * const *entry;
+
+	if ( mvalues == NULL ) return;
+	
+	if ( mvalues->count == 0 ) {
+		sieve_match_values_add(mvalues, value);
+		
+		return;
+	}
+	
+	entry = array_idx(&mvalues->values, mvalues->count);
+	str_truncate(*entry, 0);
+}
+
+void sieve_match_values_get
+	(struct sieve_interpreter *interp, unsigned int index, string_t **value_r) 
+{
+	struct mtch_interpreter_context *ctx = get_interpreter_context(interp);
+	struct sieve_match_values *mvalues;
+
+	if ( ctx == NULL || ctx->match_values == NULL ) {
+		*value_r = NULL;
+		return;
+	}
+	
+	mvalues = ctx->match_values;
+	if ( index <= mvalues->count ) {
+		string_t * const *entry = array_idx(&mvalues->values, index);
+		
+		*value_r = *entry;
+		return;
+	}
+
+	*value_r = NULL;	
+}
+
+
 /*
  * Match-type operand
  */
@@ -385,11 +522,12 @@ static bool tag_match_type_generate
 /* Match Utility */
 
 struct sieve_match_context *sieve_match_begin
-(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp, 
-	struct sieve_coded_stringlist *key_list)
+(struct sieve_interpreter *interp, const struct sieve_match_type *mtch, 
+	const struct sieve_comparator *cmp, struct sieve_coded_stringlist *key_list)
 {
 	struct sieve_match_context *mctx = t_new(struct sieve_match_context, 1);  
 
+	mctx->interp = interp;
 	mctx->match_type = mtch;
 	mctx->comparator = cmp;
 	mctx->key_list = key_list;
@@ -523,14 +661,14 @@ static bool mtch_contains_match
 /* :matches */
 
 /* Quick 'n dirty debug */
-//#define MATCH_DEBUG
+#define MATCH_DEBUG
 #ifdef MATCH_DEBUG
 #define debug_printf(...) printf (__VA_ARGS__)
 #else
 #define debug_printf(...) 
 #endif
 
-static bool _matches_section
+static inline bool _matches_section
 	(const struct sieve_comparator *cmp, 
 		const char **val, const char *vend, const char **key, const char *kend,
 		bool must_end)
@@ -585,12 +723,18 @@ static bool mtch_matches_match
 	const char *key, size_t key_size, int key_index ATTR_UNUSED)
 {
 	const struct sieve_comparator *cmp = mctx->comparator;
+	struct sieve_match_values *mvalues;
+	string_t *match = t_str_new(32);
 	const char *vend = (const char *) val + val_size;
 	const char *kend = (const char *) key + key_size;
-	const char *vp = val;
-	const char *kp = key;
-	const char *wp = key;
+	const char *vp = val; /* Value pointer */
+	const char *kp = key; /* Key pointer */
+	const char *wp = key; /* Wildcard (key) pointer */
 	
+	/* Reset match values list */
+	mvalues = sieve_match_values_start(mctx->interp);
+	sieve_match_values_add(mvalues, NULL);
+
 	/* Match the pattern as a two-level structure: 
 	 *   <pattern> = <section>*<section>*<section>....
 	 *   <section> = [text]?[text]?[text].... 
@@ -601,6 +745,7 @@ static bool mtch_matches_match
 	debug_printf("MATCH key: %s\n", key);
 	debug_printf("MATCH val: %s\n", val);
 
+	/* Loop until either key or value ends */
 	while (kp < kend && vp < vend) {
 		char wildcard;
 		
@@ -620,10 +765,21 @@ static bool mtch_matches_match
 			wp++;
 		}
 		
-		wildcard = *wp;
-		debug_printf("MATCH found wildcard: %c %d\n", wildcard, (int) (wp-key));
-		
-		/* Find this section */
+		/* Record wildcard character or \0 */
+		if ( wp < kend ) {
+			wildcard = *wp;
+			debug_printf("MATCH found wildcard: %c %d\n", wildcard, (int) (wp-key));
+		} else
+			wildcard = '\0';
+
+		if ( kp > key+1 ) { 
+			debug_printf("MATCH value (previous): %s %d\n", str_c(match), kp-key); 
+			sieve_match_values_add(mvalues, match);
+		}
+			
+		str_truncate(match, 0);
+			
+		/* Find this section (starting at kp and ending at wp-1)*/
 		if ( wp > kp ) {
 			while ( (vp < vend) && (kp < wp) ) {
 #ifdef MATCH_DEBUG
@@ -637,8 +793,12 @@ static bool mtch_matches_match
 						debug_printf("MATCH first section failed\n");
 						return FALSE; 
 					}
+					
+					/* Wildcard eats the character */
+					str_append_c(match, *vp); /* Add it to the match value */
 					vp++;
 				} else {
+					/* Section successfully matched */
 					debug_printf("MATCH matched section key: %s\n", t_strdup_until(skp, wp)); 
 					debug_printf("MATCH matched section val: %s\n", svp); 
 				}
@@ -651,9 +811,9 @@ static bool mtch_matches_match
 
 			return FALSE;
 		} 
-		
+				
 		/* Break the loop if match was successful already */
-		if (kp == kend && vp == vend) return TRUE;
+		if (kp == kend && vp == vend) break;
 		
 		/* Advance loop to next wildcard search */
 		wp++;						
@@ -662,15 +822,23 @@ static bool mtch_matches_match
 		/* Check whether string ends in a wildcard 
 		 * (avoid scnning the rest of the string)
 		 */
-		if ( kp == kend && wildcard == '*' ) 
-			return TRUE;
+		if ( kp == kend && wildcard == '*' ) {
+			str_append_n(match, vp, vend-vp);
+			vp = vend;
+			break;
+		}
 		
 		/* Current wp is escaped.. */
 		if ( wildcard == '\\' )
 			wp++;
+			
+		debug_printf("MATCH loop '*'\n"); 
 	}
 	
 	debug_printf("MATCH loop ended\n");
+
+	debug_printf("MATCH value (end loop): %s\n", str_c(match)); 
+	sieve_match_values_add(mvalues, match);
 	
 	/* By definition, the match is only successful if both value and key pattern
 	 * are exhausted.
diff --git a/src/lib-sieve/sieve-match-types.h b/src/lib-sieve/sieve-match-types.h
index bf0265d87..92a5cc90f 100644
--- a/src/lib-sieve/sieve-match-types.h
+++ b/src/lib-sieve/sieve-match-types.h
@@ -2,6 +2,7 @@
 #define __SIEVE_MATCH_TYPES_H
 
 #include "sieve-common.h"
+#include "sieve-extensions.h"
 
 enum sieve_match_type_code {
 	SIEVE_MATCH_TYPE_IS,
@@ -11,11 +12,12 @@ enum sieve_match_type_code {
 };
 
 struct sieve_match_context {
-    const struct sieve_match_type *match_type;
-    const struct sieve_comparator *comparator;
-    struct sieve_coded_stringlist *key_list;
+	struct sieve_interpreter *interp;
+	const struct sieve_match_type *match_type;
+	const struct sieve_comparator *comparator;
+	struct sieve_coded_stringlist *key_list;
 
-    void *data;
+	void *data;
 };
 
 struct sieve_match_type;
@@ -69,6 +71,21 @@ struct sieve_match_type_context {
 	void *ctx_data;
 };
 
+/* Match values */
+
+struct sieve_match_values;
+
+bool sieve_match_values_set_enabled
+	(struct sieve_interpreter *interp, bool enable);
+struct sieve_match_values *sieve_match_values_start
+	(struct sieve_interpreter *interp);
+void sieve_match_values_add
+	(struct sieve_match_values *mvalues, string_t *value);
+void sieve_match_values_get
+	(struct sieve_interpreter *interp, unsigned int index, string_t **value_r);
+
+/* ... */
+
 void sieve_match_types_link_tags
 	(struct sieve_validator *validator, 
 		struct sieve_command_registration *cmd_reg, int id_code);
@@ -106,8 +123,9 @@ bool sieve_opr_match_type_dump
 /* Match Utility */
 
 struct sieve_match_context *sieve_match_begin
-(const struct sieve_match_type *mtch, const struct sieve_comparator *cmp,
-    struct sieve_coded_stringlist *key_list);
+(struct sieve_interpreter *interp, 
+	const struct sieve_match_type *mtch, const struct sieve_comparator *cmp,
+	struct sieve_coded_stringlist *key_list);
 bool sieve_match_value
     (struct sieve_match_context *mctx, const char *value, size_t val_size);
 bool sieve_match_end(struct sieve_match_context *mctx);
diff --git a/src/lib-sieve/tst-address.c b/src/lib-sieve/tst-address.c
index bc835bf29..cef90e8c3 100644
--- a/src/lib-sieve/tst-address.c
+++ b/src/lib-sieve/tst-address.c
@@ -166,7 +166,7 @@ static bool tst_address_operation_execute
 	}
 
 	/* Initialize match context */
-	mctx = sieve_match_begin(mtch, cmp, key_list);
+	mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list);
 	
 	/* Iterate through all requested headers to match */
 	hdr_item = NULL;
diff --git a/src/lib-sieve/tst-header.c b/src/lib-sieve/tst-header.c
index d3191f448..e2fd44644 100644
--- a/src/lib-sieve/tst-header.c
+++ b/src/lib-sieve/tst-header.c
@@ -205,7 +205,7 @@ static bool tst_header_operation_execute
 		return FALSE;
 	}
 
-	mctx = sieve_match_begin(mtch, cmp, key_list); 	
+	mctx = sieve_match_begin(renv->interp, mtch, cmp, key_list); 	
 
 	/* Iterate through all requested headers to match */
 	hdr_item = NULL;
-- 
GitLab