NeoMutt  2024-04-16-36-g75b6fb
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
muttlib.c
Go to the documentation of this file.
1
33#include "config.h"
34#include <ctype.h>
35#include <errno.h>
36#include <limits.h>
37#include <pwd.h>
38#include <stdbool.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <sys/stat.h>
43#include <unistd.h>
44#include "mutt/lib.h"
45#include "address/lib.h"
46#include "config/lib.h"
47#include "email/lib.h"
48#include "core/lib.h"
49#include "alias/lib.h"
50#include "gui/lib.h"
51#include "mutt.h"
52#include "muttlib.h"
53#include "browser/lib.h"
54#include "editor/lib.h"
55#include "history/lib.h"
56#include "imap/lib.h"
57#include "ncrypt/lib.h"
58#include "question/lib.h"
59#include "globals.h"
60#include "hook.h"
61#include "mx.h"
62#include "protos.h"
63
65static const char *XdgEnvVars[] = {
66 [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
67 [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
68};
69
71static const char *XdgDefaults[] = {
72 [XDG_CONFIG_HOME] = "~/.config",
73 [XDG_CONFIG_DIRS] = "/etc/xdg",
74};
75
84void mutt_adv_mktemp(struct Buffer *buf)
85{
86 if (!(buf->data && (buf->data[0] != '\0')))
87 {
88 buf_mktemp(buf);
89 }
90 else
91 {
92 struct Buffer *prefix = buf_pool_get();
93 buf_strcpy(prefix, buf->data);
94 mutt_file_sanitize_filename(prefix->data, true);
95 const char *const c_tmp_dir = cs_subset_path(NeoMutt->sub, "tmp_dir");
96 buf_printf(buf, "%s/%s", NONULL(c_tmp_dir), buf_string(prefix));
97
98 struct stat st = { 0 };
99 if ((lstat(buf_string(buf), &st) == -1) && (errno == ENOENT))
100 goto out;
101
102 char *suffix = strchr(prefix->data, '.');
103 if (suffix)
104 {
105 *suffix = '\0';
106 suffix++;
107 }
108 buf_mktemp_pfx_sfx(buf, prefix->data, suffix);
109
110 out:
111 buf_pool_release(&prefix);
112 }
113}
114
123char *mutt_expand_path(char *buf, size_t buflen)
124{
125 return mutt_expand_path_regex(buf, buflen, false);
126}
127
135void buf_expand_path_regex(struct Buffer *buf, bool regex)
136{
137 const char *s = NULL;
138 const char *tail = "";
139
140 bool recurse = false;
141
142 struct Buffer *p = buf_pool_get();
143 struct Buffer *q = buf_pool_get();
144 struct Buffer *tmp = buf_pool_get();
145
146 do
147 {
148 recurse = false;
149 s = buf_string(buf);
150
151 switch (*s)
152 {
153 case '~':
154 {
155 if ((s[1] == '/') || (s[1] == '\0'))
156 {
158 tail = s + 1;
159 }
160 else
161 {
162 char *t = strchr(s + 1, '/');
163 if (t)
164 *t = '\0';
165
166 struct passwd *pw = getpwnam(s + 1);
167 if (pw)
168 {
169 buf_strcpy(p, pw->pw_dir);
170 if (t)
171 {
172 *t = '/';
173 tail = t;
174 }
175 else
176 {
177 tail = "";
178 }
179 }
180 else
181 {
182 /* user not found! */
183 if (t)
184 *t = '/';
185 buf_reset(p);
186 tail = s;
187 }
188 }
189 break;
190 }
191
192 case '=':
193 case '+':
194 {
195 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
196 enum MailboxType mb_type = mx_path_probe(c_folder);
197
198 /* if folder = {host} or imap[s]://host/: don't append slash */
199 if ((mb_type == MUTT_IMAP) && ((c_folder[strlen(c_folder) - 1] == '}') ||
200 (c_folder[strlen(c_folder) - 1] == '/')))
201 {
202 buf_strcpy(p, c_folder);
203 }
204 else if (mb_type == MUTT_NOTMUCH)
205 {
206 buf_strcpy(p, c_folder);
207 }
208 else if (c_folder && (c_folder[strlen(c_folder) - 1] == '/'))
209 {
210 buf_strcpy(p, c_folder);
211 }
212 else
213 {
214 buf_printf(p, "%s/", NONULL(c_folder));
215 }
216
217 tail = s + 1;
218 break;
219 }
220
221 /* elm compatibility, @ expands alias to user name */
222
223 case '@':
224 {
225 struct AddressList *al = alias_lookup(s + 1);
226 if (al && !TAILQ_EMPTY(al))
227 {
228 struct Email *e = email_new();
229 e->env = mutt_env_new();
230 mutt_addrlist_copy(&e->env->from, al, false);
231 mutt_addrlist_copy(&e->env->to, al, false);
232
234 mutt_default_save(p, e);
235
236 email_free(&e);
237 /* Avoid infinite recursion if the resulting folder starts with '@' */
238 if (*p->data != '@')
239 recurse = true;
240
241 tail = "";
242 }
243 break;
244 }
245
246 case '>':
247 {
248 const char *const c_mbox = cs_subset_string(NeoMutt->sub, "mbox");
249 buf_strcpy(p, c_mbox);
250 tail = s + 1;
251 break;
252 }
253
254 case '<':
255 {
256 const char *const c_record = cs_subset_string(NeoMutt->sub, "record");
257 buf_strcpy(p, c_record);
258 tail = s + 1;
259 break;
260 }
261
262 case '!':
263 {
264 if (s[1] == '!')
265 {
267 tail = s + 2;
268 }
269 else
270 {
271 const char *const c_spool_file = cs_subset_string(NeoMutt->sub, "spool_file");
272 buf_strcpy(p, c_spool_file);
273 tail = s + 1;
274 }
275 break;
276 }
277
278 case '-':
279 {
281 tail = s + 1;
282 break;
283 }
284
285 case '^':
286 {
288 tail = s + 1;
289 break;
290 }
291
292 default:
293 {
294 buf_reset(p);
295 tail = s;
296 }
297 }
298
299 if (regex && *(buf_string(p)) && !recurse)
300 {
302 buf_printf(tmp, "%s%s", buf_string(q), tail);
303 }
304 else
305 {
306 buf_printf(tmp, "%s%s", buf_string(p), tail);
307 }
308
309 buf_copy(buf, tmp);
310 } while (recurse);
311
314 buf_pool_release(&tmp);
315
316 /* Rewrite IMAP path in canonical form - aids in string comparisons of
317 * folders. May possibly fail, in which case buf should be the same. */
318 if (imap_path_probe(buf_string(buf), NULL) == MUTT_IMAP)
319 imap_expand_path(buf);
320}
321
328void buf_expand_path(struct Buffer *buf)
329{
330 buf_expand_path_regex(buf, false);
331}
332
342char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
343{
344 struct Buffer *tmp = buf_pool_get();
345
346 buf_addstr(tmp, NONULL(buf));
347 buf_expand_path_regex(tmp, regex);
348 mutt_str_copy(buf, buf_string(tmp), buflen);
349
350 buf_pool_release(&tmp);
351
352 return buf;
353}
354
367char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
368{
369 regmatch_t pat_match[1] = { 0 };
370 char *p = NULL;
371
372 if (!pw || !pw->pw_gecos)
373 return NULL;
374
375 memset(dest, 0, destlen);
376
377 const struct Regex *c_gecos_mask = cs_subset_regex(NeoMutt->sub, "gecos_mask");
378 if (mutt_regex_capture(c_gecos_mask, pw->pw_gecos, 1, pat_match))
379 {
380 mutt_str_copy(dest, pw->pw_gecos + pat_match[0].rm_so,
381 MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
382 }
383 else if ((p = strchr(pw->pw_gecos, ',')))
384 {
385 mutt_str_copy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
386 }
387 else
388 {
389 mutt_str_copy(dest, pw->pw_gecos, destlen);
390 }
391
392 size_t pwnl = strlen(pw->pw_name);
393
394 for (int idx = 0; dest[idx]; idx++)
395 {
396 if (dest[idx] == '&')
397 {
398 memmove(&dest[idx + pwnl], &dest[idx + 1],
399 MAX((ssize_t) (destlen - idx - pwnl - 1), 0));
400 memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
401 dest[idx] = toupper((unsigned char) dest[idx]);
402 }
403 }
404
405 return dest;
406}
407
415{
416 switch (b->type)
417 {
418 case TYPE_TEXT:
419 if (mutt_istr_equal("plain", b->subtype))
420 return false;
421 break;
422 case TYPE_APPLICATION:
424 return false;
426 return false;
427 break;
428
429 case TYPE_MULTIPART:
430 case TYPE_MESSAGE:
431 return false;
432 }
433
434 return true;
435}
436
442bool mutt_is_text_part(const struct Body *b)
443{
444 int t = b->type;
445 char *s = b->subtype;
446
448 return false;
449
450 if (t == TYPE_TEXT)
451 return true;
452
453 if (t == TYPE_MESSAGE)
454 {
455 if (mutt_istr_equal("delivery-status", s))
456 return true;
457 }
458
459 if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
460 {
461 if (mutt_istr_equal("pgp-keys", s))
462 return true;
463 }
464
465 return false;
466}
467
475void mutt_pretty_mailbox(char *buf, size_t buflen)
476{
477 if (!buf)
478 return;
479
480 char *p = buf, *q = buf;
481 size_t len;
482 enum UrlScheme scheme;
483 char tmp[PATH_MAX] = { 0 };
484
485 scheme = url_check_scheme(buf);
486
487 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
488 if ((scheme == U_IMAP) || (scheme == U_IMAPS))
489 {
490 imap_pretty_mailbox(buf, buflen, c_folder);
491 return;
492 }
493
494 if (scheme == U_NOTMUCH)
495 return;
496
497 /* if buf is an url, only collapse path component */
498 if (scheme != U_UNKNOWN)
499 {
500 p = strchr(buf, ':') + 1;
501 if (mutt_strn_equal(p, "//", 2))
502 q = strchr(p + 2, '/');
503 if (!q)
504 q = strchr(p, '\0');
505 p = q;
506 }
507
508 /* cleanup path */
509 if (strstr(p, "//") || strstr(p, "/./"))
510 {
511 /* first attempt to collapse the pathname, this is more
512 * lightweight than realpath() and doesn't resolve links */
513 while (*p)
514 {
515 if ((p[0] == '/') && (p[1] == '/'))
516 {
517 *q++ = '/';
518 p += 2;
519 }
520 else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
521 {
522 *q++ = '/';
523 p += 3;
524 }
525 else
526 {
527 *q++ = *p++;
528 }
529 }
530 *q = '\0';
531 }
532 else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
533 realpath(p, tmp))
534 {
535 mutt_str_copy(p, tmp, buflen - (p - buf));
536 }
537
538 if ((len = mutt_str_startswith(buf, c_folder)) && (buf[len] == '/'))
539 {
540 *buf++ = '=';
541 memmove(buf, buf + len, mutt_str_len(buf + len) + 1);
542 }
543 else if ((len = mutt_str_startswith(buf, HomeDir)) && (buf[len] == '/'))
544 {
545 *buf++ = '~';
546 memmove(buf, buf + len - 1, mutt_str_len(buf + len - 1) + 1);
547 }
548}
549
554void buf_pretty_mailbox(struct Buffer *buf)
555{
556 if (!buf || !buf->data)
557 return;
558 /* This reduces the size of the Buffer, so we can pass it through.
559 * We adjust the size just to make sure buf->data is not NULL though */
560 buf_alloc(buf, PATH_MAX);
561 mutt_pretty_mailbox(buf->data, buf->dsize);
562 buf_fix_dptr(buf);
563}
564
576int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
577 enum SaveAttach *opt, char **directory)
578{
579 struct stat st = { 0 };
580
581 buf_strcpy(fname, path);
582 if (access(buf_string(fname), F_OK) != 0)
583 return 0;
584 if (stat(buf_string(fname), &st) != 0)
585 return -1;
586 if (S_ISDIR(st.st_mode))
587 {
588 enum QuadOption ans = MUTT_NO;
589 if (directory)
590 {
591 switch (mw_multi_choice
592 /* L10N: Means "The path you specified as the destination file is a directory."
593 See the msgid "Save to file: " (alias.c, recvattach.c)
594 These three letters correspond to the choices in the string. */
595 (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
596 {
597 case 3: /* all */
598 mutt_str_replace(directory, buf_string(fname));
599 break;
600 case 1: /* yes */
601 FREE(directory);
602 break;
603 case -1: /* abort */
604 FREE(directory);
605 return -1;
606 case 2: /* no */
607 FREE(directory);
608 return 1;
609 }
610 }
611 /* L10N: Means "The path you specified as the destination file is a directory."
612 See the msgid "Save to file: " (alias.c, recvattach.c) */
613 else if ((ans = query_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
614 return (ans == MUTT_NO) ? 1 : -1;
615
616 struct Buffer *tmp = buf_pool_get();
617 buf_strcpy(tmp, mutt_path_basename(NONULL(attname)));
618 struct FileCompletionData cdata = { false, NULL, NULL, NULL };
619 if ((mw_get_field(_("File under directory: "), tmp, MUTT_COMP_CLEAR,
620 HC_FILE, &CompleteFileOps, &cdata) != 0) ||
621 buf_is_empty(tmp))
622 {
623 buf_pool_release(&tmp);
624 return (-1);
625 }
626 buf_concat_path(fname, path, buf_string(tmp));
627 buf_pool_release(&tmp);
628 }
629
630 if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(buf_string(fname), F_OK) == 0))
631 {
632 char buf[4096] = { 0 };
633 snprintf(buf, sizeof(buf), "%s - %s", buf_string(fname),
634 // L10N: Options for: File %s exists, (o)verwrite, (a)ppend, or (c)ancel?
635 _("File exists, (o)verwrite, (a)ppend, or (c)ancel?"));
636 switch (mw_multi_choice(buf, _("oac")))
637 {
638 case -1: /* abort */
639 return -1;
640 case 3: /* cancel */
641 return 1;
642
643 case 2: /* append */
644 *opt = MUTT_SAVE_APPEND;
645 break;
646 case 1: /* overwrite */
647 *opt = MUTT_SAVE_OVERWRITE;
648 break;
649 }
650 }
651 return 0;
652}
653
663void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
664{
665 if (addr && addr->mailbox)
666 {
667 mutt_str_copy(buf, buf_string(addr->mailbox), buflen);
668 const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
669 if (!c_save_address)
670 {
671 char *p = strpbrk(buf, "%@");
672 if (p)
673 *p = '\0';
674 }
675 mutt_str_lower(buf);
676 }
677 else
678 {
679 *buf = '\0';
680 }
681}
682
688void buf_save_path(struct Buffer *dest, const struct Address *a)
689{
690 if (a && a->mailbox)
691 {
692 buf_copy(dest, a->mailbox);
693 const bool c_save_address = cs_subset_bool(NeoMutt->sub, "save_address");
694 if (!c_save_address)
695 {
696 char *p = strpbrk(dest->data, "%@");
697 if (p)
698 {
699 *p = '\0';
700 buf_fix_dptr(dest);
701 }
702 }
703 mutt_str_lower(dest->data);
704 }
705 else
706 {
707 buf_reset(dest);
708 }
709}
710
718void mutt_safe_path(struct Buffer *dest, const struct Address *a)
719{
720 buf_save_path(dest, a);
721 for (char *p = dest->data; *p; p++)
722 if ((*p == '/') || isspace(*p) || !IsPrint((unsigned char) *p))
723 *p = '_';
724}
725
736FILE *mutt_open_read(const char *path, pid_t *thepid)
737{
738 FILE *fp = NULL;
739 struct stat st = { 0 };
740
741 size_t len = mutt_str_len(path);
742 if (len == 0)
743 {
744 return NULL;
745 }
746
747 if (path[len - 1] == '|')
748 {
749 /* read from a pipe */
750
751 char *p = mutt_str_dup(path);
752
753 p[len - 1] = 0;
754 mutt_endwin();
755 *thepid = filter_create(p, NULL, &fp, NULL, EnvList);
756 FREE(&p);
757 }
758 else
759 {
760 if (stat(path, &st) < 0)
761 return NULL;
762 if (S_ISDIR(st.st_mode))
763 {
764 errno = EINVAL;
765 return NULL;
766 }
767 fp = mutt_file_fopen(path, "r");
768 *thepid = -1;
769 }
770 return fp;
771}
772
781int mutt_save_confirm(const char *s, struct stat *st)
782{
783 int rc = 0;
784
785 enum MailboxType type = mx_path_probe(s);
786
787 if (type == MUTT_POP)
788 {
789 mutt_error(_("Can't save message to POP mailbox"));
790 return 1;
791 }
792
793 if ((type != MUTT_MAILBOX_ERROR) && (type != MUTT_UNKNOWN) && (mx_access(s, W_OK) == 0))
794 {
795 const bool c_confirm_append = cs_subset_bool(NeoMutt->sub, "confirm_append");
796 if (c_confirm_append)
797 {
798 struct Buffer *tmp = buf_pool_get();
799 buf_printf(tmp, _("Append messages to %s?"), s);
801 NeoMutt->sub, "confirm_append");
802 if (ans == MUTT_NO)
803 rc = 1;
804 else if (ans == MUTT_ABORT)
805 rc = -1;
806 buf_pool_release(&tmp);
807 }
808 }
809
810 if (type == MUTT_NNTP)
811 {
812 mutt_error(_("Can't save message to news server"));
813 return 0;
814 }
815
816 if (stat(s, st) != -1)
817 {
818 if (type == MUTT_MAILBOX_ERROR)
819 {
820 mutt_error(_("%s is not a mailbox"), s);
821 return 1;
822 }
823 }
824 else if (type != MUTT_IMAP)
825 {
826 st->st_mtime = 0;
827 st->st_atime = 0;
828
829 /* pathname does not exist */
830 if (errno == ENOENT)
831 {
832 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
833 if (c_confirm_create)
834 {
835 struct Buffer *tmp = buf_pool_get();
836 buf_printf(tmp, _("Create %s?"), s);
838 NeoMutt->sub, "confirm_create");
839 if (ans == MUTT_NO)
840 rc = 1;
841 else if (ans == MUTT_ABORT)
842 rc = -1;
843 buf_pool_release(&tmp);
844 }
845
846 /* user confirmed with MUTT_YES or set `$confirm_create` */
847 if (rc == 0)
848 {
849 /* create dir recursively */
850 char *tmp_path = mutt_path_dirname(s);
851 if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
852 {
853 /* report failure & abort */
854 mutt_perror("%s", s);
855 FREE(&tmp_path);
856 return 1;
857 }
858 FREE(&tmp_path);
859 }
860 }
861 else
862 {
863 mutt_perror("%s", s);
864 return 1;
865 }
866 }
867
868 msgwin_clear_text(NULL);
869 return rc;
870}
871
878void mutt_sleep(short s)
879{
880 const short c_sleep_time = cs_subset_number(NeoMutt->sub, "sleep_time");
881 if (c_sleep_time > s)
882 sleep(c_sleep_time);
883 else if (s)
884 sleep(s);
885}
886
893const char *mutt_make_version(void)
894{
895 static char vstring[256];
896 snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
897 return vstring;
898}
899
907void mutt_encode_path(struct Buffer *buf, const char *src)
908{
909 char *p = mutt_str_dup(src);
910 int rc = mutt_ch_convert_string(&p, cc_charset(), "us-ascii", MUTT_ICONV_NO_FLAGS);
911 size_t len = buf_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
912
913 /* convert the path to POSIX "Portable Filename Character Set" */
914 for (size_t i = 0; i < len; i++)
915 {
916 if (!isalnum(buf->data[i]) && !strchr("/.-_", buf->data[i]))
917 {
918 buf->data[i] = '_';
919 }
920 }
921 FREE(&p);
922}
923
932int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
933{
934 const char *xdg_env = mutt_str_getenv(XdgEnvVars[type]);
935 char *xdg = xdg_env ? mutt_str_dup(xdg_env) : mutt_str_dup(XdgDefaults[type]);
936 char *x = xdg; /* mutt_str_sep() changes xdg, so free x instead later */
937 char *token = NULL;
938 int rc = 0;
939
940 while ((token = mutt_str_sep(&xdg, ":")))
941 {
942 if (buf_printf(buf, "%s/%s/neomuttrc", token, PACKAGE) < 0)
943 continue;
944 buf_expand_path(buf);
945 if (access(buf_string(buf), F_OK) == 0)
946 {
947 rc = 1;
948 break;
949 }
950
951 if (buf_printf(buf, "%s/%s/Muttrc", token, PACKAGE) < 0)
952 continue;
953 buf_expand_path(buf);
954 if (access(buf_string(buf), F_OK) == 0)
955 {
956 rc = 1;
957 break;
958 }
959 }
960
961 FREE(&x);
962 return rc;
963}
964
971void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
972{
973 enum MailboxType mb_type = mx_path_probe(path);
974
975 const char *const c_folder = cs_subset_string(NeoMutt->sub, "folder");
976 if (mb_type == MUTT_IMAP)
977 {
978 imap_get_parent_path(path, buf, buflen);
979 }
980 else if (mb_type == MUTT_NOTMUCH)
981 {
982 mutt_str_copy(buf, c_folder, buflen);
983 }
984 else
985 {
986 mutt_str_copy(buf, path, buflen);
987 int n = mutt_str_len(buf);
988 if (n == 0)
989 return;
990
991 /* remove any final trailing '/' */
992 if (buf[n - 1] == '/')
993 buf[n - 1] = '\0';
994
995 /* Remove everything until the next slash */
996 for (n--; ((n >= 0) && (buf[n] != '/')); n--)
997 ; // do nothing
998
999 if (n > 0)
1000 {
1001 buf[n] = '\0';
1002 }
1003 else
1004 {
1005 buf[0] = '/';
1006 buf[1] = '\0';
1007 }
1008 }
1009}
1010
1033int mutt_inbox_cmp(const char *a, const char *b)
1034{
1035 /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1036 if ((a[0] == '+') && (b[0] == '+'))
1037 {
1038 return mutt_istr_equal(a + 1, "inbox") ? -1 :
1039 mutt_istr_equal(b + 1, "inbox") ? 1 :
1040 0;
1041 }
1042
1043 const char *a_end = strrchr(a, '/');
1044 const char *b_end = strrchr(b, '/');
1045
1046 /* If one path contains a '/', but not the other */
1047 if ((!a_end) ^ (!b_end))
1048 return 0;
1049
1050 /* If neither path contains a '/' */
1051 if (!a_end)
1052 return 0;
1053
1054 /* Compare the subpaths */
1055 size_t a_len = a_end - a;
1056 size_t b_len = b_end - b;
1057 size_t min = MIN(a_len, b_len);
1058 int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1059 (b[min + 1] != '\0') && mutt_istrn_equal(a, b, min);
1060
1061 if (!same)
1062 return 0;
1063
1064 if (mutt_istr_equal(&a[min + 1], "inbox"))
1065 return -1;
1066
1067 if (mutt_istr_equal(&b[min + 1], "inbox"))
1068 return 1;
1069
1070 return 0;
1071}
1072
1079void buf_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1080{
1081 if (!buf || !path)
1082 return;
1083
1084 buf_reset(buf);
1085
1086 for (; *path; path++)
1087 {
1088 if ((slash && (*path == '/')) || !strchr(FilenameSafeChars, *path))
1089 buf_addch(buf, '_');
1090 else
1091 buf_addch(buf, *path);
1092 }
1093}
1094
1101void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1102{
1103 if (!buf || (buflen == 0))
1104 return;
1105
1106 const bool c_size_show_bytes = cs_subset_bool(NeoMutt->sub, "size_show_bytes");
1107 const bool c_size_show_fractions = cs_subset_bool(NeoMutt->sub, "size_show_fractions");
1108 const bool c_size_show_mb = cs_subset_bool(NeoMutt->sub, "size_show_mb");
1109 const bool c_size_units_on_left = cs_subset_bool(NeoMutt->sub, "size_units_on_left");
1110
1111 if (c_size_show_bytes && (num < 1024))
1112 {
1113 snprintf(buf, buflen, "%d", (int) num);
1114 }
1115 else if (num == 0)
1116 {
1117 mutt_str_copy(buf, c_size_units_on_left ? "K0" : "0K", buflen);
1118 }
1119 else if (c_size_show_fractions && (num < 10189)) /* 0.1K - 9.9K */
1120 {
1121 snprintf(buf, buflen, c_size_units_on_left ? "K%3.1f" : "%3.1fK",
1122 (num < 103) ? 0.1 : (num / 1024.0));
1123 }
1124 else if (!c_size_show_mb || (num < 1023949)) /* 10K - 999K */
1125 {
1126 /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1127 snprintf(buf, buflen, c_size_units_on_left ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1128 }
1129 else if (c_size_show_fractions && (num < 10433332)) /* 1.0M - 9.9M */
1130 {
1131 snprintf(buf, buflen, c_size_units_on_left ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1132 }
1133 else /* 10M+ */
1134 {
1135 /* (10433332 + 52428) / 1048576 = 10 */
1136 snprintf(buf, buflen, c_size_units_on_left ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1137 }
1138}
1139
1147void add_to_stailq(struct ListHead *head, const char *str)
1148{
1149 /* don't add a NULL or empty string to the list */
1150 if (!str || (*str == '\0'))
1151 return;
1152
1153 /* check to make sure the item is not already on this list */
1154 struct ListNode *np = NULL;
1155 STAILQ_FOREACH(np, head, entries)
1156 {
1157 if (mutt_istr_equal(str, np->data))
1158 {
1159 return;
1160 }
1161 }
1163}
1164
1172void remove_from_stailq(struct ListHead *head, const char *str)
1173{
1174 if (mutt_str_equal("*", str))
1175 {
1176 mutt_list_free(head); /* "unCMD *" means delete all current entries */
1177 }
1178 else
1179 {
1180 struct ListNode *np = NULL, *tmp = NULL;
1181 STAILQ_FOREACH_SAFE(np, head, entries, tmp)
1182 {
1183 if (mutt_istr_equal(str, np->data))
1184 {
1185 STAILQ_REMOVE(head, np, ListNode, entries);
1186 FREE(&np->data);
1187 FREE(&np);
1188 break;
1189 }
1190 }
1191 }
1192}
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:765
Email Address Handling.
Email Aliases.
struct AddressList * alias_lookup(const char *name)
Find an Alias.
Definition: alias.c:277
const struct CompleteOps CompleteFileOps
Auto-Completion of Files.
Definition: complete.c:155
Select a Mailbox from a list.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:75
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:290
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:394
size_t buf_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer's contents to another Buffer.
Definition: buffer.c:600
size_t buf_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:508
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:336
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition: helpers.c:218
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:292
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
const char * cs_subset_path(const struct ConfigSubset *sub, const char *name)
Get a path config item by name.
Definition: helpers.c:169
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
Convenience wrapper for the config headers.
char * HomeDir
User's home directory.
Definition: globals.c:38
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:116
Convenience wrapper for the core headers.
MailboxType
Supported mailbox formats.
Definition: mailbox.h:41
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
@ MUTT_MAILBOX_ERROR
Error occurred examining Mailbox.
Definition: mailbox.h:43
@ MUTT_POP
'POP3' Mailbox type
Definition: mailbox.h:52
@ MUTT_NNTP
'NNTP' (Usenet) Mailbox type
Definition: mailbox.h:49
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:44
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:595
SecurityFlags mutt_is_application_pgp(const struct Body *b)
Does the message use PGP?
Definition: crypt.c:534
void mutt_endwin(void)
Shutdown curses.
Definition: curs_lib.c:151
Edit a string.
struct Email * email_new(void)
Create a new Email.
Definition: email.c:80
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:46
Structs that make up an email.
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:46
const char FilenameSafeChars[]
Set of characters <=0x7F that are safe to use in filenames.
Definition: file.c:70
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:751
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:971
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:709
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:146
pid_t filter_create(const char *cmd, FILE **fp_in, FILE **fp_out, FILE **fp_err, char **envlist)
Set up filter program.
Definition: filter.c:209
char * LastFolder
Previously selected mailbox.
Definition: globals.c:44
char * CurrentFolder
Currently selected mailbox.
Definition: globals.c:43
char ** EnvList
Private copy of the environment variables.
Definition: globals.c:78
Global variables.
const char * GitVer
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition: window.c:274
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition: question.c:64
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_perror(...)
Definition: logging2.h:93
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2345
int mutt_inbox_cmp(const char *a, const char *b)
Do two folders share the same path and one is an inbox -.
Definition: muttlib.c:1033
Convenience wrapper for the gui headers.
Read/write command history from/to a file.
@ HC_FILE
Files.
Definition: lib.h:54
void mutt_default_save(struct Buffer *path, struct Email *e)
Find the default save path for an email.
Definition: hook.c:770
Parse and execute user-defined hooks.
IMAP network mailbox.
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:162
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:581
int imap_expand_path(struct Buffer *path)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2385
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
#define IsPrint(ch)
Definition: mbyte.h:40
#define FREE(x)
Definition: memory.h:45
#define MIN(a, b)
Definition: memory.h:32
#define MAX(a, b)
Definition: memory.h:31
@ TYPE_MESSAGE
Type: 'message/*'.
Definition: mime.h:35
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
@ TYPE_TEXT
Type: 'text/*'.
Definition: mime.h:38
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition: msgwin.c:519
int mutt_ch_convert_string(char **ps, const char *from, const char *to, uint8_t flags)
Convert a string between encodings.
Definition: charset.c:831
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:72
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
const char * mutt_path_basename(const char *path)
Find the last component for a pathname.
Definition: path.c:282
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final '/'.
Definition: path.c:312
bool mutt_regex_capture(const struct Regex *regex, const char *str, size_t nmatch, regmatch_t matches[])
Match a regex against a string, with provided options.
Definition: regex.c:597
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:666
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
char * mutt_str_lower(char *str)
Convert all characters in the string to lowercase.
Definition: string.c:307
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:654
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:419
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:720
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:490
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:575
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:447
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:274
char * mutt_str_sep(char **stringp, const char *delim)
Find first occurrence of any of delim characters in *stringp.
Definition: string.c:186
Many unsorted constants and some structs.
#define PATH_MAX
Definition: mutt.h:42
#define MUTT_COMP_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:57
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:57
@ MUTT_SAVE_APPEND
Append to existing file.
Definition: mutt_attach.h:59
@ MUTT_SAVE_OVERWRITE
Overwrite existing file.
Definition: mutt_attach.h:60
@ MUTT_SAVE_NO_FLAGS
No flags set.
Definition: mutt_attach.h:58
char * mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:342
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:971
void remove_from_stailq(struct ListHead *head, const char *str)
Remove an item, matching a string, from a List.
Definition: muttlib.c:1172
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user's real name in /etc/passwd.
Definition: muttlib.c:367
void add_to_stailq(struct ListHead *head, const char *str)
Add a string to a list.
Definition: muttlib.c:1147
void buf_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:135
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:123
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:893
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:878
bool mutt_is_text_part(const struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:442
void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
Turn an email address into a filename (for saving)
Definition: muttlib.c:663
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:554
void buf_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:688
bool mutt_needs_mailcap(struct Body *b)
Does this type need a mailcap entry do display.
Definition: muttlib.c:414
int mutt_set_xdg_path(enum XdgType type, struct Buffer *buf)
Find an XDG path or its fallback.
Definition: muttlib.c:932
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1101
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:475
static const char * XdgEnvVars[]
Accepted XDG environment variables.
Definition: muttlib.c:65
void buf_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1079
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:84
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:328
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:718
int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname, enum SaveAttach *opt, char **directory)
Ask the user if overwriting is necessary.
Definition: muttlib.c:576
void mutt_encode_path(struct Buffer *buf, const char *src)
Convert a path to 'us-ascii'.
Definition: muttlib.c:907
static const char * XdgDefaults[]
XDG default locations.
Definition: muttlib.c:71
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:781
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:736
Some miscellaneous functions.
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:168
enum MailboxType mx_path_probe(const char *path)
Find a mailbox that understands a path.
Definition: mx.c:1319
API for mailboxes.
API for encryption/signing of emails.
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:91
#define WithCrypto
Definition: lib.h:116
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Prototypes for many functions.
XdgType
XDG variable types.
Definition: protos.h:43
@ XDG_CONFIG_HOME
XDG home dir: ~/.config.
Definition: protos.h:44
@ XDG_CONFIG_DIRS
XDG system dir: /etc/xdg.
Definition: protos.h:45
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition: quad.h:37
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition: question.c:342
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:327
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:402
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:362
#define TAILQ_EMPTY(head)
Definition: queue.h:721
#define NONULL(x)
Definition: string2.h:37
An email address.
Definition: address.h:36
struct Buffer * mailbox
Mailbox and host address.
Definition: address.h:38
The body of an email.
Definition: body.h:36
char * subtype
content-type subtype
Definition: body.h:60
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
String manipulation buffer.
Definition: buffer.h:36
size_t dsize
Length of data.
Definition: buffer.h:39
char * data
Pointer to data.
Definition: buffer.h:37
The envelope/body of an email.
Definition: email.h:39
struct Envelope * env
Envelope information.
Definition: email.h:68
struct AddressList to
Email's 'To' list.
Definition: envelope.h:60
struct AddressList from
Email's 'From' list.
Definition: envelope.h:59
Input for the file completion function.
Definition: curs_lib.h:40
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
Cached regular expression.
Definition: regex3.h:85
#define buf_mktemp(buf)
Definition: tmp.h:33
#define buf_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: tmp.h:34
enum UrlScheme url_check_scheme(const char *str)
Check the protocol of a URL.
Definition: url.c:226
UrlScheme
All recognised Url types.
Definition: url.h:34
@ U_NOTMUCH
Url is notmuch://.
Definition: url.h:46
@ U_UNKNOWN
Url wasn't recognised.
Definition: url.h:35
@ U_FILE
Url is file://.
Definition: url.h:36
@ U_IMAP
Url is imap://.
Definition: url.h:39
@ U_IMAPS
Url is imaps://.
Definition: url.h:40