diff --git a/src/lib-sieve/plugins/notify/cmd-notify.c b/src/lib-sieve/plugins/notify/cmd-notify.c
index 41fa21ff0abe9822201c7a2ed87b7558ca4538c1..759ac3cd554aad67eba41012208a638d2d65e25e 100644
--- a/src/lib-sieve/plugins/notify/cmd-notify.c
+++ b/src/lib-sieve/plugins/notify/cmd-notify.c
@@ -27,7 +27,9 @@
 #include "ext-notify-common.h"
 #include "ext-notify-limits.h"
 
-/* Notify command
+#include <ctype.h>
+
+/* Notify command (DEPRECATED)
  *
  * Syntax:
  *   notify [":method" string] [":id" string] [":options" string-list]
@@ -464,6 +466,80 @@ static bool cmd_notify_operation_dump
 /* 
  * Code execution
  */
+
+static void cmd_notify_construct_message
+(const struct sieve_runtime_env *renv, const char *msg_format, 
+	string_t *out_msg)
+{
+	const struct sieve_message_data *msgdata = renv->msgdata;
+  const char *p;
+
+  if ( msg_format == NULL )
+		msg_format = "$from$: $subject$";
+ 
+ 	/* Scan message for substitutions */
+	p = msg_format;
+  while ( *p != '\0' ) {
+		const char *const *header;
+
+		if ( strncasecmp(p, "$from$", 6) == 0 ) {
+			p += 6;
+		
+			/* Fetch sender from oriinal message */
+			if ( mail_get_headers_utf8(msgdata->mail, "from", &header) >= 0 )
+				 str_append(out_msg, header[0]); 
+
+		} else if ( strncasecmp(p, "$env-from$", 10) == 0 ) {
+			p += 10;
+
+			if ( msgdata->return_path != NULL ) 
+				str_append(out_msg, msgdata->return_path);
+
+		} else if ( strncasecmp(p, "$subject$", 9) == 0 ) {	
+			p += 9;
+
+			/* Fetch sender from oriinal message */
+			if ( mail_get_headers_utf8(msgdata->mail, "subject", &header) >= 0 )
+				 str_append(out_msg, header[0]); 
+			
+		} else if ( strncasecmp(p, "$text", 5) == 0 
+			&& (p[5] == '[' || p[5] == '$') ) {
+			size_t num = 0;
+			const char *begin = p;
+			bool valid = TRUE;
+
+    	p += 5;
+			if ( *p == '[' ) {
+				p += 1;
+
+				while ( i_isdigit(*p) ) {
+					num = num * 10 + (*p - '0');
+					p++;
+				}
+
+				if ( *p++ != ']' || *p++ != '$' ) {
+					str_append_n(out_msg, begin, p-begin);
+					valid = FALSE;										
+				}	    	
+			} else {
+				p += 1;			
+			}
+
+			if ( valid ) {
+				str_append(out_msg, "<body extraction not supported>");
+			}
+		} else {
+			size_t len;
+
+    	/* Find next substitution */
+    	len = strcspn(p + 1, "$") + 1; 
+
+	    /* Copy normal text */
+	    str_append_n(out_msg, p, len);
+	    p += len;
+		}
+  }
+}
  
 static int cmd_notify_operation_execute
 (const struct sieve_operation *op ATTR_UNUSED,
@@ -542,19 +618,27 @@ static int cmd_notify_operation_execute
 
 	sieve_runtime_trace(renv, "NOTIFY action");	
 
-	/* Normalize and verify all :options addresses */
+	/* Compose action */
 	if ( options != NULL ) {
 		string_t *raw_address;
+		string_t *out_message;
 		bool result;	
 
 		pool = sieve_result_pool(renv->result);
 		act = p_new(pool, struct ext_notify_action, 1);
 		if ( id != NULL )
 				act->id = p_strdup(pool, str_c(id));
-		if ( message != NULL )
-			act->message = p_strdup(pool, str_c(message));
-		act->importance = importance;
-			
+		act->importance = importance;		
+	
+		/* Process message */
+
+		out_message = t_str_new(1024);
+		cmd_notify_construct_message
+			(renv, (message == NULL ? NULL : str_c(message)), out_message);
+		act->message = p_strdup(pool, str_c(out_message));
+		
+		/* Normalize and verify all :options addresses */					
+
 		sieve_coded_stringlist_reset(options);
 			
 		p_array_init(&act->recipients, pool, 4);