Re: [exim-dev] Saving the variable part of local part suffix…

Top Page
Delete this message
Reply to this message
Author: Michael Haardt
Date:  
To: exim-dev
Subject: Re: [exim-dev] Saving the variable part of local part suffixes
> 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);
   }