<<< Date Index >>>     <<< Thread Index >>>

sendbox patch, again



Hi all,

This is a repost of my sendbox patch, trivially merged to current
mercurial.  I have posted this before and it hasn't been committed,
I think mostly because it's not a feature for the masses.  Nonetheless
it's a worthwhile feature, has been tested in my own heavy-use
environment for at least a year, so I'm hoping it will be
reconsidered.  Frankly I'm sick of maintaining my own mutt packages on
all the machines where I work, and this patch isn't very big...

The purpose of this patch is to make it possible to send mail using
Courier IMAP's Outbox feature.  I avoided calling this "outbox",
though, because that term is already used in the mutt sources.
Besides, I think sendbox is more explicit.

The Outbox feature works like this: To send mail, it's possible to
nominate a mailbox that will send mails which are saved to it.  This
mailbox acts like any other mailbox otherwise, including storing the
mails that are saved there.

This is desirable for numerous reasons, but here are the most
significant:

- on a slow connection, this allows the mail to be fcc'd and sent with
  the same transmission, rather than hitting the wire twice.

- sending mail from one's mailhost is nice because the mail originates
  from a good server, so your From address is valid and unquestioned

- single channel of mail transferral, easier network configuration and
  dealing with firewalls

- single point of authentication instead of two

- much easier to switch outgoing servers this way, instead of needing
  to reconfigure sendmail to use different outgoing servers

So this patch adds two settings: sendbox and use_sendbox.  Sendbox
specifies a mailbox to which sent messages should be saved, and
use_sendbox instructs mutt to use sendbox instead of sendmail.  
For example:

    set sendbox=imaps://my.server.com/INBOX.Outbox
    set use_sendbox=yes

Personally I set sendbox using send2-hooks so that it can be based on
whichever From address I'm using for the mail.

Regards,
Aron

P.S. I'm working on a Dovecot Outbox plugin so that this works with
more than just Courier... if anybody's interested, I can let you know
when that's ready for testing.

----------------------------------------------------------------------
Add sendbox feature

This patch lets mutt use Courier IMAP's Outbox feature.  It adds two
settings: sendbox and use_sendbox.  Sendbox specifies a mailbox to
which sent messages should be saved, and use_sendbox instructs mutt to
use sendbox instead of sendmail.

Signed-off-by: Aron Griffis <aron@xxxxxx>

diff -r 5c635c9b5982 compose.c
--- a/compose.c Fri Nov 30 10:29:49 2007 +0100
+++ b/compose.c Tue Dec 04 18:26:01 2007 -0500
@@ -1207,7 +1207,7 @@ int mutt_compose_menu (HEADER *msg,   /*
          if (msg->content->next)
            msg->content = mutt_make_multipart (msg->content);
 
-         if (mutt_write_fcc (NONULL (fname), msg, NULL, 1, NULL) < 0)
+         if (mutt_write_fcc (NONULL (fname), msg, NULL, 1, NULL, 0) < 0)
            msg->content = mutt_remove_multipart (msg->content);
          else
            mutt_message _("Message written.");
diff -r 5c635c9b5982 copy.c
--- a/copy.c    Fri Nov 30 10:29:49 2007 +0100
+++ b/copy.c    Tue Dec 04 18:26:01 2007 -0500
@@ -65,7 +65,8 @@ mutt_copy_hdr (FILE *in, FILE *out, LOFF
   buf[0] = '\n';
   buf[1] = 0;
 
-  if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | 
CH_WEED_DELIVERED)) == 0)
+  if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | 
+               CH_WEED_DELIVERED | CH_WEED_RESENT)) == 0)
   {
     /* Without these flags to complicate things
      * we can do a more efficient line to line copying
@@ -193,6 +194,9 @@ mutt_copy_hdr (FILE *in, FILE *out, LOFF
        continue;
       if ((flags & CH_WEED_DELIVERED) &&
          ascii_strncasecmp ("Delivered-To:", buf, 13) == 0)
+       continue;
+      if ((flags & CH_WEED_RESENT) &&
+         ascii_strncasecmp ("Resent-To:", buf, 10) == 0)
        continue;
       if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
          (ascii_strncasecmp ("Status:", buf, 7) == 0 ||
diff -r 5c635c9b5982 globals.h
--- a/globals.h Fri Nov 30 10:29:49 2007 +0100
+++ b/globals.h Tue Dec 04 18:26:01 2007 -0500
@@ -112,6 +112,7 @@ WHERE char *PrintCmd;
 WHERE char *PrintCmd;
 WHERE char *QueryCmd;
 WHERE char *Realname;
+WHERE char *Sendbox;
 WHERE char *SendCharset;
 WHERE char *Sendmail;
 WHERE char *Shell;
diff -r 5c635c9b5982 init.h
--- a/init.h    Fri Nov 30 10:29:49 2007 +0100
+++ b/init.h    Tue Dec 04 18:26:01 2007 -0500
@@ -2509,12 +2509,24 @@ struct option_t MuttVars[] = {
   ** In case the text cannot be converted into one of these exactly,
   ** mutt uses ``$$charset'' as a fallback.
   */
+  { "sendbox",         DT_PATH,  R_NONE, UL &Sendbox, 0 },
+  /*
+  ** .pp
+  ** Specifies a special mailbox that will
+  ** \fBsend\fP mail when written, honored when \fIuse_sendbox\fP is \fIset\fP.
+  ** To make use of this, you probably want a Courier IMAP server configured 
for
+  ** sending, see 
+  ** http://www.inter7.com/courierimap/INSTALL.html#imapsend
+  */
   { "sendmail",                DT_PATH, R_NONE, UL &Sendmail, UL SENDMAIL " 
-oem -oi" },
   /*
   ** .pp
   ** Specifies the program and arguments used to deliver mail sent by Mutt.
   ** Mutt expects that the specified program interprets additional
   ** arguments as recipient addresses.
+  ** .pp
+  ** This variable is ignored in favor of \fIsendbox\fP if \fIuse_sendbox\fP
+  ** is \fIset\fP.
   */
   { "sendmail_wait",   DT_NUM,  R_NONE, UL &SendmailWait, 0 },
   /*
@@ -2997,6 +3009,12 @@ struct option_t MuttVars[] = {
   ** Normally, the default should work.
   */
 #endif /* HAVE_GETADDRINFO */
+  { "use_sendbox",     DT_BOOL,  R_NONE, OPTUSESENDBOX, 0 },
+  /*
+  ** .pp
+  ** When \fIset\fP, mutt sends mail using \fIsendbox\fP instead
+  ** of \fIsendmail\fP.
+  */
   { "user_agent",      DT_BOOL, R_NONE, OPTXMAILER, 1},
   /*
   ** .pp
diff -r 5c635c9b5982 mutt.h
--- a/mutt.h    Fri Nov 30 10:29:49 2007 +0100
+++ b/mutt.h    Tue Dec 04 18:26:01 2007 -0500
@@ -94,6 +94,7 @@
 #define CH_NOQFROM     (1<<15) /* give CH_FROM precedence over CH_WEED? */
 #define CH_UPDATE_IRT  (1<<16) /* update In-Reply-To: */
 #define CH_UPDATE_REFS (1<<17) /* update References: */
+#define CH_WEED_RESENT (1<<18) /* weed Resent-To: header */
 
 /* flags for mutt_enter_string() */
 #define  M_ALIAS   1      /* do alias "completion" by calling up the 
alias-menu */
@@ -451,6 +452,7 @@ enum
 #ifdef HAVE_GETADDRINFO
   OPTUSEIPV6,
 #endif
+  OPTUSESENDBOX,
   OPTWAITKEY,
   OPTWEED,
   OPTWRAP,
diff -r 5c635c9b5982 protos.h
--- a/protos.h  Fri Nov 30 10:29:49 2007 +0100
+++ b/protos.h  Tue Dec 04 18:26:01 2007 -0500
@@ -366,7 +366,7 @@ void mutt_update_num_postponed (void);
 void mutt_update_num_postponed (void);
 int mutt_wait_filter (pid_t);
 int mutt_which_case (const char *);
-int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int, 
char *);
+int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int, 
char *, int);
 int mutt_write_mime_body (BODY *, FILE *);
 int mutt_write_mime_header (BODY *, FILE *);
 int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const 
char *pfx, int wraplen);
diff -r 5c635c9b5982 send.c
--- a/send.c    Fri Nov 30 10:29:49 2007 +0100
+++ b/send.c    Tue Dec 04 18:26:01 2007 -0500
@@ -973,6 +973,10 @@ static int send_message (HEADER *msg)
   short old_write_bcc;
 #endif
   
+  /* some imap servers can send mail by saving to a special folder */
+  if (option (OPTUSESENDBOX))
+    return mutt_write_fcc (NONULL (Sendbox), msg, NULL, 0, NULL, 1);
+
   /* Write out the message in MIME form. */
   mutt_mktemp (tempfile);
   if ((tempfp = safe_fopen (tempfile, "w")) == NULL)
@@ -1529,7 +1533,7 @@ main_loop:
       mutt_prepare_envelope (msg->env, 0);
       mutt_env_to_idna (msg->env, NULL, NULL); /* Handle bad IDNAs the next 
time. */
 
-      if (!Postponed || mutt_write_fcc (NONULL (Postponed), msg, (cur && 
(flags & SENDREPLY)) ? cur->env->message_id : NULL, 1, fcc) < 0)
+      if (!Postponed || mutt_write_fcc (NONULL (Postponed), msg, (cur && 
(flags & SENDREPLY)) ? cur->env->message_id : NULL, 1, fcc, 0) < 0)
       {
        msg->content = mutt_remove_multipart (msg->content);
        decode_descriptions (msg->content);
@@ -1709,7 +1713,7 @@ full_fcc:
        * message was first postponed.
        */
       msg->received = time (NULL);
-      if (mutt_write_fcc (fcc, msg, NULL, 0, NULL) == -1)
+      if (mutt_write_fcc (fcc, msg, NULL, 0, NULL, 0) == -1)
       {
        /*
         * Error writing FCC, we should abort sending.
diff -r 5c635c9b5982 sendlib.c
--- a/sendlib.c Fri Nov 30 10:29:49 2007 +0100
+++ b/sendlib.c Tue Dec 04 18:26:01 2007 -0500
@@ -2376,7 +2376,9 @@ static int _mutt_bounce_message (FILE *f
   int i, ret = 0;
   FILE *f;
   char date[SHORT_STRING], tempfile[_POSIX_PATH_MAX];
-  MESSAGE *msg = NULL;
+  MESSAGE *msg = NULL, *smsg = NULL;
+  CONTEXT sctx;
+  int ch_flags;
 
   if (!h)
   {
@@ -2393,25 +2395,67 @@ static int _mutt_bounce_message (FILE *f
 
   if (!fp) fp = msg->fp;
 
-  mutt_mktemp (tempfile);
-  if ((f = safe_fopen (tempfile, "w")) != NULL)
-  {
-    int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
-    
-    if (!option (OPTBOUNCEDELIVERED))
-      ch_flags |= CH_WEED_DELIVERED;
-    
-    fseeko (fp, h->offset, 0);
-    fprintf (f, "Resent-From: %s", resent_from);
-    fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
-    fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
-    fputs ("Resent-To: ", f);
-    mutt_write_address_list (to, f, 11, 0);
-    mutt_copy_header (fp, h, f, ch_flags, NULL);
-    fputc ('\n', f);
-    mutt_copy_bytes (fp, f, h->content->length);
+  ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
+  if (!option (OPTBOUNCEDELIVERED))
+    ch_flags |= CH_WEED_DELIVERED;
+
+  if (option (OPTUSESENDBOX)) {
+    /* some imap servers can send mail by saving to a special folder */
+    if (mx_open_mailbox (NONULL (Sendbox), M_APPEND | M_QUIET, &sctx) == NULL)
+    {
+      dprint (1, (debugfile, "_mutt_bounce_message(): unable to open mailbox 
%s"
+                 "in append-mode, aborting.\n", NONULL (Sendbox)));
+      ret = -1;
+      goto close_msg;
+    }
+    if ((smsg = mx_open_new_message (&sctx, h, M_ADD_FROM)) == NULL)
+    {
+      dprint (1, (debugfile, "_mutt_bounce_message(): mx_open_new_message "
+                 "failed in %s, aborting.\n", NONULL (Sendbox)));
+      mx_close_mailbox (&sctx, NULL);
+      ret = -1;
+      goto close_msg;
+    }
+    f = smsg->fp;
+
+    /* when using sendbox, Resent-To: headers will be unioned by the MTA to
+     * determine the recipient, so weed any old ones
+     */
+    ch_flags |= CH_WEED_RESENT;
+  } else {
+    /* create a temporary message which is the original message with Resent
+     * header fields prepended
+     */
+    mutt_mktemp (tempfile);
+    if ((f = safe_fopen (tempfile, "w")) == NULL) {
+      mutt_perror (tempfile);
+      ret = -1;
+      goto close_msg;
+    }
+  }
+
+  /* prepend the Resent header fields */
+  fprintf (f, "Resent-From: %s", resent_from);
+  fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
+  fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
+  fputs ("Resent-To: ", f);
+  mutt_write_address_list (to, f, 11, 0);
+
+  /* copy original message */
+  fseeko (fp, h->offset, 0);
+  mutt_copy_header (fp, h, f, ch_flags, NULL);
+  fputc ('\n', f);
+  mutt_copy_bytes (fp, f, h->content->length);
+
+  if (smsg) {
+    /* complete sending via Sendbox */
+    if (mx_commit_message (smsg, &sctx) != 0)
+       ret = -1;
+    mx_close_message (&smsg);
+    mx_close_mailbox (&sctx, NULL);
+  } else {
+    /* complete normal send */
     fclose (f);
-
 #if USE_SMTP
     if (SmtpUrl)
       ret = mutt_smtp_send (env_from, to, NULL, NULL, tempfile,
@@ -2422,6 +2466,7 @@ static int _mutt_bounce_message (FILE *f
                                h->content->encoding == ENC8BIT);
   }
 
+close_msg:
   if (msg)
     mx_close_message (&msg);
 
@@ -2517,7 +2562,8 @@ static void set_noconv_flags (BODY *b, s
   }
 }
 
-int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int 
post, char *fcc)
+int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int 
post, 
+                   char *fcc, int send)
 {
   CONTEXT f;
   MESSAGE *msg;
@@ -2549,7 +2595,7 @@ int mutt_write_fcc (const char *path, HE
     }
   }
 
-  hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
+  hdr->read = !(post || send); /* make sure to put it in the `cur' directory 
(maildir) */
   if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
   {
     mx_close_mailbox (&f, NULL);
@@ -2560,6 +2606,10 @@ int mutt_write_fcc (const char *path, HE
    * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header() 
    * */
   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 
0);
+
+  /* set RO flag if this is not a send or post operation */
+  if (hdr->read)
+    fprintf (msg->fp, "Status: RO\n");
 
   /* (postponment) if this was a reply of some sort, <msgid> contians the
    * Message-ID: of message replied to.  Save it using a special X-Mutt-
@@ -2575,9 +2625,6 @@ int mutt_write_fcc (const char *path, HE
    */
   if (post && fcc)
     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
-  fprintf (msg->fp, "Status: RO\n");
-
-
 
   /* (postponment) if the mail is to be signed or encrypted, save this info */
   if ((WithCrypto & APPLICATION_PGP)
@@ -2696,3 +2743,5 @@ int mutt_write_fcc (const char *path, HE
   
   return r;
 }
+
+/* vim: set sw=2: */