1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2010-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 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <unistd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <netdb.h>
24 #include <netinet/in.h>
25 #include <mailutils/cctype.h>
26 #include <mailutils/mailutils.h>
27 #include <mailutils/smtp.h>
28 #include "mu.h"
29 
30 char smtp_docstring[] = N_("run a SMTP session");
31 
32 enum smtp_session_status
33   {
34     smtp_session_disconnected,
35     smtp_session_connected,
36     smtp_session_logged_in
37   };
38 
39 static enum smtp_session_status smtp_session_status;
40 static int connect_argc;
41 static char **connect_argv;
42 
43 /* Host we are connected to. */
44 #define host connect_argv[0]
45 static int port = 25;
46 
47 static char *sender;
48 static mu_list_t recipients;
49 
50 static char *msgfile;
51 static int temp_msgfile;
52 static mu_smtp_t smtp;
53 
54 const char *
smtp_session_str(enum smtp_session_status stat)55 smtp_session_str (enum smtp_session_status stat)
56 {
57   switch (stat)
58     {
59     case smtp_session_disconnected:
60       return "disconnected";
61 
62     case smtp_session_connected:
63       return "connected";
64 
65     case smtp_session_logged_in:
66       return "logged in";
67     }
68   return "unknown";
69 }
70 
71 static void
smtp_prompt_env(void)72 smtp_prompt_env (void)
73 {
74   mu_assoc_t assoc = mutool_shell_prompt_assoc ();
75   const char *value;
76 
77   if (smtp_session_status == smtp_session_logged_in &&
78       mu_smtp_get_param (smtp, MU_SMTP_PARAM_USERNAME, &value) == 0)
79     mu_assoc_install (assoc, "user", (void*) value);
80 
81   if (smtp_session_status != smtp_session_disconnected)
82     mu_assoc_install (assoc, "host", host);
83   mu_assoc_install (assoc, "status",
84 		    (void*) smtp_session_str (smtp_session_status));
85 }
86 
87 static void
smtp_set_verbose(void)88 smtp_set_verbose (void)
89 {
90   if (smtp)
91     {
92       if (QRY_VERBOSE ())
93 	mu_smtp_trace (smtp, MU_SMTP_TRACE_SET);
94       else
95 	mu_smtp_trace (smtp, MU_SMTP_TRACE_CLR);
96     }
97 }
98 
99 static void
smtp_set_verbose_mask(void)100 smtp_set_verbose_mask (void)
101 {
102   if (smtp)
103     {
104       mu_smtp_trace_mask (smtp, QRY_VERBOSE_MASK (MU_XSCRIPT_SECURE)
105 			          ? MU_SMTP_TRACE_SET : MU_SMTP_TRACE_CLR,
106 			      MU_XSCRIPT_SECURE);
107       mu_smtp_trace_mask (smtp, QRY_VERBOSE_MASK (MU_XSCRIPT_PAYLOAD)
108 			          ? MU_SMTP_TRACE_SET : MU_SMTP_TRACE_CLR,
109 			      MU_XSCRIPT_PAYLOAD);
110     }
111 }
112 
113 static int
com_verbose(int argc,char ** argv)114 com_verbose (int argc, char **argv)
115 {
116   return shell_verbose (argc, argv,
117 			smtp_set_verbose, smtp_set_verbose_mask);
118 }
119 
120 static int
smtp_error_handler(int rc)121 smtp_error_handler (int rc)
122 {
123   if (rc == 0 || rc == MU_ERR_REPLY)
124     {
125       char code[4];
126       const char *repl;
127 
128       mu_smtp_replcode (smtp, code);
129       mu_smtp_sget_reply (smtp, &repl);
130       mu_printf ("%s %s\n", code, repl);
131     }
132   return rc;
133 }
134 
135 static int
com_disconnect(int argc MU_ARG_UNUSED,char ** argv MU_ARG_UNUSED)136 com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED)
137 {
138   if (smtp)
139     {
140       mu_smtp_disconnect (smtp);
141       mu_smtp_destroy (&smtp);
142       smtp = NULL;
143 
144       mu_argcv_free (connect_argc, connect_argv);
145       connect_argc = 0;
146       connect_argv = NULL;
147       smtp_session_status = smtp_session_disconnected;
148       smtp_prompt_env ();
149     }
150   return 0;
151 }
152 
153 static int
com_connect(int argc,char ** argv)154 com_connect (int argc, char **argv)
155 {
156   int status;
157   int tls = 0;
158   int i = 1;
159   int n;
160 
161   for (i = 1; i < argc; i++)
162     {
163       if (strcmp (argv[i], "-tls") == 0)
164 	{
165 #ifdef WITH_TLS
166 	  tls = 1;
167 #else
168 	  mu_error ("TLS not supported");
169 	  return 0;
170 #endif
171 	}
172       else
173 	break;
174     }
175 
176   argc -= i;
177   argv += i;
178 
179   if (smtp_session_status != smtp_session_disconnected)
180     com_disconnect (0, NULL);
181 
182   status = mu_smtp_create (&smtp);
183   if (status == 0)
184     {
185       mu_stream_t tcp;
186       struct mu_sockaddr *sa;
187       struct mu_sockaddr_hints hints;
188 
189       if (QRY_VERBOSE ())
190 	{
191 	  smtp_set_verbose ();
192 	  smtp_set_verbose_mask ();
193 	}
194 
195       memset (&hints, 0, sizeof (hints));
196       hints.flags = MU_AH_DETECT_FAMILY;
197       hints.port = tls ? 465 : 25;
198       hints.protocol = IPPROTO_TCP;
199       hints.socktype = SOCK_STREAM;
200       status = mu_sockaddr_from_node (&sa, argv[0], argv[1], &hints);
201       if (status == 0)
202 	{
203 	  n = port_from_sa (sa);
204 	  status = mu_tcp_stream_create_from_sa (&tcp, sa, NULL, 0);
205 	  if (status)
206 	    mu_sockaddr_free (sa);
207 	}
208       if (status == 0)
209 	{
210 #ifdef WITH_TLS
211 	  if (tls)
212 	    {
213 	      mu_stream_t tlsstream;
214 
215 	      status = mu_tls_client_stream_create (&tlsstream, tcp, tcp, 0);
216 	      mu_stream_unref (tcp);
217 	      if (status)
218 		{
219 		  mu_error ("cannot create TLS stream: %s",
220 			    mu_strerror (status));
221 		  return 0;
222 		}
223 	      tcp = tlsstream;
224 	    }
225 #endif
226 	  mu_smtp_set_carrier (smtp, tcp);
227 	  status = smtp_error_handler (mu_smtp_open (smtp));
228 	}
229       else
230 	{
231 	  mu_smtp_destroy (&smtp);
232 	  smtp = NULL;
233 	}
234     }
235 
236   if (status)
237     mu_error ("Failed to create smtp: %s", mu_strerror (status));
238   else
239     {
240       connect_argc = argc;
241       connect_argv = mu_calloc (argc + 1, sizeof (*connect_argv));
242       for (i = 0; i < argc; i++)
243 	connect_argv[i] = mu_strdup (argv[i]);
244       connect_argv[i] = NULL;
245       port = n;
246       smtp_session_status = smtp_session_connected;
247 
248       smtp_prompt_env ();
249     }
250 
251   /* Provide a default URL.  Authentication functions require it, see comment
252      in smtp_auth.c:119. */
253   mu_smtp_set_param (smtp, MU_SMTP_PARAM_URL, "smtp://");
254 
255   return status;
256 }
257 
258 static int
com_capa(int argc,char ** argv)259 com_capa (int argc, char **argv)
260 {
261   mu_iterator_t iterator = NULL;
262   int status = 0;
263   int i = 1;
264 
265   if (i < argc)
266     {
267       for (; i < argc; i++)
268 	{
269 	  const char *elt;
270 	  int rc = mu_smtp_capa_test (smtp, argv[i], &elt);
271 	  switch (rc)
272 	    {
273 	    case 0:
274 	      if (*elt)
275 		mu_printf ("%s: %s\n", argv[i], elt);
276 	      else
277 		mu_printf ("%s is set\n", argv[i]);
278 	      break;
279 
280 	    case MU_ERR_NOENT:
281 	      mu_printf ("%s is not set\n", argv[i]);
282 	      break;
283 
284 	    default:
285 	      return smtp_error_handler (rc);
286 	    }
287 	}
288     }
289   else
290     {
291       status = mu_smtp_capa_iterator (smtp, &iterator);
292 
293       if (status == 0)
294 	{
295 	  for (mu_iterator_first (iterator);
296 	       !mu_iterator_is_done (iterator); mu_iterator_next (iterator))
297 	    {
298 	      char *capa = NULL;
299 	      mu_iterator_current (iterator, (void **) &capa);
300 	      mu_printf ("CAPA: %s\n", mu_prstr (capa));
301 	    }
302 	  mu_iterator_destroy (&iterator);
303 	}
304     }
305   return status;
306 }
307 
308 static int
com_ehlo(int argc,char ** argv)309 com_ehlo (int argc, char **argv)
310 {
311   if (argc == 1)
312     {
313       if (mu_smtp_test_param (smtp, MU_SMTP_PARAM_DOMAIN))
314 	{
315 	  mu_error (_("no domain set"));
316 	  return 0;
317 	}
318     }
319   else
320     mu_smtp_set_param (smtp, MU_SMTP_PARAM_DOMAIN, argv[1]);
321   return com_capa (1, argv);
322 }
323 
324 static int
com_rset(int argc,char ** argv)325 com_rset (int argc, char **argv)
326 {
327   return smtp_error_handler (mu_smtp_rset (smtp));
328 }
329 
330 static int
com_quit(int argc,char ** argv)331 com_quit (int argc, char **argv)
332 {
333   int status = 0;
334   if (smtp)
335     {
336       if (smtp_error_handler (mu_smtp_quit (smtp)) == 0)
337 	{
338 	  status = com_disconnect (0, NULL);
339 	}
340       else
341 	{
342 	  mu_printf ("Try 'exit' to leave %s\n", mu_program_name);
343 	}
344     }
345   else
346     mu_printf ("Try 'exit' to leave %s\n", mu_program_name);
347   return status;
348 }
349 
350 static int
com_from(int argc,char ** argv)351 com_from (int argc, char **argv)
352 {
353   if (argc == 1)
354     {
355       if (!sender)
356 	{
357 	  mu_error (_("no sender address"));
358 	  return 0;
359 	}
360     }
361   else
362     {
363       free (sender);
364       sender = mu_strdup (argv[1]);
365     }
366   return smtp_error_handler (mu_smtp_mail_basic (smtp, sender, NULL));
367 }
368 
369 static int
send_rcpt_to(void * item,void * data)370 send_rcpt_to (void *item, void *data)
371 {
372   return smtp_error_handler (mu_smtp_rcpt_basic (smtp, (char*) item, NULL));
373 }
374 
375 static int
com_to(int argc,char ** argv)376 com_to (int argc, char **argv)
377 {
378   int rc;
379 
380   if (argc == 1)
381     {
382       if (mu_list_is_empty (recipients))
383 	{
384 	  mu_error (_("no recipients"));
385 	  return 1;
386 	}
387       mu_list_foreach (recipients, send_rcpt_to, NULL);
388       rc = 0;
389     }
390   else
391     {
392       if (!recipients)
393 	mu_list_create (&recipients);
394       mu_list_set_destroy_item (recipients, mu_list_free_item);
395       rc = smtp_error_handler (mu_smtp_rcpt_basic (smtp, argv[1], NULL));
396       if (rc == 0)
397 	mu_list_append (recipients, mu_strdup (argv[1]));
398     }
399   return rc;
400 }
401 
402 static int
edit(const char * file)403 edit (const char *file)
404 {
405   char *ed;
406   char *edv[3];
407   int rc, status;
408 
409   ed = getenv ("VISUAL");
410   if (!ed)
411     {
412       ed = getenv ("EDITOR");
413       if (!ed)
414 	ed = "/bin/ed";
415     }
416 
417   edv[0] = ed;
418   edv[1] = (char*) file;
419   edv[2] = NULL;
420 
421   rc = mu_spawnvp (edv[0], edv, &status);
422   if (rc)
423     mu_diag_funcall (MU_DIAG_ERROR, "mu_spawnvp", edv[0], rc);
424   return rc;
425 }
426 
427 struct rcptout
428 {
429   mu_stream_t str;
430   int n;
431 };
432 
433 static int
print_rcpt(void * item,void * data)434 print_rcpt (void *item, void *data)
435 {
436   struct rcptout *p = data;
437   if (p->n++)
438     mu_stream_write (p->str, ", ", 2, NULL);
439   mu_stream_printf (p->str, "%s", (char *)item);
440   return 0;
441 }
442 
443 static int
edit_file(const char * fname,int inplace)444 edit_file (const char *fname, int inplace)
445 {
446   int rc;
447 
448   if (fname && !inplace)
449     {
450       mu_stream_t istr, ostr;
451 
452       rc = mu_file_stream_create (&istr, fname, MU_STREAM_READ|MU_STREAM_SEEK);
453       if (rc == 0)
454 	{
455 	  char *tempfile = mu_tempname (NULL);
456 	  rc = mu_file_stream_create (&ostr, tempfile,
457 				      MU_STREAM_CREAT|MU_STREAM_WRITE);
458 	  if (rc)
459 	    {
460 	      free (tempfile);
461 	      mu_error (_("cannot create temporary file: %s"),
462 			mu_strerror (rc));
463 	      return -1;
464 	    }
465 	  rc = mu_stream_copy (ostr, istr, 0, NULL);
466 	  if (rc)
467 	    {
468 	      unlink (tempfile);
469 	      free (tempfile);
470 	      mu_error (_("error copying to temporary file: %s"),
471 			mu_strerror (rc));
472 	      return -1;
473 	    }
474 	  mu_stream_unref (ostr);
475 	  free (msgfile);
476 	  msgfile = tempfile;
477 	  temp_msgfile = 1;
478 	}
479       else if (rc != ENOENT)
480 	{
481 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_file_stream_create", fname, rc);
482 	  return 1;
483 	}
484       mu_stream_unref (istr);
485     }
486   else if (!fname)
487     {
488       struct rcptout rcptout;
489 
490       if (temp_msgfile)
491 	unlink (msgfile);
492       free (msgfile);
493       msgfile = mu_tempname (NULL);
494       temp_msgfile = 1;
495 
496       rc = mu_file_stream_create (&rcptout.str, msgfile,
497 				  MU_STREAM_CREAT|MU_STREAM_WRITE);
498       if (rc)
499 	{
500 	  mu_error (_("cannot open temporary file for writing: %s"),
501 		    mu_strerror (rc));
502 	  return 1;
503 	}
504       rcptout.n = 0;
505       if (sender)
506 	mu_stream_printf (rcptout.str, "From: %s\n", sender);
507       else
508 	mu_stream_printf (rcptout.str, "From: \n");
509       mu_stream_printf (rcptout.str, "To: ");
510       mu_list_foreach (recipients, print_rcpt, &rcptout);
511       mu_stream_write (rcptout.str, "\n", 1, NULL);
512       mu_stream_printf (rcptout.str, "Subject: \n\n");
513       mu_stream_unref (rcptout.str);
514     }
515   else
516     {
517       free (msgfile);
518       msgfile = mu_strdup (fname);
519       temp_msgfile = 0;
520     }
521 
522   do
523     {
524       if (edit (msgfile))
525 	return 1;
526     }
527   while ((rc = mu_getans ("seqSEQ", _("What now: [s]end, [e]dit, [q]uit")))
528 	 == 'e' || rc == 'E');
529 
530   return rc == 'q' || rc == 'Q';
531 }
532 
533 static int
com_send(int argc,char ** argv)534 com_send (int argc, char **argv)
535 {
536   int rc;
537   mu_stream_t instr;
538 
539   if (argc == 1)
540     {
541       if (msgfile)
542 	{
543 	  switch (mu_getans ("rReEdD",
544 			     _("Previous message exists. "
545 			       "What now: [r]euse, [e]dit, "
546 			       "[u]se as a template or\n"
547 			       "[d]rop and start from scratch")))
548 	    {
549 	    case 'r':
550 	    case 'R':
551 	      rc = 0;
552 	      break;
553 
554 	    case 'e':
555 	    case 'E':
556 	      rc = edit_file (msgfile, 1);
557 	      break;
558 
559 	    case 'd':
560 	    case 'D':
561 	      if (temp_msgfile)
562 		unlink (msgfile);
563 	      free (msgfile);
564 	      msgfile = NULL;
565 	      temp_msgfile = 0;
566 	      rc = edit_file (NULL, 0);
567 	      break;
568 
569 	    case 'u':
570 	    case 'U':
571 	      rc = edit_file (msgfile, 0);
572 	    }
573 	}
574       else
575 	rc = edit_file (NULL, 0);
576       if (rc)
577 	return 0;
578     }
579   else
580     {
581       if (temp_msgfile)
582 	unlink (msgfile);
583       free (msgfile);
584       msgfile = NULL;
585       temp_msgfile = 0;
586       msgfile = mu_strdup (argv[1]);
587     }
588 
589   rc = mu_file_stream_create (&instr, msgfile, MU_STREAM_READ|MU_STREAM_SEEK);
590   if (rc)
591     {
592       mu_diag_funcall (MU_DIAG_ERROR, "mu_file_stream_create", msgfile, rc);
593       return 1;
594     }
595 
596   rc = mu_smtp_send_stream (smtp, instr);
597   mu_stream_unref (instr);
598 
599   if (rc)
600     smtp_error_handler (rc);
601   else
602     rc = smtp_error_handler (mu_smtp_dot (smtp));
603 
604   return rc;
605 }
606 
607 static int
com_starttls(int argc,char ** argv)608 com_starttls (int argc, char **argv)
609 {
610   if (mu_smtp_capa_test (smtp, "STARTTLS", NULL) == 0)
611     return smtp_error_handler (mu_smtp_starttls (smtp));
612   else
613     mu_error (_("remote party does not offer STARTTLS"));
614   return 1;
615 }
616 
617 static int
com_auth(int argc,char ** argv)618 com_auth (int argc, char **argv)
619 {
620   int rc, i;
621 
622   rc = mu_smtp_clear_auth_mech (smtp);
623   if (rc)
624     {
625       mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_clear_auth_mech", NULL, rc);
626       return MU_ERR_FAILURE;
627     }
628   for (i = 1; i < argc; i++)
629     if ((rc = mu_smtp_add_auth_mech (smtp, argv[1])))
630       {
631 	mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_add_auth_mech", NULL, rc);
632 	return MU_ERR_FAILURE;
633       }
634 
635   rc = mu_smtp_auth (smtp);
636 
637   switch (rc)
638     {
639     case 0:
640       smtp_session_status = smtp_session_logged_in;
641       break;
642 
643     case ENOSYS:
644       mu_error (_("authentication not implemented"));
645       break;
646 
647     case MU_ERR_NOENT:
648       mu_error (_("no suitable authentication mechanism found"));
649       break;
650 
651     default:
652       smtp_error_handler (rc);
653       return rc;
654     }
655   return 0;
656 }
657 
658 static struct mu_kwd paramtab[] = {
659   { "domain",      MU_SMTP_PARAM_DOMAIN },
660   { "username",    MU_SMTP_PARAM_USERNAME },
661   { "password",    MU_SMTP_PARAM_PASSWORD },
662   { "service",     MU_SMTP_PARAM_SERVICE },
663   { "realm",       MU_SMTP_PARAM_REALM },
664   { "host",        MU_SMTP_PARAM_HOST },
665   { "url",         MU_SMTP_PARAM_URL },
666   { NULL }
667 };
668 
669 static int
get_param(int param,char * prompt,char ** retval)670 get_param (int param, char *prompt, char **retval)
671 {
672   int rc;
673 
674   if (param == MU_SMTP_PARAM_PASSWORD)
675     {
676       rc = mu_getpass (mu_strin, mu_strout, prompt, retval);
677       if (rc)
678 	mu_diag_funcall (MU_DIAG_ERROR, "mu_getpass", NULL, rc);
679     }
680   else
681     {
682       char *buf = NULL;
683       size_t size = 0;
684       rc = mu_stream_write (mu_strout, prompt, strlen (prompt), NULL);
685       if (rc)
686 	return rc;
687       mu_stream_flush (mu_strout);
688       rc = mu_stream_getline (mu_strin, &buf, &size, NULL);
689       if (rc == 0)
690 	{
691 	  mu_rtrim_cset (buf, "\n");
692 	  *retval  = buf;
693 	}
694     }
695   return rc;
696 }
697 
698 static int
com_set(int argc,char ** argv)699 com_set (int argc, char **argv)
700 {
701   int param, i, rc;
702 
703   for (i = 1; i < argc; i += 2)
704     {
705       if (mu_kwd_xlat_name (paramtab, argv[i], &param))
706 	{
707 	  mu_error (_("unrecognized parameter: %s"), argv[i]);
708 	  continue;
709 	}
710       if (i + 1 < argc)
711 	{
712 	  rc = mu_smtp_set_param (smtp, param, argv[i+1]);
713 	  if (rc)
714 	    mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_set_param", argv[i], rc);
715 	}
716       else
717 	{
718 	  char *prompt, *value;
719 	  mu_asprintf (&prompt, "%s: ", argv[i]);
720 	  rc = get_param (param, prompt, &value);
721 	  free (prompt);
722 	  if (rc)
723 	    mu_error (_("error reading value: %s"), mu_strerror (rc));
724 	  else
725 	    {
726 	      rc = mu_smtp_set_param (smtp, param, value);
727 	      if (param == MU_SMTP_PARAM_PASSWORD)
728 		memset (value, 0, strlen (value));
729 	      if (rc)
730 		mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_set_param", argv[i],
731 				 rc);
732 	      free (value);
733 	    }
734 	}
735     }
736   return 0;
737 }
738 
739 static int
com_clear(int argc,char ** argv)740 com_clear (int argc, char **argv)
741 {
742   int param, i, rc;
743 
744   if (argc > 1)
745     {
746       for (i = 1; i < argc; i++)
747 	{
748 	  if (mu_kwd_xlat_name (paramtab, argv[i], &param))
749 	    {
750 	      mu_error (_("unrecognized parameter: %s"), argv[i]);
751 	      continue;
752 	    }
753 	  rc = mu_smtp_set_param (smtp, param, NULL);
754 	  if (rc)
755 	    mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_set_param", argv[i], rc);
756 	}
757     }
758   else
759     {
760       for (i = 0; paramtab[i].name; i++)
761 	{
762 	  rc = mu_smtp_set_param (smtp, paramtab[i].tok, NULL);
763 	  if (rc)
764 	    mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_set_param",
765 			     paramtab[i].name, rc);
766 	}
767     }
768   return 0;
769 }
770 
771 static int
com_list_param(int argc,char ** argv)772 com_list_param (int argc, char **argv)
773 {
774   int param, i, rc;
775   const char *value;
776 
777   if (!smtp)
778     {
779       mu_printf ("%s\n", _("no connection yet"));
780       return 0;
781     }
782 
783   if (argc > 1)
784     {
785       for (i = 1; i < argc; i++)
786 	{
787 	  if (mu_kwd_xlat_name (paramtab, argv[i], &param))
788 	    {
789 	      mu_error (_("unrecognized parameter: %s"), argv[i]);
790 	      continue;
791 	    }
792 	  rc = mu_smtp_get_param (smtp, param, &value);
793 	  if (rc)
794 	    mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_get_param", argv[i], rc);
795 	  else if (value)
796 	    mu_printf ("%s = %s\n", argv[i], value);
797 	  else
798 	    mu_printf (_("%s not set\n"), argv[i]);
799 	}
800     }
801   else
802     {
803       for (i = 0; paramtab[i].name; i++)
804 	{
805 	  rc = mu_smtp_get_param (smtp, paramtab[i].tok, &value);
806 	  if (rc)
807 	    mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_get_param",
808 			     paramtab[i].name, rc);
809 	  else if (value)
810 	    mu_printf ("%s = %s\n", paramtab[i].name, value);
811 	  else
812 	    mu_printf (_("%s not set\n"), paramtab[i].name);
813 	}
814     }
815   return 0;
816 }
817 
818 static int
com_smtp_command(int argc,char ** argv)819 com_smtp_command (int argc, char **argv)
820 {
821   int rc;
822   mu_iterator_t itr;
823 
824   rc = mu_smtp_cmd (smtp, argc - 1, argv + 1);
825   smtp_error_handler (rc);
826   if (rc)
827     return rc;
828   rc = mu_smtp_get_reply_iterator (smtp, &itr);
829   if (rc)
830     {
831       mu_diag_funcall (MU_DIAG_ERROR, "mu_smtp_get_reply_iterator", NULL, rc);
832       return 1;
833     }
834 
835   for (mu_iterator_first (itr);
836        !mu_iterator_is_done (itr); mu_iterator_next (itr))
837     {
838       char *str = NULL;
839       mu_iterator_current (itr, (void **) &str);
840       mu_printf ("%s\n", str);
841     }
842   mu_iterator_destroy (&itr);
843   return 0;
844 }
845 
846 struct mutool_command smtp_comtab[] = {
847   { "connect",    1, 4, 0, com_connect,
848     /* TRANSLATORS: -tls is a keyword. */
849     N_("[-tls] HOSTNAME [PORT]"),
850     N_("open connection") },
851 
852   { "set",        2, -1, 0, com_set,
853     N_("PARAM [ARG...]"),
854     N_("Set connection parameter") },
855   { "clear",      1, -1, 0, com_clear,
856     N_("[PARAM...]"),
857     N_("Clear connection parameters") },
858 
859   { "list",       1, -1, 0, com_list_param,
860     N_("[PARAM...]"),
861     N_("List connection parameters") },
862 
863   { "auth",       2, -1, 0, com_auth,
864     N_("MECH [MECH...]"),
865     N_("Authenticate") },
866 
867   { "ehlo",       1, 2, 0, com_ehlo,
868     N_("[DOMAIN]"),
869     N_("Greet the server") },
870 
871   { "capa",       1, -1, 0, com_capa,
872     N_("[NAME...]"),
873     N_("list server capabilities") },
874 
875   { "starttls",   1, 1, 0, com_starttls,
876     NULL,
877     N_("initiate encrypted connection") },
878 
879   { "rset",       1, 1, 0, com_rset,
880     NULL,
881     N_("reset the session state") },
882 
883   { "from",       1, 2, 0, com_from,
884     N_("[EMAIL]"),
885     N_("set sender email") },
886 
887   { "to",       1, 2, 0, com_to,
888     N_("[EMAIL]"),
889     N_("set recipient email") },
890 
891   { "send",     1, 2, 0, com_send,
892     N_("[FILE]"),
893     N_("send message") },
894 
895   { "smtp",     2, -1, 0, com_smtp_command,
896     N_("COMMAND [ARGS...]"),
897     N_("send an arbitrary COMMAND") },
898 
899   { "quit",       1, 1, 0, com_quit,
900     NULL,
901     N_("quit the session") },
902 
903   { "verbose",    1, 4, 0, com_verbose,
904     "[on|off|mask|unmask] [secure [payload]]",
905     N_("control the protocol tracing") },
906 
907   { NULL }
908 };
909 
910 int
main(int argc,char ** argv)911 main (int argc, char **argv)
912 {
913   mu_registrar_record (mu_smtp_record);
914   mu_registrar_record (mu_smtps_record);
915 
916   mu_action_getopt (&argc, &argv, NULL, smtp_docstring, NULL);
917   if (argc)
918     {
919       mu_error (_("bad arguments"));
920       return 1;
921     }
922 
923   mutool_shell_prompt = mu_strdup ("smtp> ");
924   smtp_prompt_env ();
925   mutool_shell ("smtp", smtp_comtab);
926 
927   if (temp_msgfile)
928     unlink (msgfile);
929 
930   return 0;
931 }
932