From 06949d2b8ef400f7ef4bd0f8a37f4b5d592d983c Mon Sep 17 00:00:00 2001
From: Stephan Bosch <stephan@rename-it.nl>
Date: Wed, 4 Feb 2009 22:13:48 +0100
Subject: [PATCH] Added facilities for requiring extensions and making
 extensions mutually exclusive.

---
 src/lib-sieve/cmd-require.c      |  4 +-
 src/lib-sieve/sieve-extensions.c | 95 ++++++++++++++++++++++----------
 src/lib-sieve/sieve-extensions.h |  1 +
 src/lib-sieve/sieve-validator.c  | 47 ++++++++++++----
 src/lib-sieve/sieve-validator.h  |  5 +-
 5 files changed, 110 insertions(+), 42 deletions(-)

diff --git a/src/lib-sieve/cmd-require.c b/src/lib-sieve/cmd-require.c
index e048e6ea4..1f3e12941 100644
--- a/src/lib-sieve/cmd-require.c
+++ b/src/lib-sieve/cmd-require.c
@@ -57,7 +57,7 @@ static bool cmd_require_validate
 	if ( sieve_ast_argument_type(arg) == SAAT_STRING ) {
 		/* Single string */
 		const struct sieve_extension *ext = sieve_validator_extension_load
-			(validator, cmd, sieve_ast_argument_str(arg));	
+			(validator, arg, sieve_ast_argument_str(arg));	
 
 		if ( ext == NULL ) result = FALSE;
 		
@@ -67,7 +67,7 @@ static bool cmd_require_validate
 		
 		while ( stritem != NULL ) {
 			const struct sieve_extension *ext = sieve_validator_extension_load
-				(validator, cmd, sieve_ast_strlist_str(stritem));
+				(validator, stritem, sieve_ast_strlist_str(stritem));
 
 			if ( ext == NULL ) result = FALSE;
 	
diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c
index f58281e05..d5441ea3c 100644
--- a/src/lib-sieve/sieve-extensions.c
+++ b/src/lib-sieve/sieve-extensions.c
@@ -148,6 +148,7 @@ void sieve_extensions_deinit(void)
 struct sieve_extension_registration {
 	const struct sieve_extension *extension;
 	int id;
+	bool required;
 };
 
 static ARRAY_DEFINE(extensions, struct sieve_extension_registration); 
@@ -155,37 +156,74 @@ static struct hash_table *extension_index;
 
 static void sieve_extensions_init_registry(void)
 {	
-	p_array_init(&extensions, default_pool, 30);
+	i_array_init(&extensions, 30);
 	extension_index = hash_table_create
 		(default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp);
 }
 
+static struct sieve_extension_registration *_sieve_extension_register
+(const struct sieve_extension *extension)
+{
+	struct sieve_extension_registration *ereg = (struct sieve_extension_registration *)
+        hash_table_lookup(extension_index, extension->name);
+
+	/* Register extension if it is not registered already */
+    if ( ereg == NULL ) {
+		int ext_id = array_count(&extensions);
+
+        /* Add extension to the registry */
+
+        ereg = array_append_space(&extensions);
+        ereg->id = ext_id;
+
+        hash_table_insert(extension_index, (void *) extension->name, (void *) ereg);
+    }
+
+	/* Enable extension */
+	if ( ereg->extension == NULL && extension != NULL ) {
+		if ( extension->_id != NULL ) {
+			int ext_id = *(extension->_id);
+
+			/* Make sure extension is enabled */
+			*(extension->_id) = ereg->id;
+
+			/* Call load handler if extension was not enabled */
+			if ( ext_id == -1 && extension->load != NULL && !extension->load() ) {
+				sieve_sys_error("failed to load '%s' extension support.", 
+					extension->name);
+				return NULL;
+			}
+		}
+
+		ereg->extension = extension;
+	}
+
+	return ereg;
+}
+
 int sieve_extension_register(const struct sieve_extension *extension) 
 {
-	int ext_id = array_count(&extensions); 
 	struct sieve_extension_registration *ereg;
-	
-	ereg = array_append_space(&extensions);
-	
-	ereg->extension = extension;
-	ereg->id = ext_id;
-	
-	hash_table_insert(extension_index, (void *) extension->name, (void *) ereg);
-	
-	/* Assign ID */
-	
-	if ( extension->_id != NULL ) {
-		i_assert( *(extension->_id) == -1 ); 
-		*(extension->_id) = ext_id;
-
-		if ( extension->load != NULL && !extension->load() ) {
-			sieve_sys_error("failed to load '%s' extension support.", 
-				extension->name);
-			return -1;
-		}
+
+	/* Register the extension */
+	if ( (ereg=_sieve_extension_register(extension)) == NULL ) {
+		return -1;
 	}
 
-	return ext_id;
+	return ereg->id;
+}
+
+int sieve_extension_require(const struct sieve_extension *extension)
+{
+	struct sieve_extension_registration *ereg;
+
+	/* Register (possibly unknown) extension */
+    if ( (ereg=_sieve_extension_register(extension)) == NULL ) {
+        return -1;
+    }
+
+	ereg->required = TRUE;
+	return ereg->id;
 }
 
 int sieve_extensions_get_count(void)
@@ -209,7 +247,7 @@ const struct sieve_extension *sieve_extension_get_by_id(unsigned int ext_id)
 
 const struct sieve_extension *sieve_extension_get_by_name(const char *name) 
 {
-  struct sieve_extension_registration *ereg;
+	struct sieve_extension_registration *ereg;
 	
 	if ( *name == '@' )
 		return NULL;	
@@ -335,7 +373,7 @@ void sieve_extensions_set_string(const char *ext_string)
 
 		if ( eregs[i].extension->_id != NULL && 
 			*(eregs[i].extension->name) != '@' ) {
-			if ( disabled ) {
+			if ( disabled && !eregs[i].required ) {
 				*(eregs[i].extension->_id) = -1;
 			} else {
 				*(eregs[i].extension->_id) = eregs[i].id;
@@ -349,11 +387,12 @@ static void sieve_extensions_deinit_registry(void)
 	struct hash_iterate_context *itx = 
 		hash_table_iterate_init(extension_index);
 	void *key; 
-	void *ereg;
+	void *value;
 	
-	while ( hash_table_iterate(itx, &key, &ereg) ) {
-		const struct sieve_extension *ext = 
-			((struct sieve_extension_registration *) ereg)->extension;
+	while ( hash_table_iterate(itx, &key, &value) ) {
+		struct sieve_extension_registration *ereg =
+			(struct sieve_extension_registration *) value;
+		const struct sieve_extension *ext = ereg->extension;
 		
 		if ( ext->unload != NULL )
 			ext->unload();
diff --git a/src/lib-sieve/sieve-extensions.h b/src/lib-sieve/sieve-extensions.h
index 18ebbcdb3..7affa072e 100644
--- a/src/lib-sieve/sieve-extensions.h
+++ b/src/lib-sieve/sieve-extensions.h
@@ -90,6 +90,7 @@ void sieve_extensions_deinit(void);
  */
 
 int sieve_extension_register(const struct sieve_extension *extension);
+int sieve_extension_require(const struct sieve_extension *extension);
 int sieve_extensions_get_count(void);
 const struct sieve_extension *sieve_extension_get_by_id(unsigned int ext_id);
 const struct sieve_extension *sieve_extension_get_by_name(const char *name);
diff --git a/src/lib-sieve/sieve-validator.c b/src/lib-sieve/sieve-validator.c
index a5b053b36..f155cde6d 100644
--- a/src/lib-sieve/sieve-validator.c
+++ b/src/lib-sieve/sieve-validator.c
@@ -63,6 +63,7 @@ struct sieve_default_argument {
 
 struct sieve_validator_extension_reg {
 	const struct sieve_validator_extension *val_ext;
+	struct sieve_ast_argument *arg;
 	void *context;
 };
 
@@ -506,14 +507,16 @@ static const struct sieve_argument *sieve_validator_find_tag_by_identifier
  */
 
 const struct sieve_extension *sieve_validator_extension_load
-(struct sieve_validator *validator, struct sieve_command_context *cmd, 
+(struct sieve_validator *valdtr, struct sieve_ast_argument *ext_arg, 
 	string_t *ext_name) 
 {
+	int ext_id;
+	struct sieve_validator_extension_reg *reg;
 	const struct sieve_extension *ext;
 	const char *name = str_c(ext_name);
 
 	if ( str_len(ext_name) > 128 ) {
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(valdtr, ext_arg, 
 			"unsupported sieve capability '%s' (name is impossibly long)", 
 			str_sanitize(name, 128));
 		return NULL;
@@ -522,19 +525,26 @@ const struct sieve_extension *sieve_validator_extension_load
 	ext = sieve_extension_get_by_name(name); 
 	
 	if ( ext == NULL ) {
-		sieve_command_validate_error(validator, cmd, 
+		sieve_argument_validate_error(valdtr, ext_arg, 
 			"unsupported sieve capability '%s'", name);
 		return NULL;
 	}
 	
-	sieve_ast_extension_link(validator->ast, ext);
+	sieve_ast_extension_link(valdtr->ast, ext);
 
-	if ( ext->validator_load != NULL && !ext->validator_load(validator) ) {
-		sieve_command_validate_error(validator, cmd, 
+	if ( ext->validator_load != NULL && !ext->validator_load(valdtr) ) {
+		sieve_argument_validate_error(valdtr, ext_arg, 
 			"failed to load sieve capability '%s'", ext->name);
 		return NULL;
 	}
-	
+
+	/* Register extension no matter what and store the AST argument registering it */
+	ext_id = SIEVE_EXT_ID(ext);
+	if ( ext_id >= 0 ) {
+		reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id);
+		reg->arg = ext_arg;
+	}
+
 	return ext;
 }
 
@@ -542,24 +552,27 @@ void sieve_validator_extension_register
 (struct sieve_validator *valdtr, 
 	const struct sieve_validator_extension *val_ext, void *context)
 {
-	struct sieve_validator_extension_reg reg = { val_ext, context };
+	struct sieve_validator_extension_reg *reg;
 	int ext_id = SIEVE_EXT_ID(val_ext->ext);
 
 	if ( ext_id < 0 ) return;
 	
-	array_idx_set(&valdtr->extensions, (unsigned int) ext_id, &reg);	
+	reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id);
+	reg->val_ext = val_ext;
+	reg->context = context;
 }
 
 void sieve_validator_extension_set_context
 (struct sieve_validator *valdtr, const struct sieve_extension *ext, 
 	void *context)
 {
-	struct sieve_validator_extension_reg reg = { NULL, context };
+	struct sieve_validator_extension_reg *reg;
 	int ext_id = SIEVE_EXT_ID(ext);
 
 	if ( ext_id < 0 ) return;
 	
-	array_idx_set(&valdtr->extensions, (unsigned int) ext_id, &reg);	
+	reg = array_idx_modifiable(&valdtr->extensions, (unsigned int) ext_id);
+	reg->context = context;
 }
 
 void *sieve_validator_extension_get_context
@@ -1138,6 +1151,18 @@ static bool sieve_validate_block
 }
 
 bool sieve_validator_run(struct sieve_validator *validator) {	
+	const struct sieve_validator_extension_reg *extrs;
+    unsigned int ext_count, i;
+
+    /* Validate registered extensions */
+    extrs = array_get(&validator->extensions, &ext_count);
+    for ( i = 0; i < ext_count; i++ ) {
+        if ( extrs[i].val_ext != NULL && extrs[i].val_ext->validate != NULL ) {
+        	if ( !extrs[i].val_ext->validate(validator, extrs[i].context, extrs[i].arg) )
+				return FALSE;
+		} 
+	}
+
 	return sieve_validate_block(validator, sieve_ast_root(validator->ast));
 }
 
diff --git a/src/lib-sieve/sieve-validator.h b/src/lib-sieve/sieve-validator.h
index c16070664..2b553be68 100644
--- a/src/lib-sieve/sieve-validator.h
+++ b/src/lib-sieve/sieve-validator.h
@@ -121,11 +121,14 @@ bool sieve_validate_tag_parameter
 struct sieve_validator_extension {
 	const struct sieve_extension *ext;	
 
+	bool (*validate)(struct sieve_validator *valdtr, void *context,
+		struct sieve_ast_argument *require_arg);
+
 	void (*free)(struct sieve_validator *valdtr, void *context);
 };
 
 const struct sieve_extension *sieve_validator_extension_load
-	(struct sieve_validator *validator, struct sieve_command_context *cmd, 
+	(struct sieve_validator *validator, struct sieve_ast_argument *ext_arg, 
 		string_t *ext_name); 
 
 void sieve_validator_extension_register
-- 
GitLab