Code Snippets and Patches of 2013

I am managing my email adresses with dot-qmail files. After moving to a new server in august 2013, I choosed Postfix as my MTA :)

So then, I tried Sieve for delivery to my mailboxes. But the style of the dot-qmail files was much easier to maintain. Then I tried to get my old dot-qmail files to work together with the new Postfix setup. And yes, after some patching and scripting, the local dot-qmail delivery works perfectly together with Postfix ;)

Here is a step by step guide of the setup I am using now:

  1. Create some catchall email adresses for your domains, which sould be deliverd to you local dot-qmail files. Then create one .forward file with that content in your home directory:
    
    # pipe all mail to our local qmail-local-deliver shell script...
    | ~/bin/qmail-local-deliver
    
  2. The seekablepipe script should be saved to your ~/bin directory:
    #!/bin/sh
    # source: https://www.mail-archive.com/tmda-users@tmda.net/msg06211.html
    set -e
    tmp=/tmp/seekable.$$
    exec 3<&0 4<&1 >$tmp <$tmp
    rm $tmp
    cat <&3
    exec ${1+"$@"} 1<&4 3>&- 4>&-

    seekablepipe

  3. The qmail-local-deliver script is started via the .forward file of your home directory. This script will invoke the patched qmail-local, which then delivers the mail to your Maildir folders ;)
    #!/bin/sh
    
    ######################################################################
    # Copyright © 2013 - 2014 Tino Reichardt
    #
    # This program is free software; you can redistribute it and/or modify
    # it under the terms of the GNU General Public License Version 2, as
    # published by the Free Software Foundation.
    ######################################################################
    # ctime /TR 2013-08-22
    # mtime /TR 2014-04-11 (licensing details added)
    ######################################################################
    
    # first $home/bin
    export PATH="$HOME/bin:/usr/bin:/bin"
    
    # ORIGINAL_RECIPIENT is since Postfix 2.5
    export RECIPIENT=$ORIGINAL_RECIPIENT
    
    #DOMAIN=example.org
    #HOME=/home/username
    #LOCAL=username
    #RECIPIENT=username@example.org
    #ORIGINAL_RECIPIENT=lala-lala@example2.org
    #PWD=/var/spool/postfix
    #SENDER=sender@remote-example.org
    #USER=username
    
    # USER="username" -> okay
    # SENDER -> okay
    LOCAL=`echo $RECIPIENT|cut -d@ -f1`
    DASH="-"
    EXT=$LOCAL
    DOMAIN=`echo $RECIPIENT|cut -d@ -f2`
    
    # HOME="$HOME" -> each domain gets it's own Inbox ;)
    HOME=""
    test "$DOMAIN" = "domain1.example.org"  && HOME="/home/username/mail/domain1"
    test "$DOMAIN" = "domain2.example.org"  && HOME="/home/username/mail/domain2"
    test "$DOMAIN" = "domain3.example.org"  && HOME="/home/username/mail/domain3"
    test "$DOMAIN" = "example.org"          && HOME="/home/username/mail/other"
    test "$DOMAIN" = "lala.example.org"     && HOME="/home/username/mail/lala"
    test "$DOMAIN" = "example.com"          && HOME="/home/username/mail/othercom"
    
    if [ "x$HOME" = "x" ]; then
      HOME="/home/username/mail/default"
    fi
    
    # Delivered-To is correctly created by qmail-local
    
    # you can remove headers like this, if you want:
    #grep -v '^Delivered-To: username@example.org' | \
    #grep -v '^X-Original-To:' | \
    
    # usage: qmail-local [ -nN ] -- user homedir local dash ext domain sender aliasempty
    seekablepipe qmail-local -- \
      "$USER" "$HOME" "$LOCAL" "$DASH" \
      "$EXT" "$DOMAIN" "$SENDER" ./
    e=$?
    
    # log it in the end... for debugging...
    echo "qmail-local $e \"$USER\" \"$HOME\" \"$LOCAL\" \
      \"$DASH\" \"$EXT\" \"$DOMAIN\" \"$SENDER\"  \
      ./" >> $HOME/.maillog
    
    (($e == 111)) && exit 75
    (($e == 100)) && exit 77
    exit $e
    
    ######################################################################
    # exit codes of .forward:
    # OK		0	/* successful termination */
    # USAGE		64	/* command line usage error */
    # DATAERR	65	/* data format error */
    # NOINPUT	66	/* cannot open input */
    # NOUSER	67	/* addressee unknown */
    # NOHOST	68	/* host name unknown */
    # UNAVAILABLE	69	/* service unavailable */
    # SOFTWARE	70	/* internal software error */
    # OSERR		71	/* system error (e.g., can't fork) */
    # OSFILE	72	/* critical OS file missing */
    # CANTCREAT	73	/* can't create (user) output file */
    # IOERR		74	/* input/output error */
    # TEMPFAIL	75	/* temp failure; user is invited to retry */
    # PROTOCOL	76	/* remote error in protocol */
    # NOPERM	77	/* permission denied */
    # CONFIG	78	/* configuration error */
    ######################################################################

    qmail-local-deliver

  4. Here is the small patch of qmail-local.c, which provides some little changes for getting mail forwarding to work within the dot-qmail files.
    The patch should be applied against the original qmail-1.03.tar.gz from 1997 ;)
    I hereby place this patch for qmail into the public domain.
    You are free to modify it, distribute modified versions, etc.
    
    Tino Reichardt
    
    --- qmail-local.c	2013-10-03 11:02:42.000000000 +0200
    +++ qmail-local-tr.c	2013-10-03 11:04:06.523416132 +0200
    @@ -41,6 +41,7 @@
     void temp_qmail(fn) char *fn;
     { strerr_die5x(111,"Unable to open ",fn,": ",error_str(errno),". (#4.3.0)"); }
     
    +int numforward;
     int flagdoit;
     int flag99;
     
    @@ -260,33 +261,49 @@
       }
     }
     
    -unsigned long mailforward_qp = 0;
    -
     void mailforward(recips)
     char **recips;
     {
    - struct qmail qqt;
    - char *qqx;
    - substdio ss;
    - int match;
    + const char *sendmail = "/usr/lib/sendmail.postfix";
    + int child;
    + char **args = (char **) alloc((numforward + 2 + 1) * sizeof(char *));
    + int wstat;
    + stralloc sender_sa = {0};
    + int i;
     
      if (seek_begin(0) == -1) temp_rewind();
    - substdio_fdbuf(&ss,read,0,buf,sizeof(buf));
    + if (!stralloc_copys(&sender_sa, "-f")) temp_nomem();
    + if (!stralloc_cats(&sender_sa, sender)) temp_nomem();
    + if (!stralloc_0(&sender_sa)) temp_nomem();
     
    - if (qmail_open(&qqt) == -1) temp_fork();
    - mailforward_qp = qmail_qp(&qqt);
    - qmail_put(&qqt,dtline.s,dtline.len);
    - do
    + switch(child = fork())
       {
    -   if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; }
    -   qmail_put(&qqt,messline.s,messline.len);
    +   case -1:
    +     temp_fork();
    +   case 0:
    +     /* just pipe to the sendmail of postfix ... */
    +     args[0] = sendmail; args[1] = sender_sa.s; args[numforward + 2] = 0;
    +     for (i = 0; i < numforward; i++) args[i + 2] = recips[i];
    +     sig_pipedefault();
    +     execv(*args,args);
    +     strerr_die3x(111,"Unable to run /usr/lib/sendmail.postfix: ",error_str(errno),". (#4.3.0)");
    +  }
    +
    + wait_pid(&wstat,child);
    + if (wait_crashed(wstat))
    +   temp_childcrashed();
    +
    + /* no stralloc_free() here... but mail addys are not very long */
    + /* free(recip.s); */
    +
    + switch(wait_exitcode(wstat))
    +  {
    +   case 100:
    +   case 64: case 65: case 70: case 76: case 77: case 78: case 112: _exit(100);
    +   case 0: break;
    +   case 99: flag99 = 1; break;
    +   default: _exit(111);
       }
    - while (match);
    - qmail_from(&qqt,ueo.s);
    - while (*recips) qmail_to(&qqt,*recips++);
    - qqx = qmail_close(&qqt);
    - if (!*qqx) return;
    - strerr_die3x(*qqx == 'D' ? 100 : 111,"Unable to forward message: ",qqx + 1,".");
     }
     
     void bouncexf()
    @@ -425,12 +442,6 @@
      substdio_puts(subfdoutsmall,"+");
      substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,count_program));
      substdio_puts(subfdoutsmall,"\n");
    - if (mailforward_qp)
    -  {
    -   substdio_puts(subfdoutsmall,"qp ");
    -   substdio_put(subfdoutsmall,count_buf,fmt_ulong(count_buf,mailforward_qp));
    -   substdio_puts(subfdoutsmall,"\n");
    -  }
      substdio_flush(subfdoutsmall);
     }
     
    @@ -453,7 +464,6 @@
      int j;
      int k;
      int fd;
    - int numforward;
      char **recips;
      datetime_sec starttime;
      int flagforwardonly;

    qmail-local.c.diff

If there are questions, drop me an email and I'll try to explain it a bit better :)

DOWNLOAD

Last modified on 2013-10-03 at 11:29