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