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/util.h>
19 #include <mailutils/mime.h>
20 #include <mailutils/folder.h>
21 #include <mailutils/auth.h>
22 #include <pwd.h>
23 #ifdef HAVE_TERMIOS_H
24 # include <termios.h>
25 #endif
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28 
29 #ifdef HAVE_FCNTL_H
30 # include <fcntl.h>
31 #else
32 # include <sys/fcntl.h>
33 #endif
34 #include <mailutils/io.h>
35 
36 int
util_do_command(const char * fmt,...)37 util_do_command (const char *fmt, ...)
38 {
39   struct mu_wordsplit ws;
40   int argc;
41   char **argv;
42   int status = 0;
43   const struct mail_command_entry *entry = NULL;
44   char *cmd = NULL;
45   size_t size = 0;
46   va_list ap;
47 
48   va_start (ap, fmt);
49   status = mu_vasnprintf (&cmd, &size, fmt, ap);
50   va_end (ap);
51   if (status)
52     return status;
53 
54   if (cmd)
55     {
56       /*  Ignore comments */
57       if (cmd[0] == '#')
58 	{
59 	  free (cmd);
60 	  return 0;
61 	}
62 
63       if (cmd[0] == '\0')
64 	{
65 	  free (cmd);
66 
67 	  /* Hitting return i.e. no command, is equivalent to next
68 	     according to the POSIX spec. Note, that this applies
69 	     to interactive state only. */
70 	  if (interactive)
71 	    cmd = mu_strdup ("next");
72 	  else
73 	    return 0;
74 	}
75 
76       ws.ws_offs = 1; /* Keep one extra slot in case we need it
77 			 for expansion (see below) */
78       if (mu_wordsplit (cmd, &ws, MU_WRDSF_DEFFLAGS|MU_WRDSF_DOOFFS))
79 	{
80 	  mu_error ("\"%s\": %s", cmd, mu_wordsplit_strerror (&ws));
81 	  free (cmd);
82 	  return MU_ERR_PARSE;
83 	}
84       else
85 	{
86 	  char *p;
87 
88 	  argc = ws.ws_wordc;
89 	  argv = ws.ws_wordv + 1;
90 
91 	  /* Special case: a number alone implies "print" */
92 	  if (argc == 1
93 	      && ((strtoul (argv[0], &p, 10) > 0 && *p == 0)
94 		  || (argv[0][1] == 0 && strchr ("^$", argv[0][0]))))
95 	    {
96 	      /* Use the extra slot for "print" command */
97 	      argc++;
98 	      argv--;
99 	      argv[0] = "print";
100 	    }
101 
102 	  entry = mail_find_command (argv[0]);
103 	  free (cmd);
104 	}
105     }
106   else
107     entry = mail_find_command (mailvar_name_quit);
108 
109   if (!entry)
110     {
111       /* argv[0] might be a traditional /bin/mail contracted form, e.g.
112 	 `d*' or `p4'. */
113 
114       char *p;
115 
116       for (p = argv[0] + strlen (argv[0]) - 1;
117 	   p > argv[0] && !mu_isalpha (*p);
118 	   p--)
119 	;
120 
121       p++;
122 
123       if (strlen (p))
124 	{
125 	  /* Expand contracted form.  That's what we have kept an extra
126 	     ws slot for. */
127 	  argc++;
128 	  argv--;
129 	  argv[0] = argv[1];
130 	  argv[1] = mu_strdup (p);
131 	  *p = 0;
132 	  /* Register the new entry in WS */
133 	  ws.ws_wordc++;
134 	  ws.ws_offs = 0;
135 	}
136 
137       entry = mail_find_command (argv[0]);
138     }
139 
140   if (entry)
141     {
142       /* Make sure we are not in any if/else */
143       if (!(if_cond () == 0 && (entry->flags & EF_FLOW) == 0))
144 	status = entry->func (argc, argv);
145     }
146   else
147     {
148       if (argc)
149 	mu_error (_("Unknown command: %s"), argv[0]);
150       else
151 	mu_error (_("Invalid command"));
152       status = 1;
153     }
154 
155   mu_wordsplit_free (&ws);
156   return status;
157 }
158 
159 int
util_foreach_msg(int argc,char ** argv,int flags,msg_handler_t func,void * data)160 util_foreach_msg (int argc, char **argv, int flags,
161 		  msg_handler_t func, void *data)
162 {
163   msgset_t *list = NULL, *mp;
164   int status = 0;
165 
166   if (msgset_parse (argc, argv, flags, &list))
167       return 1;
168 
169   for (mp = list; mp; mp = mp->next)
170     {
171       mu_message_t mesg;
172 
173       if (util_get_message (mbox, msgset_msgno (mp), &mesg) == 0)
174 	{
175 	  if (func (mp, mesg, data) != 0)
176 	    status = 1;
177 	  /* Bail out if we receive an interrupt.  */
178 	  if (ml_got_interrupt () != 0)
179 	    break;
180 	}
181     }
182   msgset_free (list);
183 
184   return status;
185 }
186 
187 size_t
util_range_msg(size_t low,size_t high,int flags,msg_handler_t func,void * data)188 util_range_msg (size_t low, size_t high, int flags,
189 		msg_handler_t func, void *data)
190 {
191   size_t count, expect_count;
192 
193   if (!func)
194     flags |= MSG_SILENT;
195 
196   if (low > total)
197     return 0;
198   if (!(flags & MSG_COUNT))
199     {
200       if (high < low)
201 	return 0;
202       expect_count = high - low + 1;
203     }
204   else
205     expect_count = high;
206 
207   for (count = 0; count < expect_count && low <= total; low++)
208     {
209      mu_message_t mesg;
210 
211      if ((flags & MSG_NODELETED) && util_isdeleted (low))
212        {
213 	 if (!(flags & MSG_SILENT))
214 	   mu_error (_("%lu: Inappropriate message (has been deleted)"),
215 		       (unsigned long) low);
216 	 continue;
217        }
218 
219      if (util_get_message (mbox, low, &mesg) == 0)
220        {
221 	 count ++;
222 	 if (func)
223 	   {
224 	     msgset_t *set = msgset_make_1 (low);
225 	     func (set, mesg, data);
226 	     msgset_free (set);
227 	   }
228 	 /* Bail out if we receive an interrupt.  */
229 	 if (ml_got_interrupt () != 0)
230 	   break;
231        }
232     }
233   return count;
234 }
235 
236 /*
237  * returns the function to run for command
238  */
239 function_t *
util_command_get(const char * cmd)240 util_command_get (const char *cmd)
241 {
242   const struct mail_command_entry *entry = mail_find_command (cmd);
243   return entry ? entry->func : NULL;
244 }
245 
246 /*
247  * returns the mail_command_entry structure for the command matching cmd
248  */
249 void *
util_find_entry(void * table,size_t nmemb,size_t size,const char * cmd)250 util_find_entry (void *table, size_t nmemb, size_t size, const char *cmd)
251 {
252   int i;
253   int len = strlen (cmd);
254   char *p;
255 
256   for (p = table, i = 0; i < nmemb; i++, p += size)
257     {
258       struct mail_command *cp = (struct mail_command *)p;
259       int ll = strlen (cp->longname);
260       int sl = strlen (cp->shortname);
261 
262       if (sl > ll && !strncmp (cp->shortname, cmd, sl))
263 	return p;
264       else if (sl == len && !strcmp (cp->shortname, cmd))
265 	return p;
266       else if (sl < len && !strncmp (cp->longname, cmd, len))
267 	return p;
268     }
269   return NULL;
270 }
271 
272 int
util_help(void * table,size_t nmemb,size_t size,const char * word)273 util_help (void *table, size_t nmemb, size_t size, const char *word)
274 {
275   if (!word)
276     {
277       int i = 0;
278       mu_stream_t out;
279       char *p;
280 
281       out = open_pager (util_screen_lines () + 1);
282 
283       for (p = table, i = 0; i < nmemb; i++, p += size)
284 	{
285 	  struct mail_command *cp = (struct mail_command *)p;
286 	  if (cp->synopsis == NULL)
287 	    continue;
288 	  mu_stream_printf (out, "%s\n", cp->synopsis);
289 	}
290 
291       mu_stream_unref (out);
292 
293       return 0;
294     }
295   else
296     {
297       int status = 0;
298       struct mail_command *cp = util_find_entry (table, nmemb, size, word);
299       if (cp && cp->synopsis)
300 	mu_printf ("%s\n", cp->synopsis);
301       else
302 	{
303 	  status = 1;
304 	  mu_printf (_("Unknown command: %s\n"), word);
305 	}
306       return status;
307     }
308   return 1;
309 }
310 
311 int
util_command_list(void * table,size_t nmemb,size_t size)312 util_command_list (void *table, size_t nmemb, size_t size)
313 {
314   int i;
315   char *p;
316   int cols = util_screen_columns ();
317   int pos = 0;
318 
319   for (p = table, i = 0; i < nmemb; i++, p += size)
320     {
321       const char *cmd;
322       struct mail_command *cp = (struct mail_command *)p;
323       int len = strlen (cp->longname);
324       if (len < 1)
325 	{
326 	  cmd = cp->shortname;
327 	  len = strlen (cmd);
328 	}
329       else
330 	cmd = cp->longname;
331 
332       pos += len + 1;
333 
334       if (pos >= cols)
335 	{
336 	  pos = len + 1;
337 	  mu_printf ("\n%s ", cmd);
338 	}
339       else
340 	mu_printf ("%s ", cmd);
341     }
342   mu_printf ("\n");
343   return 0;
344 }
345 
346 /*
347  * Get the number of columns on the screen
348  * First try an ioctl() call not all shells set the COLUMNS environ.
349  */
350 int
util_getcols(void)351 util_getcols (void)
352 {
353   struct winsize ws;
354   ws.ws_col = ws.ws_row = 0;
355   if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_col == 0)
356     return 80;  /* FIXME: Should we exit()/abort() if col <= 0 ?  */
357   return ws.ws_col;
358 }
359 
360 /*
361  * Get the number of lines on the screen
362  * First try an ioctl() call not all shells set the LINES environ.
363  */
364 int
util_getlines(void)365 util_getlines (void)
366 {
367   struct winsize ws;
368   ws.ws_col = ws.ws_row = 0;
369   if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row <= 2)
370     ws.ws_row = 24;  /* FIXME: Should we exit()/abort() if row <= 2 ?  */
371   /* Reserve at least 2 line for the prompt.  */
372   return ws.ws_row - 2;
373 }
374 
375 int
util_screen_lines()376 util_screen_lines ()
377 {
378   int screen;
379   size_t n;
380 
381   if (mailvar_get (&screen, mailvar_name_screen, mailvar_type_number, 0) == 0)
382     return screen;
383   n = util_getlines();
384   util_do_command ("set screen=%lu", (unsigned long) n);
385   return n;
386 }
387 
388 int
util_screen_columns()389 util_screen_columns ()
390 {
391   int cols;
392   size_t n;
393 
394   if (mailvar_get (&cols, mailvar_name_columns, mailvar_type_number, 0) == 0)
395     return cols;
396   n = util_getcols();
397   util_do_command ("set columns=%lu", (unsigned long) n);
398   return n;
399 }
400 
401 int
util_get_crt()402 util_get_crt ()
403 {
404   int lines;
405 
406   if (mailvar_get (&lines, mailvar_name_crt, mailvar_type_number, 0) == 0)
407     return lines;
408   else if (mailvar_is_true (mailvar_name_crt))
409     return util_getlines ();
410   return 0;
411 }
412 
413 const char *
util_reply_prefix()414 util_reply_prefix ()
415 {
416   char *prefix = "Re: ";
417   mailvar_get (&prefix, mailvar_name_replyprefix, mailvar_type_string, 0);
418   return prefix;
419 }
420 
421 
422 /* ************************* */
423 
424 /*
425  * return 1 if a message is deleted
426  */
427 
428 int
util_isdeleted(size_t n)429 util_isdeleted (size_t n)
430 {
431   mu_message_t msg = NULL;
432   mu_attribute_t attr = NULL;
433 
434   mu_mailbox_get_message (mbox, n, &msg);
435   mu_message_get_attribute (msg, &attr);
436   return mu_attribute_is_deleted (attr);
437 }
438 
439 void
util_mark_read(mu_message_t msg)440 util_mark_read (mu_message_t msg)
441 {
442   mu_attribute_t attr;
443 
444   mu_message_get_attribute (msg, &attr);
445   mu_attribute_set_read (attr);
446   mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SHOWN);
447 }
448 
449 char *
util_get_homedir()450 util_get_homedir ()
451 {
452   char *homedir = mu_get_homedir ();
453   if (!homedir)
454     {
455       /* Shouldn't happen, but one never knows */
456       mu_error (_("Cannot get homedir"));
457       exit (EXIT_FAILURE);
458     }
459   return homedir;
460 }
461 
462 char *
util_fullpath(const char * inpath)463 util_fullpath (const char *inpath)
464 {
465   return mu_tilde_expansion (inpath, MU_HIERARCHY_DELIMITER, NULL);
466 }
467 
468 char *
util_folder_path(const char * name)469 util_folder_path (const char *name)
470 {
471   char *folder;
472   int rc;
473 
474   if (mailvar_get (&folder, mailvar_name_folder, mailvar_type_string, 1))
475     return NULL;
476 
477   if (!name)
478     return NULL;
479 
480   rc = mu_mailbox_expand_name (name, &folder);
481   if (rc)
482     {
483       mu_diag_funcall (MU_DIAG_ERROR, "mailbox_expand_name", name, rc);
484       return NULL;
485     }
486   return folder;
487 }
488 
489 int outfilename_mode;
490 
491 char *
util_outfilename(mu_address_t addr)492 util_outfilename (mu_address_t addr)
493 {
494   char *buf, *p;
495   int rc;
496 
497   if ((rc = mu_address_aget_email (addr, 1, &buf)) != 0)
498     {
499       mu_error (_("Cannot determine sender name: %s"), mu_strerror (rc));
500       return NULL;
501     }
502 
503   switch (mailvar_is_true (mailvar_name_mailx)
504 	  ? outfilename_local : outfilename_mode)
505     {
506     case outfilename_local:
507       p = strchr (buf, '@');
508       if (p)
509 	*p = 0;
510       break;
511 
512     case outfilename_email:
513       break;
514 
515     case outfilename_domain:
516       p = strchr (buf, '@');
517       if (p)
518 	{
519 	  p++;
520 	  memmove (buf, p, strlen (p) + 1);
521 	}
522       else
523 	{
524 	  free (buf);
525 	  buf = mu_strdup ("localdomain");
526 	}
527       break;
528     }
529   return buf;
530 }
531 
532 char *
util_message_sender(mu_message_t msg,int strip)533 util_message_sender (mu_message_t msg, int strip)
534 {
535   mu_address_t addr = get_sender_address (msg);
536   char *buf;
537   int rc;
538 
539   if (!addr)
540     {
541       mu_envelope_t env = NULL;
542       const char *buffer;
543       mu_message_get_envelope (msg, &env);
544       if (mu_envelope_sget_sender (env, &buffer)
545 	  || mu_address_create (&addr, buffer))
546 	{
547 	  mu_error (_("Cannot determine sender name"));
548 	  return NULL;
549 	}
550     }
551 
552   if (strip)
553     {
554       buf = util_outfilename (addr);
555     }
556   else if ((rc = mu_address_aget_printable (addr, &buf)) != 0)
557     {
558       mu_error (_("Cannot determine sender name: %s"), mu_strerror (rc));
559       buf = NULL;
560     }
561 
562   mu_address_destroy (&addr);
563   return buf;
564 }
565 
566 char *
util_get_sender(int msgno,int strip)567 util_get_sender (int msgno, int strip)
568 {
569   mu_message_t msg;
570   if (mu_mailbox_get_message (mbox, msgno, &msg) == 0)
571     {
572       return util_message_sender (msg, strip);
573     }
574   return 0;
575 }
576 
577 void
util_slist_print(mu_list_t list,int nl)578 util_slist_print (mu_list_t list, int nl)
579 {
580   mu_iterator_t itr;
581   char *name;
582 
583   if (!list || mu_list_get_iterator (list, &itr))
584     return;
585 
586   for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
587        mu_iterator_next (itr))
588     {
589       mu_iterator_current (itr, (void **)&name);
590       mu_printf ("%s%c", name, nl ? '\n' : ' ');
591     }
592   mu_iterator_destroy (&itr);
593 }
594 
595 int
util_slist_lookup(mu_list_t list,const char * str)596 util_slist_lookup (mu_list_t list, const char *str)
597 {
598   mu_iterator_t itr;
599   char *name;
600   int rc = 0;
601 
602   if (!list || mu_list_get_iterator (list, &itr))
603     return 0;
604 
605   for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
606        mu_iterator_next (itr))
607     {
608       mu_iterator_current (itr, (void **)&name);
609       if (mu_c_strcasecmp (name, str) == 0)
610 	{
611 	  rc = 1;
612 	  break;
613 	}
614     }
615   mu_iterator_destroy (&itr);
616   return rc;
617 }
618 
619 static int
util_slist_compare(const void * a,const void * b)620 util_slist_compare (const void *a, const void *b)
621 {
622   return mu_c_strcasecmp (a, b);
623 }
624 
625 void
util_slist_add(mu_list_t * plist,char * value)626 util_slist_add (mu_list_t *plist, char *value)
627 {
628   mu_list_t list;
629 
630   if (!*plist)
631     {
632       if (mu_list_create (&list))
633 	return;
634       mu_list_set_destroy_item (list, mu_list_free_item);
635       mu_list_set_comparator (list, util_slist_compare);
636       *plist = list;
637     }
638   else
639     list = *plist;
640   mu_list_append (list, mu_strdup (value));
641 }
642 
643 void
util_slist_remove(mu_list_t * list,char * value)644 util_slist_remove (mu_list_t *list, char *value)
645 {
646   mu_list_remove (*list, value);
647 }
648 
649 void
util_slist_destroy(mu_list_t * list)650 util_slist_destroy (mu_list_t *list)
651 {
652   mu_list_destroy (list);
653 }
654 
655 char *
util_slist_to_string(mu_list_t list,const char * delim)656 util_slist_to_string (mu_list_t list, const char *delim)
657 {
658   mu_iterator_t itr;
659   char *name;
660   char *str = NULL;
661 
662   if (!list || mu_list_get_iterator (list, &itr))
663     return NULL;
664 
665   for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
666        mu_iterator_next (itr))
667     {
668       mu_iterator_current (itr, (void **)&name);
669       if (str && delim)
670 	util_strcat (&str, delim);
671       util_strcat (&str, name);
672     }
673   mu_iterator_destroy (&itr);
674   return str;
675 }
676 
677 void
util_strcat(char ** dest,const char * str)678 util_strcat (char **dest, const char *str)
679 {
680   if (!*dest)
681     *dest = mu_strdup (str);
682   else
683     {
684       int dlen = strlen (*dest) + 1;
685       int slen = strlen (str) + 1;
686       char *newp = realloc (*dest, dlen + slen);
687 
688       if (!newp)
689 	return;
690 
691       *dest = newp;
692       memcpy (newp + dlen - 1, str, slen);
693     }
694 }
695 
696 char *
util_outfolder_name(char * str)697 util_outfolder_name (char *str)
698 {
699   char *template = NULL;
700   char *folder;
701   char *outfolder;
702   int rc;
703 
704   if (!str)
705     {
706       mailvar_get (&template, mailvar_name_record, mailvar_type_string, 0);
707       str = template;
708     }
709   else
710     {
711       switch (*str)
712 	{
713 	case '/':
714 	case '~':
715 	case '+':
716 	  break;
717 
718 	default:
719 	  if (mailvar_is_true (mailvar_name_mailx))
720 	    {
721 	      if (mailvar_get (NULL, mailvar_name_outfolder,
722 			       mailvar_type_whatever, 0) == 0)
723 		{
724 		  if (mailvar_get (&folder, mailvar_name_folder,
725 				   mailvar_type_string, 0) == 0)
726 		    template = mu_make_file_name (folder, str);
727 		}
728 	      str = template;
729 	    }
730 	  else if (mailvar_get (&outfolder, mailvar_name_outfolder,
731 				mailvar_type_string, 0) == 0)
732 	    {
733 	      str = template = mu_make_file_name (outfolder, str);
734 	    }
735 	  else if (mailvar_is_true (mailvar_name_outfolder))
736 	    {
737 	      if (mailvar_get (&folder, mailvar_name_folder,
738 			       mailvar_type_string, 0) == 0)
739 		template = mu_make_file_name (folder, str);
740 	      str = template;
741 	    }
742 	  else
743 	    str = NULL;
744 	  break;
745 	}
746     }
747 
748   if (str)
749     {
750       char *tilde_template = NULL;
751       char *exp;
752 
753       if (mailvar_is_true (mailvar_name_mailx))
754 	{
755 	  switch (*str)
756 	    {
757 	    case '/':
758 	    case '~':
759 	    case '+':
760 	      break;
761 
762 	    default:
763 	      if (mu_asprintf (&tilde_template, "~/%s", str))
764 		{
765 		  mu_alloc_die ();
766 		}
767 	      str = tilde_template;
768 	    }
769 	}
770 
771       rc = mu_mailbox_expand_name (str, &exp);
772       if (rc)
773 	{
774 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_expand_name", str, rc);
775 	  str = NULL;
776 	}
777       else
778 	str = exp;
779       free (tilde_template);
780     }
781   free (template);
782 
783   return str;
784 }
785 
786 /* Save an outgoing message. The SAVEFILE argument overrides the setting
787    of the "record" variable. */
788 void
util_save_outgoing(mu_message_t msg,char * savefile)789 util_save_outgoing (mu_message_t msg, char *savefile)
790 {
791   char *filename = util_outfolder_name (savefile);
792   if (filename)
793     {
794       int rc;
795       mu_mailbox_t outbox;
796 
797       rc = mu_mailbox_create_default (&outbox, filename);
798       if (rc)
799 	{
800 	  mu_error (_("Cannot create output mailbox `%s': %s"),
801 		    filename, mu_strerror (rc));
802 	}
803       else
804 	{
805 	  rc = mu_mailbox_open (outbox, MU_STREAM_WRITE | MU_STREAM_CREAT);
806 	  if (rc)
807 	    mu_error (_("Cannot open output mailbox `%s': %s"),
808 		      filename, mu_strerror (rc));
809 	  else
810 	    {
811 	      rc = mu_mailbox_append_message (outbox, msg);
812 	      if (rc)
813 		mu_error (_("Cannot append message to `%s': %s"),
814 			  filename, mu_strerror (rc));
815 	    }
816 
817 	  mu_mailbox_close (outbox);
818 	  mu_mailbox_destroy (&outbox);
819 	}
820       free (filename);
821     }
822 }
823 
824 //FIXME
825 static int
util_descend_subparts(mu_message_t mesg,msgset_t * msgset,mu_message_t * part)826 util_descend_subparts (mu_message_t mesg, msgset_t *msgset, mu_message_t *part)
827 {
828   unsigned int i;
829 
830   for (i = 2; i <= msgset_length (msgset); i++)
831     {
832       mu_message_t submsg = NULL;
833       size_t nparts = 0;
834       char *type = NULL;
835       mu_header_t hdr = NULL;
836 
837       mu_message_get_header (mesg, &hdr);
838       util_get_content_type (hdr, &type, NULL);
839       if (mu_c_strncasecmp (type, "message/rfc822", strlen (type)) == 0)
840 	{
841 	  if (mu_message_unencapsulate (mesg, &submsg, NULL))
842 	    {
843 	      mu_error (_("Cannot unencapsulate message/part"));
844 	      return 1;
845 	    }
846 	  mesg = submsg;
847 	}
848 
849       mu_message_get_num_parts (mesg, &nparts);
850       if (nparts < msgset->crd[i])
851 	{
852 	  mu_error (_("No such (sub)part in the message: %lu"),
853 		      (unsigned long) msgset->crd[i]);
854 	  return 1;
855 	}
856 
857       if (mu_message_get_part (mesg, msgset->crd[i], &submsg))
858 	{
859 	  mu_error (_("Cannot get (sub)part from the message: %lu"),
860 		      (unsigned long) msgset->crd[i]);
861 	  return 1;
862 	}
863 
864       mesg = submsg;
865     }
866 
867   *part = mesg;
868   return 0;
869 }
870 
871 void
util_msgset_iterate(msgset_t * msgset,int (* fun)(mu_message_t,msgset_t *,void *),void * closure)872 util_msgset_iterate (msgset_t *msgset,
873 		     int (*fun) (mu_message_t, msgset_t *, void *),
874 		     void *closure)
875 {
876   for (; msgset; msgset = msgset->next)
877     {
878       mu_message_t mesg;
879 
880       if (mu_mailbox_get_message (mbox, msgset_msgno (msgset), &mesg) != 0)
881 	return;
882 
883       if (util_descend_subparts (mesg, msgset, &mesg) == 0)
884 	(*fun) (mesg, msgset, closure);
885     }
886 }
887 
888 void
util_get_content_type(mu_header_t hdr,char ** value,char ** args)889 util_get_content_type (mu_header_t hdr, char **value, char **args)
890 {
891   char *type = NULL;
892   util_get_hdr_value (hdr, MU_HEADER_CONTENT_TYPE, &type);
893   if (type == NULL || *type == '\0')
894     {
895       if (type)
896 	free (type);
897       type = mu_strdup ("text/plain"); /* Default.  */
898     }
899   else
900     {
901       char *p;
902       p = strchr (type, ';');
903       if (p)
904 	{
905 	  *p++ = 0;
906 	  if (args)
907 	    *args = p;
908 	}
909     }
910   *value = type;
911 }
912 
913 void
util_get_hdr_value(mu_header_t hdr,const char * name,char ** value)914 util_get_hdr_value (mu_header_t hdr, const char *name, char **value)
915 {
916   int status = mu_header_aget_value_unfold (hdr, name, value);
917   if (status == 0)
918     mu_rtrim_class (*value, MU_CTYPE_SPACE);
919   else
920     {
921       if (status != MU_ERR_NOENT)
922 	mu_diag_funcall (MU_DIAG_ERROR, "mu_header_aget_value_unfold", name,
923 			 status);
924       *value = NULL;
925     }
926 }
927 
928 int
util_merge_addresses(char ** addr_str,const char * value)929 util_merge_addresses (char **addr_str, const char *value)
930 {
931   mu_address_t addr, new_addr;
932   int rc;
933 
934   if ((rc = mu_address_create (&new_addr, value)) != 0)
935     return rc;
936 
937   if ((rc = mu_address_create (&addr, *addr_str)) != 0)
938     {
939       mu_address_destroy (&new_addr);
940       return rc;
941     }
942 
943   rc = mu_address_union (&addr, new_addr);
944   if (rc == 0)
945     {
946       char *val;
947 
948       rc = mu_address_aget_printable (addr, &val);
949       if (rc == 0)
950 	{
951 	  if (!val)
952 	    return MU_ERR_NOENT;
953 	  free (*addr_str);
954 	  *addr_str = val;
955 	}
956     }
957 
958   mu_address_destroy (&addr);
959   mu_address_destroy (&new_addr);
960   return rc;
961 }
962 
963 int
is_address_field(const char * name)964 is_address_field (const char *name)
965 {
966   static char *address_fields[] = {
967     MU_HEADER_TO,
968     MU_HEADER_CC,
969     MU_HEADER_BCC,
970     0
971   };
972   char **p;
973 
974   for (p = address_fields; *p; p++)
975     if (mu_c_strcasecmp (*p, name) == 0)
976       return 1;
977   return 0;
978 }
979 
980 void
util_address_expand_aliases(mu_address_t * paddr)981 util_address_expand_aliases (mu_address_t *paddr)
982 {
983   struct mu_address hint;
984   mu_address_t addr = *paddr;
985   mu_address_t new_addr = NULL;
986   int rc;
987 
988   memset (&hint, 0, sizeof (hint));
989   while (addr)
990     {
991       struct mu_address *next = addr->next;
992       addr->next = NULL;
993 
994       if (addr->domain == NULL)
995 	{
996 	  char *exp = alias_expand (addr->local_part);
997 	  if (exp)
998 	    {
999 	      mu_address_destroy (&addr);
1000 	      rc = mu_address_create_hint (&addr, exp, &hint, 0);
1001 	      if (rc)
1002 		{
1003 		  mu_error (_("Cannot parse address `%s': %s"),
1004 			    exp, mu_strerror (rc));
1005 		  free (exp);
1006 		  continue;
1007 		}
1008 	      free (exp);
1009 	    }
1010 	}
1011       mu_address_union (&new_addr, addr);
1012       mu_address_destroy (&addr);
1013 
1014       addr = next;
1015     }
1016   *paddr = new_addr;
1017 }
1018 
1019 int
util_header_expand_aliases(mu_header_t * phdr)1020 util_header_expand_aliases (mu_header_t *phdr)
1021 {
1022   size_t i, nfields = 0;
1023   mu_header_t hdr;
1024   int errcnt = 0, rc;
1025 
1026   if (mailvar_is_true (mailvar_name_inplacealiases))
1027     /* If inplacealiases was set, aliases have been already expanded */
1028     return 0;
1029 
1030   rc = mu_header_create (&hdr, "", 0);
1031   if (rc)
1032     {
1033       mu_error (_("Cannot create temporary header: %s"), mu_strerror (rc));
1034       return 1;
1035     }
1036 
1037   mu_header_get_field_count (*phdr, &nfields);
1038   for (i = 1; i <= nfields; i++)
1039     {
1040       const char *name;
1041       char *value;
1042 
1043       if (mu_header_sget_field_name (*phdr, i, &name))
1044 	continue;
1045 
1046       if (mu_header_aget_field_value (*phdr, i, &value))
1047 	continue;
1048 
1049       if (is_address_field (name))
1050 	{
1051 	  struct mu_address hint;
1052 	  const char *s;
1053 	  mu_address_t a = NULL;
1054 
1055 	  mu_string_unfold (value, NULL);
1056 
1057 	  memset (&hint, 0, sizeof (hint));
1058 	  rc = mu_address_create_hint (&a, value, &hint, 0);
1059 	  if (rc == 0)
1060 	    {
1061 	      mu_address_t ha = NULL;
1062 	      util_address_expand_aliases (&a);
1063 	      if (mu_header_sget_value (hdr, name, &s) == 0)
1064 		mu_address_create_hint (&ha, s, &hint, 0);
1065 	      mu_address_union (&ha, a);
1066 	      mu_address_destroy (&a);
1067 	      a = ha;
1068 	    }
1069 	  else
1070 	    {
1071 	      mu_error (_("Cannot parse address `%s': %s"),
1072 			value, mu_strerror (rc));
1073 	    }
1074 
1075 	  if (a)
1076 	    {
1077 	      const char *newvalue;
1078 
1079 	      rc = mu_address_sget_printable (a, &newvalue);
1080 	      if (rc == 0 && newvalue)
1081 		mu_header_set_value (hdr, name, newvalue, 1);
1082 	      mu_address_destroy (&a);
1083 	    }
1084 	}
1085       else
1086 	mu_header_append (hdr, name, value);
1087       free (value);
1088     }
1089 
1090   if (errcnt == 0)
1091     {
1092       mu_header_destroy (phdr);
1093       *phdr = hdr;
1094     }
1095   else
1096     mu_header_destroy (&hdr);
1097 
1098   return errcnt;
1099 }
1100 
1101 int
util_get_message(mu_mailbox_t mbox,size_t msgno,mu_message_t * msg)1102 util_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *msg)
1103 {
1104   int status;
1105 
1106   if (msgno > total)
1107     {
1108       util_error_range (msgno);
1109       return MU_ERR_NOENT;
1110     }
1111 
1112   status = mu_mailbox_get_message (mbox, msgno, msg);
1113   if (status)
1114     {
1115       mu_error (_("Cannot get message %lu: %s"),
1116 		  (unsigned long) msgno, mu_strerror (status));
1117       return status;
1118     }
1119 
1120   return 0;
1121 }
1122 
1123 int
util_get_message_part(mu_mailbox_t mbox,msgset_t * msgset,mu_message_t * ret_msg)1124 util_get_message_part (mu_mailbox_t mbox, msgset_t *msgset,
1125 		       mu_message_t *ret_msg)
1126 {
1127   int status, ismime;
1128   mu_message_t msg;
1129   size_t i;
1130 
1131   status = mu_mailbox_get_message (mbox, msgset_msgno (msgset), &msg);
1132   if (status)
1133     {
1134       mu_error (_("Cannot get message %lu: %s"),
1135 		(unsigned long) msgset_msgno (msgset), mu_strerror (status));
1136       return status;
1137     }
1138 
1139   for (i = 2; i <= msgset_length (msgset); i++)
1140     {
1141       status = mu_message_is_multipart (msg, &ismime);
1142       if (status)
1143 	{
1144 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_message_is_multipart",
1145 			   NULL, status);
1146 	  return status;
1147 	}
1148 
1149       if (!ismime)
1150 	{
1151 	  char *s = msgset_part_str (msgset, i);
1152 	  mu_error (_("%s: not a multipart message"), s);
1153 	  free (s);
1154 	  return status;
1155 	}
1156 
1157       status = mu_message_get_part (msg, msgset->crd[i], &msg);
1158       if (status)
1159 	{
1160 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_part",
1161 			   NULL, status);
1162 	  return status;
1163 	}
1164     }
1165   *ret_msg = msg;
1166   return 0;
1167 }
1168 
1169 int
util_error_range(size_t msgno)1170 util_error_range (size_t msgno)
1171 {
1172   mu_error (_("%lu: invalid message number"), (unsigned long) msgno);
1173   return 1;
1174 }
1175 
1176 void
util_noapp()1177 util_noapp ()
1178 {
1179   mu_error (_("No applicable messages"));
1180 }
1181 
1182 void
util_cache_command(mu_list_t * list,const char * fmt,...)1183 util_cache_command (mu_list_t *list, const char *fmt, ...)
1184 {
1185   char *cmd = NULL;
1186   size_t size = 0;
1187   va_list ap;
1188 
1189   va_start (ap, fmt);
1190   mu_vasnprintf (&cmd, &size, fmt, ap);
1191   va_end (ap);
1192 
1193   if (!*list)
1194     mu_list_create (list);
1195 
1196   mu_list_append (*list, cmd);
1197 }
1198 
1199 static int
_run_and_free(void * item,void * data)1200 _run_and_free (void *item, void *data)
1201 {
1202   util_do_command ("%s", (char *) item);
1203   free (item);
1204   return 0;
1205 }
1206 
1207 void
util_run_cached_commands(mu_list_t * list)1208 util_run_cached_commands (mu_list_t *list)
1209 {
1210   mu_list_foreach (*list, _run_and_free, NULL);
1211   mu_list_destroy (list);
1212 }
1213 
1214 char *
util_get_charset(void)1215 util_get_charset (void)
1216 {
1217   char *charset;
1218 
1219   if (mailvar_get (&charset, mailvar_name_charset, mailvar_type_string, 0))
1220     return NULL;
1221 
1222   if (mu_c_strcasecmp (charset, "auto") == 0)
1223     {
1224       struct mu_lc_all lc_all = { .flags = 0 };
1225       char *tmp = getenv ("LC_ALL");
1226       if (!tmp)
1227 	tmp = getenv ("LANG");
1228 
1229       if (tmp && mu_parse_lc_all (tmp, &lc_all, MU_LC_CSET) == 0)
1230 	{
1231 	  charset = mu_strdup (lc_all.charset);
1232 	  mu_lc_all_free (&lc_all);
1233 	}
1234       else
1235 	charset = NULL;
1236     }
1237   else
1238     charset = mu_strdup (charset);
1239 
1240   return charset;
1241 }
1242 
1243 void
util_rfc2047_decode(char ** value)1244 util_rfc2047_decode (char **value)
1245 {
1246   char *charset, *tmp;
1247   int rc;
1248 
1249   if (!*value)
1250     return;
1251   charset = util_get_charset ();
1252   if (!charset)
1253     return;
1254 
1255   rc = mu_rfc2047_decode (charset, *value, &tmp);
1256   free (charset);
1257 
1258   if (rc)
1259     {
1260       if (mailvar_is_true (mailvar_name_verbose))
1261 	mu_error (_("Cannot decode line `%s': %s"), *value, mu_strerror (rc));
1262     }
1263   else
1264     {
1265       free (*value);
1266       *value = tmp;
1267     }
1268 }
1269 
1270 const char *
util_url_to_string(mu_url_t url)1271 util_url_to_string (mu_url_t url)
1272 {
1273   const char *scheme;
1274   if (mu_url_sget_scheme (url, &scheme) == 0)
1275     {
1276       if (strcmp (scheme, "file") == 0 || strcmp (scheme, "mbox") == 0)
1277 	{
1278 	  const char *path;
1279 	  if (mu_url_sget_path (url, &path) == 0)
1280 	    return path;
1281 	}
1282     }
1283   return mu_url_to_string (url);
1284 }
1285 
1286 mu_stream_t
open_pager(size_t lines)1287 open_pager (size_t lines)
1288 {
1289   const char *pager;
1290   unsigned pagelines = util_get_crt ();
1291   mu_stream_t str;
1292 
1293   if (pagelines && lines > pagelines && (pager = getenv ("PAGER")))
1294     {
1295       int rc = mu_command_stream_create (&str, pager, MU_STREAM_WRITE);
1296       if (rc)
1297 	{
1298 	  mu_diag_funcall (MU_DIAG_ERROR, "mu_prog_stream_create",
1299 			   pager, rc);
1300 	  str = mu_strout;
1301 	  mu_stream_ref (str);
1302 	}
1303     }
1304   else
1305     {
1306       str = mu_strout;
1307       mu_stream_ref (str);
1308     }
1309   return str;
1310 }
1311 
1312 int
util_get_folder(mu_folder_t * pfolder,mu_url_t url,int type)1313 util_get_folder (mu_folder_t *pfolder, mu_url_t url, int type)
1314 {
1315   mu_folder_t folder;
1316   int rc;
1317 
1318   rc = mu_folder_create_from_record (&folder, url, NULL);
1319   if (rc)
1320     {
1321       mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_create",
1322 		       mu_url_to_string (url), rc);
1323       return -1;
1324     }
1325 
1326   if (!mu_folder_is_local (folder))
1327     {
1328       if (type == local_folder)
1329 	{
1330 	  /* TRANSLATORS: The subject of this sentence ("folder") is the
1331 	     name of the variable. Don't translate it. */
1332 	  mu_error ("%s", _("folder must be set to a local folder"));
1333 	  mu_folder_destroy (&folder);
1334 	  return -1;
1335 	}
1336 
1337       /* Set ticket for a remote folder */
1338       rc = mu_folder_attach_ticket (folder);
1339       if (rc)
1340 	{
1341 	  mu_authority_t auth = NULL;
1342 
1343 	  if (mu_folder_get_authority (folder, &auth) == 0 && auth)
1344 	    {
1345 	      mu_ticket_t tct;
1346 	      mu_noauth_ticket_create (&tct);
1347 	      rc = mu_authority_set_ticket (auth, tct);
1348 	      if (rc)
1349 		mu_diag_funcall (MU_DIAG_ERROR, "mu_authority_set_ticket",
1350 				 NULL, rc);
1351 	    }
1352 	}
1353     }
1354 
1355   rc = mu_folder_open (folder, MU_STREAM_READ);
1356   if (rc)
1357     {
1358       mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_open",
1359 		       mu_url_to_string (url), rc);
1360       mu_folder_destroy (&folder);
1361       return -1;
1362     }
1363 
1364   *pfolder = folder;
1365   return 0;
1366 }
1367 
1368