[Dovecot] Procmail patch for dovecot delivery

Gerhard Wiesinger lists at wiesinger.com
Wed Jul 5 22:07:22 EEST 2006


Hello!

As discussed in the previous thread about "Dovecot deliver logging 
problem and procmail" I have made a small patch for procmail to deliver 
through dovecot's deliver program (or any other delivery program).

So Procmail does not write the mailboxes directly any more. So delivery is 
done through pipes and an external program which can deliver the files.

Since dovecot's deliver program can not handle absolute pathes, theses 
files are handled directly by procmail. See the configuration options 
below for details.

Also INBOX handling needs special handling because this is handled through 
the /var/spool/mail/$LOGNAME folder which dovecot deliver can not handle. 
So this is mapped (when configured) to the INBOX folder.

For debugging purposes you can add the VERBOSE=on variable. A sample 
configuration for dovecot usage with documentation is discussed below. 
Some other delivery agents (e.g. cyrus) might need other options.

Keep in mind that the environment expansions with $ in the 
DELIVER_PROGRAM must be escaped with a backslash. BOXNAME and RECIPIENT 
are 2 new variables which can be used.

Status of the patch is that it "works for me". There is no other 
guarantee ...

So I hope you like the patch. Maybe someone can make a short review of the 
code changes.

Feel free for any feedback.

Ciao,
Gerhard

############################################################################

#Set on when debugging
VERBOSE=on

#Replace `mail' with your mail directory (Pine uses mail, Elm uses Mail)
MAILDIR=$HOME/Mail

#Directory for storing procmail log and rc files
PMDIR=$HOME/.procmail

LOGFILE=$PMDIR/log

##############################################################################
#
# Alternate delivery options through external program
#
##############################################################################

#######################
# Options for Mailboxes
#######################
# Possibilites for Mailboxes:
# Mailbox is INBOX
#  1.) Deliver through mbox
#  2.) Deliver through external delivery program
# Mailbox has an absolute path
#  1.) Deliver through mbox
#  2.) Deliver through external delivery program

###############
# INBOX options
###############
# When set, delivers the INBOX throuh the external program
# When not set, absolute path mailboxes are delivered through procmail.
DELIVER_INBOX=yes
# When set and the mailbox is the INBOX (e.g. /var/spool/mail/$LOGNAME)
# it is replaced with a custom string (default INBOX)
DELIVER_REPLACE_INBOX_NAME=yes
# When DELIVER_REPLACE_INBOX_NAME is set the mailbox name is replaced with 
# INBOX (default) or when DELIVER_NEW_INBOX_NAME is set, this name is used
DELIVER_NEW_INBOX_NAME=INBOX

#######################
# Absoulte Path options
#######################
# When set, absolute path mailboxes are delivered through the external program
# When not set, absolute path mailboxes are delivered through procmail.
#DELIVER_ABSOLUTE_PATH=yes

##################
# Delivery options
##################
# Do not lock anything even the rule has a lock assigned
DELIVER_PIPE_NO_LOCK=yes
# Deliver in raw mode even the rule flag states that it should be delivered in raw mode
DELIVER_RAW_MODE=yes
# When set, remove the first From line of the mbox file through the external program
DELIVER_REMOVE_FROM=yes

################
# Global options
################
# When set and the mail ends in a folder it is piped through the program
# Global Switch for enabling alternate delivery method
# All environment variables can be used with \$NAME
# \$BOXNAME is the name of the mailbox (maybe converted to the rules discussed above)
# \$RECIPIENT is the recipient name, typically the user name
DELIVER_PROGRAM="/usr/libexec/dovecot/deliver -m \$BOXNAME -d \$RECIPIENT -c /etc/dovecot.conf"

:0
$DEFAULT
-------------- next part --------------
diff -Nur procmail-3.22-orig/src/mailfold.c procmail-3.22/src/mailfold.c
--- procmail-3.22-orig/src/mailfold.c	2001-09-11 06:58:34.000000000 +0200
+++ procmail-3.22/src/mailfold.c	2006-07-05 20:32:22.000000000 +0200
@@ -191,9 +191,107 @@
   return opena(buf);
 }
 
-int writefolder(boxname,linkfolder,source,len,ignwerr,dolock)
- char*boxname,*linkfolder;const char*source;long len;const int ignwerr,dolock;
+int isyes(const char* s)
+{
+  int result = 0;
+  char* env = getenv(s);
+  if (env)
+  {
+    result = !strcmp(env, "yes");
+  }
+  return result;
+}
+
+int writefolder(boxname,linkfolder,source,len,ignwerr,dolock, trydeliver)
+ char*boxname,*linkfolder;const char*source;long len;const int ignwerr,dolock, trydeliver;
 { char*chp,*chp2;mode_t mode;int fd,type;
+
+// GW:
+  char* program = getenv("DELIVER_PROGRAM");
+  char* s_org_mail = getenv("ORGMAIL");
+
+  int deliver_inbox = isyes("DELIVER_INBOX");
+  int deliver_absolute_path = isyes("DELIVER_ABSOLUTE_PATH");
+  int isabsolute = boxname[0] == '/';
+  int isinbox = 0;
+  if (s_org_mail) isinbox = !strcmp(s_org_mail, boxname);
+
+  if (trydeliver && program && ((!isinbox && !isabsolute) || (isinbox && deliver_inbox) || (isabsolute && deliver_absolute_path)) )
+  {
+    char slen[16];
+    int oldrawnonl;
+    int retval;
+    size_t str_len;
+    char* tmp;
+
+    int deliver_raw_mode = isyes("DELIVER_RAW_MODE");
+    int deliver_remove_from = isyes("DELIVER_REMOVE_FROM");
+    int deliver_replace_inbox_name = isyes("DELIVER_REPLACE_INBOX_NAME");
+
+    char* s_deliver_new_inbox_name = getenv("DELIVER_NEW_INBOX_NAME");
+    char* recipient = getenv("RECIPIENT");
+    char* inbox_name = "INBOX";
+
+    if (!recipient)
+    {
+      yell("NO RECIPIENT", "");
+    }
+    else
+    {
+      yell("RECIPIENT", recipient);
+    }
+
+    if (isinbox && deliver_replace_inbox_name)
+    {
+      if (s_deliver_new_inbox_name)
+      {
+        inbox_name = s_deliver_new_inbox_name;
+      }
+      boxname = inbox_name;
+    }
+
+    yell("boxname=", boxname);
+    yell("deliver through external program before expansion", program);
+
+#define BOXNAME "BOXNAME"
+    // put it into environment
+    str_len = STRLEN(BOXNAME) + 2 + strlen(boxname);
+    strcpy(tmp=malloc(str_len),BOXNAME);
+    strlcat(tmp, "=", str_len);
+    strlcat(tmp, boxname, str_len);
+    sputenv(tmp);
+    free(tmp);
+
+    // sprintf(slen, "len=%i", (int)len);
+    // yell("slen=", slen);
+
+    if (deliver_remove_from)
+    {
+      if(source==themail.p)
+        source=skipFrom_(source,&len); /* skip leading From_? */
+    }
+
+    // sprintf(slen, "len=%i", (int)len);
+    // yell("slen=", slen);
+
+    metaparse(program);
+
+    // Remove environment
+    sputenv(BOXNAME);
+
+    inittmout(buf);
+    oldrawnonl = rawnonl;
+    
+    if (deliver_raw_mode)
+    {
+        rawnonl = 1; /* raw mode */
+    }
+    retval = !pipin(buf, source, len, 1);   /* regular program */
+    rawnonl = oldrawnonl;
+
+    return retval;
+  }
+
   if(*boxname=='|'&&(!linkfolder||linkfolder==Tmnate))
    { setlastfolder(boxname);
      fd=rdup(Deliverymode==2?STDOUT:savstdout);
diff -Nur procmail-3.22-orig/src/mailfold.h procmail-3.22/src/mailfold.h
--- procmail-3.22-orig/src/mailfold.h	2000-12-31 07:08:34.000000000 +0100
+++ procmail-3.22/src/mailfold.h	2006-07-03 22:56:24.000000000 +0200
@@ -4,7 +4,7 @@
  dump P((const int s,const int type,const char*source,long len));
 int
  writefolder P((char*boxname,char*linkfolder,const char*source,long len,
-  const int ignwerr,const int dolock));
+  const int ignwerr,const int dolock, const int trydeliver));
 void
  logabstract P((const char*const lstfolder)),
  concon P((const int ch)),
diff -Nur procmail-3.22-orig/src/procmail.c procmail-3.22/src/procmail.c
--- procmail-3.22-orig/src/procmail.c	2001-09-11 06:59:14.000000000 +0200
+++ procmail-3.22/src/procmail.c	2006-07-04 20:12:35.000000000 +0200
@@ -306,7 +306,22 @@
 	 }
 	gargv=argv+argc;			 /* save it for nextrcfile() */
 	if(Deliverymode)	/* try recipient without changing case first */
-	 { if(!(pass=auth_finduser(chp2,-1)))	    /* chp2 is the recipient */
+	 {
+           // GW
+           size_t str_len;
+           char* tmp;
+
+#define RECIPIENT "RECIPIENT"
+
+           // put it into environment
+           str_len = STRLEN(RECIPIENT) + 2 + strlen(chp2);
+           strcpy(tmp=malloc(str_len),RECIPIENT);
+           strlcat(tmp, "=", str_len);
+           strlcat(tmp, chp2, str_len);
+           sputenv(tmp);
+           free(tmp);
+
+           if(!(pass=auth_finduser(chp2,-1)))	    /* chp2 is the recipient */
 	    { static const char unkuser[]="Unknown user";
 	      nlog(unkuser);logqnl(chp2);syslog(LOG_ERR,slogstr,unkuser,chp2);
 	      return EX_NOUSER;			/* we don't handle strangers */
@@ -498,12 +513,12 @@
 	setuid(uid);			   /* make sure we have enough space */
 	if(linebuf<(len=strlen(chp)+strlen(lockext)+UNIQnamelen))
 	   allocbuffers(linebuf=len,1);	   /* to perform the lock & delivery */
-	if(writefolder(chp,(char*)0,themail.p,filled,0,1))	  /* default */
+	if(writefolder(chp,(char*)0,themail.p,filled,0,1,1))	  /* default */
 	   succeed=1;
       }						       /* if all else failed */
      if(!succeed&&*(chp2=(char*)tgetenv(orgmail))&&strcmp(chp2,chp))
       { rawnonl=0;
-	if(writefolder(chp2,(char*)0,themail.p,filled,0,0))   /* don't panic */
+	if(writefolder(chp2,(char*)0,themail.p,filled,0,0,0))   /* don't panic */
 	   succeed=1;				      /* try the last resort */
       }
      if(succeed)				     /* should we panic now? */
@@ -876,14 +891,29 @@
 	      goto setlsucc;
 	    }
 	   if(i)
-	    { if(ofiltflag)	       /* protect who use bogus filter-flags */
+	    {
+              char* deliver_pipe_no_lock = 0;
+              int dolock = 1;
+
+              if(ofiltflag)	       /* protect who use bogus filter-flags */
 		 startchar=themail.p,tobesent=filled;	    /* whole message */
 tostdout:     rawnonl=flags[RAW_NONL];
-	      if(locknext)		     /* write to a file or directory */
+              // GW
+              deliver_pipe_no_lock = getenv("DELIVER_PIPE_NO_LOCK");
+              if (deliver_pipe_no_lock)
+              {
+                if (!strcmp(deliver_pipe_no_lock, "yes"))
+                {
+                  yell("disable deliver locking", "DELIVER_PIPE_NO_LOCK=yes");
+                  dolock = 0;
+                }
+              }
+
+	      if(locknext && dolock)		     /* write to a file or directory */
 		 lcllock(tolock,buf);
 	      inittmout(buf);		  /* to break messed-up kernel locks */
 	      if(writefolder(buf,strchr(buf,'\0')+1,startchar,tobesent,
-		  ignwerr,0)&&
+		  ignwerr,0,1)&&
 		 (succeed=1,!flags[CONTINUE]))
 frmailed:      { if(ifstack.vals)
 		    free(ifstack.vals);


More information about the dovecot mailing list