1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-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 #include "mail.h"
18 #include "mailutils/cli.h"
19 #include "mailutils/mu_auth.h"
20 
21 /* Global variables and constants*/
22 mu_mailbox_t mbox;            /* Mailbox being operated upon */
23 size_t total;                 /* Total number of messages in the mailbox */
24 int interactive;              /* Is the session interactive */
25 int read_recipients;          /* Read recipients from the message (mail -t) */
26 mu_url_t secondary_url;       /* URL of the mailbox given with the -f option */
27 static mu_list_t command_list;/* List of commands to be executed after parsing
28 				 command line */
29 const char *program_version = "mail (" PACKAGE_STRING ")";
30 
31 
32 #define HINT_SEND_MODE   0x1
33 #define HINT_FILE_OPTION 0x2
34 #define HINT_BYNAME      0x4
35 
36 int hint;
37 char *file;
38 char *user;
39 
40 int mime_option;
41 int skip_empty_attachments;
42 char *default_encoding;
43 char *default_content_type;
44 static char *content_name;
45 static char *content_filename;
46 
47 static void
cli_f_option(struct mu_parseopt * po,struct mu_option * opt,char const * arg)48 cli_f_option (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
49 {
50   hint |= HINT_FILE_OPTION;
51 }
52 
53 static void
cli_file_option(struct mu_parseopt * po,struct mu_option * opt,char const * arg)54 cli_file_option (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
55 {
56   if (arg)
57     file = mu_strdup (arg);
58   hint |= HINT_FILE_OPTION;
59 }
60 
61 static void
cli_command_option(struct mu_parseopt * po,struct mu_option * opt,char const * arg)62 cli_command_option (struct mu_parseopt *po, struct mu_option *opt,
63 		    char const *arg)
64 {
65   switch (opt->opt_short)
66     {
67     case 'e':
68       util_cache_command (&command_list, "setq mode=exist");
69       break;
70 
71     case 'p':
72       util_cache_command (&command_list, "setq mode=print");
73       break;
74 
75     case 'r':
76       util_cache_command (&command_list, "set return-address=%s", arg);
77       hint |= HINT_SEND_MODE;
78       break;
79 
80     case 'q':
81       util_cache_command (&command_list, "set quit");
82       break;
83 
84     case 't':
85       read_recipients = 1;
86       util_cache_command (&command_list, "set editheaders");
87       hint |= HINT_SEND_MODE;
88       break;
89 
90     case 'H':
91       util_cache_command (&command_list, "setq mode=headers");
92       break;
93 
94     case 'i':
95       util_cache_command (&command_list, "set ignore");
96       break;
97 
98     case 'n':
99       util_do_command ("set norc");
100       break;
101 
102     case 'N':
103       util_cache_command (&command_list, "set noheader");
104       break;
105 
106     case 'E':
107       util_cache_command (&command_list, "%s", arg);
108       break;
109 
110     case 'F':
111       hint |= HINT_SEND_MODE;
112       hint |= HINT_BYNAME;
113       break;
114 
115     case 0:
116       mu_parseopt_error (po, _("--%s: option should have been recognized"),
117 			 opt->opt_long);
118       exit (po->po_exit_error);
119 
120     default:
121       mu_parseopt_error (po, _("-%c: option should have been recognized"),
122 			 opt->opt_short);
123       exit (po->po_exit_error);
124     }
125 }
126 
127 static void
cli_subject(struct mu_parseopt * po,struct mu_option * opt,char const * arg)128 cli_subject (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
129 {
130   hint |= HINT_SEND_MODE;
131   send_append_header2 (MU_HEADER_SUBJECT, arg, COMPOSE_REPLACE);
132   util_cache_command (&command_list, "set noasksub");
133 }
134 
135 static void
cli_append(struct mu_parseopt * po,struct mu_option * opt,char const * arg)136 cli_append (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
137 {
138   hint |= HINT_SEND_MODE;
139   send_append_header (arg);
140 }
141 
142 static void
cli_attach(struct mu_parseopt * po,struct mu_option * opt,char const * arg)143 cli_attach (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
144 {
145   int fd = -1;
146 
147   hint |= HINT_SEND_MODE;
148   if (strcmp (arg, "-") == 0)
149     {
150       arg = NULL;
151       fd = 0;
152     }
153   if (send_attach_file (fd, arg, content_filename, content_name,
154 			default_content_type, default_encoding))
155     exit (po->po_exit_error);
156 
157   mime_option = 1;
158 
159   free (content_name);
160   content_name = NULL;
161   free (content_filename);
162   content_filename = NULL;
163 }
164 
165 static void
cli_attach_fd(struct mu_parseopt * po,struct mu_option * opt,char const * arg)166 cli_attach_fd (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
167 {
168   int rc, fd;
169 
170   hint |= HINT_SEND_MODE;
171   rc = mu_str_to_c (arg, mu_c_int, &fd, NULL);
172   if (rc)
173     {
174       mu_parseopt_error (po, _("%s: bad descriptor"), arg);
175       exit (po->po_exit_error);
176     }
177 
178   send_attach_file (fd, NULL, content_filename, content_name,
179 		    default_content_type, default_encoding);
180 
181   free (content_name);
182   content_name = NULL;
183   free (content_filename);
184   content_filename = NULL;
185 }
186 
187 static struct mu_option mail_options[] = {
188   { NULL,     'f', NULL,      MU_OPTION_HIDDEN,
189     NULL,
190     mu_c_string, NULL, cli_f_option },
191   { "file",   0,  N_("FILE"), MU_OPTION_ARG_OPTIONAL|MU_OPTION_HIDDEN,
192     NULL,
193     mu_c_string, NULL, cli_file_option },
194 
195   { "exist",  'e', NULL,      MU_OPTION_DEFAULT,
196     N_("return true if mail exists"),
197     mu_c_string, NULL, cli_command_option },
198 
199   { "byname", 'F', NULL,      MU_OPTION_DEFAULT,
200     N_("save messages according to sender"),
201     mu_c_string, NULL, cli_command_option },
202 
203   { "headers", 'H', NULL,     MU_OPTION_DEFAULT,
204     N_("write a header summary and exit"),
205     mu_c_string, NULL, cli_command_option },
206 
207   { "ignore",  'i', NULL,     MU_OPTION_DEFAULT,
208     N_("ignore interrupts"),
209     mu_c_string, NULL, cli_command_option },
210 
211   { "norc",    'n', NULL,     MU_OPTION_DEFAULT,
212     N_("do not read the system mailrc file"),
213     mu_c_string, NULL, cli_command_option },
214 
215   { "nosum",   'N', NULL,     MU_OPTION_DEFAULT,
216     N_("do not display initial header summary"),
217     mu_c_string, NULL, cli_command_option },
218 
219   { "print",   'p', NULL,     MU_OPTION_DEFAULT,
220     N_("print all mail to standard output"),
221     mu_c_string, NULL, cli_command_option },
222   { "read",    0,   NULL,     MU_OPTION_ALIAS },
223 
224   { "return-address", 'r', N_("ADDRESS"), MU_OPTION_DEFAULT,
225     N_("use address as the return address when sending mail"),
226     mu_c_string, NULL, cli_command_option },
227 
228   { "quit",    'q', NULL,     MU_OPTION_DEFAULT,
229     N_("cause interrupts to terminate program"),
230     mu_c_string, NULL, cli_command_option },
231 
232   { "subject", 's', N_("SUBJ"), MU_OPTION_DEFAULT,
233     N_("send a message with the given SUBJECT"),
234     mu_c_string, NULL, cli_subject },
235 
236   { "to",      't', NULL,       MU_OPTION_DEFAULT,
237     N_("read recipients from the message header"),
238     mu_c_string, NULL, cli_command_option },
239 
240   { "user",    'u', N_("USER"), MU_OPTION_DEFAULT,
241     N_("operate on USER's mailbox"),
242     mu_c_string, &user },
243 
244   { "append",   'a', N_("HEADER: VALUE"), MU_OPTION_DEFAULT,
245     N_("append given header to the message being sent"),
246     mu_c_string, NULL, cli_append },
247 
248   { "alternative", 0, NULL, MU_OPTION_DEFAULT,
249     N_("force multipart/alternative content type"),
250     mu_c_bool, &multipart_alternative },
251 
252   { "skip-empty-attachments", 0, NULL, MU_OPTION_DEFAULT,
253     N_("skip attachments with empty body"),
254     mu_c_bool, &skip_empty_attachments },
255 
256   { "exec" ,    'E', N_("COMMAND"), MU_OPTION_DEFAULT,
257     N_("execute COMMAND"),
258     mu_c_string, NULL, cli_command_option },
259 
260   { "encoding",  0, N_("NAME"), MU_OPTION_DEFAULT,
261     N_("set encoding for subsequent --attach options"),
262     mu_c_string, &default_encoding },
263 
264   { "content-type", 0, N_("TYPE"), MU_OPTION_DEFAULT,
265     N_("set content type for subsequent --attach options"),
266     mu_c_string, &default_content_type },
267 
268   { "content-name", 0, N_("NAME"), MU_OPTION_DEFAULT,
269     /* TRANSLATORS: Don't translate "Content-Type" and "name"! */
270     N_("set the Content-Type name parameter for the next --attach option"),
271     mu_c_string, &content_name },
272   { "content-filename", 0, N_("NAME"), MU_OPTION_DEFAULT,
273     /* TRANSLATORS: Don't translate "Content-Disposition" and "filename"! */
274     N_("set the Content-Disposition filename parameter for the next --attach option"),
275     mu_c_string, &content_filename },
276 
277   { "attach",  'A', N_("FILE"), MU_OPTION_DEFAULT,
278     N_("attach FILE"),
279     mu_c_string, NULL, cli_attach },
280 
281   { "attach-fd",  0, N_("FD"), MU_OPTION_DEFAULT,
282     N_("attach from file descriptor FD"),
283     mu_c_string, NULL, cli_attach_fd },
284 
285   { "mime",    'M',  NULL, MU_OPTION_DEFAULT,
286     N_("compose MIME messages"),
287     mu_c_bool, &mime_option },
288 
289   MU_OPTION_END
290 }, *options[] = { mail_options, NULL };
291 
292 static const char *alt_args[] = {
293   N_("[OPTION...] [file]"),
294   N_("--file [OPTION...] [file]"),
295   N_("--file=file [OPTION...]"),
296   NULL
297 };
298 
299 static struct mu_cli_setup cli = {
300   options,
301   NULL,
302   /* TRANSLATORS: "mail" is the name of the program. Don't translate it. */
303   N_("GNU mail -- process mail messages.\n"
304      "If -f or --file is given, mail operates on the mailbox named "
305      "by the first argument, or the user's mbox, if no argument given."),
306   N_("[address...]"),
307   alt_args,
308   NULL,
309   1,
310   1
311 };
312 
313 static char *mail_capa[] = {
314   "address",
315   "debug",
316   "mailbox",
317   "locking",
318   NULL
319 };
320 
321 static char *
mail_cmdline(void * closure,int cont MU_ARG_UNUSED)322 mail_cmdline (void *closure, int cont MU_ARG_UNUSED)
323 {
324   char *prompt = (char*) closure;
325   char *rc;
326 
327   while (1)
328     {
329       if (mailvar_is_true (mailvar_name_autoinc)
330 	  && !mu_mailbox_is_updated (mbox))
331 	{
332 	  mu_mailbox_messages_count (mbox, &total);
333 	  page_invalidate (0);
334 	  mu_printf (_("New mail has arrived.\n"));
335 	}
336 
337       rc = ml_readline (prompt);
338 
339       if (ml_got_interrupt ())
340 	{
341 	  mu_error (_("Interrupt"));
342 	  continue;
343 	}
344 
345       if (!rc && mailvar_is_true (mailvar_name_ignoreeof))
346 	{
347 	  mu_error (_("Use \"quit\" to quit."));
348 	  continue;
349 	}
350 
351       break;
352     }
353   return rc;
354 }
355 
356 static char *default_setup[] = {
357   /* "set noallnet", */
358   "setq append",
359   "set asksub",
360   "set crt",
361   "set noaskbcc",
362   "set askcc",
363   "set noautoprint",
364   "set nobang",
365   "set nocmd",
366   /*  "set nodebug",*/
367   "set nodot",
368   "set escape=~",
369   "set noflipr",
370   "set nofolder",
371   "set header",
372   "set nohold",
373   "set noignore",
374   "set noignoreeof",
375   "set indentprefix=\"\t\"",
376   "setq keep",
377   "set nokeepsave",
378   "set nometoo",
379   "set noonehop",
380   "set nooutfolder",
381   "set nopage",
382   "set prompt=\"? \"",
383   "set norecord",
384   "set save",
385   "set nosendmail",
386   "set nosendwait",
387   "set noshowto",
388   "set nosign",
389   "set noSign",
390   "set toplines=5",
391   "set autoinc",
392   "set regex",
393   "set replyprefix=\"Re: \"",
394   "set charset=auto",
395   "set useragent",
396   "unfold subject",
397   "sender mail-followup-to reply-to from",
398   "set nocmd",
399   "set metamail",
400   "set recursivealiases",
401   "set noinplacealiases",
402   "set fromfield",
403   "set headline=\"%>%a%4m %18f %16d %3L/%-5o %s\"",
404   "unset folder",
405   "set fullnames",
406   "set outfilename=local",
407 
408   /* Start in mail reading mode */
409   "setq mode=read",
410   "set noquit",
411   "set rc",
412 
413   "set noflipr",
414   "set noshowto",
415   "set nobang",
416 
417   "set nullbody", /* Null message body is traditionally allowed */
418   "set nullbodymsg=\"" N_("Null message body; hope that's ok") "\"",
419 
420   /* These settings are not yet used */
421   "set noonehop",
422   "set nosendwait",
423 };
424 
425 static void
do_and_quit(const char * command)426 do_and_quit (const char *command)
427 {
428   int rc = util_do_command ("%s", command);
429   mu_mailbox_close (mbox);
430   exit (rc != 0);
431 }
432 
433 int
main(int argc,char ** argv)434 main (int argc, char **argv)
435 {
436   char *mode = NULL, *prompt = NULL, *p;
437   int i, rc;
438 
439   mu_stdstream_setup (MU_STDSTREAM_RESET_NONE);
440   set_cursor (1);
441 
442   /* Native Language Support */
443   MU_APP_INIT_NLS ();
444 
445   /* Register the desired formats.  */
446   mu_register_all_formats ();
447 
448   mu_auth_register_module (&mu_auth_tls_module);
449 
450   interactive = isatty (fileno (stdin));
451 #ifdef HAVE_SIGACTION
452   {
453     struct sigaction act;
454     act.sa_handler = SIG_IGN;
455     sigemptyset (&act.sa_mask);
456     act.sa_flags = 0;
457     sigaction (SIGPIPE, &act, NULL);
458   }
459 #else
460   signal (SIGPIPE, SIG_IGN);
461 #endif
462 
463   /* set up the default environment */
464   if (!getenv ("HOME"))
465     setenv ("HOME", util_get_homedir (), 0);
466 
467   /* Set up the default environment */
468   setenv ("DEAD", util_fullpath ("~/dead.letter"), 0);
469   setenv ("EDITOR", "ed", 0);
470   setenv ("LISTER", "ls", 0);
471   setenv ("MAILRC", util_fullpath ("~/.mailrc"), 0);
472   setenv ("MBOX", util_fullpath ("~/mbox"), 0);
473   setenv ("PAGER", "more", 0);
474   setenv ("SHELL", "sh", 0);
475   setenv ("VISUAL", "vi", 0);
476 
477   /* set defaults for execution */
478   util_do_command ("setq PID=\"%lu\"", (unsigned long) getpid ());
479   for (i = 0; i < sizeof (default_setup)/sizeof (default_setup[0]); i++)
480     util_do_command ("%s", default_setup[i]);
481 
482   p = getenv ("LINES");
483   if (p && p[strspn (p, "0123456789")] == 0)
484     util_do_command ("set screen=%s", p);
485   else
486     util_do_command ("set screen=%d", util_getlines ());
487 
488   p = getenv ("COLUMNS");
489   if (p && p[strspn (p, "0123456789")] == 0)
490     util_do_command ("set columns=%s", p);
491   else
492     util_do_command ("set columns=%d", util_getcols ());
493 
494   /* Set the default mailer to sendmail. FIXME: Minor memory leak. */
495   mailvar_set (mailvar_name_sendmail,
496 	       mu_strdup ("sendmail:" PATH_SENDMAIL), mailvar_type_string,
497 	       MOPTF_OVERWRITE);
498 
499   /* argument parsing */
500   mu_cli (argc, argv, &cli, mail_capa, NULL, &argc, &argv);
501 
502   if (default_content_type || default_encoding)
503     mime_option = 1;
504   if (mime_option)
505     util_cache_command (&command_list, "set mime");
506 
507   if (read_recipients)
508     {
509       argv += argc;
510       argc = 0;
511     }
512 
513   if ((hint & (HINT_SEND_MODE|HINT_FILE_OPTION)) ==
514       (HINT_SEND_MODE|HINT_FILE_OPTION))
515     {
516       mu_error (_("conflicting options"));
517       exit (1);
518     }
519   else if (hint & HINT_FILE_OPTION)
520     {
521       if (file)
522 	{
523 	  if (argc)
524 	    {
525 	      mu_error (_("-f requires at most one command line argument"));
526 	      exit (1);
527 	    }
528 	}
529       else if (argc)
530 	{
531 	  if (argc > 1)
532 	    {
533 	      mu_error (_("-f requires at most one command line argument"));
534 	      exit (1);
535 	    }
536 	  file = mu_strdup (argv[0]);
537 	}
538       else if (user)
539 	mu_asprintf (&file, "~/%s/mbox", user);
540       else
541 	file = mu_strdup ("~/mbox");
542     }
543   else if (argc || (hint & HINT_SEND_MODE))
544     util_cache_command (&command_list, "setq mode=send");
545   else if (user)
546     mu_asprintf (&file, "%%%s", user);
547 
548 
549   /* read system-wide mail.rc and user's .mailrc */
550   if (mailvar_is_true (mailvar_name_rc))
551     util_do_command ("source %s", SITE_MAIL_RC);
552   if ((p = getenv ("MAILRC")) && *p)
553     util_do_command ("source %s", getenv ("MAILRC"));
554 
555   util_run_cached_commands (&command_list);
556 
557   if (interactive)
558     {
559       /* Reset standard error stream so that it does not print program
560 	 name before the actual diagnostic message. */
561       mu_stream_t errstr;
562       int rc = mu_stdstream_strerr_create (&errstr, MU_STRERR_STDERR, 0, 0,
563 					   NULL, NULL);
564       if (rc == 0)
565 	{
566 	  mu_stream_destroy (&mu_strerr);
567 	  mu_strerr = errstr;
568 	}
569     }
570   else
571     {
572       util_do_command ("set nocrt");
573       util_do_command ("set noasksub");
574       util_do_command ("set noaskcc");
575       util_do_command ("set noaskbcc");
576     }
577 
578   /* how should we be running? */
579   if (mailvar_get (&mode, mailvar_name_mode, mailvar_type_string, 1))
580     exit (EXIT_FAILURE);
581 
582   /* Interactive mode */
583 
584   ml_readline_init ();
585   mail_set_my_name (user);
586 
587   /* Mode is just sending */
588   if (strcmp (mode, "send") == 0)
589     {
590       --argv;
591       ++argc;
592       if (hint & HINT_BYNAME)
593 	argv[0] = "Mail";
594       else
595 	argv[0] = "mail";
596       return mail_send (argc, argv)
597 	       ? (mailvar_is_true (mailvar_name_mailx) ? 0 : EXIT_FAILURE)
598 	       : 0;
599     }
600   /* Or acting as a normal reader */
601   else
602     {
603       if ((rc = mu_mailbox_create_default (&mbox, file)) != 0)
604 	{
605 	  if (file)
606 	    mu_error (_("Cannot create mailbox %s: %s"), file,
607 			mu_strerror (rc));
608 	  else
609 	    mu_error (_("Cannot create mailbox: %s"),
610 			mu_strerror (rc));
611 	  exit (EXIT_FAILURE);
612 	}
613 
614       if (file)
615 	{
616 	  /* Save URL of the file for further use */
617 	  mu_url_t url;
618 
619 	  if (mu_mailbox_get_url (mbox, &url) == 0)
620 	    {
621 	      rc = mu_url_dup (url, &secondary_url);
622 	      if (rc)
623 		{
624 		  mu_diag_funcall (MU_DIAG_ERROR, "mu_url_dup", NULL, rc);
625 		  exit (EXIT_FAILURE);
626 		}
627 	    }
628 	  /* Destroy the content of file prior to freeing it: it can contain
629 	     password, although such usage is discouraged */
630 	  memset (file, 0, strlen (file));
631 	  free (file);
632 	}
633 
634       if ((rc = mu_mailbox_open (mbox, MU_STREAM_RDWR|MU_STREAM_CREAT)) != 0)
635 	{
636 	  if (rc == EACCES)
637 	    {
638 	      rc = mu_mailbox_open (mbox, MU_STREAM_READ);
639 	      if (rc == 0)
640 		mu_diag_output (MU_DIAG_WARNING, _("mailbox opened read-only"));
641 	      //FIXME: Disable 'q' and similar commands
642 	    }
643 	  if (rc)
644 	    {
645 	      mu_url_t url = NULL;
646 	      mu_mailbox_get_url (mbox, &url);
647 	      mu_error (_("Cannot open mailbox %s: %s"),
648 			mu_url_to_string (url), mu_strerror (rc));
649 	      mu_mailbox_destroy (&mbox);
650 	    }
651 	}
652 
653       if (rc)
654 	total = 0;
655       else
656 	{
657 	  if ((rc = mu_mailbox_scan (mbox, 1, &total)) != 0)
658 	    {
659 	      mu_url_t url = NULL;
660 	      mu_mailbox_get_url (mbox, &url);
661 	      mu_error (_("Cannot read mailbox %s: %s"),
662 			  mu_url_to_string (url), mu_strerror (rc));
663 	      exit (EXIT_FAILURE);
664 	    }
665 
666 	  if (strcmp (mode, "exist") == 0)
667 	    {
668 	      mu_mailbox_close (mbox);
669 	      return (total < 1) ? 1 : 0;
670 	    }
671 	  else if (strcmp (mode, "print") == 0)
672 	    do_and_quit ("print *");
673 	  else if (strcmp (mode, "headers") == 0)
674 	    do_and_quit ("from *");
675 	  else if (strcmp (mode, "read"))
676 	    {
677 	      mu_error (_("Unknown mode `%s'"), mode);
678 	      util_do_command (mailvar_name_quit);
679 	      return 1;
680 	    }
681 	}
682 
683       if (total == 0
684 	  && (strcmp (mode, "read")
685 	      || !mailvar_is_true (mailvar_name_emptystart)))
686 	{
687 	  if (secondary_url)
688 	    mail_summary (0, NULL);
689 	  else
690 	    mu_printf (_("No mail for %s\n"), user ? user : mail_whoami ());
691 	  return 1;
692 	}
693 
694       /* initial commands */
695       if (mailvar_is_true (mailvar_name_header))
696 	{
697 	  util_do_command ("summary");
698 	  util_do_command ("headers");
699 	}
700 
701       mailvar_get (&prompt, mailvar_name_prompt, mailvar_type_string, 0);
702       mail_mainloop (mail_cmdline, (void*) prompt, 1);
703       mu_printf ("\n");
704       util_do_command (mailvar_name_quit);
705       return 0;
706     }
707   /* We should never reach this point */
708   return 1;
709 }
710 
711 
712 void
mail_mainloop(char * (* input)(void *,int),void * closure,int do_history)713 mail_mainloop (char *(*input) (void *, int),
714 	       void *closure, int do_history)
715 {
716   char *command, *cmd;
717 
718   while ((command = (*input) (closure, 0)) != NULL)
719     {
720       int len = strlen (command);
721       while (len > 0 && command[len-1] == '\\')
722 	{
723 	  char *buf;
724 	  char *command2 = (*input) (closure, 1);
725 
726 	  if (!command2)
727 	    {
728 	      command[len-1] = 0;
729 	      break;
730 	    }
731 	  command[len-1] = '\0';
732 	  buf = mu_alloc ((len + strlen (command2)) * sizeof (char));
733 	  strcpy (buf, command);
734 	  strcat (buf, command2);
735 	  free (command);
736 	  command = buf;
737 	  len = strlen (command);
738 	}
739       cmd = mu_str_stripws (command);
740       util_do_command ("%s", cmd);
741 #ifdef WITH_READLINE
742       if (do_history && !(mu_isspace (cmd[0]) || cmd[0] == '#'))
743 	add_history (cmd);
744 #endif
745       free (command);
746     }
747 }
748 
749 int
mail_warranty(int argc MU_ARG_UNUSED,char ** argv MU_ARG_UNUSED)750 mail_warranty (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
751 {
752   mu_version_print (mu_strout);
753   return 0;
754 }
755