> I suggest a different aproach now: A new router option, like
> sieve_vacation_directory, named local_part_subaddress, that is available
> as variable in Exim filters and as subaddress extension in Sieve. Since
> it is a router option, no change to the address_item is needed. (Philip
> will be glad to read that.:-)
In fact, two new router options. I am not sure if this is interesting
to Exim filters, because Exim filters could contain the string expression
right away, so I did not add new variables. Opinions?
I append an experimental patch against the 4.51 snapshot, in case anybody
wants to play with it. The two new router options are sieve_subaddress
and sieve_useraddress. A common example may be:
local_part_suffix = "-*"
local_part_suffix_optional
sieve_subaddress = "${sg{$local_part_suffix}{^-}{}}"
sieve_useraddress = "$local_part"
This patch also contains the patch to allow using the vacation extension
when testing filters, but not yet code to allow the subaddress extension
in filters.
My current suggestion for the documentation is:
The extension "subaddress" is specified using the following grammar
extension.
address-part =/ ":user" / ":detail"
RFC 3598 uses the model of a user part, followed by a separator character
and the subaddress. In the real world, subaddresses are encoded in a
number different ways, so this implementation relies entirely on Exim
to set user and detail from the envelope "to" address, thus moving from
syntactic sugar to a real abstraction.
RFC 3598 allows to check other addresses than the envelope "to" address.
There is a pretty good chance to get wrong results by applying the
encoding rules for the currently processed envelope "to" address to other
addresses. If processing other addresses, this implementation always
uses the local part for :user and any test concerning :detail will be false.
Michael
----------------------------------------------------------------------
--- src/rda.c.orig 2005-03-01 11:22:08.000000000 +0100
+++ src/rda.c 2005-03-17 11:11:59.000000000 +0100
@@ -333,6 +333,8 @@
options the options bits
include_directory restrain to this directory
sieve_vacation_directory passed to sieve_interpret
+ useraddress passed to sieve_interpret
+ subaddress passed to sieve_interpret
generated where to hang generated addresses
error for error messages
eblockp for details of skipped syntax errors
@@ -348,7 +350,7 @@
static int
rda_extract(redirect_block *rdata, int options, uschar *include_directory,
- uschar *sieve_vacation_directory, address_item **generated, uschar **error,
+ uschar *sieve_vacation_directory, uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error,
error_block **eblockp, int *filtertype)
{
uschar *data;
@@ -407,7 +409,7 @@
*error = US"Sieve filtering not enabled";
return FF_ERROR;
}
- frc = sieve_interpret(data, options, sieve_vacation_directory, generated,
+ frc = sieve_interpret(data, options, sieve_vacation_directory, useraddress, subaddress, generated,
error);
}
@@ -515,6 +517,8 @@
plus ENOTDIR and EACCES handling bits
include_directory restrain :include: to this directory
sieve_vacation_directory directory passed to sieve_interpret()
+ useraddress user address string expression
+ subaddress subaddress string expression
ugid uid/gid to run under - if NULL, no change
generated where to hang generated addresses, initially NULL
error pointer for error message
@@ -541,7 +545,7 @@
int
rda_interpret(redirect_block *rdata, int options, uschar *include_directory,
- uschar *sieve_vacation_directory, ugid_block *ugid, address_item **generated,
+ uschar *sieve_vacation_directory, uschar *useraddress, uschar *subaddress, ugid_block *ugid, address_item **generated,
uschar **error, error_block **eblockp, int *filtertype, uschar *rname)
{
int fd, rc, pfd[2];
@@ -586,7 +590,7 @@
Ustrstr(data, ":include:") == NULL)) /* and there's no :include: */
{
return rda_extract(rdata, options, include_directory,
- sieve_vacation_directory, generated, error, eblockp, filtertype);
+ sieve_vacation_directory, useraddress, subaddress, generated, error, eblockp, filtertype);
}
/* We need to run the processing code in a sub-process. However, if we can
@@ -631,7 +635,7 @@
/* Now do the business */
yield = rda_extract(rdata, options, include_directory,
- sieve_vacation_directory, generated, error, eblockp, filtertype);
+ sieve_vacation_directory, useraddress, subaddress, generated, error, eblockp, filtertype);
/* Pass back whether it was a filter, and the return code and any overall
error text via the pipe. */
--- src/sieve.c.rel 2005-03-10 12:31:01.000000000 +0100
+++ src/sieve.c 2005-03-17 12:36:17.000000000 +0100
@@ -4,7 +4,7 @@
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) Michael Haardt 2003,2004 */
+/* Copyright (c) Michael Haardt 2003-2005 */
/* See the file NOTICE for conditions of use and distribution. */
/* This code was contributed by Michael Haardt. */
@@ -29,8 +29,9 @@
#undef RFC_EOL
/* Define this for development of the subaddress Sieve extension. */
-/* The code is currently broken. */
-#undef SUBADDRESS
+/* The code is currently broken, because it can not split addresses */
+/* in headers into user and detail. */
+#define SUBADDRESS
/* Define this for the vacation Sieve extension. */
#define VACATION
@@ -64,6 +65,8 @@
int vacation_ran;
#endif
uschar *vacation_directory;
+ const uschar *subaddress;
+ const uschar *useraddress;
int require_copy;
int require_iascii_numeric;
};
@@ -1723,10 +1726,8 @@
case ADDRPART_LOCALPART: part=extracted_addr; part[domain-1]='\0'; break;
case ADDRPART_DOMAIN: part=extracted_addr+domain; break;
#ifdef SUBADDRESS
- case ADDRPART_DETAIL:
- part=NULL;
+ case ADDRPART_DETAIL: part=NULL; break;
#endif
- break;
}
*end_addr = saveend;
@@ -2031,9 +2032,7 @@
case ADDRPART_LOCALPART: envelopeExpr=CUS "${local_part:$sender_address}"; break;
case ADDRPART_DOMAIN: envelopeExpr=CUS "${domain:$sender_address}"; break;
#ifdef SUBADDRESS
- case ADDRPART_DETAIL:
- envelopeExpr=CUS 0;
- break;
+ case ADDRPART_DETAIL: envelopeExpr=CUS 0; break;
#endif
}
}
@@ -2043,8 +2042,8 @@
{
case ADDRPART_ALL: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix@$domain"; break;
#ifdef SUBADDRESS
- case ADDRPART_USER: envelopeExpr=CUS "$local_part_prefix$local_part"; break;
- case ADDRPART_DETAIL: envelopeExpr=CUS "$local_part_suffix"; break;
+ case ADDRPART_USER: envelopeExpr=filter->useraddress; break;
+ case ADDRPART_DETAIL: envelopeExpr=filter->subaddress; break;
#endif
case ADDRPART_LOCALPART: envelopeExpr=CUS "$local_part_prefix$local_part$local_part_suffix"; break;
case ADDRPART_DOMAIN: envelopeExpr=CUS "$domain"; break;
@@ -2517,10 +2516,12 @@
if (filter_personal(aliases,TRUE))
{
+ if (filter_test == FTEST_NONE)
+ {
+ /* ensure oncelog directory exists; failure will be detected later */
- /* ensure oncelog directory exists; failure will be detected later */
-
- (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
+ (void)directory_make(NULL, filter->vacation_directory, 0700, FALSE);
+ }
/* build oncelog filename */
@@ -2538,88 +2539,99 @@
md5_start(&base);
md5_end(&base, key.character, key.length, digest);
for (i = 0; i < 16; i++) sprintf(CS (hexdigest+2*i), "%02X", digest[i]);
- capacity=Ustrlen(filter->vacation_directory);
- start=capacity;
- once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
- once=string_cat(once,&capacity,&start,hexdigest,33);
- once[start] = '\0';
-
- /* process subject */
-
- if (subject.length==-1)
- {
- expand_header(&subject,&str_subject);
- while (subject.length>=4 && Ustrncmp(subject.character,"Re: ",4)==0)
- {
- subject.character+=4;
- subject.length-=4;
- }
- capacity=6;
- start=6;
- subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
- subject.length=start;
- }
-
- /* add address to list of generated addresses */
-
- addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
- setflag(addr, af_pfr);
- setflag(addr, af_ignore_error);
- addr->next = *generated;
- *generated = addr;
- addr->reply = store_get(sizeof(reply_item));
- memset(addr->reply,0,sizeof(reply_item)); /* XXX */
- addr->reply->to = string_copy(sender_address);
- addr->reply->from = expand_string(US"$local_part@$domain");
- /* Allocation is larger than neccessary, but enough even for split MIME words */
- buffer_capacity=16+4*subject.length;
- buffer=store_get(buffer_capacity);
- addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
- addr->reply->oncelog=once;
- addr->reply->once_repeat=days*86400;
+ if (filter_test != FTEST_NONE)
+ {
+ debug_printf("Sieve: mail was personal, vacation file basename: %s\n", hexdigest);
+ }
+ else
+ {
+ capacity=Ustrlen(filter->vacation_directory);
+ start=capacity;
+ once=string_cat(filter->vacation_directory,&capacity,&start,US"/",1);
+ once=string_cat(once,&capacity,&start,hexdigest,33);
+ once[start] = '\0';
- /* build body and MIME headers */
+ /* process subject */
- if (reason_is_mime)
- {
- uschar *mime_body,*reason_end;
+ if (subject.length==-1)
+ {
+ expand_header(&subject,&str_subject);
+ while (subject.length>=4 && Ustrncmp(subject.character,"Re: ",4)==0)
+ {
+ subject.character+=4;
+ subject.length-=4;
+ }
+ capacity=6;
+ start=6;
+ subject.character=string_cat(US"Auto: ",&capacity,&start,subject.character,subject.length);
+ subject.length=start;
+ }
+
+ /* add address to list of generated addresses */
+
+ addr = deliver_make_addr(string_sprintf(">%.256s", sender_address), FALSE);
+ setflag(addr, af_pfr);
+ setflag(addr, af_ignore_error);
+ addr->next = *generated;
+ *generated = addr;
+ addr->reply = store_get(sizeof(reply_item));
+ memset(addr->reply,0,sizeof(reply_item)); /* XXX */
+ addr->reply->to = string_copy(sender_address);
+ addr->reply->from = expand_string(US"$local_part@$domain");
+ /* Allocation is larger than neccessary, but enough even for split MIME words */
+ buffer_capacity=16+4*subject.length;
+ buffer=store_get(buffer_capacity);
+ addr->reply->subject=parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity);
+ addr->reply->oncelog=once;
+ addr->reply->once_repeat=days*86400;
+
+ /* build body and MIME headers */
+
+ if (reason_is_mime)
+ {
+ uschar *mime_body,*reason_end;
#ifdef RFC_EOL
- static const uschar nlnl[]="\r\n\r\n";
+ static const uschar nlnl[]="\r\n\r\n";
#else
- static const uschar nlnl[]="\n\n";
+ static const uschar nlnl[]="\n\n";
#endif
- for
- (
- mime_body=reason.character,reason_end=reason.character+reason.length;
- mime_body<(reason_end-sizeof(nlnl)-1) && memcmp(mime_body,nlnl,sizeof(nlnl)-1);
- ++mime_body
- );
- capacity = 0;
- start = 0;
- addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
- addr->reply->headers[start] = '\0';
- capacity = 0;
- start = 0;
- if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=sizeof(nlnl)-1;
- else mime_body=reason_end-1;
- addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
- addr->reply->text[start] = '\0';
- }
- else
- {
- struct String qp;
+ for
+ (
+ mime_body=reason.character,reason_end=reason.character+reason.length;
+ mime_body<(reason_end-sizeof(nlnl)-1) && memcmp(mime_body,nlnl,sizeof(nlnl)-1);
+ ++mime_body
+ );
+ capacity = 0;
+ start = 0;
+ addr->reply->headers = string_cat(NULL,&capacity,&start,reason.character,mime_body-reason.character);
+ addr->reply->headers[start] = '\0';
+ capacity = 0;
+ start = 0;
+ if (mime_body+(sizeof(nlnl)-1)<reason_end) mime_body+=sizeof(nlnl)-1;
+ else mime_body=reason_end-1;
+ addr->reply->text = string_cat(NULL,&capacity,&start,mime_body,reason_end-mime_body);
+ addr->reply->text[start] = '\0';
+ }
+ else
+ {
+ struct String qp;
- capacity = 0;
- start = reason.length;
- addr->reply->headers = US"MIME-Version: 1.0\n"
- "Content-Type: text/plain;\n"
- "\tcharset=\"utf-8\"\n"
- "Content-Transfer-Encoding: quoted-printable";
- addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
+ capacity = 0;
+ start = reason.length;
+ addr->reply->headers = US"MIME-Version: 1.0\n"
+ "Content-Type: text/plain;\n"
+ "\tcharset=\"utf-8\"\n"
+ "Content-Transfer-Encoding: quoted-printable";
+ addr->reply->text = quoted_printable_encode(&reason,&qp)->character;
+ }
}
}
- }
+ else if (filter_test != FTEST_NONE)
+ {
+ debug_printf("Sieve: mail was not personal, vacation would ignore it\n");
+ }
+ }
}
#endif
else break;
@@ -2727,7 +2739,7 @@
#ifdef VACATION
else if (eq_asciicase(check,&str_vacation,0))
{
- if (filter->vacation_directory == NULL)
+ if (filter_test == FTEST_NONE && filter->vacation_directory == NULL)
{
filter->errmsg=CUS "vacation disabled";
return -1;
@@ -2767,6 +2779,8 @@
options controls whether various special things are allowed, and requests
special actions (not currently used)
sieve_vacation_directory where to store vacation "once" files
+ useraddress string expression for :user part of address
+ subaddress string expression for :subaddress part of address
generated where to hang newly-generated addresses
error where to pass back an error text
@@ -2780,7 +2794,7 @@
int
sieve_interpret(uschar *filter, int options, uschar *vacation_directory,
- address_item **generated, uschar **error)
+ uschar *useraddress, uschar *subaddress, address_item **generated, uschar **error)
{
struct Sieve sieve;
int r;
@@ -2806,6 +2820,9 @@
}
}
+sieve.useraddress = useraddress == NULL ? CUS "$local_part_prefix$local_part$local_part_suffix" : useraddress;
+sieve.subaddress = subaddress;
+
#ifdef COMPILE_SYNTAX_CHECKER
if (parse_start(&sieve,0,generated)==1)
#else
--- src/functions.h.orig 2005-03-17 11:13:08.000000000 +0100
+++ src/functions.h 2005-03-17 11:14:21.000000000 +0100
@@ -179,7 +179,7 @@
#ifdef WITH_CONTENT_SCAN
extern int recv_line(int, uschar *, int);
#endif
-extern int rda_interpret(redirect_block *, int, uschar *, uschar *, ugid_block *,
+extern int rda_interpret(redirect_block *, int, uschar *, uschar *, uschar *, uschar *, ugid_block *,
address_item **, uschar **, error_block **, int *, uschar *);
extern int rda_is_filter(const uschar *);
extern BOOL readconf_depends(driver_instance *, uschar *);
@@ -242,8 +242,8 @@
extern void sha1_end(sha1 *, const uschar *, int, uschar *);
extern void sha1_mid(sha1 *, const uschar *);
extern void sha1_start(sha1 *);
-extern int sieve_interpret(uschar *, int, uschar *, address_item **,
- uschar **);
+extern int sieve_interpret(uschar *, int, uschar *, uschar *, uschar *,
+ address_item **, uschar **);
extern void sigalrm_handler(int);
extern void smtp_closedown(uschar *);
extern int smtp_connect(host_item *, int, int, uschar *, int, BOOL);
--- src/routers/queryprogram.c.orig 2005-03-17 11:17:25.000000000 +0100
+++ src/routers/queryprogram.c 2005-03-17 11:16:53.000000000 +0100
@@ -356,6 +356,8 @@
RDO_REWRITE, /* rewrite generated addresses */
NULL, /* :include: directory not relevant */
NULL, /* sieve vacation directory not relevant */
+ NULL, /* sieve user address not relevant */
+ NULL, /* sieve subaddress not relevant */
&ugid, /* uid/gid (but not set) */
&generated, /* where to hang the results */
&(addr->message), /* where to put messages */
--- src/routers/redirect.h.orig 2005-03-17 11:24:03.000000000 +0100
+++ src/routers/redirect.h 2005-03-17 11:24:36.000000000 +0100
@@ -25,6 +25,8 @@
uschar *include_directory;
uschar *pipe_transport_name;
uschar *reply_transport_name;
+ uschar *sieve_subaddress;
+ uschar *sieve_useraddress;
uschar *sieve_vacation_directory;
uschar *syntax_errors_text;
uschar *syntax_errors_to;
--- src/routers/redirect.c.orig 2005-03-17 11:20:49.000000000 +0100
+++ src/routers/redirect.c 2005-03-17 12:12:59.000000000 +0100
@@ -97,6 +97,10 @@
(void *)offsetof(redirect_router_options_block, reply_transport_name) },
{ "rewrite", opt_bit | (RDON_REWRITE << 16),
(void *)offsetof(redirect_router_options_block, bit_options) },
+ { "sieve_subaddress", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, sieve_subaddress) },
+ { "sieve_useraddress", opt_stringptr,
+ (void *)offsetof(redirect_router_options_block, sieve_useraddress) },
{ "sieve_vacation_directory", opt_stringptr,
(void *)offsetof(redirect_router_options_block, sieve_vacation_directory) },
{ "skip_syntax_errors", opt_bool,
@@ -138,6 +142,8 @@
NULL, /* include_directory */
NULL, /* pipe_transport_name */
NULL, /* reply_transport_name */
+ NULL, /* sieve_subaddress */
+ NULL, /* sieve_useraddress */
NULL, /* sieve_vacation_directory */
NULL, /* syntax_errors_text */
NULL, /* syntax_errors_to */
@@ -607,7 +613,7 @@
}
frc = rda_interpret(&redirect, options, ob->include_directory,
- ob->sieve_vacation_directory, &ugid, &generated, &(addr->message),
+ ob->sieve_vacation_directory, ob->sieve_useraddress, ob->sieve_subaddress, &ugid, &generated, &(addr->message),
ob->skip_syntax_errors? &eblock : NULL, &filtertype,
string_sprintf("%s router (recipient is %s)", rblock->name, addr->address));
--- src/deliver.c.orig 2005-03-17 11:26:08.000000000 +0100
+++ src/deliver.c 2005-03-17 11:26:34.000000000 +0100
@@ -4639,6 +4639,8 @@
RDO_REWRITE,
NULL, /* No :include: restriction (not used in filter) */
NULL, /* No sieve vacation directory (not sieve!) */
+ NULL, /* No sieve subaddress (not sieve!) */
+ NULL, /* No sieve useraddress (not sieve!) */
&ugid, /* uid/gid data */
&addr_new, /* Where to hang generated addresses */
&filter_message, /* Where to put error message */
--- src/filtertest.c.orig 2005-03-17 11:27:23.000000000 +0100
+++ src/filtertest.c 2005-03-17 11:28:12.000000000 +0100
@@ -271,7 +271,7 @@
else
{
yield = (filter_type == FILTER_SIEVE)?
- sieve_interpret(filebuf, RDO_REWRITE, NULL, &generated, &error)
+ sieve_interpret(filebuf, RDO_REWRITE, NULL, NULL, NULL, &generated, &error)
:
filter_interpret(filebuf, RDO_REWRITE, &generated, &error);
}