commit-mailutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Mailutils branch, master, updated. release-2.2-620-g8ad4747


From: Sergey Poznyakoff
Subject: [SCM] GNU Mailutils branch, master, updated. release-2.2-620-g8ad4747
Date: Wed, 18 Jul 2012 20:16:50 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Mailutils".

http://git.savannah.gnu.org/cgit/mailutils.git/commit/?id=8ad474712f6758cdff637c5391867706957dfd74

The branch, master has been updated
       via  8ad474712f6758cdff637c5391867706957dfd74 (commit)
      from  ca45a7aad841db4231bdd598e9a977a36dbf90c3 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 8ad474712f6758cdff637c5391867706957dfd74
Author: Sergey Poznyakoff <address@hidden>
Date:   Wed Jul 18 23:02:14 2012 +0300

    Implement --attach option in mail; fix dead.letter functionality
    
    * NEWS: Update.
    * doc/imprimatur: Upgrade.
    * libmailutils/mime/attachment.c (mu_message_create_attachment): Bugfixes.
    * mail/mail.c: New options --attach, --content-type and
    --encoding.
    * mail/mail.h (default_encoding, default_content_type): New externs.
    (send_attach_file): New proto.
    * mail/send.c (send_attach_file): New function.
    (save_dead_message_env): New function.
    (save_dead_message): Rewrite.
    (mail_send0): Attach files, if requested.

-----------------------------------------------------------------------

Summary of changes:
 NEWS                           |   42 +++++-
 doc/imprimatur                 |    2 +-
 libmailutils/mime/attachment.c |    9 +-
 mail/mail.c                    |   26 +++-
 mail/mail.h                    |    3 +
 mail/send.c                    |  356 +++++++++++++++++++++++++++++++++++++---
 6 files changed, 409 insertions(+), 29 deletions(-)

diff --git a/NEWS b/NEWS
index 4017176..8cac522 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-GNU mailutils NEWS -- history of user-visible changes. 2012-06-07
+GNU mailutils NEWS -- history of user-visible changes. 2012-07-18
 Copyright (C) 2002-2012 Free Software Foundation, Inc.
 See the end of file for copying conditions.
 
@@ -82,7 +82,7 @@ are not used to avoid compromising security.
 
 See <http://mailutils.org/wiki/debug_level>.
 
-** Imap4d undergone a lot of changes to comply to existing RFCs
+** Imap4d underwent a lot of changes to comply to existing RFCs
 
 ** Pop3d and imap4d allow for mailbox-independent compulsory locking
 
@@ -114,6 +114,27 @@ header field with the given date.
 
 See <http://mailutils.org/wiki/Timestamp_(Sieve_test)>.
 
+** mail: sending attachments
+
+The mail[x] utility now allows for sending attachments.  Any number of
+files can be attached to the composed letter by using the `--attach'
+(`-A') options.  The files will be attached in the same order in which
+they appear in the command line.  By default, each attachment is
+assigned the content type "application/octet-stream" and is encoded
+using Base64.  This can be changed using the `--content-type' and
+`--encoding' options.  These options affect all attachments that
+appear after them in the command line, until next occurrence of the
+same option or end of command line, whichever occurs first.  For
+example:
+
+  mail -A prog --encoding quoted-printable --content-type text/c \
+       -A main.c -A ext.h
+
+Here, the file "prog" will be attached witg the content type
+"application/octet-stream" and encoding base64, while the files
+"main.c" and "ext.h" will be marked with content type "text/c" and
+encoded using "quoted-printable" algorithm.
+
 ** MH: improved compatibility with other implementations
 
 ** MH inc: new option --moveto
@@ -181,12 +202,29 @@ imap4d, pop3d, comsat) will be built.  Its counterpart,
 `--enable-build-clients' controls whether client utilities will be
 built.
 
+The effect of both options is overridden by the `--enable-build-*'
+options for particular components.  For example, to build only
+the "mail" utility:
+
+  ./configure --disable-build-clients --enable-build-mail
+
+** The --with-mailbindir option
+
+This option changes installation directory for the "mail" utility.
+  
 ** DBM options
 
 It is normally not needed to specify --with-gdbm, --with-berkeley-db
 or --with-ndbm explicitly.  Configuration will automatically pick up
 all available DBM libraries it can use.
 
+The option `--with-dbm' can be used to enable or disable building of
+all available DBM interfaces.  Its effect is overridden by `--with-*'
+options for particular interfaces.  For example, to build only GDBM
+(even if another databases are supported by the system):
+
+  ./configure --without-dbm --with-gdbm
+
 ** Nntp client is not yet implemented
 
 ** Link with GSASL by default
diff --git a/doc/imprimatur b/doc/imprimatur
index 04255b6..f32ef19 160000
--- a/doc/imprimatur
+++ b/doc/imprimatur
@@ -1 +1 @@
-Subproject commit 04255b6d5551952b4e0c94da15988f573e3e9fc4
+Subproject commit f32ef1983968e755cd580b06e369476d7e7f88b6
diff --git a/libmailutils/mime/attachment.c b/libmailutils/mime/attachment.c
index ff160da..b55e93d 100644
--- a/libmailutils/mime/attachment.c
+++ b/libmailutils/mime/attachment.c
@@ -88,12 +88,15 @@ mu_message_create_attachment (const char *content_type, 
const char *encoding,
                             "Content-Transfer-Encoding: %s\n"
                             "Content-Disposition: attachment; filename=%s\n\n",
                             content_type, name, encoding, name);
-         if (ret)
+         if (ret == 0)
            {
              if ((ret = mu_header_create (&hdr, header,
                                           strlen (header))) == 0)
                {
+                 mu_stream_t bstr;
                  mu_message_get_body (*newmsg, &body);
+                 mu_body_get_streamref (body, &bstr);
+                 
                  if ((ret = mu_file_stream_create (&fstream, filename,
                                                    MU_STREAM_READ)) == 0)
                    {
@@ -101,10 +104,12 @@ mu_message_create_attachment (const char *content_type, 
const char *encoding,
                                                   MU_FILTER_ENCODE,
                                                   MU_STREAM_READ)) == 0)
                        {
-                         mu_body_set_stream (body, tstream, *newmsg);
+                         mu_stream_copy (bstr, tstream, 0, NULL);
+                         mu_stream_unref (tstream);
                          mu_message_set_header (*newmsg, hdr, NULL);
                        }
                    }
+                 mu_stream_unref (bstr);
                  free (header);
                }
            }
diff --git a/mail/mail.c b/mail/mail.c
index 045745c..e2eb63d 100644
--- a/mail/mail.c
+++ b/mail/mail.c
@@ -31,7 +31,9 @@ static char doc[] = N_("GNU mail -- process mail messages.\n"
 "by the first argument, or the user's mbox, if no argument given.\n");
 static char args_doc[] = N_("[address...]\n-f [OPTION...] [file]\n--file 
[OPTION...] [file]\n--file=file [OPTION...]");
 
-#define F_OPTION 256
+#define F_OPTION       256
+#define F_ENCODING     257
+#define F_CONTENT_TYPE 258
 
 static struct argp_option options[] = {
   { NULL,     'f', NULL,      OPTION_HIDDEN, NULL, 0 },
@@ -57,6 +59,12 @@ static struct argp_option options[] = {
    N_("append given header to the message being sent"), 0},
   {"exec",    'E', N_("COMMAND"), 0,
    N_("execute COMMAND"), 0 },
+  {"encoding", F_ENCODING, N_("NAME"), 0,
+   N_("set encoding for subsequent --attach options"), 0 },
+  {"content-type", F_CONTENT_TYPE, N_("TYPE"), 0,
+   N_("set content type for subsequent --attach options"), 0 },
+  {"attach",  'A', N_("FILE"), 0,
+   N_("attach FILE"), 0 },
   { NULL,      0, NULL, 0, NULL, 0 }
 };
 
@@ -84,6 +92,17 @@ parse_opt (int key, char *arg, struct argp_state *state)
       args->hint |= HINT_SEND_MODE;
       send_append_header (arg);
       break;
+
+    case 'A':
+      args->hint |= HINT_SEND_MODE;
+      if (send_attach_file (arg))
+       exit (1);
+      break;
+      
+    case F_CONTENT_TYPE:
+      free (default_content_type);
+      default_content_type = mu_strdup (arg);
+      break;
       
     case 'e':
       util_cache_command (&command_list, "setq mode=exist");
@@ -139,6 +158,11 @@ parse_opt (int key, char *arg, struct argp_state *state)
     case 'E':
       util_cache_command (&command_list, "%s", arg);
       break;
+
+    case F_ENCODING:
+      free (default_encoding);
+      default_encoding = mu_strdup (arg);
+      break;
       
     case 'F':
       util_cache_command (&command_list, "set byname");
diff --git a/mail/mail.h b/mail/mail.h
index 2dbe8d1..c116b1b 100644
--- a/mail/mail.h
+++ b/mail/mail.h
@@ -171,6 +171,8 @@ extern mu_mailbox_t mbox;
 extern size_t total;
 extern int interactive;
 extern const char *program_version;
+extern char *default_encoding;
+extern char *default_content_type;
 
 /* Functions */
 extern int mail_alias (int argc, char **argv);
@@ -256,6 +258,7 @@ extern char *mail_expand_name (const char *name);
 
 extern void send_append_header (char *text);
 extern void send_append_header2 (char *name, char *value, int mode);
+extern int send_attach_file (const char *name);
 
 extern int escape_shell (int argc, char **argv, compose_env_t *env);
 extern int escape_command (int argc, char **argv, compose_env_t *env);
diff --git a/mail/send.c b/mail/send.c
index 4d2ce20..3b8d446 100644
--- a/mail/send.c
+++ b/mail/send.c
@@ -15,6 +15,7 @@
    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include "mail.h"
+#include <mailutils/mime.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -128,6 +129,233 @@ mail_sendheader (int argc, char **argv)
     }
   return 0;
 }
+
+/* Attachments */
+struct atchinfo
+{
+  char *encoding;
+  char *content_type;
+  char *filename;
+};
+
+static mu_list_t attlist;
+
+char *default_encoding;
+char *default_content_type;
+
+static void
+atchinfo_free (void *p)
+{
+  struct atchinfo *ap = p;
+  free (ap->encoding);
+  free (ap->content_type);
+  free (ap->filename);
+  free (ap);
+}
+
+int
+send_attach_file (const char *name)
+{
+  int rc;
+  struct stat st;
+  struct atchinfo *aptr;
+  
+  if (stat (name, &st))
+    {
+      if (errno == ENOENT)
+       {
+         mu_error (_("%s: file does not exist"), name);
+         return 1;
+       }
+      else
+       {
+         mu_error (_("%s: cannot stat: %s"), name, mu_strerror (errno));
+         return 1;
+       }
+    }
+
+  if (!S_ISREG (st.st_mode))
+    {
+      mu_error (_("%s: not a regular file"), name);
+      return 1;
+    }
+
+  if (!attlist)
+    {
+      rc = mu_list_create (&attlist);
+      if (rc)
+       {
+         mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
+         exit (1);
+       }
+      mu_list_set_destroy_item (attlist, atchinfo_free);
+    }
+  aptr = mu_alloc (sizeof (*aptr));
+  aptr->encoding = mu_strdup (default_encoding ?
+                             default_encoding : "base64");
+  aptr->content_type = mu_strdup (default_content_type ?
+                                 default_content_type :
+                                   "application/octet-stream");
+  aptr->filename = mu_strdup (name);
+  rc = mu_list_append (attlist, aptr);
+  if (rc)
+    {
+      mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", NULL, rc);
+      exit (1);
+    }
+  return 0;
+}
+
+static int
+saveatt (void *item, void *data)
+{
+  struct atchinfo *aptr = item;
+  mu_mime_t mime = data;
+  mu_message_t part;
+  mu_header_t hdr;
+  int rc;
+  size_t nparts;
+  char *p;
+  
+  rc = mu_message_create_attachment (aptr->content_type, aptr->encoding,
+                                    aptr->filename, &part);
+  if (rc)
+    {
+      mu_error (_("cannot attach \"%s\": %s"), aptr->filename,
+               mu_strerror (rc));
+      return 1;
+    }
+
+  mu_mime_get_num_parts        (mime, &nparts);
+  mu_message_get_header (part, &hdr);
+  mu_rfc2822_msg_id (nparts, &p);
+  mu_header_set_value (hdr, MU_HEADER_CONTENT_ID, p, 1);
+  free (p);
+
+  rc = mu_mime_add_part (mime, part);
+  mu_message_unref (part);
+  if (rc)
+    {
+      mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_add_part", aptr->filename, rc);
+      return 1;
+    }
+
+  return 0;
+}
+
+static int
+add_attachments (mu_message_t *pmsg, mu_mime_t *pmime)
+{
+  mu_message_t inmsg, outmsg, part;
+  mu_body_t body;
+  mu_header_t inhdr, outhdr;
+  mu_iterator_t itr;
+  mu_mime_t mime;
+  mu_stream_t str, output;
+  int rc;
+  char *p;
+  
+  if (mu_list_is_empty (attlist))
+    {
+      *pmime = NULL;
+      return 0;
+    }
+  
+  inmsg = *pmsg;
+
+  /* Create a mime object */
+  rc = mu_mime_create (&mime, NULL, 0);
+  if (rc)
+    {
+      mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_create", NULL, rc);
+      return 1;
+    }
+
+  /* Add original message as its first part */
+  /* 1. Create the part and obtain a reference to its stream */
+  mu_message_create (&part, NULL);
+  mu_message_get_body (part, &body);
+  mu_body_get_streamref (body, &output);
+
+  /* 2. Get original body stream and copy it out to the part's body */
+  mu_message_get_body (inmsg, &body);
+  mu_body_get_streamref (body, &str);
+  mu_stream_copy (output, str, 0, NULL);
+
+  mu_stream_close (output);
+  mu_stream_destroy (&output);
+
+  mu_message_get_header (inmsg, &inhdr);
+  mu_header_get_iterator (inhdr, &itr);
+
+  /* 3. Copy "Content-*" headers from the original message */
+  mu_message_get_header (part, &outhdr);
+  for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+       mu_iterator_next (itr))
+    {
+      const char *name, *value;
+      
+      if (mu_iterator_current_kv (itr, (const void **)&name,
+                                 (void**)&value) == 0)
+       {
+         if (mu_c_strncasecmp (name, "Content-", 8) == 0)
+           mu_header_set_value (outhdr, name, value, 0);
+       }
+    }
+  
+  /* 4. Add the content type and content ID headers. */
+  mu_header_set_value (outhdr, MU_HEADER_CONTENT_TYPE, "text/plain", 0);
+  mu_rfc2822_msg_id (0, &p);
+  mu_header_set_value (outhdr, MU_HEADER_CONTENT_ID, p, 1);
+  free (p);
+
+  /* 5. Add part to the mime object */
+  mu_mime_add_part (mime, part);
+  mu_message_unref (part);
+
+  /* Add the respective attachments */
+  rc = mu_list_foreach (attlist, saveatt, mime);
+  if (rc)
+    {
+      mu_mime_destroy (&mime);
+      return 1;
+    }
+
+  /* Get the resulting message */
+  rc = mu_mime_get_message (mime, &outmsg);
+
+  if (rc)
+    {
+      mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_get_message", NULL, rc);
+      mu_mime_destroy (&mime);
+      return 1;
+    }
+
+  /* Copy rest of headers from the original message */
+  mu_message_get_header (outmsg, &outhdr);
+  for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
+       mu_iterator_next (itr))
+    {
+      const char *name, *value;
+      
+      if (mu_iterator_current_kv (itr, (const void **)&name,
+                                 (void**)&value) == 0)
+       {
+         if (mu_c_strcasecmp (name, MU_HEADER_MIME_VERSION) == 0 ||
+             mu_c_strncasecmp (name, "Content-", 8) == 0)
+           continue;
+         mu_header_append (outhdr, name, value);
+       }
+    }
+  mu_iterator_destroy (&itr);
+
+  mu_message_unref (outmsg);
+  mu_message_unref (inmsg);
+  *pmsg = outmsg;
+  *pmime = mime;
+  return 0;
+}
+
 
 
 /* Send-related commands */
@@ -342,17 +570,21 @@ fill_body (mu_message_t msg, mu_stream_t instr)
 }
 
 static int
-save_dead_message (compose_env_t *env)
+save_dead_message_env (compose_env_t *env)
 {
   if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0)
     {
-      mu_stream_t dead_letter;
+      mu_stream_t dead_letter, str;
       int rc;
+      time_t t;
+      struct tm *tm;
       const char *name = getenv ("DEAD");
-
+      char *sender;
+      
       /* FIXME: Use MU_STREAM_APPEND if appenddeadletter, instead of the
         stream manipulations below */
-      rc = mu_file_stream_create (&dead_letter, name, MU_STREAM_WRITE);
+      rc = mu_file_stream_create (&dead_letter, name,
+                                 MU_STREAM_CREAT|MU_STREAM_WRITE);
       if (rc)
        {
          mu_error (_("Cannot open file %s: %s"), name, strerror (rc));
@@ -364,8 +596,73 @@ save_dead_message (compose_env_t *env)
       else
        mu_stream_truncate (dead_letter, 0);
 
+      time (&t);
+      tm = gmtime (&t);
+      sender = mu_get_user_email (NULL);
+      if (!sender)
+       sender = mu_strdup ("UNKNOWN");
+      mu_stream_printf (dead_letter, "From %s ", sender);
+      free (sender);
+      mu_c_streamftime (dead_letter, "%c%n", tm, NULL);
+
+      if (mu_header_get_streamref (env->header, &str) == 0)
+       {
+         mu_stream_copy (dead_letter, str, 0, NULL);
+         mu_stream_unref (str);
+       }
+      else
+       mu_stream_write (dead_letter, "\n", 1, NULL);
+      
       mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL);
       mu_stream_copy (dead_letter, env->compstr, 0, NULL);
+      mu_stream_write (dead_letter, "\n", 1, NULL);
+      mu_stream_destroy (&dead_letter);
+    }
+  return 0;
+}
+
+static int
+save_dead_message (mu_message_t msg)
+{
+  if (mailvar_get (NULL, "save", mailvar_type_boolean, 0) == 0)
+    {
+      mu_stream_t dead_letter, str;
+      int rc;
+      time_t t;
+      struct tm *tm;
+      const char *name = getenv ("DEAD");
+      char *sender;
+      
+      /* FIXME: Use MU_STREAM_APPEND if appenddeadletter, instead of the
+        stream manipulations below */
+      rc = mu_file_stream_create (&dead_letter, name,
+                                 MU_STREAM_CREAT|MU_STREAM_WRITE);
+      if (rc)
+       {
+         mu_error (_("Cannot open file %s: %s"), name, strerror (rc));
+         return 1;
+       }
+      if (mailvar_get (NULL, "appenddeadletter",
+                      mailvar_type_boolean, 0) == 0)
+       mu_stream_seek (dead_letter, 0, MU_SEEK_END, NULL);
+      else
+       mu_stream_truncate (dead_letter, 0);
+
+      time (&t);
+      tm = gmtime (&t);
+      sender = mu_get_user_email (NULL);
+      if (!sender)
+       sender = mu_strdup ("UNKNOWN");
+      mu_stream_printf (dead_letter, "From %s ", sender);
+      free (sender);
+      mu_c_streamftime (dead_letter, "%c%n", tm, NULL);
+
+      if (mu_message_get_streamref (msg, &str) == 0)
+       {
+         mu_stream_copy (dead_letter, str, 0, NULL);
+         mu_stream_unref (str);
+       }
+      mu_stream_write (dead_letter, "\n", 1, NULL);
       mu_stream_destroy (&dead_letter);
     }
   return 0;
@@ -445,6 +742,7 @@ mail_send0 (compose_env_t *env, int save_to)
   if (rc)
     {
       mu_error (_("Cannot open temporary file: %s"), mu_strerror (rc));
+      mu_list_destroy (&attlist);
       return 1;
     }
 
@@ -541,8 +839,9 @@ mail_send0 (compose_env_t *env, int save_to)
   /* If interrupted, dump the file to dead.letter.  */
   if (int_cnt)
     {
-      save_dead_message (env);
+      save_dead_message_env (env);
       mu_stream_destroy (&env->compstr);
+      mu_list_destroy (&attlist);
       return 1;
     }
 
@@ -558,18 +857,30 @@ mail_send0 (compose_env_t *env, int save_to)
 
   if (util_header_expand (&env->header) == 0)
     {
+      mu_mime_t mime = NULL;
       mu_message_t msg = NULL;
-      int rc;
       int status = 0;
-      
-      mu_message_create (&msg, NULL);
-      
-      /* Fill the body.  */
-      mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL);
-      rc = fill_body (msg, env->compstr);
-
-      if (rc == 0)
+      int sendit = (compose_header_get (env, MU_HEADER_TO, NULL) ||
+                   compose_header_get (env, MU_HEADER_CC, NULL) ||
+                   compose_header_get (env, MU_HEADER_BCC, NULL));
+      do
        {
+         status = mu_message_create (&msg, NULL);
+         if (status)
+           break;
+         /* Fill the body. */
+         mu_stream_seek (env->compstr, 0, MU_SEEK_SET, NULL);
+         status = fill_body (msg, env->compstr);
+         if (status)
+           break;
+         
+         mu_message_set_header (msg, env->header, NULL);
+         env->header = NULL;
+         
+         status = add_attachments (&msg, &mime);
+         if (status)
+           break;
+         
          /* Save outgoing message */
          if (save_to)
            {
@@ -628,30 +939,29 @@ mail_send0 (compose_env_t *env, int save_to)
            }
          
          /* Do we need to Send the message on the wire?  */
-         if (status == 0 &&
-             (compose_header_get (env, MU_HEADER_TO, NULL) ||
-              compose_header_get (env, MU_HEADER_CC, NULL) ||
-              compose_header_get (env, MU_HEADER_BCC, NULL)))
+         if (status == 0 && sendit)
            {
-             mu_message_set_header (msg, env->header, NULL);
-             env->header = NULL;
              status = send_message (msg);
              if (status)
                {
                  mu_error (_("cannot send message: %s"),
                            mu_strerror (status));
-                 save_dead_message (env);
+                 save_dead_message (msg);
                }
            }
-       }  
+       }
+      while (0);
+
       mu_stream_destroy (&env->compstr);
       mu_message_destroy (&msg, NULL);
+      mu_mime_destroy (&mime);
       return status;
     }
   else
-    save_dead_message (env);
+    save_dead_message_env (env);
   
   mu_stream_destroy (&env->compstr);
+  mu_list_destroy (&attlist);
   return 1;
 }
 


hooks/post-receive
-- 
GNU Mailutils



reply via email to

[Prev in Thread] Current Thread [Next in Thread]