NeoMutt  2018-07-16 +2481-68dcde
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 #ifdef HAVE_SYS_SYSCALL_H
63 #include <sys/syscall.h>
64 #endif
65 #ifdef USE_IMAP
66 #include "imap/imap.h"
67 #endif
68 
69 /* These Config Variables are only used in muttlib.c */
70 struct Regex *C_GecosMask;
71 
72 static const char *xdg_env_vars[] = {
73  [XDG_CONFIG_HOME] = "XDG_CONFIG_HOME",
74  [XDG_CONFIG_DIRS] = "XDG_CONFIG_DIRS",
75 };
76 
77 static const char *xdg_defaults[] = {
78  [XDG_CONFIG_HOME] = "~/.config",
79  [XDG_CONFIG_DIRS] = "/etc/xdg",
80 };
81 
90 void mutt_adv_mktemp(struct Buffer *buf)
91 {
92  if (!(buf->data && buf->data[0]))
93  {
94  mutt_buffer_mktemp(buf);
95  }
96  else
97  {
98  struct Buffer *prefix = mutt_buffer_pool_get();
99  mutt_buffer_strcpy(prefix, buf->data);
100  mutt_file_sanitize_filename(prefix->data, true);
101  mutt_buffer_printf(buf, "%s/%s", NONULL(C_Tmpdir), mutt_b2s(prefix));
102 
103  struct stat sb;
104  if ((lstat(mutt_b2s(buf), &sb) == -1) && (errno == ENOENT))
105  goto out;
106 
107  char *suffix = strchr(prefix->data, '.');
108  if (suffix)
109  {
110  *suffix = '\0';
111  suffix++;
112  }
113  mutt_buffer_mktemp_pfx_sfx(buf, prefix->data, suffix);
114 
115  out:
116  mutt_buffer_pool_release(&prefix);
117  }
118 }
119 
128 char *mutt_expand_path(char *buf, size_t buflen)
129 {
130  return mutt_expand_path_regex(buf, buflen, false);
131 }
132 
140 void mutt_buffer_expand_path_regex(struct Buffer *buf, bool regex)
141 {
142  const char *s = NULL;
143  const char *tail = "";
144 
145  bool recurse = false;
146 
147  struct Buffer *p = mutt_buffer_pool_get();
148  struct Buffer *q = mutt_buffer_pool_get();
149  struct Buffer *tmp = mutt_buffer_pool_get();
150 
151  do
152  {
153  recurse = false;
154  s = mutt_b2s(buf);
155 
156  switch (*s)
157  {
158  case '~':
159  {
160  if ((s[1] == '/') || (s[1] == '\0'))
161  {
163  tail = s + 1;
164  }
165  else
166  {
167  char *t = strchr(s + 1, '/');
168  if (t)
169  *t = '\0';
170 
171  struct passwd *pw = getpwnam(s + 1);
172  if (pw)
173  {
174  mutt_buffer_strcpy(p, pw->pw_dir);
175  if (t)
176  {
177  *t = '/';
178  tail = t;
179  }
180  else
181  tail = "";
182  }
183  else
184  {
185  /* user not found! */
186  if (t)
187  *t = '/';
189  tail = s;
190  }
191  }
192  break;
193  }
194 
195  case '=':
196  case '+':
197  {
198  enum MailboxType mb_type = mx_path_probe(C_Folder, NULL);
199 
200  /* if folder = {host} or imap[s]://host/: don't append slash */
201  if ((mb_type == MUTT_IMAP) && ((C_Folder[strlen(C_Folder) - 1] == '}') ||
202  (C_Folder[strlen(C_Folder) - 1] == '/')))
203  {
205  }
206  else if (mb_type == MUTT_NOTMUCH)
208  else if (C_Folder && (C_Folder[strlen(C_Folder) - 1] == '/'))
210  else
211  mutt_buffer_printf(p, "%s/", NONULL(C_Folder));
212 
213  tail = s + 1;
214  break;
215  }
216 
217  /* elm compatibility, @ expands alias to user name */
218 
219  case '@':
220  {
221  struct AddressList *al = mutt_alias_lookup(s + 1);
222  if (!TAILQ_EMPTY(al))
223  {
224  struct Email *e = email_new();
225  e->env = mutt_env_new();
226  mutt_addrlist_copy(&e->env->from, al, false);
227  mutt_addrlist_copy(&e->env->to, al, false);
228 
229  /* TODO: fix mutt_default_save() to use Buffer */
231  mutt_default_save(p->data, p->dsize, e);
233 
234  email_free(&e);
235  /* Avoid infinite recursion if the resulting folder starts with '@' */
236  if (*p->data != '@')
237  recurse = true;
238 
239  tail = "";
240  }
241  break;
242  }
243 
244  case '>':
245  {
247  tail = s + 1;
248  break;
249  }
250 
251  case '<':
252  {
254  tail = s + 1;
255  break;
256  }
257 
258  case '!':
259  {
260  if (s[1] == '!')
261  {
263  tail = s + 2;
264  }
265  else
266  {
268  tail = s + 1;
269  }
270  break;
271  }
272 
273  case '-':
274  {
276  tail = s + 1;
277  break;
278  }
279 
280  case '^':
281  {
283  tail = s + 1;
284  break;
285  }
286 
287  default:
288  {
290  tail = s;
291  }
292  }
293 
294  if (regex && *(mutt_b2s(p)) && !recurse)
295  {
297  mutt_buffer_printf(tmp, "%s%s", mutt_b2s(q), tail);
298  }
299  else
300  mutt_buffer_printf(tmp, "%s%s", mutt_b2s(p), tail);
301 
302  mutt_buffer_strcpy(buf, mutt_b2s(tmp));
303  } while (recurse);
304 
308 
309 #ifdef USE_IMAP
310  /* Rewrite IMAP path in canonical form - aids in string comparisons of
311  * folders. May possibly fail, in which case buf should be the same. */
312  if (imap_path_probe(mutt_b2s(buf), NULL) == MUTT_IMAP)
313  imap_expand_path(buf);
314 #endif
315 }
316 
324 {
325  mutt_buffer_expand_path_regex(buf, false);
326 }
327 
337 char *mutt_expand_path_regex(char *buf, size_t buflen, bool regex)
338 {
339  struct Buffer *tmp = mutt_buffer_pool_get();
340 
341  mutt_buffer_addstr(tmp, NONULL(buf));
342  mutt_buffer_expand_path_regex(tmp, regex);
343  mutt_str_strfcpy(buf, mutt_b2s(tmp), buflen);
344 
346 
347  return buf;
348 }
349 
362 char *mutt_gecos_name(char *dest, size_t destlen, struct passwd *pw)
363 {
364  regmatch_t pat_match[1];
365  size_t pwnl;
366  char *p = NULL;
367 
368  if (!pw || !pw->pw_gecos)
369  return NULL;
370 
371  memset(dest, 0, destlen);
372 
373  if (mutt_regex_capture(C_GecosMask, pw->pw_gecos, 1, pat_match))
374  {
375  mutt_str_strfcpy(dest, pw->pw_gecos + pat_match[0].rm_so,
376  MIN(pat_match[0].rm_eo - pat_match[0].rm_so + 1, destlen));
377  }
378  else if ((p = strchr(pw->pw_gecos, ',')))
379  mutt_str_strfcpy(dest, pw->pw_gecos, MIN(destlen, p - pw->pw_gecos + 1));
380  else
381  mutt_str_strfcpy(dest, pw->pw_gecos, destlen);
382 
383  pwnl = strlen(pw->pw_name);
384 
385  for (int idx = 0; dest[idx]; idx++)
386  {
387  if (dest[idx] == '&')
388  {
389  memmove(&dest[idx + pwnl], &dest[idx + 1],
390  MAX((ssize_t)(destlen - idx - pwnl - 1), 0));
391  memcpy(&dest[idx], pw->pw_name, MIN(destlen - idx - 1, pwnl));
392  dest[idx] = toupper((unsigned char) dest[idx]);
393  }
394  }
395 
396  return dest;
397 }
398 
405 bool mutt_needs_mailcap(struct Body *m)
406 {
407  switch (m->type)
408  {
409  case TYPE_TEXT:
410  if (mutt_str_strcasecmp("plain", m->subtype) == 0)
411  return false;
412  break;
413  case TYPE_APPLICATION:
415  return false;
417  return false;
418  break;
419 
420  case TYPE_MULTIPART:
421  case TYPE_MESSAGE:
422  return false;
423  }
424 
425  return true;
426 }
427 
433 bool mutt_is_text_part(struct Body *b)
434 {
435  int t = b->type;
436  char *s = b->subtype;
437 
439  return false;
440 
441  if (t == TYPE_TEXT)
442  return true;
443 
444  if (t == TYPE_MESSAGE)
445  {
446  if (mutt_str_strcasecmp("delivery-status", s) == 0)
447  return true;
448  }
449 
450  if (((WithCrypto & APPLICATION_PGP) != 0) && (t == TYPE_APPLICATION))
451  {
452  if (mutt_str_strcasecmp("pgp-keys", s) == 0)
453  return true;
454  }
455 
456  return false;
457 }
458 
459 static FILE *fp_random;
460 
468 int mutt_randbuf(void *buf, size_t buflen)
469 {
470  if (buflen > 1048576)
471  {
472  mutt_error(_("mutt_randbuf buflen=%zu"), buflen);
473  return -1;
474  }
475 /* XXX switch to HAVE_GETRANDOM and getrandom() in about 2017 */
476 #if defined(SYS_getrandom) && defined(__linux__)
477  long ret;
478  do
479  {
480  ret = syscall(SYS_getrandom, buf, buflen, 0, 0, 0, 0);
481  } while ((ret == -1) && (errno == EINTR));
482  if (ret == buflen)
483  return 0;
484 #endif
485  /* let's try urandom in case we're on an old kernel, or the user has
486  * configured selinux, seccomp or something to not allow getrandom */
487  if (!fp_random)
488  {
489  fp_random = fopen("/dev/urandom", "rb");
490  if (!fp_random)
491  {
492  mutt_error(_("open /dev/urandom: %s"), strerror(errno));
493  return -1;
494  }
495  setbuf(fp_random, NULL);
496  }
497  if (fread(buf, 1, buflen, fp_random) != buflen)
498  {
499  mutt_error(_("read /dev/urandom: %s"), strerror(errno));
500  return -1;
501  }
502 
503  return 0;
504 }
505 
506 static const unsigned char base32[] = "abcdefghijklmnopqrstuvwxyz234567";
507 
513 void mutt_rand_base32(void *buf, size_t buflen)
514 {
515  uint8_t *p = buf;
516 
517  if (mutt_randbuf(p, buflen) < 0)
518  mutt_exit(1);
519  for (size_t pos = 0; pos < buflen; pos++)
520  p[pos] = base32[p[pos] % 32];
521 }
522 
527 uint32_t mutt_rand32(void)
528 {
529  uint32_t num = 0;
530 
531  if (mutt_randbuf(&num, sizeof(num)) < 0)
532  mutt_exit(1);
533  return num;
534 }
535 
540 uint64_t mutt_rand64(void)
541 {
542  uint64_t num = 0;
543 
544  if (mutt_randbuf(&num, sizeof(num)) < 0)
545  mutt_exit(1);
546  return num;
547 }
548 
557 void mutt_buffer_mktemp_full(struct Buffer *buf, const char *prefix,
558  const char *suffix, const char *src, int line)
559 {
560  mutt_buffer_printf(buf, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
561  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
562  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
563 
564  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, mutt_b2s(buf));
565  if (unlink(mutt_b2s(buf)) && (errno != ENOENT))
566  {
567  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
568  line, mutt_b2s(buf), strerror(errno), errno);
569  }
570 }
571 
583 void mutt_mktemp_full(char *buf, size_t buflen, const char *prefix,
584  const char *suffix, const char *src, int line)
585 {
586  size_t n =
587  snprintf(buf, buflen, "%s/%s-%s-%d-%d-%" PRIu64 "%s%s", NONULL(C_Tmpdir),
588  NONULL(prefix), NONULL(ShortHostname), (int) getuid(),
589  (int) getpid(), mutt_rand64(), suffix ? "." : "", NONULL(suffix));
590  if (n >= buflen)
591  {
593  "%s:%d: ERROR: insufficient buffer space to hold temporary "
594  "filename! buflen=%zu but need %zu\n",
595  src, line, buflen, n);
596  }
597  mutt_debug(LL_DEBUG3, "%s:%d: mutt_mktemp returns \"%s\"\n", src, line, buf);
598  if (unlink(buf) && (errno != ENOENT))
599  {
600  mutt_debug(LL_DEBUG1, "%s:%d: ERROR: unlink(\"%s\"): %s (errno %d)\n", src,
601  line, buf, strerror(errno), errno);
602  }
603 }
604 
612 void mutt_pretty_mailbox(char *buf, size_t buflen)
613 {
614  if (!buf)
615  return;
616 
617  char *p = buf, *q = buf;
618  size_t len;
619  enum UrlScheme scheme;
620  char tmp[PATH_MAX];
621 
622  scheme = url_check_scheme(buf);
623 
624  if ((scheme == U_IMAP) || (scheme == U_IMAPS))
625  {
626  imap_pretty_mailbox(buf, buflen, C_Folder);
627  return;
628  }
629 
630  if (scheme == U_NOTMUCH)
631  return;
632 
633  /* if buf is an url, only collapse path component */
634  if (scheme != U_UNKNOWN)
635  {
636  p = strchr(buf, ':') + 1;
637  if (strncmp(p, "//", 2) == 0)
638  q = strchr(p + 2, '/');
639  if (!q)
640  q = strchr(p, '\0');
641  p = q;
642  }
643 
644  /* cleanup path */
645  if (strstr(p, "//") || strstr(p, "/./"))
646  {
647  /* first attempt to collapse the pathname, this is more
648  * lightweight than realpath() and doesn't resolve links */
649  while (*p)
650  {
651  if ((p[0] == '/') && (p[1] == '/'))
652  {
653  *q++ = '/';
654  p += 2;
655  }
656  else if ((p[0] == '/') && (p[1] == '.') && (p[2] == '/'))
657  {
658  *q++ = '/';
659  p += 3;
660  }
661  else
662  *q++ = *p++;
663  }
664  *q = '\0';
665  }
666  else if (strstr(p, "..") && ((scheme == U_UNKNOWN) || (scheme == U_FILE)) &&
667  realpath(p, tmp))
668  {
669  mutt_str_strfcpy(p, tmp, buflen - (p - buf));
670  }
671 
672  if ((len = mutt_str_startswith(buf, C_Folder, CASE_MATCH)) && (buf[len] == '/'))
673  {
674  *buf++ = '=';
675  memmove(buf, buf + len, mutt_str_strlen(buf + len) + 1);
676  }
677  else if ((len = mutt_str_startswith(buf, HomeDir, CASE_MATCH)) && (buf[len] == '/'))
678  {
679  *buf++ = '~';
680  memmove(buf, buf + len - 1, mutt_str_strlen(buf + len - 1) + 1);
681  }
682 }
683 
689 {
690  if (!buf || !buf->data)
691  return;
692  /* This reduces the size of the Buffer, so we can pass it through.
693  * We adjust the size just to make sure buf->data is not NULL though */
695  mutt_pretty_mailbox(buf->data, buf->dsize);
697 }
698 
710 int mutt_check_overwrite(const char *attname, const char *path, struct Buffer *fname,
711  enum SaveAttach *opt, char **directory)
712 {
713  struct stat st;
714 
715  mutt_buffer_strcpy(fname, path);
716  if (access(mutt_b2s(fname), F_OK) != 0)
717  return 0;
718  if (stat(mutt_b2s(fname), &st) != 0)
719  return -1;
720  if (S_ISDIR(st.st_mode))
721  {
722  enum QuadOption ans = MUTT_NO;
723  if (directory)
724  {
725  switch (mutt_multi_choice
726  /* L10N: Means "The path you specified as the destination file is a directory."
727  See the msgid "Save to file: " (alias.c, recvattach.c)
728  These three letters correspond to the choices in the string. */
729  (_("File is a directory, save under it: (y)es, (n)o, (a)ll?"), _("yna")))
730  {
731  case 3: /* all */
732  mutt_str_replace(directory, mutt_b2s(fname));
733  break;
734  case 1: /* yes */
735  FREE(directory);
736  break;
737  case -1: /* abort */
738  FREE(directory);
739  return -1;
740  case 2: /* no */
741  FREE(directory);
742  return 1;
743  }
744  }
745  /* L10N: Means "The path you specified as the destination file is a directory."
746  See the msgid "Save to file: " (alias.c, recvattach.c) */
747  else if ((ans = mutt_yesorno(_("File is a directory, save under it?"), MUTT_YES)) != MUTT_YES)
748  return (ans == MUTT_NO) ? 1 : -1;
749 
750  struct Buffer *tmp = mutt_buffer_pool_get();
752  if (mutt_get_field(_("File under directory: "), tmp->data, tmp->dsize,
753  MUTT_FILE | MUTT_CLEAR) != 0)
754  {
756  return -1;
757  }
759  if (mutt_buffer_is_empty(tmp))
760  {
762  return (-1);
763  }
764  mutt_buffer_concat_path(fname, path, mutt_b2s(tmp));
766  }
767 
768  if ((*opt == MUTT_SAVE_NO_FLAGS) && (access(mutt_b2s(fname), F_OK) == 0))
769  {
770  switch (
771  mutt_multi_choice(_("File exists, (o)verwrite, (a)ppend, or (c)ancel?"),
772  // L10N: Options for: File exists, (o)verwrite, (a)ppend, or (c)ancel?
773  _("oac")))
774  {
775  case -1: /* abort */
776  return -1;
777  case 3: /* cancel */
778  return 1;
779 
780  case 2: /* append */
781  *opt = MUTT_SAVE_APPEND;
782  break;
783  case 1: /* overwrite */
784  *opt = MUTT_SAVE_OVERWRITE;
785  break;
786  }
787  }
788  return 0;
789 }
790 
800 void mutt_save_path(char *buf, size_t buflen, const struct Address *addr)
801 {
802  if (addr && addr->mailbox)
803  {
804  mutt_str_strfcpy(buf, addr->mailbox, buflen);
805  if (!C_SaveAddress)
806  {
807  char *p = strpbrk(buf, "%@");
808  if (p)
809  *p = '\0';
810  }
811  mutt_str_strlower(buf);
812  }
813  else
814  *buf = '\0';
815 }
816 
825 void mutt_safe_path(char *buf, size_t buflen, const struct Address *a)
826 {
827  mutt_save_path(buf, buflen, a);
828  for (char *p = buf; *p; p++)
829  if ((*p == '/') || IS_SPACE(*p) || !IsPrint((unsigned char) *p))
830  *p = '_';
831 }
832 
844 void mutt_expando_format(char *buf, size_t buflen, size_t col, int cols, const char *src,
845  format_t *callback, unsigned long data, MuttFormatFlags flags)
846 {
847  char prefix[128], tmp[1024];
848  char *cp = NULL, *wptr = buf;
849  char ch;
850  char if_str[128], else_str[128];
851  size_t wlen, count, len, wid;
852  FILE *fp_filter = NULL;
853  char *recycler = NULL;
854 
855  char src2[256];
856  mutt_str_strfcpy(src2, src, mutt_str_strlen(src) + 1);
857  src = src2;
858 
859  prefix[0] = '\0';
860  buflen--; /* save room for the terminal \0 */
861  wlen = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ? 3 : 0;
862  col += wlen;
863 
864  if ((flags & MUTT_FORMAT_NOFILTER) == 0)
865  {
866  int off = -1;
867 
868  /* Do not consider filters if no pipe at end */
869  int n = mutt_str_strlen(src);
870  if ((n > 1) && (src[n - 1] == '|'))
871  {
872  /* Scan backwards for backslashes */
873  off = n;
874  while ((off > 0) && (src[off - 2] == '\\'))
875  off--;
876  }
877 
878  /* If number of backslashes is even, the pipe is real. */
879  /* n-off is the number of backslashes. */
880  if ((off > 0) && (((n - off) % 2) == 0))
881  {
882  char srccopy[1024];
883  int i = 0;
884 
885  mutt_debug(LL_DEBUG3, "fmtpipe = %s\n", src);
886 
887  strncpy(srccopy, src, n);
888  srccopy[n - 1] = '\0';
889 
890  /* prepare Buffers */
891  struct Buffer srcbuf = mutt_buffer_make(0);
892  mutt_buffer_addstr(&srcbuf, srccopy);
893  /* note: we are resetting dptr and *reading* from the buffer, so we don't
894  * want to use mutt_buffer_reset(). */
895  srcbuf.dptr = srcbuf.data;
896  struct Buffer word = mutt_buffer_make(0);
897  struct Buffer cmd = mutt_buffer_make(0);
898 
899  /* Iterate expansions across successive arguments */
900  do
901  {
902  /* Extract the command name and copy to command line */
903  mutt_debug(LL_DEBUG3, "fmtpipe +++: %s\n", srcbuf.dptr);
904  if (word.data)
905  *word.data = '\0';
906  mutt_extract_token(&word, &srcbuf, MUTT_TOKEN_NO_FLAGS);
907  mutt_debug(LL_DEBUG3, "fmtpipe %2d: %s\n", i++, word.data);
908  mutt_buffer_addch(&cmd, '\'');
909  mutt_expando_format(tmp, sizeof(tmp), 0, cols, word.data, callback,
910  data, flags | MUTT_FORMAT_NOFILTER);
911  for (char *p = tmp; p && *p; p++)
912  {
913  if (*p == '\'')
914  {
915  /* shell quoting doesn't permit escaping a single quote within
916  * single-quoted material. double-quoting instead will lead
917  * shell variable expansions, so break out of the single-quoted
918  * span, insert a double-quoted single quote, and resume. */
919  mutt_buffer_addstr(&cmd, "'\"'\"'");
920  }
921  else
922  mutt_buffer_addch(&cmd, *p);
923  }
924  mutt_buffer_addch(&cmd, '\'');
925  mutt_buffer_addch(&cmd, ' ');
926  } while (MoreArgs(&srcbuf));
927 
928  mutt_debug(LL_DEBUG3, "fmtpipe > %s\n", cmd.data);
929 
930  col -= wlen; /* reset to passed in value */
931  wptr = buf; /* reset write ptr */
932  pid_t pid = mutt_create_filter(cmd.data, NULL, &fp_filter, NULL);
933  if (pid != -1)
934  {
935  int rc;
936 
937  n = fread(buf, 1, buflen /* already decremented */, fp_filter);
938  mutt_file_fclose(&fp_filter);
939  rc = mutt_wait_filter(pid);
940  if (rc != 0)
941  mutt_debug(LL_DEBUG1, "format pipe cmd exited code %d\n", rc);
942  if (n > 0)
943  {
944  buf[n] = '\0';
945  while ((n > 0) && ((buf[n - 1] == '\n') || (buf[n - 1] == '\r')))
946  buf[--n] = '\0';
947  mutt_debug(LL_DEBUG5, "fmtpipe < %s\n", buf);
948 
949  /* If the result ends with '%', this indicates that the filter
950  * generated %-tokens that neomutt can expand. Eliminate the '%'
951  * marker and recycle the string through mutt_expando_format().
952  * To literally end with "%", use "%%". */
953  if ((n > 0) && (buf[n - 1] == '%'))
954  {
955  n--;
956  buf[n] = '\0'; /* remove '%' */
957  if ((n > 0) && (buf[n - 1] != '%'))
958  {
959  recycler = mutt_str_strdup(buf);
960  if (recycler)
961  {
962  /* buflen is decremented at the start of this function
963  * to save space for the terminal nul char. We can add
964  * it back for the recursive call since the expansion of
965  * format pipes does not try to append a nul itself. */
966  mutt_expando_format(buf, buflen + 1, col, cols, recycler,
967  callback, data, flags);
968  FREE(&recycler);
969  }
970  }
971  }
972  }
973  else
974  {
975  /* read error */
976  mutt_debug(LL_DEBUG1, "error reading from fmtpipe: %s (errno=%d)\n",
977  strerror(errno), errno);
978  *wptr = '\0';
979  }
980  }
981  else
982  {
983  /* Filter failed; erase write buffer */
984  *wptr = '\0';
985  }
986 
987  mutt_buffer_dealloc(&cmd);
988  mutt_buffer_dealloc(&srcbuf);
989  mutt_buffer_dealloc(&word);
990  return;
991  }
992  }
993 
994  while (*src && (wlen < buflen))
995  {
996  if (*src == '%')
997  {
998  if (*++src == '%')
999  {
1000  *wptr++ = '%';
1001  wlen++;
1002  col++;
1003  src++;
1004  continue;
1005  }
1006 
1007  if (*src == '?')
1008  {
1009  /* change original %? to new %< notation */
1010  /* %?x?y&z? to %<x?y&z> where y and z are nestable */
1011  char *p = (char *) src;
1012  *p = '<';
1013  /* skip over "x" */
1014  for (; *p && *p != '?'; p++)
1015  ;
1016  /* nothing */
1017  if (*p == '?')
1018  p++;
1019  /* fix up the "y&z" section */
1020  for (; *p && *p != '?'; p++)
1021  {
1022  /* escape '<' and '>' to work inside nested-if */
1023  if ((*p == '<') || (*p == '>'))
1024  {
1025  memmove(p + 2, p, mutt_str_strlen(p) + 1);
1026  *p++ = '\\';
1027  *p++ = '\\';
1028  }
1029  }
1030  if (*p == '?')
1031  *p = '>';
1032  }
1033 
1034  if (*src == '<')
1035  {
1036  flags |= MUTT_FORMAT_OPTIONAL;
1037  ch = *(++src); /* save the character to switch on */
1038  src++;
1039  cp = prefix;
1040  count = 0;
1041  while ((count < sizeof(prefix)) && (*src != '?'))
1042  {
1043  *cp++ = *src++;
1044  count++;
1045  }
1046  *cp = '\0';
1047  }
1048  else
1049  {
1050  flags &= ~MUTT_FORMAT_OPTIONAL;
1051 
1052  /* eat the format string */
1053  cp = prefix;
1054  count = 0;
1055  while ((count < sizeof(prefix)) && (isdigit((unsigned char) *src) || (*src == '.') ||
1056  (*src == '-') || (*src == '=')))
1057  {
1058  *cp++ = *src++;
1059  count++;
1060  }
1061  *cp = '\0';
1062 
1063  if (!*src)
1064  break; /* bad format */
1065 
1066  ch = *src++; /* save the character to switch on */
1067  }
1068 
1069  if (flags & MUTT_FORMAT_OPTIONAL)
1070  {
1071  int lrbalance;
1072 
1073  if (*src != '?')
1074  break; /* bad format */
1075  src++;
1076 
1077  /* eat the 'if' part of the string */
1078  cp = if_str;
1079  count = 0;
1080  lrbalance = 1;
1081  while ((lrbalance > 0) && (count < sizeof(if_str)) && *src)
1082  {
1083  if ((src[0] == '%') && (src[1] == '>'))
1084  {
1085  /* This is a padding expando; copy two chars and carry on */
1086  *cp++ = *src++;
1087  *cp++ = *src++;
1088  count += 2;
1089  continue;
1090  }
1091 
1092  if (*src == '\\')
1093  {
1094  src++;
1095  *cp++ = *src++;
1096  }
1097  else if ((src[0] == '%') && (src[1] == '<'))
1098  {
1099  lrbalance++;
1100  }
1101  else if (src[0] == '>')
1102  {
1103  lrbalance--;
1104  }
1105  if (lrbalance == 0)
1106  break;
1107  if ((lrbalance == 1) && (src[0] == '&'))
1108  break;
1109  *cp++ = *src++;
1110  count++;
1111  }
1112  *cp = '\0';
1113 
1114  /* eat the 'else' part of the string (optional) */
1115  if (*src == '&')
1116  src++; /* skip the & */
1117  cp = else_str;
1118  count = 0;
1119  while ((lrbalance > 0) && (count < sizeof(else_str)) && *src)
1120  {
1121  if ((src[0] == '%') && (src[1] == '>'))
1122  {
1123  /* This is a padding expando; copy two chars and carry on */
1124  *cp++ = *src++;
1125  *cp++ = *src++;
1126  count += 2;
1127  continue;
1128  }
1129 
1130  if (*src == '\\')
1131  {
1132  src++;
1133  *cp++ = *src++;
1134  }
1135  else if ((src[0] == '%') && (src[1] == '<'))
1136  {
1137  lrbalance++;
1138  }
1139  else if (src[0] == '>')
1140  {
1141  lrbalance--;
1142  }
1143  if (lrbalance == 0)
1144  break;
1145  if ((lrbalance == 1) && (src[0] == '&'))
1146  break;
1147  *cp++ = *src++;
1148  count++;
1149  }
1150  *cp = '\0';
1151 
1152  if (!*src)
1153  break; /* bad format */
1154 
1155  src++; /* move past the trailing '>' (formerly '?') */
1156  }
1157 
1158  /* handle generic cases first */
1159  if ((ch == '>') || (ch == '*'))
1160  {
1161  /* %>X: right justify to EOL, left takes precedence
1162  * %*X: right justify to EOL, right takes precedence */
1163  int soft = ch == '*';
1164  int pl, pw;
1165  pl = mutt_mb_charlen(src, &pw);
1166  if (pl <= 0)
1167  {
1168  pl = 1;
1169  pw = 1;
1170  }
1171 
1172  /* see if there's room to add content, else ignore */
1173  if (((col < cols) && (wlen < buflen)) || soft)
1174  {
1175  int pad;
1176 
1177  /* get contents after padding */
1178  mutt_expando_format(tmp, sizeof(tmp), 0, cols, src + pl, callback, data, flags);
1179  len = mutt_str_strlen(tmp);
1180  wid = mutt_strwidth(tmp);
1181 
1182  pad = (cols - col - wid) / pw;
1183  if (pad >= 0)
1184  {
1185  /* try to consume as many columns as we can, if we don't have
1186  * memory for that, use as much memory as possible */
1187  if (wlen + (pad * pl) + len > buflen)
1188  pad = (buflen > (wlen + len)) ? ((buflen - wlen - len) / pl) : 0;
1189  else
1190  {
1191  /* Add pre-spacing to make multi-column pad characters and
1192  * the contents after padding line up */
1193  while ((col + (pad * pw) + wid < cols) && (wlen + (pad * pl) + len < buflen))
1194  {
1195  *wptr++ = ' ';
1196  wlen++;
1197  col++;
1198  }
1199  }
1200  while (pad-- > 0)
1201  {
1202  memcpy(wptr, src, pl);
1203  wptr += pl;
1204  wlen += pl;
1205  col += pw;
1206  }
1207  }
1208  else if (soft)
1209  {
1210  int offset = ((flags & MUTT_FORMAT_ARROWCURSOR) && C_ArrowCursor) ? 3 : 0;
1211  int avail_cols = (cols > offset) ? (cols - offset) : 0;
1212  /* \0-terminate buf for length computation in mutt_wstr_trunc() */
1213  *wptr = '\0';
1214  /* make sure right part is at most as wide as display */
1215  len = mutt_wstr_trunc(tmp, buflen, avail_cols, &wid);
1216  /* truncate left so that right part fits completely in */
1217  wlen = mutt_wstr_trunc(buf, buflen - len, avail_cols - wid, &col);
1218  wptr = buf + wlen;
1219  /* Multi-column characters may be truncated in the middle.
1220  * Add spacing so the right hand side lines up. */
1221  while ((col + wid < avail_cols) && (wlen + len < buflen))
1222  {
1223  *wptr++ = ' ';
1224  wlen++;
1225  col++;
1226  }
1227  }
1228  if ((len + wlen) > buflen)
1229  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1230  memcpy(wptr, tmp, len);
1231  wptr += len;
1232  }
1233  break; /* skip rest of input */
1234  }
1235  else if (ch == '|')
1236  {
1237  /* pad to EOL */
1238  int pl, pw;
1239  pl = mutt_mb_charlen(src, &pw);
1240  if (pl <= 0)
1241  {
1242  pl = 1;
1243  pw = 1;
1244  }
1245 
1246  /* see if there's room to add content, else ignore */
1247  if ((col < cols) && (wlen < buflen))
1248  {
1249  int c = (cols - col) / pw;
1250  if ((c > 0) && (wlen + (c * pl) > buflen))
1251  c = ((signed) (buflen - wlen)) / pl;
1252  while (c > 0)
1253  {
1254  memcpy(wptr, src, pl);
1255  wptr += pl;
1256  wlen += pl;
1257  col += pw;
1258  c--;
1259  }
1260  }
1261  break; /* skip rest of input */
1262  }
1263  else
1264  {
1265  bool to_lower = false;
1266  bool no_dots = false;
1267 
1268  while ((ch == '_') || (ch == ':'))
1269  {
1270  if (ch == '_')
1271  to_lower = true;
1272  else if (ch == ':')
1273  no_dots = true;
1274 
1275  ch = *src++;
1276  }
1277 
1278  /* use callback function to handle this case */
1279  src = callback(tmp, sizeof(tmp), col, cols, ch, src, prefix, if_str,
1280  else_str, data, flags);
1281 
1282  if (to_lower)
1283  mutt_str_strlower(tmp);
1284  if (no_dots)
1285  {
1286  char *p = tmp;
1287  for (; *p; p++)
1288  if (*p == '.')
1289  *p = '_';
1290  }
1291 
1292  len = mutt_str_strlen(tmp);
1293  if ((len + wlen) > buflen)
1294  len = mutt_wstr_trunc(tmp, buflen - wlen, cols - col, NULL);
1295 
1296  memcpy(wptr, tmp, len);
1297  wptr += len;
1298  wlen += len;
1299  col += mutt_strwidth(tmp);
1300  }
1301  }
1302  else if (*src == '\\')
1303  {
1304  if (!*++src)
1305  break;
1306  switch (*src)
1307  {
1308  case 'f':
1309  *wptr = '\f';
1310  break;
1311  case 'n':
1312  *wptr = '\n';
1313  break;
1314  case 'r':
1315  *wptr = '\r';
1316  break;
1317  case 't':
1318  *wptr = '\t';
1319  break;
1320  case 'v':
1321  *wptr = '\v';
1322  break;
1323  default:
1324  *wptr = *src;
1325  break;
1326  }
1327  src++;
1328  wptr++;
1329  wlen++;
1330  col++;
1331  }
1332  else
1333  {
1334  int bytes, width;
1335  /* in case of error, simply copy byte */
1336  bytes = mutt_mb_charlen(src, &width);
1337  if (bytes < 0)
1338  {
1339  bytes = 1;
1340  width = 1;
1341  }
1342  if ((bytes > 0) && ((wlen + bytes) < buflen))
1343  {
1344  memcpy(wptr, src, bytes);
1345  wptr += bytes;
1346  src += bytes;
1347  wlen += bytes;
1348  col += width;
1349  }
1350  else
1351  {
1352  src += buflen - wlen;
1353  wlen = buflen;
1354  }
1355  }
1356  }
1357  *wptr = '\0';
1358 }
1359 
1370 FILE *mutt_open_read(const char *path, pid_t *thepid)
1371 {
1372  FILE *fp = NULL;
1373  struct stat s;
1374 
1375  size_t len = mutt_str_strlen(path);
1376  if (len == 0)
1377  {
1378  return NULL;
1379  }
1380 
1381  if (path[len - 1] == '|')
1382  {
1383  /* read from a pipe */
1384 
1385  char *p = mutt_str_strdup(path);
1386 
1387  p[len - 1] = 0;
1388  mutt_endwin();
1389  *thepid = mutt_create_filter(p, NULL, &fp, NULL);
1390  FREE(&p);
1391  }
1392  else
1393  {
1394  if (stat(path, &s) < 0)
1395  return NULL;
1396  if (S_ISDIR(s.st_mode))
1397  {
1398  errno = EINVAL;
1399  return NULL;
1400  }
1401  fp = fopen(path, "r");
1402  *thepid = -1;
1403  }
1404  return fp;
1405 }
1406 
1415 int mutt_save_confirm(const char *s, struct stat *st)
1416 {
1417  char tmp[PATH_MAX];
1418  int ret = 0;
1419 
1420  enum MailboxType magic = mx_path_probe(s, NULL);
1421 
1422 #ifdef USE_POP
1423  if (magic == MUTT_POP)
1424  {
1425  mutt_error(_("Can't save message to POP mailbox"));
1426  return 1;
1427  }
1428 #endif
1429 
1430  if ((magic != MUTT_MAILBOX_ERROR) && (magic != MUTT_UNKNOWN) && !mx_access(s, W_OK))
1431  {
1432  if (C_Confirmappend)
1433  {
1434  snprintf(tmp, sizeof(tmp), _("Append messages to %s?"), s);
1435  enum QuadOption ans = mutt_yesorno(tmp, MUTT_YES);
1436  if (ans == MUTT_NO)
1437  ret = 1;
1438  else if (ans == MUTT_ABORT)
1439  ret = -1;
1440  }
1441  }
1442 
1443 #ifdef USE_NNTP
1444  if (magic == MUTT_NNTP)
1445  {
1446  mutt_error(_("Can't save message to news server"));
1447  return 0;
1448  }
1449 #endif
1450 
1451  if (stat(s, st) != -1)
1452  {
1453  if (magic == MUTT_MAILBOX_ERROR)
1454  {
1455  mutt_error(_("%s is not a mailbox"), s);
1456  return 1;
1457  }
1458  }
1459  else if (magic != MUTT_IMAP)
1460  {
1461  st->st_mtime = 0;
1462  st->st_atime = 0;
1463 
1464  /* pathname does not exist */
1465  if (errno == ENOENT)
1466  {
1467  if (C_Confirmcreate)
1468  {
1469  snprintf(tmp, sizeof(tmp), _("Create %s?"), s);
1470  enum QuadOption ans = mutt_yesorno(tmp, MUTT_YES);
1471  if (ans == MUTT_NO)
1472  ret = 1;
1473  else if (ans == MUTT_ABORT)
1474  ret = -1;
1475  }
1476 
1477  /* user confirmed with MUTT_YES or set C_Confirmcreate */
1478  if (ret == 0)
1479  {
1480  /* create dir recursively */
1481  char *tmp_path = mutt_path_dirname(s);
1482  if (mutt_file_mkdir(tmp_path, S_IRWXU) == -1)
1483  {
1484  /* report failure & abort */
1485  mutt_perror(s);
1486  FREE(&tmp_path);
1487  return 1;
1488  }
1489  FREE(&tmp_path);
1490  }
1491  }
1492  else
1493  {
1494  mutt_perror(s);
1495  return 1;
1496  }
1497  }
1498 
1500  return ret;
1501 }
1502 
1509 void mutt_sleep(short s)
1510 {
1511  if (C_SleepTime > s)
1512  sleep(C_SleepTime);
1513  else if (s)
1514  sleep(s);
1515 }
1516 
1523 const char *mutt_make_version(void)
1524 {
1525  static char vstring[256];
1526  snprintf(vstring, sizeof(vstring), "NeoMutt %s%s", PACKAGE_VERSION, GitVer);
1527  return vstring;
1528 }
1529 
1538 void mutt_encode_path(char *buf, size_t buflen, const char *src)
1539 {
1540  char *p = mutt_str_strdup(src);
1541  int rc = mutt_ch_convert_string(&p, C_Charset, "us-ascii", 0);
1542  /* 'src' may be NULL, such as when called from the pop3 driver. */
1543  size_t len = mutt_str_strfcpy(buf, (rc == 0) ? p : src, buflen);
1544 
1545  /* convert the path to POSIX "Portable Filename Character Set" */
1546  for (size_t i = 0; i < len; i++)
1547  {
1548  if (!isalnum(buf[i]) && !strchr("/.-_", buf[i]))
1549  {
1550  buf[i] = '_';
1551  }
1552  }
1553  FREE(&p);
1554 }
1555 
1565 int mutt_set_xdg_path(enum XdgType type, char *buf, size_t bufsize)
1566 {
1567  const char *xdg_env = mutt_str_getenv(xdg_env_vars[type]);
1568  char *xdg = xdg_env ? mutt_str_strdup(xdg_env) : mutt_str_strdup(xdg_defaults[type]);
1569  char *x = xdg; /* strsep() changes xdg, so free x instead later */
1570  char *token = NULL;
1571  int rc = 0;
1572 
1573  while ((token = strsep(&xdg, ":")))
1574  {
1575  if (snprintf(buf, bufsize, "%s/%s/neomuttrc", token, PACKAGE) < 0)
1576  continue;
1577  mutt_expand_path(buf, bufsize);
1578  if (access(buf, F_OK) == 0)
1579  {
1580  rc = 1;
1581  break;
1582  }
1583 
1584  if (snprintf(buf, bufsize, "%s/%s/Muttrc", token, PACKAGE) < 0)
1585  continue;
1586  mutt_expand_path(buf, bufsize);
1587  if (access(buf, F_OK) == 0)
1588  {
1589  rc = 1;
1590  break;
1591  }
1592  }
1593 
1594  FREE(&x);
1595  return rc;
1596 }
1597 
1604 void mutt_get_parent_path(const char *path, char *buf, size_t buflen)
1605 {
1606  enum MailboxType mb_magic = mx_path_probe(path, NULL);
1607 
1608  if (mb_magic == MUTT_IMAP)
1609  imap_get_parent_path(path, buf, buflen);
1610  else if (mb_magic == MUTT_NOTMUCH)
1611  mutt_str_strfcpy(buf, C_Folder, buflen);
1612  else
1613  {
1614  mutt_str_strfcpy(buf, path, buflen);
1615  int n = mutt_str_strlen(buf);
1616  if (n == 0)
1617  return;
1618 
1619  /* remove any final trailing '/' */
1620  if (buf[n - 1] == '/')
1621  buf[n - 1] = '\0';
1622 
1623  /* Remove everything until the next slash */
1624  for (n--; ((n >= 0) && (buf[n] != '/')); n--)
1625  ;
1626 
1627  if (n > 0)
1628  buf[n] = '\0';
1629  else
1630  {
1631  buf[0] = '/';
1632  buf[1] = '\0';
1633  }
1634  }
1635 }
1636 
1659 int mutt_inbox_cmp(const char *a, const char *b)
1660 {
1661  /* fast-track in case the paths have been mutt_pretty_mailbox'ified */
1662  if ((a[0] == '+') && (b[0] == '+'))
1663  {
1664  return (mutt_str_strcasecmp(a + 1, "inbox") == 0) ?
1665  -1 :
1666  (mutt_str_strcasecmp(b + 1, "inbox") == 0) ? 1 : 0;
1667  }
1668 
1669  const char *a_end = strrchr(a, '/');
1670  const char *b_end = strrchr(b, '/');
1671 
1672  /* If one path contains a '/', but not the other */
1673  if ((!a_end) ^ (!b_end))
1674  return 0;
1675 
1676  /* If neither path contains a '/' */
1677  if (!a_end)
1678  return 0;
1679 
1680  /* Compare the subpaths */
1681  size_t a_len = a_end - a;
1682  size_t b_len = b_end - b;
1683  size_t min = MIN(a_len, b_len);
1684  int same = (a[min] == '/') && (b[min] == '/') && (a[min + 1] != '\0') &&
1685  (b[min + 1] != '\0') && (mutt_str_strncasecmp(a, b, min) == 0);
1686 
1687  if (!same)
1688  return 0;
1689 
1690  if (mutt_str_strcasecmp(&a[min + 1], "inbox") == 0)
1691  return -1;
1692 
1693  if (mutt_str_strcasecmp(&b[min + 1], "inbox") == 0)
1694  return 1;
1695 
1696  return 0;
1697 }
1698 
1705 void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
1706 {
1707  if (!buf || !path)
1708  return;
1709 
1710  mutt_buffer_reset(buf);
1711 
1712  for (; *path; path++)
1713  {
1714  if ((slash && (*path == '/')) || !strchr(filename_safe_chars, *path))
1715  mutt_buffer_addch(buf, '_');
1716  else
1717  mutt_buffer_addch(buf, *path);
1718  }
1719 }
1720 
1727 void mutt_str_pretty_size(char *buf, size_t buflen, size_t num)
1728 {
1729  if (!buf || (buflen == 0))
1730  return;
1731 
1732  if (C_SizeShowBytes && (num < 1024))
1733  {
1734  snprintf(buf, buflen, "%d", (int) num);
1735  }
1736  else if (num == 0)
1737  {
1738  mutt_str_strfcpy(buf, C_SizeUnitsOnLeft ? "K0" : "0K", buflen);
1739  }
1740  else if (C_SizeShowFractions && (num < 10189)) /* 0.1K - 9.9K */
1741  {
1742  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "K%3.1f" : "%3.1fK",
1743  (num < 103) ? 0.1 : (num / 1024.0));
1744  }
1745  else if (!C_SizeShowMb || (num < 1023949)) /* 10K - 999K */
1746  {
1747  /* 51 is magic which causes 10189/10240 to be rounded up to 10 */
1748  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("K%zu") : ("%zuK"), (num + 51) / 1024);
1749  }
1750  else if (C_SizeShowFractions && (num < 10433332)) /* 1.0M - 9.9M */
1751  {
1752  snprintf(buf, buflen, C_SizeUnitsOnLeft ? "M%3.1f" : "%3.1fM", num / 1048576.0);
1753  }
1754  else /* 10M+ */
1755  {
1756  /* (10433332 + 52428) / 1048576 = 10 */
1757  snprintf(buf, buflen, C_SizeUnitsOnLeft ? ("M%zu") : ("%zuM"), (num + 52428) / 1048576);
1758  }
1759 }
pid_t mutt_create_filter(const char *s, FILE **fp_in, FILE **fp_out, FILE **fp_err)
Set up filter program.
Definition: filter.c:217
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:844
#define mutt_buffer_mktemp(buf)
Definition: muttlib.h:79
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:286
int mutt_randbuf(void *buf, size_t buflen)
Fill a buffer with randomness.
Definition: muttlib.c:468
The envelope/body of an email.
Definition: email.h:39
#define MUTT_CLEAR
Clear input if printable character is pressed.
Definition: mutt.h:70
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:100
#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:2425
Window management.
GUI miscellaneous curses (window drawing) routines.
&#39;NNTP&#39; (Usenet) Mailbox type
Definition: mailbox.h:51
Structs that make up an email.
User aborted the question (with Ctrl-G)
Definition: quad.h:37
#define mutt_buffer_mktemp_pfx_sfx(buf, prefix, suffix)
Definition: muttlib.h:80
WHERE bool C_Confirmappend
Config: Confirm before appending emails to a mailbox.
Definition: globals.h:214
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:412
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:612
WHERE short C_SleepTime
Config: Time to pause after certain info messages.
Definition: globals.h:155
No flags set.
Definition: mutt_attach.h:54
Pass files through external commands (filters)
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:111
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:56
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:48
WHERE char * C_Record
Config: Folder to save &#39;sent&#39; messages.
Definition: globals.h:136
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:38
WHERE bool C_Confirmcreate
Config: Confirm before creating a new mailbox.
Definition: globals.h:215
void mutt_default_save(char *path, size_t pathlen, struct Email *e)
Find the default save path for an email.
Definition: hook.c:662
Flags to control mutt_expando_format()
static const char * xdg_env_vars[]
Definition: muttlib.c:72
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:527
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:688
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:1415
#define mutt_get_field(field, buf, buflen, complete)
Definition: curs_lib.h:86
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:1049
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:140
WHERE bool C_SizeShowFractions
Config: Show size fractions with a single decimal place.
Definition: globals.h:263
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:337
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:128
#define MoreArgs(buf)
Definition: buffer.h:43
void mutt_sleep(short s)
Sleep for a while.
Definition: muttlib.c:1509
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: curs_lib.c:332
int imap_expand_path(struct Buffer *buf)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2471
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:583
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:264
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:871
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:150
WHERE char * C_Mbox
Config: Folder that receives read emails (see Move)
Definition: globals.h:122
IMAP network mailbox.
struct Envelope * env
Envelope information.
Definition: email.h:91
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:265
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:299
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:293
void mutt_safe_path(char *buf, size_t buflen, const struct Address *a)
Make a safe filename from an email address.
Definition: muttlib.c:825
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:262
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:1538
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
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:613
int mutt_file_sanitize_regex(struct Buffer *dest, const char *src)
Escape any regex-magic characters in a string.
Definition: file.c:632
#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:843
Prototypes for many functions.
SecurityFlags mutt_is_application_pgp(struct Body *m)
Does the message use PGP?
Definition: crypt.c:560
const char * line
Definition: common.c:36
void mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
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:499
WHERE char * C_Folder
Config: Base folder for a set of mailboxes.
Definition: globals.h:124
const char * GitVer
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1266
#define PATH_MAX
Definition: mutt.h:52
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:2649
XdgType
XDG variable types.
Definition: protos.h:46
struct Regex * C_GecosMask
Config: Regex for parsing GECOS field of /etc/passwd.
Definition: muttlib.c:70
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:1727
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:1659
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:1216
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:619
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:433
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:66
uint64_t mutt_rand64(void)
Create a 64-bit random number.
Definition: muttlib.c:540
int mutt_set_xdg_path(enum XdgType type, char *buf, size_t bufsize)
Find an XDG path or its fallback.
Definition: muttlib.c:1565
static FILE * fp_random
Definition: muttlib.c:459
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:710
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:557
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
static const unsigned char base32[]
Definition: muttlib.c:506
void mutt_buffer_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:323
MailboxType
Supported mailbox formats.
Definition: mailbox.h:42
Log at debug level 1.
Definition: logging.h:56
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:362
WHERE char * C_Spoolfile
Config: Inbox.
Definition: globals.h:149
void mutt_buffer_sanitize_filename(struct Buffer *buf, const char *path, short slash)
Replace unsafe characters in a filename.
Definition: muttlib.c:1705
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:405
char * C_Tmpdir
Config: Directory for temporary files.
Definition: file.c:53
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:55
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:90
#define FREE(x)
Definition: memory.h:40
const char filename_safe_chars[]
Definition: file.c:58
int mx_access(const char *path, int flags)
Wrapper for access, checks permissions on a given mailbox.
Definition: mx.c:170
#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:60
int mutt_wait_filter(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:228
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:1370
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:1236
XDG system dir: /etc/xdg.
Definition: protos.h:49
static const char * xdg_defaults[]
Definition: muttlib.c:77
&#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:1604
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:513
Type: &#39;application/*&#39;.
Definition: mime.h:33
Log at debug level 3.
Definition: logging.h:58
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:688
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:255
#define MUTT_TOKEN_NO_FLAGS
No flags are set.
Definition: mutt.h:78
SaveAttach
Options for saving attachments.
Definition: mutt_attach.h:52
const char * mutt_make_version(void)
Generate the NeoMutt version string.
Definition: muttlib.c:1523
#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:800
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:200
void mutt_buffer_concat_path(struct Buffer *buf, const char *dir, const char *fname)
Join a directory name and a filename.
Definition: buffer.c:352