+#ifdef ENOTIFY
+ else if (parse_identifier(filter,CUS "notify"))
+ {
+ /*
+ notify-command = "notify" { notify-options } <method: string> ";"
+ notify-options = [":from" string]
+ [":importance" <"1" / "2" / "3">]
+ [":options" 1*(string-list / number)]
+ [":message" string]
+ */
+
+ int m;
+ struct String from;
+ struct String importance;
+ struct String message;
+ struct String method;
+ struct Notification *already;
+ string_item *recipient;
+ struct String header;
+ struct String subject;
+ struct String body;
+ uschar *envelope_from;
+ struct String auto_submitted_value;
+ uschar *auto_submitted_def;
+
+ if (!filter->require_enotify)
+ {
+ filter->errmsg=CUS "missing previous require \"enotify\";";
+ return -1;
+ }
+ from.character=(uschar*)0;
+ from.length=-1;
+ importance.character=(uschar*)0;
+ importance.length=-1;
+ message.character=(uschar*)0;
+ message.length=-1;
+ recipient=NULL;
+ header.length=-1;
+ header.character=(uschar*)0;
+ subject.length=-1;
+ subject.character=(uschar*)0;
+ body.length=-1;
+ body.character=(uschar*)0;
+ envelope_from=(sender_address && sender_address[0]) ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : US "";
+ for (;;)
+ {
+ if (parse_white(filter)==-1) return -1;
+ if (parse_identifier(filter,CUS ":from")==1)
+ {
+ if (parse_white(filter)==-1) return -1;
+ if ((m=parse_string(filter,&from))!=1)
+ {
+ if (m==0) filter->errmsg=CUS "from string expected";
+ return -1;
+ }
+ }
+ else if (parse_identifier(filter,CUS ":importance")==1)
+ {
+ if (parse_white(filter)==-1) return -1;
+ if ((m=parse_string(filter,&importance))!=1)
+ {
+ if (m==0) filter->errmsg=CUS "importance string expected";
+ return -1;
+ }
+ if (importance.length!=1 || importance.character[0]<'1' || importance.character[0]>'3')
+ {
+ filter->errmsg=CUS "invalid importance";
+ return -1;
+ }
+ }
+ else if (parse_identifier(filter,CUS ":options")==1)
+ {
+ if (parse_white(filter)==-1) return -1;
+ }
+ else if (parse_identifier(filter,CUS ":message")==1)
+ {
+ if (parse_white(filter)==-1) return -1;
+ if ((m=parse_string(filter,&message))!=1)
+ {
+ if (m==0) filter->errmsg=CUS "message string expected";
+ return -1;
+ }
+ }
+ else break;
+ }
+ if (parse_white(filter)==-1) return -1;
+ if ((m=parse_string(filter,&method))!=1)
+ {
+ if (m==0) filter->errmsg=CUS "missing method string";
+ return -1;
+ }
+ if (parse_semicolon(filter)==-1) return -1;
+ if (parse_mailto_uri(filter,method.character,&recipient,&header,&subject,&body)!=1)
+ return -1;
+ if (exec)
+ {
+ if (message.length==-1) message=subject;
+ if (message.length==-1) expand_header(&message,&str_subject);
+ expand_header(&auto_submitted_value,&str_auto_submitted);
+ auto_submitted_def=expand_string(string_sprintf("${if def:header_auto-submitted {true}{false}}"));
+ if (auto_submitted_value.character == NULL || auto_submitted_def == NULL)
+ {
+ filter->errmsg=CUS "header string expansion failed";
+ return -1;
+ }
+ if (Ustrcmp(auto_submitted_def,"true")!=0 || Ustrcmp(auto_submitted_value.character,"no")==0)
+ {
+ for (already=filter->notified; already; already=already->next)
+ {
+ if (already->method.length==method.length
+ && (method.length==-1 || Ustrcmp(already->method.character,method.character)==0)
+ && already->importance.length==importance.length
+ && (importance.length==-1 || Ustrcmp(already->importance.character,importance.character)==0)
+ && already->message.length==message.length
+ && (message.length==-1 || Ustrcmp(already->message.character,message.character)==0))
+ break;
+ }
+ if (already==(struct Notification*)0)
+ /* New notification, process it */
+ {
+ struct Notification *sent;
+ sent=store_get(sizeof(struct Notification));
+ sent->method=method;
+ sent->importance=importance;
+ sent->message=message;
+ sent->next=filter->notified;
+ filter->notified=sent;
+ #ifndef COMPILE_SYNTAX_CHECKER
+ if (filter_test == FTEST_NONE)
+ {
+ string_item *p;
+ int pid,fd;
+
+ if ((pid = child_open_exim2(&fd,envelope_from,envelope_from))>=1)
+ {
+ FILE *f;
+ uschar *buffer;
+ int buffer_capacity;
+
+ f = fdopen(fd, "wb");
+ fprintf(f,"From: %s\n",from.length==-1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character);
+ for (p=recipient; p; p=p->next) fprintf(f,"To: %s\n",p->text);
+ fprintf(f,"Auto-Submitted: auto-notified; %s\n",filter->enotify_mailto_owner);
+ if (header.length>0) fprintf(f,"%s",header.character);
+ if (message.length==-1)
+ {
+ message.character=US"Notification";
+ message.length=Ustrlen(message.character);
+ }
+ /* Allocation is larger than neccessary, but enough even for split MIME words */
+ buffer_capacity=32+4*message.length;
+ buffer=store_get(buffer_capacity);
+ if (message.length!=-1) fprintf(f,"Subject: %s\n",parse_quote_2047(message.character, message.length, US"utf-8", buffer, buffer_capacity, TRUE));
+ fprintf(f,"\n");
+ if (body.length>0) fprintf(f,"%s\n",body.character);
+ fflush(f);
+ (void)fclose(f);
+ (void)child_close(pid, 0);
+ }
+ }
+ if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
+ {
+ debug_printf("Notification to `%s': '%s'.\n",method.character,message.length!=-1 ? message.character : CUS "");
+ }
+#endif
+ }
+ else
+ {
+ if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
+ {
+ debug_printf("Repeated notification to `%s' ignored.\n",method.character);
+ }
+ }
+ }
+ else
+ {
+ if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
+ {
+ debug_printf("Ignoring notification, triggering message contains Auto-submitted: field.\n");
+ }
+ }
+ }
+ }
+#endif