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