NeoMutt  2019-12-07
Teaching an old dog new tricks
DOXYGEN
muttlib.c
Go to the documentation of this file.
1 
31 #include "config.h"
32 #include <ctype.h>
33 #include <errno.h>
34 #include <inttypes.h>
35 #include <limits.h>
36 #include <pwd.h>
37 #include <regex.h>
38 #include <stdbool.h>
39 #include <stdint.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45 #include "mutt/mutt.h"
46 #include "address/lib.h"
47 #include "config/lib.h"
48 #include "email/lib.h"
49 #include "core/lib.h"
50 #include "mutt.h"
51 #include "muttlib.h"
52 #include "alias.h"
53 #include "curs_lib.h"
54 #include "filter.h"
55 #include "format_flags.h"
56 #include "globals.h"
57 #include "hook.h"
58 #include "mutt_window.h"
59 #include "mx.h"
60 #include "ncrypt/ncrypt.h"
61 #include "protos.h"
62 #if defined(HAVE_SYSCALL_H)
63 #include <syscall.h>
64 #elif defined(HAVE_SYS_SYSCALL_H)
65 #include <sys/syscall.h>
66 #endif
67 #ifdef USE_IMAP
68 #include "imap/imap.h"
69 #endif
70 
71 /* These Config Variables are only used in muttlib.c */
72 struct Regex *C_GecosMask;
73 
74 static FILE *fp_random;
75 
76 static const unsigned char base32[] = "abcdefghijklmnopqrstuvwxyz234567";
77 
78 static const char *xdg_env_vars[] = {
79  [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
80  [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
81 };
82 
83 static const char *xdg_defaults[] = {
84  [XDG_CONFIG_HOME] = "~/.config",
85  [XDG_CONFIG_DIRS] = "/etc/xdg",
86 };
87 
96 void mutt_adv_mktemp(struct Buffer *buf)
97 {
98  if (!(buf->data && buf->data[0]))
99  {
100  mutt_buffer_mktemp(buf);
101  }
102  else
103  {
104  struct Buffer *prefix = mutt_buffer_pool_get();
105  mutt_buffer_strcpy(prefix, buf->data);
106  mutt_file_sanitize_filename(prefix->data, true);
107  mutt_buffer_printf(buf, "%s/%s", NONULL(C_Tmpdir), mutt_b2s(prefix));
108 
109  struct stat sb;
110  if ((lstat(mutt_b2s(buf), &sb) == -1) && (errno == ENOENT))
111  goto out;
112 
113  char *suffix = strchr(prefix->data, '.');
114  if (suffix)
115  {
116  *suffix = '\0';
117  suffix++;
118  }
119  mutt_buffer_mktemp_pfx_sfx(buf, prefix->data, suffix);
120 
121  out:
122  mutt_buffer_pool_release(&prefix);
123  }
124 }
125 
134 char *mutt_expand_path(char *buf, size_t buflen)
135 {
136  return mutt_expand_path_regex(buf, buflen, false);
137 }
138 
146 void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
147 {
148  const char *s = NULL;
149  const char *tail = "";
150 
151  bool recurse = false;
152 
153  struct Buffer *p = mutt_buffer_pool_get();
154  struct Buffer *q = mutt_buffer_pool_get();
155  struct Buffer *tmp = mutt_buffer_pool_get();
156 
157  do
158  {
159  recurse = false;
160  s = mutt_b2s(buf);
161 
162  switch (*s)
163  {
164  case '~':
165  {
166  if ((s[1] == '/') || (s[1] == '\0'))
167  {
169  tail = s + 1;
170  }
171  else
172  {
173  char *t = strchr(s + 1, '/');
174  if (t)
175  *t = '\0';
176 
177  struct passwd *pw = getpwnam(s + 1);
178  if (pw)
179  {
180  mutt_buffer_strcpy(p, pw->pw_dir);
181  if (t)
182  {
183  *t = '/';
184  tail = t;
185  }
186  else
187  tail = "";
188  }
189  else
190  {
191  /* user not found! */
192  if (t)
193  *t = '/';
195  tail = s;
196  }
197  }
198  break;
199  }
200 
201  case '=':
202  case '+':
203  {
204  enum MailboxType mb_type = mx_path_probe(C_Folder, NULL);
205 
206  /* if folder = {host} or imap[s]://host/: don't append slash */
207  if ((mb_type == MUTT_IMAP) && ((C_Folder[strlen(C_Folder) - 1] == '}') ||
208  (C_Folder[strlen(C_Folder) - 1] == '/')))
209  {
211  }
212  else if (mb_type == MUTT_NOTMUCH)
214  else if (C_Folder && (C_Folder[strlen(C_Folder) - 1] == '/'))
216  else
217  mutt_buffer_printf(p, "%s/", NONULL(C_Folder));
218 
219  tail = s + 1;
220  break;
221  }
222 
223  /* elm compatibility, @ expands alias to user name */
224 
225  case '@':
226  {
227  struct AddressList *al = mutt_alias_lookup(s + 1);
228  if (!TAILQ_EMPTY(al))
229  {
230  struct Email *e = email_new();
231  e->env = mutt_env_new();
232  mutt_addrlist_copy(&e->env->from, al, false);
233  mutt_addrlist_copy(&e->env->to, al, false);
234 
235  /* TODO: fix mutt_default_save() to use Buffer */
237  mutt_default_save(p->data, p->dsize, e);
239 
240  email_free(&e);
241  /* Avoid infinite recursion if the resulting folder starts with '@' */
242  if (*p->data != '@')
243  recurse = true;
244 
245  tail = "";
246  }
247  break;
248  }
249 
250  case '>':
251  {
253  tail = s + 1;
254  break;
255  }
256 
257  case '<':
258  {
260  tail = s + 1;
261  break;
262  }
263 
264  case '!':
265  {
266  if (s[1] == '!')
267  {
269  tail = s + 2;
270  }
271  else
272  {
274  tail = s + 1;
275  }
276  break;
277  }
278 
279  case '-':
280  {
282  tail = s + 1;
283  break;
284  }
285 
286  case '^':
287  {
289  tail = s + 1;
290  break;
291  }
292 
293  default:
294  {
296  tail = s;
297  }
298  }
299 
300  if (regex && *(mutt_b2s(p)) && !recurse)
301  {
303  mutt_buffer_printf(tmp, "%s%s", mutt_b2s(q), tail);
304  }
305  else
306  mutt_buffer_printf(tmp, "%s%s", mutt_b2s(p), tail);
307 
308  mutt_buffer_copy(buf, tmp);
309  } while (recurse);
310 
314 
315 #ifdef USE_IMAP
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(mutt_b2s(buf), NULL) == MUTT_IMAP)
319  imap_expand_path(buf);
320  else
321 #endif
322  {
323  /* Resolve symbolic links */
324  struct stat st;
325  int rc = lstat(mutt_b2s(buf), &st);
326  if ((rc != -1) && S_ISLNK(st.st_mode))
327  {
328  char path[PATH_MAX];
329  if (realpath(mutt_b2s(buf), path))
330  {
331  mutt_buffer_strcpy(buf, path);
332  }
333  }
334  }
335 }
336 
344 {
345  mutt_buffer_expand_path_regex(buf, false);
346 }
347 
357 char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
358 {
359  struct Buffer *tmp = mutt_buffer_pool_get();
360 
361  mutt_buffer_addstr(tmp, NONULL(buf));
362  mutt_buffer_expand_path_regex(tmp, regex);
363  mutt_str_strfcpy(buf, mutt_b2s(tmp), buflen);
364 
366 
367  return buf;
368 }
369 
382 char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
383 {
384  regmatch_t pat_match[1];
385  size_t pwnl;
386  char *p = NULL;
387 
388  if (!pw || !pw->pw_gecos)
389  return NULL;
390 
391  memset(dest, 0, destlen);
392 
393  if (mutt_regex_capture(C_GecosMask, pw->pw_gecos, 1, pat_match))
394  {
395  mutt_str_strfcpy(dest, pw->pw_gecos + pat_match[0].rm_so,
396  MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
397  }
398  else if ((p = strchr(pw->pw_gecos, ',')))
399  mutt_str_strfcpy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
400  else
401  mutt_str_strfcpy(dest, pw->pw_gecos, destlen);
402 
403  pwnl = strlen(pw->pw_name);
404 
405  for (int idx = 0; dest[idx]; idx++)
406  {
407  if (dest[idx] == '&')
408  {
409  memmove(&dest[idx + pwnl], &dest[idx + 1],
410  MAX((ssize_t)(destlen - idx - pwnl - 1), 0));
411  memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
412  dest[idx] = toupper((unsigned char) dest[idx]);
413  }
414  }
415 
416  return dest;
417 }
418 
425 bool mutt_needs_mailcap(struct Body *m)
426 {
427  switch (m->type)
428  {
429  case TYPE_TEXT:
430  if (mutt_str_strcasecmp("plain", m->subtype) == 0)
431  return false;
432  break;
433  case TYPE_APPLICATION:
435  return false;
437  return false;
438  break;
439 
440  case TYPE_MULTIPART:
441  case TYPE_MESSAGE:
442  return false;
443  }
444 
445  return true;
446 }
447 
453 bool mutt_is_text_part(struct Body *b)
454 {
455  int t = b->type;
456  char *s = b->subtype;
457 
459  return false;
460 
461  if (t == TYPE_TEXT)
462  return true;
463 
464  if (t == TYPE_MESSAGE)
465  {
466  if (mutt_str_strcasecmp("delivery-status", s) == 0)
467  return true;
468  }
469 
470  if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
471  {
472  if (mutt_str_strcasecmp("pgp-keys", s) == 0)
473  return true;
474  }
475 
476  return false;
477 }
478 
486 int mutt_randbuf(void *buf, size_t buflen)
487 {
488  if (buflen > 1048576)
489  {
490  mutt_error(_("mutt_randbuf buflen=%zu"), buflen);
491  return -1;
492  }
493 /* XXX switch to HAVE_GETRANDOM and getrandom() in about 2017 */
494 #if defined(SYS_getrandom) && defined(__linux__)
495  long ret;
496  do
497  {
498  ret = syscall(SYS_getrandom, buf, buflen, 0, 0, 0, 0);
499  } while ((ret == -1) && (errno == EINTR));
500  if (ret == buflen)
501  return 0;
502 #endif
503  /* let's try urandom in case we're on an old kernel, or the user has
504  * configured selinux, seccomp or something to not allow getrandom */
505  if (!fp_random)
506  {
507  fp_random = fopen("/dev/urandom", "rb");
508  if (!fp_random)
509  {
510  mutt_error(_("open /dev/urandom: %s"), strerror(errno));
511  return -1;
512  }
513  setbuf(fp_random, NULL);
514  }
515  if (fread(buf, 1, buflen, fp_random) != buflen)
516  {
517  mutt_error(_("read /dev/urandom: %s"), strerror(errno));
518  return -1;
519  }
520 
521  return 0;
522 }
523 
529 void mutt_rand_base32(void *buf, size_t buflen)
530 {
531  uint8_t *p = buf;
532 
533  if (mutt_randbuf(p, buflen) < 0)
534  mutt_exit(1);
535  for (size_t pos = 0; pos < buflen; pos++)
536  p[pos] = base32[p[pos] % 32];
537 }
538 
543 uint32_t mutt_rand32(void)
544 {
545  uint32_t num = 0;
546 
547  if (mutt_randbuf(&num, sizeof(num)) < 0)
548  mutt_exit(1);
549  return num;
550 }
551 
556 uint64_t mutt_rand64(void)
557 {
558  uint64_t num = 0;
559 
560  if (mutt_randbuf(&num, sizeof(num)) < 0)
561  mutt_exit(1);
562  return num;
563 }
564 
573 void mutt_buffer_mktemp_full(struct Buffer *buf, const char *prefix,
574  const char *suffix, const char *src, int line)
575 {
576  mutt_buffer_printf(buf, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
577  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
578  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
579 
580  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, mutt_b2s(buf));
581  if (unlink(mutt_b2s(buf)) && (errno != ENOENT))
582  {
583  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
584  line, mutt_b2s(buf), strerror(errno), errno);
585  }
586 }
587 
599 void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix,
600  const char *suffix, const char *src, int line)
601 {
602  size_t n =
603  snprintf(buf, buflen, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
604  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
605  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
606  if (n >= buflen)
607  {
609  "%s:%d: ERROR: insufficient buffer space to hold temporary "
610  "filename! buflen=%zu but need %zu\n",
611  src, line, buflen, n);
612  }
613  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, buf);
614  if (unlink(buf) && (errno != ENOENT))
615  {
616  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
617  line, buf, strerror(errno), errno);
618  }
619 }
620 
628 void mutt_pretty_mailbox(char *buf, size_t buflen)
629 {
630  if (!buf)
631  return;
632 
633  char *p = buf, *q = buf;
634  size_t len;
635  enum UrlScheme scheme;
636  char tmp[PATH_MAX];
637 
638  scheme = url_check_scheme(buf);
639 
640  if ((scheme == U_IMAP) || (scheme == U_IMAPS))
641  {
642  imap_pretty_mailbox(buf, buflen, C_Folder);
643  return;
644  }
645 
646  if (scheme == U_NOTMUCH)
647  return;
648 
649  /* if buf is an url, only collapse path component */
650  if (scheme != U_UNKNOWN)
651  {
652  p = strchr(buf, ':') + 1;
653  if (strncmp(p, "//", 2) == 0)
654  q = strchr(p + 2, '/');
655  if (!q)
656  q = strchr(p, '\0');
657  p = q;
658  }
659 
660  /* cleanup path */
661  if (strstr(p, "//") || strstr(p, "/./"))
662  {
663  /* first attempt to collapse the pathname, this is more
664  * lightweight than realpath() and doesn't resolve links */
665  while (*p)
666  {
667  if ((p[0] == '/') && (p[1] == '/'))
668  {
669  *q++ = '/';
670  p += 2;
671  }
672  else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
673  {
674  *q++ = '/';
675  p += 3;
676  }
677  else
678  *q++ = *p++;
679  }
680  *q = '\0';
681  }
682  else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
683  realpath(p, tmp))
684  {
685  mutt_str_strfcpy(p, tmp, buflen - (p - buf));
686  }
687 
688  if ((len = mutt_str_startswith(buf, C_Folder, CASE_MATCH)) && (buf[len] == '/'))
689  {
690  *buf++ = '=';
691  memmove(buf, buf + len, mutt_str_strlen(buf + len) + 1);
692  }
693  else if ((len = mutt_str_startswith(buf, HomeDir, CASE_MATCH)) && (buf[len] == '/'))
694  {
695  *buf++ = '~';
696  memmove(buf, buf + len - 1, mutt_str_strlen(buf + len - 1) + 1);
697  }
698 }
699 
705 {
706  if (!buf || !buf->data)
707  return;
708  /* This reduces the size of the Buffer, so we can pass it through.
709  * We adjust the size just to make sure buf->data is not NULL though */
711  mutt_pretty_mailbox(buf->data, buf->dsize);
713 }
714 
726 int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
727  enum SaveAttach *opt, char **directory)
728 {
729  struct stat st;
730 
731  mutt_buffer_strcpy(fname, path);
732  if (access(mutt_b2s(fname), F_OK) != 0)
733  return 0;
734  if (stat(mutt_b2s(fname), &st) != 0)
735  return -1;
736  if (S_ISDIR(st.st_mode))
737  {
738  enum QuadOption ans = MUTT_NO;
739  if (directory)
740  {
741  switch (mutt_multi_choice
742  /* L10N: Means "The path you specified as the destination file is a directory."
743  See the msgid "Save to file: " (alias.c, recvattach.c)
744  These three letters correspond to the choices in the string. */
745  (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
746  {
747  case 3: /* all */
748  mutt_str_replace(directory, mutt_b2s(fname));
749  break;
750  case 1: /* yes */
751  FREE(directory);
752  break;
753  case -1: /* abort */
754  FREE(directory);
755  return -1;
756  case 2: /* no */
757  FREE(directory);
758  return 1;
759  }
760  }
761  /* L10N: Means "The path you specified as the destination file is a directory."
762  See the msgid "Save to file: " (alias.c, recvattach.c) */
763  else if ((ans = mutt_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
764  return (ans == MUTT_NO) ? 1 : -1;
765 
766  struct Buffer *tmp = mutt_buffer_pool_get();
768  if ((mutt_buffer_get_field(_("File under directory: "), tmp, MUTT_FILE | MUTT_CLEAR) != 0) ||
770  {
772  return (-1);
773  }
774  mutt_buffer_concat_path(fname, path, mutt_b2s(tmp));
776  }
777 
778  if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(mutt_b2s(fname), F_OK) == 0))
779  {
780  switch (
781  mutt_multi_choice(_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"),
782  // L10N: Options for: File exists, (o)verwrite, (a)ppend, or (c)ancel?
783  _("oac")))
784  {
785  case -1: /* abort */
786  return -1;
787  case 3: /* cancel */
788  return 1;
789 
790  case 2: /* append */
791  *opt = MUTT_SAVE_APPEND;
792  break;
793  case 1: /* overwrite */
794  *opt = MUTT_SAVE_OVERWRITE;
795  break;
796  }
797  }
798  return 0;
799 }
800 
810 void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
811 {
812  if (addr && addr->mailbox)
813  {
814  mutt_str_strfcpy(buf, addr->mailbox, buflen);
815  if (!C_SaveAddress)
816  {
817  char *p = strpbrk(buf, "%@");
818  if (p)
819  *p = '\0';
820  }
821  mutt_str_strlower(buf);
822  }
823  else
824  *buf = '\0';
825 }
826 
832 void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
833 {
834  if (a && a->mailbox)
835  {
836  mutt_buffer_strcpy(dest, a->mailbox);
837  if (!C_SaveAddress)
838  {
839  char *p = strpbrk(dest->data, "%@");
840  if (p)
841  {
842  *p = '\0';
843  mutt_buffer_fix_dptr(dest);
844  }
845  }
846  mutt_str_strlower(dest->data);
847  }
848  else
849  mutt_buffer_reset(dest);
850 }
851 
859 void mutt_safe_path(struct Buffer *dest, const struct Address *a)
860 {
861  mutt_buffer_save_path(dest, a);
862  for (char *p = dest->data; *p; p++)
863  if ((*p == '/') || IS_SPACE(*p) || !IsPrint((unsigned char) *p))
864  *p = '_';
865 }
866 
878 void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
879  format_t *callback, unsigned long data, MuttFormatFlags flags)
880 {
881  char prefix[128], tmp[1024];
882  char *cp = NULL, *wptr = buf;
883  char ch;
884  char if_str[128], else_str[128];
885  size_t wlen, count, len, wid;
886  FILE *fp_filter = NULL;
887  char *recycler = NULL;
888 
889  char src2[256];
890  mutt_str_strfcpy(src2, src, mutt_str_strlen(src) + 1);
891  src = src2;
892 
893  prefix[0] = '\0';
894  buflen--; /* save room for the terminal \0 */
895  wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ? 3 : 0;
896  col += wlen;
897 
898  if ((flags & MUTT_FORMAT_NOFILTER) == 0)
899  {
900  int off = -1;
901 
902  /* Do not consider filters if no pipe at end */
903  int n = mutt_str_strlen(src);
904  if ((n > 1) && (src[n - 1] == '|'))
905  {
906  /* Scan backwards for backslashes */
907  off = n;
908  while ((off > 0) && (src[off - 2] == '\\'))
909  off--;
910  }
911 
912  /* If number of backslashes is even, the pipe is real. */
913  /* n-off is the number of backslashes. */
914  if ((off > 0) && (((n - off) % 2) == 0))
915  {
916  char srccopy[1024];
917  int i = 0;
918 
919  mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
920 
921  strncpy(srccopy, src, n);
922  srccopy[n - 1] = '\0';
923 
924  /* prepare Buffers */
925  struct Buffer srcbuf = mutt_buffer_make(0);
926  mutt_buffer_addstr(&srcbuf, srccopy);
927  /* note: we are resetting dptr and *reading* from the buffer, so we don't
928  * want to use mutt_buffer_reset(). */
929  srcbuf.dptr = srcbuf.data;
930  struct Buffer word = mutt_buffer_make(0);
931  struct Buffer cmd = mutt_buffer_make(0);
932 
933  /* Iterate expansions across successive arguments */
934  do
935  {
936  /* Extract the command name and copy to command line */
937  mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
938  if (word.data)
939  *word.data = '\0';
940  mutt_extract_token(&word, &srcbuf, MUTT_TOKEN_NO_FLAGS);
941  mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
942  mutt_buffer_addch(&cmd, '\'');
943  mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
944  data, flags | MUTT_FORMAT_NOFILTER);
945  for (char *p = tmp; p && *p; p++)
946  {
947  if (*p == '\'')
948  {
949  /* shell quoting doesn't permit escaping a single quote within
950  * single-quoted material. double-quoting instead will lead
951  * shell variable expansions, so break out of the single-quoted
952  * span, insert a double-quoted single quote, and resume. */
953  mutt_buffer_addstr(&cmd, "'\"'\"'");
954  }
955  else
956  mutt_buffer_addch(&cmd, *p);
957  }
958  mutt_buffer_addch(&cmd, '\'');
959  mutt_buffer_addch(&cmd, ' ');
960  } while (MoreArgs(&srcbuf));
961 
962  mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
963 
964  col -= wlen; /* reset to passed in value */
965  wptr = buf; /* reset write ptr */
966  pid_t pid = mutt_create_filter(cmd.data, NULL, &fp_filter, NULL);
967  if (pid != -1)
968  {
969  int rc;
970 
971  n = fread(buf, 1, buflen /* already decremented */, fp_filter);
972  mutt_file_fclose(&fp_filter);
973  rc = mutt_wait_filter(pid);
974  if (rc != 0)
975  mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
976  if (n > 0)
977  {
978  buf[n] = '\0';
979  while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
980  buf[--n] = '\0';
981  mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
982 
983  /* If the result ends with '%', this indicates that the filter
984  * generated %-tokens that neomutt can expand. Eliminate the '%'
985  * marker and recycle the string through mutt_expando_format().
986  * To literally end with "%", use "%%". */
987  if ((n > 0) && (buf[n - 1] == '%'))
988  {
989  n--;
990  buf[n] = '\0'; /* remove '%' */
991  if ((n > 0) && (buf[n - 1] != '%'))
992  {
993  recycler = mutt_str_strdup(buf);
994  if (recycler)
995  {
996  /* buflen is decremented at the start of this function
997  * to save space for the terminal nul char. We can add
998  * it back for the recursive call since the expansion of
999  * format pipes does not try to append a nul itself. */
1000  mutt_expando_format(buf, buflen + 1, col, cols, recycler,
1001  callback, data, flags);
1002  FREE(&recycler);
1003  }
1004  }
1005  }
1006  }
1007  else
1008  {
1009  /* read error */
1010  mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
1011  strerror(errno), errno);
1012  *wptr = '\0';
1013  }
1014  }
1015  else
1016  {
1017  /* Filter failed; erase write buffer */
1018  *wptr = '\0';
1019  }
1020 
1021  mutt_buffer_dealloc(&cmd);
1022  mutt_buffer_dealloc(&srcbuf);
1023  mutt_buffer_dealloc(&word);
1024  return;
1025  }
1026  }
1027 
1028  while (*src && (wlen < buflen))
1029  {
1030  if (*src == '%')
1031  {
1032  if (*++src == '%')
1033  {
1034  *wptr++ = '%';
1035  wlen++;
1036  col++;
1037  src++;
1038  continue;
1039  }
1040 
1041  if (*src == '?')
1042  {
1043  /* change original %? to new %< notation */
1044  /* %?x?y&z? to %<x?y&z> where y and z are nestable */
1045  char *p = (char *) src;
1046  *p = '<';
1047  /* skip over "x" */
1048  for (; *p && *p != '?'; p++)
1049  ;
1050  /* nothing */
1051  if (*p == '?')
1052  p++;
1053  /* fix up the "y&z" section */
1054  for (; *p && *p != '?'; p++)
1055  {
1056  /* escape '<' and '>' to work inside nested-if */
1057  if ((*p == '<') || (*p == '>'))
1058  {
1059  memmove(p + 2, p, mutt_str_strlen(p) + 1);
1060  *p++ = '\\';
1061  *p++ = '\\';
1062  }
1063  }
1064  if (*p == '?')
1065  *p = '>';
1066  }
1067 
1068  if (*src == '<')
1069  {
1070  flags |= MUTT_FORMAT_OPTIONAL;
1071  ch = *(++src); /* save the character to switch on */
1072  src++;
1073  cp = prefix;
1074  count = 0;
1075  while ((count < sizeof(prefix)) && (*src != '?'))
1076  {
1077  *cp++ = *src++;
1078  count++;
1079  }
1080  *cp = '\0';
1081  }
1082  else
1083  {
1084  flags &= ~MUTT_FORMAT_OPTIONAL;
1085 
1086  /* eat the format string */
1087  cp = prefix;
1088  count = 0;
1089  while ((count < sizeof(prefix)) && (isdigit((unsigned char) *src) || (*src == '.') ||
1090  (*src == '-') || (*src == '=')))
1091  {
1092  *cp++ = *src++;
1093  count++;
1094  }
1095  *cp = '\0';
1096 
1097  if (!*src)
1098  break; /* bad format */
1099 
1100  ch = *src++; /* save the character to switch on */
1101  }
1102 
1103  if (flags & MUTT_FORMAT_OPTIONAL)
1104  {
1105  int lrbalance;
1106 
1107  if (*src != '?')
1108  break; /* bad format */
1109  src++;
1110 
1111  /* eat the 'if' part of the string */
1112  cp = if_str;
1113  count = 0;
1114  lrbalance = 1;
1115  while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
1116  {
1117  if ((src[0] == '%') && (src[1] == '>'))
1118  {
1119  /* This is a padding expando; copy two chars and carry on */
1120  *cp++ = *src++;
1121  *cp++ = *src++;
1122  count += 2;
1123  continue;
1124  }
1125 
1126  if (*src == '\\')
1127  {
1128  src++;
1129  *cp++ = *src++;
1130  }
1131  else if ((src[0] == '%') && (src[1] == '<'))
1132  {
1133  lrbalance++;
1134  }
1135  else if (src[0] == '>')
1136  {
1137  lrbalance--;
1138  }
1139  if (lrbalance == 0)
1140  break;
1141  if ((lrbalance == 1) && (src[0] == '&'))
1142  break;
1143  *cp++ = *src++;
1144  count++;
1145  }
1146  *cp = '\0';
1147 
1148  /* eat the 'else' part of the string (optional) */
1149  if (*src == '&')
1150  src++; /* skip the & */
1151  cp = else_str;
1152  count = 0;
1153  while ((lrbalance > 0) && (count < sizeof(else_str)) && *src)
1154  {
1155  if ((src[0] == '%') && (src[1] == '>'))
1156  {
1157  /* This is a padding expando; copy two chars and carry on */
1158  *cp++ = *src++;
1159  *cp++ = *src++;
1160  count += 2;
1161  continue;
1162  }
1163 
1164  if (*src == '\\')
1165  {
1166  src++;
1167  *cp++ = *src++;
1168  }
1169  else if ((src[0] == '%') && (src[1] == '<'))
1170  {
1171  lrbalance++;
1172  }
1173  else if (src[0] == '>')
1174  {
1175  lrbalance--;
1176  }
1177  if (lrbalance == 0)
1178  break;
1179  if ((lrbalance == 1) && (src[0] == '&'))
1180  break;
1181  *cp++ = *src++;
1182  count++;
1183  }
1184  *cp = '\0';
1185 
1186  if (!*src)
1187  break; /* bad format */
1188 
1189  src++; /* move past the trailing '>' (formerly '?') */
1190  }
1191 
1192  /* handle generic cases first */
1193  if ((ch == '>') || (ch == '*'))
1194  {
1195  /* %>X: right justify to EOL, left takes precedence
1196  * %*X: right justify to EOL, right takes precedence */
1197  int soft = ch == '*';
1198  int pl, pw;
1199  pl = mutt_mb_charlen(src, &pw);
1200  if (pl <= 0)
1201  {
1202  pl = 1;
1203  pw = 1;
1204  }
1205 
1206  /* see if there's room to add content, else ignore */
1207  if (((col < cols) && (wlen < buflen)) || soft)
1208  {
1209  int pad;
1210 
1211  /* get contents after padding */
1212  mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1213  len = mutt_str_strlen(tmp);
1214  wid = mutt_strwidth(tmp);
1215 
1216  pad = (cols - col - wid) / pw;
1217  if (pad >= 0)
1218  {
1219  /* try to consume as many columns as we can, if we don't have
1220  * memory for that, use as much memory as possible */
1221  if (wlen + (pad * pl) + len > buflen)
1222  pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1223  else
1224  {
1225  /* Add pre-spacing to make multi-column pad characters and
1226  * the contents after padding line up */
1227  while ((col + (pad * pw) + wid < cols) && (wlen + (pad * pl) + len < buflen))
1228  {
1229  *wptr++ = ' ';
1230  wlen++;
1231  col++;
1232  }
1233  }
1234  while (pad-- > 0)
1235  {
1236  memcpy(wptr, src, pl);
1237  wptr += pl;
1238  wlen += pl;
1239  col += pw;
1240  }
1241  }
1242  else if (soft)
1243  {
1244  int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ? 3 : 0;
1245  int avail_cols = (cols > offset) ? (cols - offset) : 0;
1246  /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1247  *wptr = '\0';
1248  /* make sure right part is at most as wide as display */
1249  len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1250  /* truncate left so that right part fits completely in */
1251  wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1252  wptr = buf + wlen;
1253  /* Multi-column characters may be truncated in the middle.
1254  * Add spacing so the right hand side lines up. */
1255  while ((col + wid < avail_cols) && (wlen + len < buflen))
1256  {
1257  *wptr++ = ' ';
1258  wlen++;
1259  col++;
1260  }
1261  }
1262  if ((len + wlen) > buflen)
1263  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1264  memcpy(wptr, tmp, len);
1265  wptr += len;
1266  }
1267  break; /* skip rest of input */
1268  }
1269  else if (ch == '|')
1270  {
1271  /* pad to EOL */
1272  int pl, pw;
1273  pl = mutt_mb_charlen(src, &pw);
1274  if (pl <= 0)
1275  {
1276  pl = 1;
1277  pw = 1;
1278  }
1279 
1280  /* see if there's room to add content, else ignore */
1281  if ((col < cols) && (wlen < buflen))
1282  {
1283  int c = (cols - col) / pw;
1284  if ((c > 0) && (wlen + (c * pl) > buflen))
1285  c = ((signed) (buflen - wlen)) / pl;
1286  while (c > 0)
1287  {
1288  memcpy(wptr, src, pl);
1289  wptr += pl;
1290  wlen += pl;
1291  col += pw;
1292  c--;
1293  }
1294  }
1295  break; /* skip rest of input */
1296  }
1297  else
1298  {
1299  bool to_lower = false;
1300  bool no_dots = false;
1301 
1302  while ((ch == '_') || (ch == ':'))
1303  {
1304  if (ch == '_')
1305  to_lower = true;
1306  else if (ch == ':')
1307  no_dots = true;
1308 
1309  ch = *src++;
1310  }
1311 
1312  /* use callback function to handle this case */
1313  src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1314  else_str, data, flags);
1315 
1316  if (to_lower)
1317  mutt_str_strlower(tmp);
1318  if (no_dots)
1319  {
1320  char *p = tmp;
1321  for (; *p; p++)
1322  if (*p == '.')
1323  *p = '_';
1324  }
1325 
1326  len = mutt_str_strlen(tmp);
1327  if ((len + wlen) > buflen)
1328  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1329 
1330  memcpy(wptr, tmp, len);
1331  wptr += len;
1332  wlen += len;
1333  col += mutt_strwidth(tmp);
1334  }
1335  }
1336  else if (*src == '\\')
1337  {
1338  if (!*++src)
1339  break;
1340  switch (*src)
1341  {
1342  case 'f':
1343  *wptr = '\f';
1344  break;
1345  case 'n':
1346  *wptr = '\n';
1347  break;
1348  case 'r':
1349  *wptr = '\r';
1350  break;
1351  case 't':
1352  *wptr = '\t';
1353  break;
1354  case 'v':
1355  *wptr = '\v';
1356  break;
1357  default:
1358  *wptr = *src;
1359  break;
1360  }
1361  src++;
1362  wptr++;
1363  wlen++;
1364  col++;
1365  }
1366  else
1367  {
1368  int bytes, width;
1369  /* in case of error, simply copy byte */
1370  bytes = mutt_mb_charlen(src, &width);
1371  if (bytes < 0)
1372  {
1373  bytes = 1;
1374  width = 1;
1375  }
1376  if ((bytes > 0) && ((wlen + bytes) < buflen))
1377  {
1378  memcpy(wptr, src, bytes);
1379  wptr += bytes;
1380  src += bytes;
1381  wlen += bytes;
1382  col += width;
1383  }
1384  else
1385  {
1386  src += buflen - wlen;
1387  wlen = buflen;
1388  }
1389  }
1390  }
1391  *wptr = '\0';
1392 }
1393 
1404 FILE *mutt_open_read(const char *path, pid_t *thepid)
1405 {
1406  FILE *fp = NULL;
1407  struct stat s;
1408 
1409  size_t len = mutt_str_strlen(path);
1410  if (len == 0)
1411  {
1412  return NULL;
1413  }
1414 
1415  if (path[len - 1] == '|')
1416  {
1417  /* read from a pipe */
1418 
1419  char *p = mutt_str_strdup(path);
1420 
1421  p[len - 1] = 0;
1422  mutt_endwin();
1423  *thepid = mutt_create_filter(p, NULL, &fp, NULL);
1424  FREE(&p);
1425  }
1426  else
1427  {
1428  if (stat(path, &s) < 0)
1429  return NULL;
1430  if (S_ISDIR(s.st_mode))
1431  {
1432  errno = EINVAL;
1433  return NULL;
1434  }
1435  fp = fopen(path, "r");
1436  *thepid = -1;
1437  }
1438  return fp;
1439 }
1440 
1449 int mutt_save_confirm(const char *s, struct stat *st)
1450 {
1451  int ret = 0;
1452 
1453  enum MailboxType magic = mx_path_probe(s, NULL);
1454 
1455 #ifdef USE_POP
1456  if (magic == MUTT_POP)
1457  {
1458  mutt_error(_("Can't save message to POP mailbox"));
1459  return 1;
1460  }
1461 #endif
1462 
1463  if ((magic != MUTT_MAILBOX_ERROR) && (magic != MUTT_UNKNOWN) && !mx_access(s, W_OK))
1464  {
1465  if (C_Confirmappend)
1466  {
1467  struct Buffer *tmp = mutt_buffer_pool_get();
1468  mutt_buffer_printf(tmp, _("Append messages to %s?"), s);
1469  enum QuadOption ans = mutt_yesorno(mutt_b2s(tmp), MUTT_YES);
1470  if (ans == MUTT_NO)
1471  ret = 1;
1472  else if (ans == MUTT_ABORT)
1473  ret = -1;
1475  }
1476  }
1477 
1478 #ifdef USE_NNTP
1479  if (magic == MUTT_NNTP)
1480  {
1481  mutt_error(_("Can't save message to news server"));
1482  return 0;
1483  }
1484 #endif
1485 
1486  if (stat(s, st) != -1)
1487  {
1488  if (magic == MUTT_MAILBOX_ERROR)
1489  {
1490  mutt_error(_("%s is not a mailbox"), s);
1491  return 1;
1492  }
1493  }
1494  else if (magic != MUTT_IMAP)
1495  {
1496  st->st_mtime = 0;
1497  st->st_atime = 0;
1498 
1499  /* pathname does not exist */
1500  if (errno == ENOENT)
1501  {
1502  if (C_Confirmcreate)
1503  {
1504  struct Buffer *tmp = mutt_buffer_pool_get();
1505  mutt_buffer_printf(tmp, _("Create %s?"), s);
1506  enum QuadOption ans = mutt_yesorno(mutt_b2s(tmp), MUTT_YES);
1507  if (ans == MUTT_NO)
1508  ret = 1;
1509  else if (ans == MUTT_ABORT)
1510  ret = -1;
1512  }
1513 
1514  /* user confirmed with MUTT_YES or set C_Confirmcreate */
1515  if (ret == 0)
1516  {
1517  /* create dir recursively */
1518  char *tmp_path = mutt_path_dirname(s);
1519  if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1520  {
1521  /* report failure & abort */
1522  mutt_perror(s);
1523  FREE(&tmp_path);
1524  return 1;
1525  }
1526  FREE(&tmp_path);
1527  }
1528  }
1529  else
1530  {
1531  mutt_perror(s);
1532  return 1;
1533  }
1534  }
1535 
1537  return ret;
1538 }
1539 
1546 void mutt_sleep(short s)
1547 {
1548  if (C_SleepTime > s)
1549  sleep(C_SleepTime);
1550  else if (s)
1551  sleep(s);
1552 }
1553 
1560 const char *mutt_make_version(void)
1561 {
1562  static char vstring[256];
1563  snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1564  return vstring;
1565 }
1566 
1575 void mutt_encode_path(char *buf, size_t buflen, const char *src)
1576 {
1577  char *p = mutt_str_strdup(src);
1578  int rc = mutt_ch_convert_string(&p, C_Charset, "us-ascii", 0);
1579  /* 'src' may be NULL, such as when called from the pop3 driver. */
1580  size_t len = mutt_str_strfcpy(buf, (rc == 0) ? p : src, buflen);
1581 
1582  /* convert the path to POSIX "Portable Filename Character Set" */
1583  for (size_t i = 0; i < len; i++)
1584  {
1585  if (!isalnum(buf[i]) && !strchr("/.-_", buf[i]))
1586  {
1587  buf[i] = '_';
1588  }
1589  }
1590  FREE(&p);
1591 }
1592 
1600 void mutt_buffer_encode_path(struct Buffer *buf, const char *src)
1601 {
1602  char *p = mutt_str_strdup(src);
1603  int rc = mutt_ch_convert_string(&p, C_Charset, "utf-8", 0);
1604  mutt_buffer_strcpy(buf, (rc == 0) ? NONULL(p) : NONULL(src));
1605  FREE(&p);
1606 }
1607 
1617 int mutt_set_xdg_path(enum XdgType type, char *buf, size_t bufsize)
1618 {
1619  const char *xdg_env = mutt_str_getenv(xdg_env_vars[type]);
1620  char *xdg = xdg_env ? mutt_str_strdup(xdg_env) : mutt_str_strdup(xdg_defaults[type]);
1621  char *x = xdg; /* strsep() changes xdg, so free x instead later */
1622  char *token = NULL;
1623  int rc = 0;
1624 
1625  while ((token = strsep(&xdg, ":")))
1626  {
1627  if (snprintf(buf, bufsize, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1628  continue;
1629  mutt_expand_path(buf, bufsize);
1630  if (access(buf, F_OK) == 0)
1631  {
1632  rc = 1;
1633  break;
1634  }
1635 
1636  if (snprintf(buf, bufsize, "%s/%s/Muttrc", token, PACKAGE) < 0)
1637  continue;
1638  mutt_expand_path(buf, bufsize);
1639  if (access(buf, F_OK) == 0)
1640  {
1641  rc = 1;
1642  break;
1643  }
1644  }
1645 
1646  FREE(&x);
1647  return rc;
1648 }
1649 
1656 void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1657 {
1658  enum MailboxType mb_magic = mx_path_probe(path, NULL);
1659 
1660  if (mb_magic == MUTT_IMAP)
1661  imap_get_parent_path(path, buf, buflen);
1662  else if (mb_magic == MUTT_NOTMUCH)
1663  mutt_str_strfcpy(buf, C_Folder, buflen);
1664  else
1665  {
1666  mutt_str_strfcpy(buf, path, buflen);
1667  int n = mutt_str_strlen(buf);
1668  if (n == 0)
1669  return;
1670 
1671  /* remove any final trailing '/' */
1672  if (buf[n - 1] == '/')
1673  buf[n - 1] = '\0';
1674 
1675  /* Remove everything until the next slash */
1676  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1677  ;
1678 
1679  if (n > 0)
1680  buf[n] = '\0';
1681  else
1682  {
1683  buf[0] = '/';
1684  buf[1] = '\0';
1685  }
1686  }
1687 }
1688 
1711 int mutt_inbox_cmp(const char *a, const char *b)
1712 {
1713  /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1714  if ((a[0] == '+') && (b[0] == '+'))
1715  {
1716  return (mutt_str_strcasecmp(a + 1, "inbox") == 0) ?
1717  -1 :
1718  (mutt_str_strcasecmp(b + 1, "inbox") == 0) ? 1 : 0;
1719  }
1720 
1721  const char *a_end = strrchr(a, '/');
1722  const char *b_end = strrchr(b, '/');
1723 
1724  /* If one path contains a '/', but not the other */
1725  if ((!a_end) ^ (!b_end))
1726  return 0;
1727 
1728  /* If neither path contains a '/' */
1729  if (!a_end)
1730  return 0;
1731 
1732  /* Compare the subpaths */
1733  size_t a_len = a_end - a;
1734  size_t b_len = b_end - b;
1735  size_t min = MIN(a_len, b_len);
1736  int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1737  (b[min + 1] != '\0') && (mutt_str_strncasecmp(a, b, min) == 0);
1738 
1739  if (!same)
1740  return 0;
1741 
1742  if (mutt_str_strcasecmp(&a[min + 1], "inbox") == 0)
1743  return -1;
1744 
1745  if (mutt_str_strcasecmp(&b[min + 1], "inbox") == 0)
1746  return 1;
1747 
1748  return 0;
1749 }
1750 
1757 void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1758 {
1759  if (!buf || !path)
1760  return;
1761 
1762  mutt_buffer_reset(buf);
1763 
1764  for (; *path; path++)
1765  {
1766  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
1767  mutt_buffer_addch(buf, '_');
1768  else
1769  mutt_buffer_addch(buf, *path);
1770  }
1771 }
1772 
1779 void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1780 {
1781  if (!buf || (buflen == 0))
1782  return;
1783 
1784  if (C_SizeShowBytes && (num < 1024))
1785  {
1786  snprintf(buf, buflen, "%d", (int) num);
1787  }
1788  else if (num == 0)
1789  {
1790  mutt_str_strfcpy(buf, C_SizeUnitsOnLeft ? "K0" : "0K", buflen);
1791  }
1792  else if (C_SizeShowFractions && (num < 10189)) /* 0.1K - 9.9K */
1793  {
1794  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "K%3.1f" : "%3.1fK",
1795  (num < 103) ? 0.1 : (num / 1024.0));
1796  }
1797  else if (!C_SizeShowMb || (num < 1023949)) /* 10K - 999K */
1798  {
1799  /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1800  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1801  }
1802  else if (C_SizeShowFractions && (num < 10433332)) /* 1.0M - 9.9M */
1803  {
1804  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1805  }
1806  else /* 10M+ */
1807  {
1808  /* (10433332 + 52428) / 1048576 = 10 */
1809  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1810  }
1811 }
pid_t mutt_create_filter(const char *s, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:209
Url is notmuch://.
Definition: url.h:45
void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src, format_t *callback, unsigned long data, MuttFormatFlags flags)
Expand expandos (x) in a string.
Definition: muttlib.c:878
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:78
uint8_t MuttFormatFlags
Flags for mutt_expando_format(), e.g. MUTT_FORMAT_FORCESUBJ.
Definition: format_flags.h:29
#define NONULL(x)
Definition: string2.h:37
int mutt_ch_convert_string(char **ps, const char *from, const char *to, int flags)
Convert a string between encodings.
Definition: charset.c:748
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
struct AddressList * mutt_alias_lookup(const char *s)
Find an Alias.
Definition: alias.c:285
int mutt_randbuf(void *buf, size_t buflen)
Fill a buffer with randomness.
Definition: muttlib.c:486
The envelope/body of an email.
Definition: email.h:37
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:68
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define MIN(a, b)
Definition: memory.h:31
#define mutt_perror(...)
Definition: logging.h:85
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe()
Definition: imap.c:2474
Window management.
GUI miscellaneous curses (window drawing) routines.
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:51
Structs that make up an email.
size_t mutt_buffer_copy(struct Buffer *dst, const struct Buffer *src)
Copy a Buffer&#39;s contents to another Buffer.
Definition: buffer.c:445
User aborted the question (with Ctrl-G)
Definition: quad.h:37
#define mutt_buffer_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: muttlib.h:79
WHERE bool C_Confirmappend
Config: Confirm before appending emails to a mailbox.
Definition: globals.h:211
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
char * mutt_path_dirname(const char *path)
Return a path up to, but not including, the final &#39;/&#39;.
Definition: path.c:354
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:628
WHERE short C_SleepTime
Config: Time to pause after certain info messages.
Definition: globals.h:151
No flags set.
Definition: mutt_attach.h:55
Pass files through external commands (filters)
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
void mutt_addrlist_copy(struct AddressList *dst, const struct AddressList *src, bool prune)
Copy a list of addresses into another list.
Definition: address.c:727
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
Url is imaps://.
Definition: url.h:39
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
String manipulation buffer.
Definition: buffer.h:33
Overwrite existing file.
Definition: mutt_attach.h:57
Url wasn&#39;t recognised.
Definition: url.h:34
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:46
Error occurred examining Mailbox.
Definition: mailbox.h:45
An email address.
Definition: address.h:34
WHERE char * LastFolder
Previously selected mailbox.
Definition: globals.h:56
char * mailbox
Mailbox and host address.
Definition: address.h:37
Url is imap://.
Definition: url.h:38
Match case when comparing strings.
Definition: string2.h:67
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:666
XDG home dir: ~/.config.
Definition: protos.h:49
void mutt_buffer_save_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:832
WHERE char * C_Record
Config: Folder to save &#39;sent&#39; messages.
Definition: globals.h:132
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int mutt_mb_charlen(const char *s, int *width)
Count the bytes in a (multibyte) character.
Definition: mbyte.c:55
#define IsPrint(ch)
Definition: mbyte.h:39
WHERE bool C_Confirmcreate
Config: Confirm before creating a new mailbox.
Definition: globals.h:212
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:656
Flags to control mutt_expando_format()
static const char * xdg_env_vars[]
Definition: muttlib.c:78
enum UrlScheme url_check_scheme(const char *s)
Check the protocol of a URL.
Definition: url.c:132
uint32_t mutt_rand32(void)
Create a 32-bit random number.
Definition: muttlib.c:543
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:704
Representation of a single alias to an email address.
int mutt_save_confirm(const char *s, struct stat *st)
Ask the user to save.
Definition: muttlib.c:1449
The body of an email.
Definition: body.h:34
Convenience wrapper for the config headers.
Hundreds of global variables to back the user variables.
const char * mutt_str_getenv(const char *name)
Get an environment variable.
Definition: string.c:1048
Email Address Handling.
void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:146
WHERE bool C_SizeShowFractions
Config: Show size fractions with a single decimal place.
Definition: globals.h:260
Some miscellaneous functions.
#define MAX(a, b)
Definition: memory.h:30
char * mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
Create the canonical path (with regex char escaping)
Definition: muttlib.c:357
size_t dsize
Length of data.
Definition: buffer.h:37
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:134
#define MoreArgs(buf)
Definition: buffer.h:43
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1546
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:375
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2520
void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix, const char *suffix, const char *src, int line)
Create a temporary filename.
Definition: muttlib.c:599
Parse and execute user-defined hooks.
API for mailboxes.
char * mutt_str_strlower(char *s)
convert all characters in the string to lowercase
Definition: string.c:509
WHERE bool C_SizeShowMb
Config: Show sizes in megabytes for sizes greater than 1 megabyte.
Definition: globals.h:261
UrlScheme
All recognised Url types.
Definition: url.h:32
struct AddressList from
Email&#39;s &#39;From&#39; list.
Definition: envelope.h:57
int mutt_file_mkdir(const char *path, mode_t mode)
Recursively create directories.
Definition: file.c:873
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:95
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
WHERE char * C_Mbox
Config: Folder that receives read emails (see Move)
Definition: globals.h:118
IMAP network mailbox.
struct Envelope * env
Envelope information.
Definition: email.h:89
Convenience wrapper for the core headers.
WHERE char * HomeDir
User&#39;s home directory.
Definition: globals.h:50
WHERE bool C_SizeUnitsOnLeft
Config: Show the units as a prefix to the size.
Definition: globals.h:262
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:300
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
const char * mutt_path_basename(const char *f)
Find the last component for a pathname.
Definition: path.c:307
WHERE char * CurrentFolder
Currently selected mailbox.
Definition: globals.h:55
WHERE bool C_SizeShowBytes
Config: Show smaller sizes in bytes.
Definition: globals.h:259
void mutt_encode_path(char *buf, size_t buflen, const char *src)
Convert a path into the user&#39;s preferred character set.
Definition: muttlib.c:1575
struct Envelope * mutt_env_new(void)
Create a new Envelope.
Definition: envelope.c:42
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_buffer_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:181
void mutt_safe_path(struct Buffer *dest, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:859
char * subtype
content-type subtype
Definition: body.h:37
void mutt_file_sanitize_filename(char *path, bool slash)
Replace unsafe characters in a filename.
Definition: file.c:615
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:634
#define mutt_b2s(buf)
Definition: buffer.h:41
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: curs_lib.c:862
Prototypes for many functions.
SecurityFlags mutt_is_application_pgp(struct Body *m)
Does the message use PGP?
Definition: crypt.c:559
const char * line
Definition: common.c:36
int mutt_str_strncasecmp(const char *a, const char *b, size_t l)
Compare two strings ignoring case (to a maximum), safely.
Definition: string.c:656
&#39;POP3&#39; Mailbox type
Definition: mailbox.h:54
void mutt_endwin(void)
Shutdown curses/slang.
Definition: curs_lib.c:544
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: globals.h:120
const char * GitVer
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1287
#define PATH_MAX
Definition: mutt.h:50
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
int mutt_extract_token(struct Buffer *dest, struct Buffer *tok, TokenFlags flags)
Extract one token from a string.
Definition: init.c:2674
XdgType
XDG variable types.
Definition: protos.h:47
struct Regex * C_GecosMask
Config: Regex for parsing GECOS field of /etc/passwd.
Definition: muttlib.c:72
Type: &#39;text/*&#39;.
Definition: mime.h:38
char * dptr
Current read/write position.
Definition: buffer.h:36
void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
Display an abbreviated size, like 3.4K.
Definition: muttlib.c:1779
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:1711
char * data
Pointer to data.
Definition: buffer.h:35
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition: curs_lib.c:1237
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
SecurityFlags mutt_is_application_smime(struct Body *m)
Does the message use S/MIME?
Definition: crypt.c:618
API for encryption/signing of emails.
bool mutt_is_text_part(struct Body *b)
Is this part of an email in plain text?
Definition: muttlib.c:453
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:453
#define MUTT_FILE
Do file completion.
Definition: mutt.h:64
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: muttlib.c:556
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
int mutt_set_xdg_path(enum XdgType type, char *buf, size_t bufsize)
Find an XDG path or its fallback.
Definition: muttlib.c:1617
static FILE * fp_random
Definition: muttlib.c:74
unsigned int type
content-type primary type
Definition: body.h:65
&#39;Notmuch&#39; (virtual) Mailbox type
Definition: mailbox.h:53
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: ncrypt.h:134
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
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:726
#define mutt_buffer_get_field(field, buf, complete)
Definition: curs_lib.h:84
Type: &#39;message/*&#39;.
Definition: mime.h:35
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:594
#define IS_SPACE(ch)
Definition: string2.h:38
void mutt_exit(int code)
Leave NeoMutt NOW.
Definition: main.c:210
void mutt_buffer_mktemp_full(struct Buffer *buf, const char *prefix, const char *suffix, const char *src, int line)
Create a temporary file.
Definition: muttlib.c:573
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:343
static const unsigned char base32[]
Definition: muttlib.c:76
MailboxType
Supported mailbox formats.
Definition: mailbox.h:42
Log at debug level 1.
Definition: logging.h:40
char * mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
Lookup a user&#39;s real name in /etc/passwd.
Definition: muttlib.c:382
WHERE char * C_Spoolfile
Config: Inbox.
Definition: globals.h:145
void mutt_buffer_encode_path(struct Buffer *buf, const char *src)
Convert a path into the user&#39;s preferred character set.
Definition: muttlib.c:1600
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1757
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
Cached regular expression.
Definition: regex3.h:57
#define mutt_error(...)
Definition: logging.h:84
bool mutt_needs_mailcap(struct Body *m)
Does this type need a mailcap entry do display.
Definition: muttlib.c:425
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:55
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:628
Append to existing file.
Definition: mutt_attach.h:56
WHERE char * ShortHostname
Short version of the hostname.
Definition: globals.h:51
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:96
#define FREE(x)
Definition: memory.h:40
size_t mutt_buffer_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:374
const char filename_safe_chars[]
Definition: file.c:60
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:169
#define MUTT_FORMAT_NOFILTER
Do not allow filtering on this pass.
Definition: format_flags.h:37
#define MUTT_FORMAT_OPTIONAL
Allow optional field processing.
Definition: format_flags.h:33
char * C_Charset
Config: Default character set for displaying text on screen.
Definition: charset.c:54
struct AddressList to
Email&#39;s &#39;To&#39; list.
Definition: envelope.h:58
struct MuttWindow * MuttMessageWindow
Message Window.
Definition: mutt_window.c:42
struct Email * email_new(void)
Create a new Email.
Definition: email.c:68
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
#define TAILQ_EMPTY(head)
Definition: queue.h:715
Log at debug level 5.
Definition: logging.h:44
int mutt_wait_filter(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:220
void email_free(struct Email **ptr)
Free an Email.
Definition: email.c:41
FILE * mutt_open_read(const char *path, pid_t *thepid)
Run a command to read from.
Definition: muttlib.c:1404
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
enum MailboxType mx_path_probe(const char *path, struct stat *st)
Find a mailbox that understands a path.
Definition: mx.c:1290
XDG system dir: /etc/xdg.
Definition: protos.h:50
static const char * xdg_defaults[]
Definition: muttlib.c:83
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:52
#define WithCrypto
Definition: ncrypt.h:160
void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
Find the parent of a path (or mailbox)
Definition: muttlib.c:1656
const char * format_t(char *buf, size_t buflen, size_t col, int cols, char op, const char *src, const char *prec, const char *if_str, const char *else_str, unsigned long data, MuttFormatFlags flags)
typedef format_t - Prototype for a mutt_expando_format() callback function
Definition: format_flags.h:61
static int pad(FILE *fp, int col, int i)
Write some padding to a file.
Definition: help.c:257
Convenience wrapper for the library headers.
void mutt_rand_base32(void *buf, size_t buflen)
Fill a buffer with a base32-encoded random string.
Definition: muttlib.c:529
Type: &#39;application/*&#39;.
Definition: mime.h:33
Log at debug level 3.
Definition: logging.h:42
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:703
Url is file://.
Definition: url.h:35
WHERE bool C_SaveAddress
Config: Use sender&#39;s full address as a default save folder.
Definition: globals.h:252
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:76
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:53
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1560
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: ncrypt.h:135
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:810
void mutt_buffer_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:265
WHERE bool C_ArrowCursor
Config: Use an arrow &#39;->&#39; instead of highlighting in the index.
Definition: globals.h:197