diff --git a/src/lib-sieve/sieve-extensions.c b/src/lib-sieve/sieve-extensions.c
index a4efa9bb9b9cb5f444b76030a2f009e43e6dc734..6002abb5cf3656f10dc776ba2c8729182a9d704d 100644
--- a/src/lib-sieve/sieve-extensions.c
+++ b/src/lib-sieve/sieve-extensions.c
@@ -356,12 +356,14 @@ static void sieve_extension_disable(struct sieve_extension_registration *ereg)
 
 void sieve_extensions_set_string(const char *ext_string)
 {
-	ARRAY_DEFINE(enabled_extensions, const struct sieve_extension_registration *);
-	const struct sieve_extension_registration *const *ena_eregs;
+	ARRAY_DEFINE(enabled_extensions, const struct sieve_extension *);
+	ARRAY_DEFINE(disabled_extensions, const struct sieve_extension *);
+	const struct sieve_extension *const *ext_enabled;
+	const struct sieve_extension *const *ext_disabled;
 	struct sieve_extension_registration *eregs;
-	const char *bp = ext_string;
-	const char *ep = bp;
-	unsigned int i, ext_count, ena_count;
+	const char **ext_names;
+	unsigned int i, ext_count, ena_count, dis_count;
+	bool relative = FALSE;
 
 	if ( ext_string == NULL ) {
 		/* Enable all */
@@ -373,62 +375,99 @@ void sieve_extensions_set_string(const char *ext_string)
 		return;	
 	}
 
-	t_array_init(&enabled_extensions, array_count(&extensions));
+	T_BEGIN {
+		t_array_init(&enabled_extensions, array_count(&extensions));
+		t_array_init(&disabled_extensions, array_count(&extensions));
 
-	do {
-		const char *name;
+		ext_names = t_strsplit_spaces(ext_string, " \t");
 
-		ep = strchr(bp, ' ');
-		if ( ep == NULL ) 
-			name = bp;
-		else { 
-			name = t_strdup_until(bp, ep);
-			bp = ep + 1;
-		}
+		while ( *ext_names != NULL ) {
+			const char *name = *ext_names;
+
+			ext_names++;
 
-		if ( *name != '\0' ) {
-			const struct sieve_extension_registration *ereg;
+			if ( *name != '\0' ) {
+				const struct sieve_extension_registration *ereg;
+				char op = '\0'; /* No add/remove operation */
 	
-			if ( *name == '@' )
-				ereg = NULL;
-			else
-				ereg = (const struct sieve_extension_registration *) 
-					hash_table_lookup(extension_index, name);
-
-			if ( ereg == NULL ) {
-				sieve_sys_warning("ignored unknown extension '%s' while configuring "
-					"available extensions", name);
-				continue;
-			}
+				if ( *name == '+' 		/* Add to existing config */
+					|| *name == '-' ) {	/* Remove from existing config */
+				 	op = *name++;
+				 	relative = TRUE;
+				}
 
-			array_append(&enabled_extensions, &ereg, 1);
-		}
+				if ( *name == '@' )
+					ereg = NULL;
+				else
+					ereg = (const struct sieve_extension_registration *) 
+						hash_table_lookup(extension_index, name);
+	
+				if ( ereg == NULL ) {
+					sieve_sys_warning(
+						"ignored unknown extension '%s' while configuring "
+						"available extensions", name);
+					continue;
+				}
 
-	} while ( *bp == '\0' || ep != NULL );
+				if ( op == '-' )
+					array_append(&disabled_extensions, &ereg->extension, 1);
+				else
+					array_append(&enabled_extensions, &ereg->extension, 1);
+			}
+		}
 
-	eregs = array_get_modifiable(&extensions, &ext_count);
-	ena_eregs = array_get(&enabled_extensions, &ena_count);
+		eregs = array_get_modifiable(&extensions, &ext_count);
+		ext_enabled = array_get(&enabled_extensions, &ena_count);
+		ext_disabled = array_get(&disabled_extensions, &dis_count);
+
+		/* Set new extension status */
+
+		for ( i = 0; i < ext_count; i++ ) {
+			unsigned int j;
+			bool disabled = TRUE;
+
+			/* If extensions are specified relative to the default set,
+			 * we first need to check which ones are disabled 
+			 */
+
+			if ( relative ) {
+				/* Enable if core extension */
+				for ( j = 0; j < sieve_core_extensions_count; j++ ) {
+					if ( sieve_core_extensions[j] == eregs[i].extension ) {
+						disabled = FALSE;
+						break;
+					}
+    			}
+
+				/* Disable if explicitly disabled */
+				for ( j = 0; j < dis_count; j++ ) {
+					if ( ext_disabled[j] == eregs[i].extension ) {
+						disabled = TRUE;
+						break;
+					}
+				}
+			} 
 
-	/* Set new extension status */
-	for ( i = 0; i < ext_count; i++ ) {
-		unsigned int j;
-		bool disabled = TRUE;
+			/* Enable if listed with '+' or no prefix */
+	
+			for ( j = 0; j < ena_count; j++ ) {
+				if ( ext_enabled[j] == eregs[i].extension ) {
+					disabled = FALSE;
+					break;
+				}		
+			}
 
-		for ( j = 0; j < ena_count; j++ ) {
-			if ( ena_eregs[j] == &eregs[i] ) {
-				disabled = FALSE;
-				break;
-			}		
-		}
+			/* Perform actual activation/deactivation */
 
-		if ( eregs[i].extension->_id != NULL && 
-			*(eregs[i].extension->name) != '@' ) {
-			if ( disabled && !eregs[i].required )
-				sieve_extension_disable(&eregs[i]);
-			else
-				sieve_extension_enable(&eregs[i]);
+			if ( eregs[i].extension->_id != NULL && 
+				*(eregs[i].extension->name) != '@' ) {
+				if ( disabled && !eregs[i].required )
+					sieve_extension_disable(&eregs[i]);
+				else
+					sieve_extension_enable(&eregs[i]);
+			}
 		}
-	}
+	} T_END;
 }
 
 static void sieve_extensions_deinit_registry(void)