1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2003-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 /* MH send command */
18 
19 #include <mh.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <stdarg.h>
23 #include <pwd.h>
24 
25 static char prog_doc[] = N_("Send messages");
26 static char args_doc[] = N_("FILE [FILE...]");
27 
28 static const char *draftfolder;  /* Use this draft folder */
29 static int use_draftfolder = 1;
30 static int use_draft;
31 
32 static int append_msgid;         /* Append Message-ID: header */
33 static int background;           /* Operate in the background */
34 
35 static int split_message;            /* Split the message */
36 static unsigned long split_interval; /* Interval in seconds between sending two
37 					successive partial messages */
38 static size_t split_size = 76*632;   /* Size of split parts */
39 static int verbose;              /* Produce verbose diagnostics */
40 static int watch;                /* Watch the delivery process */
41 
42 static int keep_files;           /* Keep draft files */
43 
44 #define DEFAULT_USER_AGENT "MH (" PACKAGE_STRING ")"
45 
46 #define WATCH(c) do {\
47   if (watch)\
48     watch_printf c;\
49 } while (0)
50 
51 static int add_file (char const *name);
52 static void mesg_list_fixup (void);
53 
54 static void
add_alias(struct mu_parseopt * po,struct mu_option * opt,char const * arg)55 add_alias (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
56 {
57   mh_alias_read (arg, 1);
58 }
59 
60 static void
set_draftfolder(struct mu_parseopt * po,struct mu_option * opt,char const * arg)61 set_draftfolder (struct mu_parseopt *po, struct mu_option *opt,
62 		 char const *arg)
63 {
64   draftfolder = mu_strdup (arg);
65   use_draftfolder = 1;
66 }
67 
68 static void
add_draftmessage(struct mu_parseopt * po,struct mu_option * opt,char const * arg)69 add_draftmessage (struct mu_parseopt *po, struct mu_option *opt,
70 		  char const *arg)
71 {
72   add_file (arg);
73 }
74 
75 static void
set_split_opt(struct mu_parseopt * po,struct mu_option * opt,char const * arg)76 set_split_opt (struct mu_parseopt *po, struct mu_option *opt,
77 	       char const *arg)
78 {
79   char *errmsg;
80   int rc = mu_str_to_c (arg, opt->opt_type, opt->opt_ptr, &errmsg);
81   if (rc)
82     {
83       char const *errtext;
84       if (errmsg)
85 	errtext = errmsg;
86       else
87 	errtext = mu_strerror (rc);
88 
89       mu_parseopt_error (po, "%s%s: %s", po->po_long_opt_start,
90 			 opt->opt_long, errtext);
91       free (errmsg);
92 
93       if (!(po->po_flags & MU_PARSEOPT_NO_ERREXIT))
94 	exit (po->po_exit_error);
95     }
96   split_message = 1;
97 }
98 
99 static struct mu_option options[] = {
100   { "alias",        0,    N_("FILE"), MU_OPTION_DEFAULT,
101     N_("specify additional alias file"),
102     mu_c_string, NULL, add_alias },
103   { "draft",        0,    NULL, MU_OPTION_DEFAULT,
104     N_("use prepared draft"),
105     mu_c_bool, &use_draft },
106   { "draftfolder",  0,   N_("FOLDER"), MU_OPTION_DEFAULT,
107     N_("specify the folder for message drafts"),
108     mu_c_string, NULL, set_draftfolder },
109   { "draftmessage", 0,   N_("MSG"), MU_OPTION_DEFAULT,
110     N_("use MSG from the draftfolder as a draft"),
111     mu_c_string, NULL, add_draftmessage },
112   { "nodraftfolder", 0, NULL, MU_OPTION_DEFAULT,
113     N_("undo the effect of the last --draftfolder option"),
114     mu_c_int, &use_draftfolder, NULL, "1" },
115   { "filter",        0, N_("FILE"), MU_OPTION_HIDDEN,
116     N_("use filter FILE to preprocess the body of the message"),
117     mu_c_string, NULL, mh_opt_notimpl },
118   { "nofilter",      0, NULL, MU_OPTION_HIDDEN,
119     N_("undo the effect of the last --filter option"),
120     mu_c_int, NULL, mh_opt_notimpl },
121   { "format",        0, NULL, MU_OPTION_HIDDEN,
122     N_("reformat To: and Cc: addresses"),
123     mu_c_bool, NULL, mh_opt_notimpl_warning },
124   { "noformat",      0, NULL, MU_OPTION_HIDDEN },
125   { "forward",       0, NULL, MU_OPTION_HIDDEN,
126     N_("in case of failure forward the draft along with the failure notice to the sender"),
127     mu_c_bool, NULL, mh_opt_notimpl_warning },
128   { "noforward",     0, NULL, MU_OPTION_HIDDEN, "" },
129   { "mime",          0, NULL, MU_OPTION_HIDDEN,
130     N_("use MIME encapsulation"),
131     mu_c_bool, NULL, mh_opt_notimpl_warning },
132   { "msgid",         0, NULL, MU_OPTION_DEFAULT,
133     N_("add Message-ID: field"),
134     mu_c_bool, &append_msgid },
135   { "push",          0, NULL, MU_OPTION_DEFAULT,
136     N_("run in the background"),
137     mu_c_bool, &background },
138   { "preserve",      0, NULL, MU_OPTION_DEFAULT,
139     N_("keep draft files"),
140     mu_c_bool, &keep_files },
141   { "keep",          0, NULL, MU_OPTION_ALIAS },
142   { "split",         0, N_("SECONDS"), MU_OPTION_DEFAULT,
143    N_("split the draft into several partial messages and send them with SECONDS interval"),
144     mu_c_ulong, &split_interval, set_split_opt },
145   { "chunksize",     0, N_("NUMBER"), MU_OPTION_DEFAULT,
146     N_("set the size of chunk for --split (in bytes)"),
147     mu_c_size, &split_size },
148   { "verbose",       0, NULL, MU_OPTION_DEFAULT,
149     N_("print the transcript of interactions with the transport system"),
150     mu_c_bool, &verbose },
151   { "watch",         0, NULL, MU_OPTION_DEFAULT,
152     N_("monitor the delivery of mail"),
153     mu_c_bool, &watch },
154   { "width",         0, N_("NUMBER"), MU_OPTION_HIDDEN,
155     N_("make header fields no longer than NUMBER columns"),
156     mu_c_uint, NULL, mh_opt_notimpl_warning },
157 
158   MU_OPTION_END
159 };
160 
161 static void
watch_printf(const char * fmt,...)162 watch_printf (const char *fmt, ...)
163 {
164   va_list ap;
165 
166   va_start (ap, fmt);
167   vfprintf (stderr, fmt, ap);
168   fprintf (stderr, "\n");
169   va_end (ap);
170 }
171 
172 struct list_elt           /* Element of the send list */
173 {
174   const char *file_name;  /* Duplicated in msg stream, but there's no way
175 			     to get it from there */
176   mu_message_t msg;       /* Corresponding message */
177 };
178 
179 static mu_list_t mesg_list;
180 static mu_property_t mts_profile;
181 
182 int
add_file(char const * name)183 add_file (char const *name)
184 {
185   struct list_elt *elt;
186 
187   if (!mesg_list && mu_list_create (&mesg_list))
188     {
189       mu_error (_("cannot create message list"));
190       return 1;
191     }
192 
193   elt = mu_alloc (sizeof *elt);
194   elt->file_name = name;
195   elt->msg = NULL;
196   return mu_list_append (mesg_list, elt);
197 }
198 
199 int
checkdraft(const char * name)200 checkdraft (const char *name)
201 {
202   struct stat st;
203 
204   if (stat (name, &st))
205     {
206       mu_error (_("unable to stat draft file %s: %s"), name,
207 		mu_strerror (errno));
208       return 1;
209     }
210   return 0;
211 }
212 
213 int
elt_fixup(void * item,void * data)214 elt_fixup (void *item, void *data)
215 {
216   struct list_elt *elt = item;
217 
218   elt->file_name = mh_expand_name (draftfolder, elt->file_name, NAME_ANY);
219   if (checkdraft (elt->file_name))
220     exit (1);
221   elt->msg = mh_file_to_message (NULL, elt->file_name);
222   if (!elt->msg)
223     return MU_ERR_USER0;
224 
225   return 0;
226 }
227 
228 void
mesg_list_fixup()229 mesg_list_fixup ()
230 {
231   if (mesg_list && mu_list_foreach (mesg_list, elt_fixup, NULL))
232     exit (1);
233 }
234 
235 void
read_mts_profile()236 read_mts_profile ()
237 {
238   char *name;
239   const char *p;
240   char *hostname = NULL;
241   int rc;
242 
243   name = getenv ("MTSTAILOR");
244   if (name)
245     mts_profile = mh_read_property_file (name, 1);
246   else
247     {
248       mu_property_t local_profile;
249 
250       name = mh_expand_name (MHLIBDIR, "mtstailor", NAME_ANY);
251       mts_profile = mh_read_property_file (name, 1);
252 
253       name = mu_tilde_expansion ("~/.mtstailor", MU_HIERARCHY_DELIMITER, NULL);
254       local_profile = mh_read_property_file (name, 1);
255 
256       mh_property_merge (mts_profile, local_profile);
257       mu_property_destroy (&local_profile);
258     }
259 
260   rc = mu_property_aget_value (mts_profile, "localname", &hostname);
261   if (rc == MU_ERR_NOENT)
262     {
263       rc = mu_get_host_name (&hostname);
264       if (rc)
265 	{
266 	  mu_error (_("cannot get system host name: %s"), mu_strerror (rc));
267 	  exit (1);
268 	}
269     }
270   else if (rc)
271     {
272       mu_diag_funcall (MU_DIAG_ERROR, "mu_profile_aget_value", "localname", rc);
273       exit (1);
274     }
275 
276   rc = mu_property_sget_value (mts_profile, "localdomain", &p);
277   if (rc == 0)
278     {
279       hostname = mu_realloc (hostname, strlen (hostname) + 1 + strlen (p) + 1);
280       strcat (hostname, ".");
281       strcat (hostname, p);
282     }
283   else if (rc != MU_ERR_NOENT)
284     {
285       mu_diag_funcall (MU_DIAG_ERROR, "mu_profile_sget_value",
286 		       "localdomain", rc);
287       exit (1);
288     }
289 
290   rc = mu_set_user_email_domain (hostname);
291   free (hostname);
292   if (rc)
293     {
294       mu_error (_("cannot set user mail domain: %s"), mu_strerror (rc));
295       exit (1);
296     }
297 
298   rc = mu_property_sget_value (mts_profile, "username", &p);
299   if (rc == 0)
300     {
301       size_t len;
302       const char *domain;
303       char *newemail;
304       int rc;
305 
306       rc = mu_get_user_email_domain (&domain);
307       if (rc)
308 	{
309 	  mu_error (_("cannot get user email: %s"), mu_strerror (rc));
310 	  exit (1);
311 	}
312       len = strlen (p) + 1 + strlen (domain) + 1;
313       newemail = mu_alloc (len);
314       strcpy (newemail, p);
315       strcat (newemail, "@");
316       strcat (newemail, domain);
317 
318       rc = mu_set_user_email (newemail);
319       if (rc)
320 	{
321 	  mu_error (_("cannot set user email (%s): %s"),
322 		    newemail, mu_strerror (rc));
323 	  exit (1);
324 	}
325 
326       free (newemail);
327     }
328   else if (rc != MU_ERR_NOENT)
329     {
330       mu_diag_funcall (MU_DIAG_ERROR, "mu_profile_sget_value",
331 		       "username", rc);
332       exit (1);
333     }
334 }
335 
336 
337 mu_mailer_t
open_mailer()338 open_mailer ()
339 {
340   const char *url = mu_mhprop_get_value (mts_profile,
341 					 "url",
342 					 "sendmail:/usr/sbin/sendmail");
343   mu_mailer_t mailer;
344   int status;
345 
346   WATCH ((_("Creating mailer %s"), url));
347   status = mu_mailer_create (&mailer, url);
348   if (status)
349     {
350       mu_error (_("cannot create mailer `%s'"), url);
351       return NULL;
352     }
353 
354   if (verbose)
355     {
356       mu_debug_set_category_level (MU_DEBCAT_MAILER,
357                                    MU_DEBUG_LEVEL_UPTO (MU_DEBUG_PROT));
358     }
359 
360   WATCH ((_("Opening mailer %s"), url));
361   status = mu_mailer_open (mailer, MU_STREAM_RDWR);
362   if (status)
363     {
364       mu_error (_("cannot open mailer `%s': %s"), url, mu_strerror (status));
365       return NULL;
366     }
367   return mailer;
368 }
369 
370 static void
create_message_id(mu_header_t hdr)371 create_message_id (mu_header_t hdr)
372 {
373   char *p = mh_create_message_id (0);
374   mu_header_set_value (hdr, MU_HEADER_MESSAGE_ID, p, 1);
375   free (p);
376 }
377 
378 static const char *
get_sender_personal()379 get_sender_personal ()
380 {
381   const char *s = mh_global_profile_get ("signature", getenv ("SIGNATURE"));
382   if (!s)
383     {
384       struct passwd *pw = getpwuid (getuid ());
385       if (pw && pw->pw_gecos[0])
386 	{
387 	  char *p = strchr (pw->pw_gecos, ',');
388 	  if (p)
389 	    *p = 0;
390 	  s = pw->pw_gecos;
391 	}
392     }
393   return s;
394 }
395 
396 static void
set_address_header(mu_header_t hdr,char * name,mu_address_t addr)397 set_address_header (mu_header_t hdr, char *name, mu_address_t addr)
398 {
399   const char *value;
400   if (mu_address_sget_printable (addr, &value) == 0)
401     mu_header_set_value (hdr, name, value, 1);
402   /* FIXME: Error reporting */
403 }
404 
405 void
expand_aliases(mu_message_t msg)406 expand_aliases (mu_message_t msg)
407 {
408   mu_header_t hdr;
409   mu_address_t addr_to = NULL,
410                addr_cc = NULL,
411                addr_bcc = NULL;
412 
413   mh_expand_aliases (msg, &addr_to, &addr_cc, &addr_bcc);
414 
415   mu_message_get_header (msg, &hdr);
416   if (addr_to)
417     {
418       set_address_header (hdr, MU_HEADER_TO, addr_to);
419       mu_address_destroy (&addr_to);
420     }
421 
422   if (addr_cc)
423     {
424       set_address_header (hdr, MU_HEADER_CC, addr_cc);
425       mu_address_destroy (&addr_cc);
426     }
427 
428   if (addr_bcc)
429     {
430       set_address_header (hdr, MU_HEADER_BCC, addr_bcc);
431       mu_address_destroy (&addr_bcc);
432     }
433 }
434 
435 void
fix_fcc(mu_message_t msg)436 fix_fcc (mu_message_t msg)
437 {
438   mu_header_t hdr;
439   char *fcc;
440 
441   mu_message_get_header (msg, &hdr);
442   if (mu_header_aget_value (hdr, MU_HEADER_FCC, &fcc) == 0)
443     {
444       struct mu_wordsplit ws;
445       int need_fixup = 0;
446       size_t fixup_len = 0;
447 
448       ws.ws_delim = ",";
449       if (mu_wordsplit (fcc, &ws,
450 			MU_WRDSF_DEFFLAGS | MU_WRDSF_DELIM | MU_WRDSF_WS))
451 	{
452 	  mu_error (_("cannot split line `%s': %s"), fcc,
453 		    mu_wordsplit_strerror (&ws));
454 	}
455       else
456 	{
457 	  size_t i;
458 
459 	  for (i = 0; i < ws.ws_wordc; i += 2)
460 	    {
461 	      if (strchr ("+%~/=", ws.ws_wordv[i][0]) == NULL)
462 		{
463 		  need_fixup++;
464 		  fixup_len ++;
465 		}
466 	      fixup_len += strlen (ws.ws_wordv[i]);
467 	    }
468 
469 	  if (need_fixup)
470 	    {
471 	      char *p;
472 
473 	      /* the new fcc string contains: folder names - fixup_len
474 		 characters, ws.ws_wordc - 1 comma-space pairs and a
475 		 terminating nul */
476 	      fcc = realloc (fcc, fixup_len + ws.ws_wordc - 1 + 1);
477 	      for (i = 0, p = fcc; i < ws.ws_wordc; i++)
478 		{
479 		  if (strchr ("+%~/=", ws.ws_wordv[i][0]) == NULL)
480 		    *p++ = '+';
481 		  strcpy (p, ws.ws_wordv[i]);
482 		  p += strlen (p);
483 		  *p++ = ',';
484 		  *p++ = ' ';
485 		}
486 	      *p = 0;
487 	    }
488 	}
489 
490       mu_wordsplit_free (&ws);
491 
492       if (need_fixup)
493 	{
494 	  mu_header_set_value (hdr, MU_HEADER_FCC, fcc, 1);
495 	  WATCH ((_("Fixed fcc: %s"), fcc));
496 	}
497       free (fcc);
498     }
499 }
500 
501 /* Convert MH-style DCC headers to normal BCC.
502    FIXME: Normally we should iterate through the headers to catch
503    multiple Dcc occurrences (the same holds true for Fcc as well),
504    however at the time of this writing we have mu_header_get_field_value,
505    but we miss mu_header_set_field_value. */
506 void
fix_dcc(mu_message_t msg)507 fix_dcc (mu_message_t msg)
508 {
509   mu_header_t hdr;
510   char *dcc;
511 
512   mu_message_get_header (msg, &hdr);
513   if (mu_header_aget_value (hdr, MU_HEADER_DCC, &dcc) == 0)
514     {
515       char *bcc = NULL;
516 
517       mu_header_set_value (hdr, MU_HEADER_DCC, NULL, 1);
518       mu_header_aget_value (hdr, MU_HEADER_BCC, &bcc);
519       if (bcc)
520 	{
521 	  char *newbcc = realloc (bcc, strlen (bcc) + 1 + strlen (dcc) + 1);
522 	  if (!newbcc)
523 	    {
524 	      mu_error (_("not enough memory"));
525 	      free (dcc);
526 	      free (bcc);
527 	      return;
528 	    }
529 	  bcc = newbcc;
530 	  strcat (bcc, ",");
531 	  strcat (bcc, dcc);
532 	  free (dcc);
533 	}
534       else
535 	bcc = dcc;
536 
537       WATCH ((_("Fixed bcc: %s"), bcc));
538       mu_header_set_value (hdr, MU_HEADER_BCC, bcc, 1);
539       free (bcc);
540     }
541 }
542 
543 void
backup_file(const char * file_name)544 backup_file (const char *file_name)
545 {
546   char *new_name = mu_alloc (strlen (file_name) + 2);
547   char *p = strrchr (file_name, '/');
548   if (p)
549     {
550       size_t len = p - file_name + 1;
551       memcpy (new_name, file_name, len);
552       new_name[len++] = ',';
553       strcpy (new_name + len, p + 1);
554     }
555   else
556     {
557       new_name[0] = ',';
558       strcpy (new_name + 1, file_name);
559     }
560   WATCH ((_("Renaming %s to %s"), file_name, new_name));
561 
562   if (unlink (new_name) && errno != ENOENT)
563     mu_diag_funcall (MU_DIAG_ERROR, "unlink", new_name, errno);
564   else
565     {
566       int rc = mu_rename_file (file_name, new_name, MU_COPY_OVERWRITE);
567       if (rc)
568 	mu_error (_("cannot rename %s to %s: %s"),
569 		  file_name, new_name, mu_strerror (errno));
570     }
571   free (new_name);
572 }
573 
574 int
_action_send(void * item,void * data)575 _action_send (void *item, void *data)
576 {
577   struct list_elt *elt = item;
578   mu_message_t msg = elt->msg;
579   int rc;
580   mu_mailer_t mailer;
581   mu_header_t hdr;
582   size_t n;
583 
584   WATCH ((_("Getting message %s"), elt->file_name));
585 
586   if (mu_message_get_header (msg, &hdr) == 0)
587     {
588       char date[80];
589       time_t t = time (NULL);
590       struct tm *tm = localtime (&t);
591 
592       mu_strftime (date, sizeof date, "%a, %d %b %Y %H:%M:%S %z", tm);
593       mu_header_set_value (hdr, MU_HEADER_DATE, date, 1);
594 
595       if (mu_header_get_value (hdr, MU_HEADER_FROM, NULL, 0, &n))
596 	{
597 	  char *from;
598 	  char *email = mu_get_user_email (NULL);
599 	  const char *pers = get_sender_personal ();
600 	  if (pers)
601 	    {
602 	      mu_asprintf (&from, "\"%s\" <%s>", pers, email);
603 	      free (email);
604 	    }
605 	  else
606 	    from = email;
607 
608 	  mu_header_set_value (hdr, MU_HEADER_FROM, from, 1);
609 	  free (from);
610 	}
611 
612       if (append_msgid
613 	  && mu_header_get_value (hdr, MU_HEADER_MESSAGE_ID, NULL, 0, &n))
614 	create_message_id (hdr);
615 
616       if (mu_header_get_value (hdr, MU_HEADER_USER_AGENT, NULL, 0, &n))
617 	{
618 	  const char *p = mu_mhprop_get_value (mts_profile,
619 					       "user-agent",
620 					       mu_mhprop_get_value (mts_profile,
621 								    "x-mailer",
622 								    "yes"));
623 
624 	  if (!strcmp (p, "yes"))
625 	    mu_header_set_value (hdr, MU_HEADER_USER_AGENT,
626 				 DEFAULT_USER_AGENT, 0);
627 	  else if (strcmp (p, "no"))
628 	    mu_header_remove (hdr, MU_HEADER_USER_AGENT, 1);
629 	}
630     }
631 
632   expand_aliases (msg);
633   fix_fcc (msg);
634   fix_dcc (msg);
635 
636   mailer = open_mailer ();
637   if (!mailer)
638     return MU_ERR_FAILURE;
639 
640   WATCH ((_("Sending message %s"), elt->file_name));
641   if (split_message)
642     {
643       struct timeval delay;
644       delay.tv_sec = split_interval;
645       delay.tv_usec = 0;
646       rc = mu_mailer_send_fragments (mailer, msg,
647 				     split_size, &delay,
648 				     NULL, NULL);
649     }
650   else
651     rc = mu_mailer_send_message (mailer, msg, NULL, NULL);
652   if (rc)
653     {
654       mu_error(_("cannot send message: %s"), mu_strerror (rc));
655       return MU_ERR_FAILURE;
656     }
657 
658   WATCH ((_("Destroying the mailer")));
659   mu_mailer_close (mailer);
660   mu_mailer_destroy (&mailer);
661 
662   if (!keep_files)
663     backup_file (elt->file_name);
664 
665   return 0;
666 }
667 
668 static int
_add_to_mesg_list(size_t num,mu_message_t msg,void * data)669 _add_to_mesg_list (size_t num, mu_message_t msg, void *data)
670 {
671   char const *path = data;
672   struct list_elt *elt;
673   size_t uid;
674   int rc;
675   char *file_name;
676 
677   if (!mesg_list && mu_list_create (&mesg_list))
678     {
679       mu_error (_("cannot create message list"));
680       return 1;
681     }
682 
683   mu_message_get_uid (msg, &uid);
684   file_name = mu_make_file_name (path, mu_umaxtostr (0, uid));
685   if (!use_draft)
686     {
687       if (!mh_usedraft (file_name))
688 	exit (0);
689     }
690 
691   elt = mu_alloc (sizeof *elt);
692   elt->msg = msg;
693   elt->file_name = file_name;
694   rc = mu_list_append (mesg_list, elt);
695   if (rc)
696     {
697       mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", NULL, rc);
698       exit (1);
699     }
700   return 0;
701 }
702 
703 static void
addfolder(const char * folder,int argc,char ** argv)704 addfolder (const char *folder, int argc, char **argv)
705 {
706   mu_url_t url;
707   const char *path;
708   mu_msgset_t msgset;
709 
710   mu_mailbox_t mbox = mh_open_folder (folder, MU_STREAM_READ);
711   if (!mbox)
712     {
713       mu_error (_("cannot open folder %s: %s"), folder,
714 		mu_strerror (errno));
715       exit (1);
716     }
717 
718   mh_msgset_parse (&msgset, mbox, argc, argv, "cur");
719   if (!use_draft)
720     {
721       size_t count = 0;
722       mu_msgset_count (msgset, &count);
723       if (count > 1)
724 	use_draft = 1;
725     }
726 
727   mu_mailbox_get_url (mbox, &url);
728   mu_url_sget_path (url, &path);
729   mu_msgset_foreach_message (msgset, _add_to_mesg_list, (void*)path);
730 
731   mu_msgset_free (msgset);
732 }
733 
734 /* Usage cases:
735  *
736  * 1. send
737  *   a) If Draft-Folder is set: ask whether to use "cur" message from that
738  *      folder as a draft;
739  *   b) If Draft-Folder is not set: ask whether to use $(Path)/draft;
740  * 2. send -draft
741  *   Use $(Path)/draft
742  * 3. send MSG
743  *   Use $(Path)/MSG
744  * 4. send -draftmessage MSG
745  *   Same as (3)
746  * 5. send -draftfolder DIR
747  *   Use "cur" from that folder
748  * 6. send -draftfolder DIR MSG
749  *   Use MSG from folder DIR
750  * 7. send -draftfolder DIR -draftmessage MSG
751  *   Same as 6.
752  */
753 
754 int
main(int argc,char ** argv)755 main (int argc, char **argv)
756 {
757   mu_mailbox_t mbox = NULL;
758   char *p;
759   int rc;
760 
761   mh_getopt (&argc, &argv, options, 0, args_doc, prog_doc, NULL);
762 
763   mh_read_aliases ();
764   /* Process the mtstailor file */
765   read_mts_profile ();
766 
767   if (!draftfolder)
768     {
769       if (mu_list_is_empty (mesg_list) && argc == 0)
770 	{
771 	  char *dfolder =
772 	    (!use_draft && use_draftfolder)
773 	                ? (char*) mh_global_profile_get ("Draft-Folder", NULL)
774 	                : NULL;
775 
776 	  if (dfolder)
777 	    addfolder (dfolder, 0, NULL);
778 	  else
779 	    {
780 	      char *df = mh_expand_name (mu_folder_directory (), "draft",
781 	                                 NAME_ANY);
782 	      if (checkdraft (df))
783 		exit (1);
784 	      if (!use_draft && !mh_usedraft (df))
785 		exit (0);
786 	      add_file (df);
787 	      mesg_list_fixup ();
788 	    }
789 	}
790       else
791 	{
792 	  while (argc--)
793 	    add_file (*argv++);
794 	  mesg_list_fixup ();
795 	}
796     }
797   else
798     {
799       /* -draftfolder is supplied */
800       draftfolder = mh_expand_name (mu_folder_directory (),
801 				    draftfolder, NAME_ANY);
802       use_draft = 1;
803       mesg_list_fixup ();
804       if (mu_list_is_empty (mesg_list) || argc != 0)
805 	addfolder (draftfolder, argc, argv);
806     }
807 
808   /* Detach from the console if required */
809   if (background && (rc = mu_daemon ()) != 0)
810     {
811       mu_error (_("cannot switch to background: %s"), mu_strerror (rc));
812       return 1;
813     }
814 
815   /* Prepend url specifier to the folder dir. We won't need this
816      when the default format becomes configurable */
817   mu_asprintf (&p, "mh:%s", mu_folder_directory ());
818   mu_set_folder_directory (p);
819   free (p);
820 
821   /* Finally, do the work */
822   rc = mu_list_foreach (mesg_list, _action_send, NULL);
823 
824   mu_mailbox_destroy (&mbox);
825   return !!rc;
826 }
827 
828