NeoMutt  2018-07-16 +2481-68dcde
Teaching an old dog new tricks
DOXYGEN
imap.c
Go to the documentation of this file.
1 
32 #include "config.h"
33 #include <limits.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 #include "imap_private.h"
40 #include "mutt/mutt.h"
41 #include "config/lib.h"
42 #include "email/lib.h"
43 #include "core/lib.h"
44 #include "conn/conn.h"
45 #include "mutt.h"
46 #include "imap.h"
47 #include "auth.h"
48 #include "commands.h"
49 #include "curs_lib.h"
50 #include "globals.h"
51 #include "hook.h"
52 #include "message.h"
53 #include "mutt_account.h"
54 #include "mutt_logging.h"
55 #include "mutt_socket.h"
56 #include "muttlib.h"
57 #include "mx.h"
58 #include "pattern.h"
59 #include "progress.h"
60 #include "sort.h"
61 #ifdef ENABLE_NLS
62 #include <libintl.h>
63 #endif
64 
65 struct stat;
66 
67 /* These Config Variables are only used in imap/imap.c */
68 bool C_ImapIdle;
70 
77 static int check_capabilities(struct ImapAccountData *adata)
78 {
79  if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
80  {
81  imap_error("check_capabilities", adata->buf);
82  return -1;
83  }
84 
85  if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
86  {
87  mutt_error(
88  _("This IMAP server is ancient. NeoMutt does not work with it."));
89  return -1;
90  }
91 
92  return 0;
93 }
94 
104 static char *get_flags(struct ListHead *hflags, char *s)
105 {
106  /* sanity-check string */
107  const size_t plen = mutt_str_startswith(s, "FLAGS", CASE_IGNORE);
108  if (plen == 0)
109  {
110  mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
111  return NULL;
112  }
113  s += plen;
114  SKIPWS(s);
115  if (*s != '(')
116  {
117  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
118  return NULL;
119  }
120 
121  /* update caller's flags handle */
122  while (*s && (*s != ')'))
123  {
124  s++;
125  SKIPWS(s);
126  const char *flag_word = s;
127  while (*s && (*s != ')') && !IS_SPACE(*s))
128  s++;
129  const char ctmp = *s;
130  *s = '\0';
131  if (*flag_word)
132  mutt_list_insert_tail(hflags, mutt_str_strdup(flag_word));
133  *s = ctmp;
134  }
135 
136  /* note bad flags response */
137  if (*s != ')')
138  {
139  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
140  mutt_list_free(hflags);
141 
142  return NULL;
143  }
144 
145  s++;
146 
147  return s;
148 }
149 
159 static void set_flag(struct Mailbox *m, AclFlags aclflag, int flag,
160  const char *str, char *flags, size_t flsize)
161 {
162  if (m->rights & aclflag)
163  if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
164  mutt_str_strcat(flags, flsize, str);
165 }
166 
180 static int make_msg_set(struct Mailbox *m, struct Buffer *buf, int flag,
181  bool changed, bool invert, int *pos)
182 {
183  int count = 0; /* number of messages in message set */
184  unsigned int setstart = 0; /* start of current message range */
185  int n;
186  bool started = false;
187 
188  struct ImapAccountData *adata = imap_adata_get(m);
189  if (!adata || (adata->mailbox != m))
190  return -1;
191 
192  struct Email **emails = m->emails;
193 
194  for (n = *pos; (n < m->msg_count) && (mutt_buffer_len(buf) < IMAP_MAX_CMDLEN); n++)
195  {
196  bool match = false; /* whether current message matches flag condition */
197  /* don't include pending expunged messages.
198  *
199  * TODO: can we unset active in cmd_parse_expunge() and
200  * cmd_parse_vanished() instead of checking for index != INT_MAX. */
201  if (emails[n]->active && (emails[n]->index != INT_MAX))
202  {
203  switch (flag)
204  {
205  case MUTT_DELETED:
206  if (emails[n]->deleted != imap_edata_get(emails[n])->deleted)
207  match = invert ^ emails[n]->deleted;
208  break;
209  case MUTT_FLAG:
210  if (emails[n]->flagged != imap_edata_get(emails[n])->flagged)
211  match = invert ^ emails[n]->flagged;
212  break;
213  case MUTT_OLD:
214  if (emails[n]->old != imap_edata_get(emails[n])->old)
215  match = invert ^ emails[n]->old;
216  break;
217  case MUTT_READ:
218  if (emails[n]->read != imap_edata_get(emails[n])->read)
219  match = invert ^ emails[n]->read;
220  break;
221  case MUTT_REPLIED:
222  if (emails[n]->replied != imap_edata_get(emails[n])->replied)
223  match = invert ^ emails[n]->replied;
224  break;
225  case MUTT_TAG:
226  if (emails[n]->tagged)
227  match = true;
228  break;
229  case MUTT_TRASH:
230  if (emails[n]->deleted && !emails[n]->purge)
231  match = true;
232  break;
233  }
234  }
235 
236  if (match && (!changed || emails[n]->changed))
237  {
238  count++;
239  if (setstart == 0)
240  {
241  setstart = imap_edata_get(emails[n])->uid;
242  if (!started)
243  {
244  mutt_buffer_add_printf(buf, "%u", imap_edata_get(emails[n])->uid);
245  started = true;
246  }
247  else
248  mutt_buffer_add_printf(buf, ",%u", imap_edata_get(emails[n])->uid);
249  }
250  /* tie up if the last message also matches */
251  else if (n == (m->msg_count - 1))
252  mutt_buffer_add_printf(buf, ":%u", imap_edata_get(emails[n])->uid);
253  }
254  /* End current set if message doesn't match or we've reached the end
255  * of the mailbox via inactive messages following the last match. */
256  else if (setstart && (emails[n]->active || (n == adata->mailbox->msg_count - 1)))
257  {
258  if (imap_edata_get(emails[n - 1])->uid > setstart)
259  mutt_buffer_add_printf(buf, ":%u", imap_edata_get(emails[n - 1])->uid);
260  setstart = 0;
261  }
262  }
263 
264  *pos = n;
265 
266  return count;
267 }
268 
277 static bool compare_flags_for_copy(struct Email *e)
278 {
279  struct ImapEmailData *edata = e->edata;
280 
281  if (e->read != edata->read)
282  return true;
283  if (e->old != edata->old)
284  return true;
285  if (e->flagged != edata->flagged)
286  return true;
287  if (e->replied != edata->replied)
288  return true;
289 
290  return false;
291 }
292 
302 static int sync_helper(struct Mailbox *m, AclFlags right, int flag, const char *name)
303 {
304  int count = 0;
305  int rc;
306  char buf[1024];
307 
308  if (!m)
309  return -1;
310 
311  if ((m->rights & right) == 0)
312  return 0;
313 
314  if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
315  return 0;
316 
317  snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
318  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, false);
319  if (rc < 0)
320  return rc;
321  count += rc;
322 
323  buf[0] = '-';
324  rc = imap_exec_msgset(m, "UID STORE", buf, flag, true, true);
325  if (rc < 0)
326  return rc;
327  count += rc;
328 
329  return count;
330 }
331 
340 static int do_search(const struct PatternList *search, bool allpats)
341 {
342  int rc = 0;
343  const struct Pattern *pat = NULL;
344 
345  SLIST_FOREACH(pat, search, entries)
346  {
347  switch (pat->op)
348  {
349  case MUTT_PAT_BODY:
350  case MUTT_PAT_HEADER:
351  case MUTT_PAT_WHOLE_MSG:
352  if (pat->string_match)
353  rc++;
354  break;
356  rc++;
357  break;
358  default:
359  if (pat->child && do_search(pat->child, true))
360  rc++;
361  }
362 
363  if (!allpats)
364  break;
365  }
366 
367  return rc;
368 }
369 
382 static int compile_search(struct Mailbox *m, const struct PatternList *pat, struct Buffer *buf)
383 {
384  struct Pattern *firstpat = SLIST_FIRST(pat);
385 
386  if (do_search(pat, false) == 0)
387  return 0;
388 
389  if (firstpat->pat_not)
390  mutt_buffer_addstr(buf, "NOT ");
391 
392  if (firstpat->child)
393  {
394  int clauses;
395 
396  clauses = do_search(firstpat->child, true);
397  if (clauses > 0)
398  {
399  mutt_buffer_addch(buf, '(');
400 
401  while (clauses)
402  {
403  if (do_search(firstpat->child, false))
404  {
405  if ((firstpat->op == MUTT_PAT_OR) && (clauses > 1))
406  mutt_buffer_addstr(buf, "OR ");
407  clauses--;
408 
409  if (compile_search(m, firstpat->child, buf) < 0)
410  return -1;
411 
412  if (clauses)
413  mutt_buffer_addch(buf, ' ');
414  }
415 
416  SLIST_REMOVE_HEAD(firstpat->child, entries);
417  }
418 
419  mutt_buffer_addch(buf, ')');
420  }
421  }
422  else
423  {
424  char term[256];
425  char *delim = NULL;
426 
427  switch (firstpat->op)
428  {
429  case MUTT_PAT_HEADER:
430  mutt_buffer_addstr(buf, "HEADER ");
431 
432  /* extract header name */
433  delim = strchr(firstpat->p.str, ':');
434  if (!delim)
435  {
436  mutt_error(_("Header search without header name: %s"), firstpat->p.str);
437  return -1;
438  }
439  *delim = '\0';
440  imap_quote_string(term, sizeof(term), firstpat->p.str, false);
441  mutt_buffer_addstr(buf, term);
442  mutt_buffer_addch(buf, ' ');
443 
444  /* and field */
445  *delim = ':';
446  delim++;
447  SKIPWS(delim);
448  imap_quote_string(term, sizeof(term), delim, false);
449  mutt_buffer_addstr(buf, term);
450  break;
451  case MUTT_PAT_BODY:
452  mutt_buffer_addstr(buf, "BODY ");
453  imap_quote_string(term, sizeof(term), firstpat->p.str, false);
454  mutt_buffer_addstr(buf, term);
455  break;
456  case MUTT_PAT_WHOLE_MSG:
457  mutt_buffer_addstr(buf, "TEXT ");
458  imap_quote_string(term, sizeof(term), firstpat->p.str, false);
459  mutt_buffer_addstr(buf, term);
460  break;
462  {
463  struct ImapAccountData *adata = imap_adata_get(m);
464  if (!(adata->capabilities & IMAP_CAP_X_GM_EXT_1))
465  {
466  mutt_error(_("Server-side custom search not supported: %s"),
467  firstpat->p.str);
468  return -1;
469  }
470  }
471  mutt_buffer_addstr(buf, "X-GM-RAW ");
472  imap_quote_string(term, sizeof(term), firstpat->p.str, false);
473  mutt_buffer_addstr(buf, term);
474  break;
475  }
476  }
477 
478  return 0;
479 }
480 
491 static size_t longest_common_prefix(char *dest, const char *src, size_t start, size_t dlen)
492 {
493  size_t pos = start;
494 
495  while ((pos < dlen) && dest[pos] && (dest[pos] == src[pos]))
496  pos++;
497  dest[pos] = '\0';
498 
499  return pos;
500 }
501 
512 static int complete_hosts(char *buf, size_t buflen)
513 {
514  // struct Connection *conn = NULL;
515  int rc = -1;
516  size_t matchlen;
517 
518  matchlen = mutt_str_strlen(buf);
519  struct MailboxList ml = neomutt_mailboxlist_get_all(NeoMutt, MUTT_MAILBOX_ANY);
520  struct MailboxNode *np = NULL;
521  STAILQ_FOREACH(np, &ml, entries)
522  {
524  continue;
525 
526  if (rc)
527  {
528  mutt_str_strfcpy(buf, mailbox_path(np->mailbox), buflen);
529  rc = 0;
530  }
531  else
532  longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen, buflen);
533  }
535 
536 #if 0
537  TAILQ_FOREACH(conn, mutt_socket_head(), entries)
538  {
539  struct Url url;
540  char urlstr[1024];
541 
542  if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
543  continue;
544 
545  mutt_account_tourl(&conn->account, &url);
546  /* FIXME: how to handle multiple users on the same host? */
547  url.user = NULL;
548  url.path = NULL;
549  url_tostring(&url, urlstr, sizeof(urlstr), 0);
550  if (mutt_str_strncmp(buf, urlstr, matchlen) == 0)
551  {
552  if (rc)
553  {
554  mutt_str_strfcpy(buf, urlstr, buflen);
555  rc = 0;
556  }
557  else
558  longest_common_prefix(buf, urlstr, matchlen, buflen);
559  }
560  }
561 #endif
562 
563  return rc;
564 }
565 
573 int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
574 {
575  char buf[2048], mbox[1024];
576 
577  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
578  snprintf(buf, sizeof(buf), "CREATE %s", mbox);
579 
580  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
581  {
582  mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
583  return -1;
584  }
585 
586  return 0;
587 }
588 
599 int imap_access(const char *path)
600 {
601  if (imap_path_status(path, false) >= 0)
602  return 0;
603  else
604  return -1;
605 }
606 
615 int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
616 {
617  char oldmbox[1024];
618  char newmbox[1024];
619  int rc = 0;
620 
621  imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
622  imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
623 
624  struct Buffer *buf = mutt_buffer_pool_get();
625  mutt_buffer_printf(buf, "RENAME %s %s", oldmbox, newmbox);
626 
628  rc = -1;
629 
631 
632  return rc;
633 }
634 
642 int imap_delete_mailbox(struct Mailbox *m, char *path)
643 {
644  char buf[PATH_MAX + 7];
645  char mbox[PATH_MAX];
646  struct Url *url = url_parse(path);
647 
648  struct ImapAccountData *adata = imap_adata_get(m);
649  imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
650  url_free(&url);
651  snprintf(buf, sizeof(buf), "DELETE %s", mbox);
653  return -1;
654 
655  return 0;
656 }
657 
662 static void imap_logout(struct ImapAccountData *adata)
663 {
664  /* we set status here to let imap_handle_untagged know we _expect_ to
665  * receive a bye response (so it doesn't freak out and close the conn) */
666  if (adata->state == IMAP_DISCONNECTED)
667  {
668  return;
669  }
670 
671  adata->status = IMAP_BYE;
672  imap_cmd_start(adata, "LOGOUT");
673  if ((C_ImapPollTimeout <= 0) || (mutt_socket_poll(adata->conn, C_ImapPollTimeout) != 0))
674  {
675  while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
676  ;
677  }
678  mutt_socket_close(adata->conn);
679  adata->state = IMAP_DISCONNECTED;
680 }
681 
687 void imap_logout_all(void)
688 {
689  struct Account *np = NULL;
690  TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
691  {
692  if (np->magic != MUTT_IMAP)
693  continue;
694 
695  struct ImapAccountData *adata = np->adata;
696  if (!adata)
697  continue;
698 
699  struct Connection *conn = adata->conn;
700  if (!conn || (conn->fd < 0))
701  continue;
702 
703  mutt_message(_("Closing connection to %s..."), conn->account.host);
704  imap_logout(np->adata);
706  }
707 }
708 
723 int imap_read_literal(FILE *fp, struct ImapAccountData *adata,
724  unsigned long bytes, struct Progress *pbar)
725 {
726  char c;
727  bool r = false;
728  struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
729 
731  mutt_buffer_alloc(&buf, bytes + 10);
732 
733  mutt_debug(LL_DEBUG2, "reading %ld bytes\n", bytes);
734 
735  for (unsigned long pos = 0; pos < bytes; pos++)
736  {
737  if (mutt_socket_readchar(adata->conn, &c) != 1)
738  {
739  mutt_debug(LL_DEBUG1, "error during read, %ld bytes read\n", pos);
740  adata->status = IMAP_FATAL;
741 
742  mutt_buffer_dealloc(&buf);
743  return -1;
744  }
745 
746  if (r && (c != '\n'))
747  fputc('\r', fp);
748 
749  if (c == '\r')
750  {
751  r = true;
752  continue;
753  }
754  else
755  r = false;
756 
757  fputc(c, fp);
758 
759  if (pbar && !(pos % 1024))
760  mutt_progress_update(pbar, pos, -1);
762  mutt_buffer_addch(&buf, c);
763  }
764 
766  {
767  mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
768  mutt_buffer_dealloc(&buf);
769  }
770  return 0;
771 }
772 
782 {
783  struct ImapAccountData *adata = imap_adata_get(m);
784  struct ImapMboxData *mdata = imap_mdata_get(m);
785  if (!adata || !mdata)
786  return;
787 
788  struct Email *e = NULL;
789 
790 #ifdef USE_HCACHE
791  mdata->hcache = imap_hcache_open(adata, mdata);
792 #endif
793 
794  for (int i = 0; i < m->msg_count; i++)
795  {
796  e = m->emails[i];
797 
798  if (e->index == INT_MAX)
799  {
800  mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
801 
802  e->deleted = true;
803 
804  imap_cache_del(m, e);
805 #ifdef USE_HCACHE
806  imap_hcache_del(mdata, imap_edata_get(e)->uid);
807 #endif
808 
809  mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
810 
811  imap_edata_free((void **) &e->edata);
812  }
813  else
814  {
815  e->index = i;
816  /* NeoMutt has several places where it turns off e->active as a
817  * hack. For example to avoid FLAG updates, or to exclude from
818  * imap_exec_msgset.
819  *
820  * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
821  * flag becomes set (e.g. a flag update to a modified header),
822  * this function will be called by imap_cmd_finish().
823  *
824  * The ctx_update_tables() will free and remove these "inactive" headers,
825  * despite that an EXPUNGE was not received for them.
826  * This would result in memory leaks and segfaults due to dangling
827  * pointers in the msn_index and uid_hash.
828  *
829  * So this is another hack to work around the hacks. We don't want to
830  * remove the messages, so make sure active is on.
831  */
832  e->active = true;
833  }
834  }
835 
836 #ifdef USE_HCACHE
837  imap_hcache_close(mdata);
838 #endif
839 
842 }
843 
851 {
852  if (mutt_socket_open(adata->conn) < 0)
853  return -1;
854 
855  adata->state = IMAP_CONNECTED;
856 
857  if (imap_cmd_step(adata) != IMAP_RES_OK)
858  {
859  imap_close_connection(adata);
860  return -1;
861  }
862 
863  if (mutt_str_startswith(adata->buf, "* OK", CASE_IGNORE))
864  {
865  if (!mutt_str_startswith(adata->buf, "* OK [CAPABILITY", CASE_IGNORE) &&
866  check_capabilities(adata))
867  {
868  goto bail;
869  }
870 #ifdef USE_SSL
871  /* Attempt STARTTLS if available and desired. */
872  if (!adata->conn->ssf && (C_SslForceTls || (adata->capabilities & IMAP_CAP_STARTTLS)))
873  {
874  enum QuadOption ans;
875 
876  if (C_SslForceTls)
877  ans = MUTT_YES;
878  else if ((ans = query_quadoption(C_SslStarttls,
879  _("Secure connection with TLS?"))) == MUTT_ABORT)
880  {
881  goto err_close_conn;
882  }
883  if (ans == MUTT_YES)
884  {
885  enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_NO_FLAGS);
886  if (rc == IMAP_EXEC_FATAL)
887  goto bail;
888  if (rc != IMAP_EXEC_ERROR)
889  {
890  if (mutt_ssl_starttls(adata->conn))
891  {
892  mutt_error(_("Could not negotiate TLS connection"));
893  goto err_close_conn;
894  }
895  else
896  {
897  /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
898  if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS))
899  goto bail;
900  }
901  }
902  }
903  }
904 
905  if (C_SslForceTls && !adata->conn->ssf)
906  {
907  mutt_error(_("Encrypted connection unavailable"));
908  goto err_close_conn;
909  }
910 #endif
911  }
912  else if (mutt_str_startswith(adata->buf, "* PREAUTH", CASE_IGNORE))
913  {
914  adata->state = IMAP_AUTHENTICATED;
915  if (check_capabilities(adata) != 0)
916  goto bail;
917  FREE(&adata->capstr);
918  }
919  else
920  {
921  imap_error("imap_open_connection()", adata->buf);
922  goto bail;
923  }
924 
925  return 0;
926 
927 #ifdef USE_SSL
928 err_close_conn:
929  imap_close_connection(adata);
930 #endif
931 bail:
932  FREE(&adata->capstr);
933  return -1;
934 }
935 
941 {
942  if (adata->state != IMAP_DISCONNECTED)
943  {
944  mutt_socket_close(adata->conn);
945  adata->state = IMAP_DISCONNECTED;
946  }
947  adata->seqno = false;
948  adata->nextcmd = false;
949  adata->lastcmd = false;
950  adata->status = 0;
951  memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
952 }
953 
963 bool imap_has_flag(struct ListHead *flag_list, const char *flag)
964 {
965  if (STAILQ_EMPTY(flag_list))
966  return false;
967 
968  struct ListNode *np = NULL;
969  STAILQ_FOREACH(np, flag_list, entries)
970  {
971  if (mutt_str_strcasecmp(np->data, flag) == 0)
972  return true;
973 
974  if (mutt_str_strcmp(np->data, "\\*") == 0)
975  return true;
976  }
977 
978  return false;
979 }
980 
984 static int compare_uid(const void *a, const void *b)
985 {
986  const struct Email *ea = *(struct Email const *const *) a;
987  const struct Email *eb = *(struct Email const *const *) b;
988  return imap_edata_get((struct Email *) ea)->uid -
989  imap_edata_get((struct Email *) eb)->uid;
990 }
991 
1007 int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post,
1008  int flag, bool changed, bool invert)
1009 {
1010  struct ImapAccountData *adata = imap_adata_get(m);
1011  if (!adata || (adata->mailbox != m))
1012  return -1;
1013 
1014  struct Email **emails = NULL;
1015  short oldsort;
1016  int pos;
1017  int rc;
1018  int count = 0;
1019 
1020  struct Buffer cmd = mutt_buffer_make(0);
1021 
1022  /* We make a copy of the headers just in case resorting doesn't give
1023  exactly the original order (duplicate messages?), because other parts of
1024  the ctx are tied to the header order. This may be overkill. */
1025  oldsort = C_Sort;
1026  if (C_Sort != SORT_ORDER)
1027  {
1028  emails = m->emails;
1029  m->emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1030  memcpy(m->emails, emails, m->msg_count * sizeof(struct Email *));
1031 
1032  C_Sort = SORT_ORDER;
1033  qsort(m->emails, m->msg_count, sizeof(struct Email *), compare_uid);
1034  }
1035 
1036  pos = 0;
1037 
1038  do
1039  {
1040  mutt_buffer_reset(&cmd);
1041  mutt_buffer_add_printf(&cmd, "%s ", pre);
1042  rc = make_msg_set(m, &cmd, flag, changed, invert, &pos);
1043  if (rc > 0)
1044  {
1045  mutt_buffer_add_printf(&cmd, " %s", post);
1046  if (imap_exec(adata, cmd.data, IMAP_CMD_QUEUE) != IMAP_EXEC_SUCCESS)
1047  {
1048  rc = -1;
1049  goto out;
1050  }
1051  count += rc;
1052  }
1053  } while (rc > 0);
1054 
1055  rc = count;
1056 
1057 out:
1058  mutt_buffer_dealloc(&cmd);
1059  if (oldsort != C_Sort)
1060  {
1061  C_Sort = oldsort;
1062  FREE(&m->emails);
1063  m->emails = emails;
1064  }
1065 
1066  return rc;
1067 }
1068 
1084 int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e,
1085  struct Buffer *cmd, enum QuadOption *err_continue)
1086 {
1087  struct ImapAccountData *adata = imap_adata_get(m);
1088  if (!adata || (adata->mailbox != m))
1089  return -1;
1090 
1091  char flags[1024];
1092  char *tags = NULL;
1093  char uid[11];
1094 
1095  if (!compare_flags_for_copy(e))
1096  {
1097  if (e->deleted == imap_edata_get(e)->deleted)
1098  e->changed = false;
1099  return 0;
1100  }
1101 
1102  snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
1103  mutt_buffer_reset(cmd);
1104  mutt_buffer_addstr(cmd, "UID STORE ");
1105  mutt_buffer_addstr(cmd, uid);
1106 
1107  flags[0] = '\0';
1108 
1109  set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags, sizeof(flags));
1110  set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags, sizeof(flags));
1111  set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags, sizeof(flags));
1112  set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags, sizeof(flags));
1113  set_flag(m, MUTT_ACL_DELETE, imap_edata_get(e)->deleted, "\\Deleted ", flags,
1114  sizeof(flags));
1115 
1116  if (m->rights & MUTT_ACL_WRITE)
1117  {
1118  /* restore system flags */
1119  if (imap_edata_get(e)->flags_system)
1120  mutt_str_strcat(flags, sizeof(flags), imap_edata_get(e)->flags_system);
1121  /* set custom flags */
1122  tags = driver_tags_get_with_hidden(&e->tags);
1123  if (tags)
1124  {
1125  mutt_str_strcat(flags, sizeof(flags), tags);
1126  FREE(&tags);
1127  }
1128  }
1129 
1131 
1132  /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
1133  * explicitly revoke all system flags (if we have permission) */
1134  if (!*flags)
1135  {
1136  set_flag(m, MUTT_ACL_SEEN, 1, "\\Seen ", flags, sizeof(flags));
1137  set_flag(m, MUTT_ACL_WRITE, 1, "Old ", flags, sizeof(flags));
1138  set_flag(m, MUTT_ACL_WRITE, 1, "\\Flagged ", flags, sizeof(flags));
1139  set_flag(m, MUTT_ACL_WRITE, 1, "\\Answered ", flags, sizeof(flags));
1140  set_flag(m, MUTT_ACL_DELETE, !imap_edata_get(e)->deleted, "\\Deleted ",
1141  flags, sizeof(flags));
1142 
1143  /* erase custom flags */
1144  if ((m->rights & MUTT_ACL_WRITE) && imap_edata_get(e)->flags_remote)
1145  mutt_str_strcat(flags, sizeof(flags), imap_edata_get(e)->flags_remote);
1146 
1148 
1149  mutt_buffer_addstr(cmd, " -FLAGS.SILENT (");
1150  }
1151  else
1152  mutt_buffer_addstr(cmd, " FLAGS.SILENT (");
1153 
1154  mutt_buffer_addstr(cmd, flags);
1155  mutt_buffer_addstr(cmd, ")");
1156 
1157  /* after all this it's still possible to have no flags, if you
1158  * have no ACL rights */
1159  if (*flags && (imap_exec(adata, cmd->data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS) &&
1160  err_continue && (*err_continue != MUTT_YES))
1161  {
1162  *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1163  if (*err_continue != MUTT_YES)
1164  return -1;
1165  }
1166 
1167  /* server have now the updated flags */
1168  FREE(&imap_edata_get(e)->flags_remote);
1170 
1171  if (e->deleted == imap_edata_get(e)->deleted)
1172  e->changed = false;
1173 
1174  return 0;
1175 }
1176 
1186 int imap_check_mailbox(struct Mailbox *m, bool force)
1187 {
1188  if (!m || !m->account)
1189  return -1;
1190 
1191  struct ImapAccountData *adata = imap_adata_get(m);
1192  struct ImapMboxData *mdata = imap_mdata_get(m);
1193 
1194  /* overload keyboard timeout to avoid many mailbox checks in a row.
1195  * Most users don't like having to wait exactly when they press a key. */
1196  int rc = 0;
1197 
1198  /* try IDLE first, unless force is set */
1199  if (!force && C_ImapIdle && (adata->capabilities & IMAP_CAP_IDLE) &&
1200  ((adata->state != IMAP_IDLE) || (mutt_date_epoch() >= adata->lastread + C_ImapKeepalive)))
1201  {
1202  if (imap_cmd_idle(adata) < 0)
1203  return -1;
1204  }
1205  if (adata->state == IMAP_IDLE)
1206  {
1207  while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1208  {
1209  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1210  {
1211  mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1212  return -1;
1213  }
1214  }
1215  if (rc < 0)
1216  {
1217  mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1218  adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1219  }
1220  }
1221 
1222  if ((force || ((adata->state != IMAP_IDLE) &&
1223  (mutt_date_epoch() >= adata->lastread + C_Timeout))) &&
1224  (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1225  {
1226  return -1;
1227  }
1228 
1229  /* We call this even when we haven't run NOOP in case we have pending
1230  * changes to process, since we can reopen here. */
1231  imap_cmd_finish(adata);
1232 
1233  if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1234  rc = MUTT_REOPENED;
1235  else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1236  rc = MUTT_NEW_MAIL;
1237  else if (mdata->check_status & IMAP_FLAGS_PENDING)
1238  rc = MUTT_FLAGS;
1239 
1241 
1242  return rc;
1243 }
1244 
1252 static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
1253 {
1254  char *uid_validity_flag = NULL;
1255  char cmd[2048];
1256 
1257  if (!adata || !mdata)
1258  return -1;
1259 
1260  /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1261  * IDLEd elsewhere.
1262  * adata->mailbox may be NULL for connections other than the current
1263  * mailbox's.. #3216. */
1264  if (adata->mailbox && (adata->mailbox->mdata == mdata))
1265  {
1266  adata->mailbox->has_new = false;
1267  return mdata->messages;
1268  }
1269 
1270  if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1271  uid_validity_flag = "UIDVALIDITY";
1272  else if (adata->capabilities & IMAP_CAP_STATUS)
1273  uid_validity_flag = "UID-VALIDITY";
1274  else
1275  {
1276  mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1277  return -1;
1278  }
1279 
1280  snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1281  mdata->munge_name, uid_validity_flag);
1282 
1283  int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_NO_FLAGS | IMAP_CMD_POLL);
1284  if (rc < 0)
1285  {
1286  mutt_debug(LL_DEBUG1, "Error queueing command\n");
1287  return rc;
1288  }
1289  return mdata->messages;
1290 }
1291 
1296 {
1297  int rc = imap_mailbox_status(m, true);
1298  if (rc > 0)
1299  rc = 0;
1300  return rc;
1301 }
1302 
1309 int imap_path_status(const char *path, bool queue)
1310 {
1311  struct Mailbox *m = mx_mbox_find2(path);
1312  if (m)
1313  return imap_mailbox_status(m, queue);
1314 
1315  // FIXME(sileht): Is that case possible ?
1316  struct ImapAccountData *adata = NULL;
1317  struct ImapMboxData *mdata = NULL;
1318 
1319  if (imap_adata_find(path, &adata, &mdata) < 0)
1320  return -1;
1321  int rc = imap_status(adata, mdata, queue);
1322  imap_mdata_free((void *) &mdata);
1323  return rc;
1324 }
1325 
1334 int imap_mailbox_status(struct Mailbox *m, bool queue)
1335 {
1336  struct ImapAccountData *adata = imap_adata_get(m);
1337  struct ImapMboxData *mdata = imap_mdata_get(m);
1338  if (!adata || !mdata)
1339  return -1;
1340  return imap_status(adata, mdata, queue);
1341 }
1342 
1350 int imap_search(struct Mailbox *m, const struct PatternList *pat)
1351 {
1352  struct Buffer buf;
1353  struct ImapAccountData *adata = imap_adata_get(m);
1354  for (int i = 0; i < m->msg_count; i++)
1355  m->emails[i]->matched = false;
1356 
1357  if (do_search(pat, true) == 0)
1358  return 0;
1359 
1360  mutt_buffer_init(&buf);
1361  mutt_buffer_addstr(&buf, "UID SEARCH ");
1362  if (compile_search(m, pat, &buf) < 0)
1363  {
1364  FREE(&buf.data);
1365  return -1;
1366  }
1367  if (imap_exec(adata, buf.data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1368  {
1369  FREE(&buf.data);
1370  return -1;
1371  }
1372 
1373  FREE(&buf.data);
1374  return 0;
1375 }
1376 
1384 int imap_subscribe(char *path, bool subscribe)
1385 {
1386  struct ImapAccountData *adata = NULL;
1387  struct ImapMboxData *mdata = NULL;
1388  char buf[2048];
1389  char errstr[256];
1390  struct Buffer err, token;
1391 
1392  if (imap_adata_find(path, &adata, &mdata) < 0)
1393  return -1;
1394 
1396  {
1397  char mbox[1024];
1398  mutt_buffer_init(&token);
1399  mutt_buffer_init(&err);
1400  err.data = errstr;
1401  err.dsize = sizeof(errstr);
1402  size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1403  imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1404  if (mutt_parse_rc_line(mbox, &token, &err))
1405  mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", errstr);
1406  FREE(&token.data);
1407  }
1408 
1409  if (subscribe)
1410  mutt_message(_("Subscribing to %s..."), mdata->name);
1411  else
1412  mutt_message(_("Unsubscribing from %s..."), mdata->name);
1413 
1414  snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1415 
1416  if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1417  {
1418  imap_mdata_free((void *) &mdata);
1419  return -1;
1420  }
1421 
1422  if (subscribe)
1423  mutt_message(_("Subscribed to %s"), mdata->name);
1424  else
1425  mutt_message(_("Unsubscribed from %s"), mdata->name);
1426  imap_mdata_free((void *) &mdata);
1427  return 0;
1428 }
1429 
1441 int imap_complete(char *buf, size_t buflen, char *path)
1442 {
1443  struct ImapAccountData *adata = NULL;
1444  struct ImapMboxData *mdata = NULL;
1445  char tmp[2048];
1446  struct ImapList listresp;
1447  char completion[1024];
1448  int clen;
1449  size_t matchlen = 0;
1450  int completions = 0;
1451  int rc;
1452 
1453  if (imap_adata_find(path, &adata, &mdata) < 0)
1454  {
1455  mutt_str_strfcpy(buf, path, buflen);
1456  return complete_hosts(buf, buflen);
1457  }
1458 
1459  /* fire off command */
1460  snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1461  C_ImapListSubscribed ? "LSUB" : "LIST", mdata->real_name);
1462 
1463  imap_cmd_start(adata, tmp);
1464 
1465  /* and see what the results are */
1466  mutt_str_strfcpy(completion, mdata->name, sizeof(completion));
1467  imap_mdata_free((void *) &mdata);
1468 
1469  adata->cmdresult = &listresp;
1470  do
1471  {
1472  listresp.name = NULL;
1473  rc = imap_cmd_step(adata);
1474 
1475  if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1476  {
1477  /* if the folder isn't selectable, append delimiter to force browse
1478  * to enter it on second tab. */
1479  if (listresp.noselect)
1480  {
1481  clen = strlen(listresp.name);
1482  listresp.name[clen++] = listresp.delim;
1483  listresp.name[clen] = '\0';
1484  }
1485  /* copy in first word */
1486  if (!completions)
1487  {
1488  mutt_str_strfcpy(completion, listresp.name, sizeof(completion));
1489  matchlen = strlen(completion);
1490  completions++;
1491  continue;
1492  }
1493 
1494  matchlen = longest_common_prefix(completion, listresp.name, 0, matchlen);
1495  completions++;
1496  }
1497  } while (rc == IMAP_RES_CONTINUE);
1498  adata->cmdresult = NULL;
1499 
1500  if (completions)
1501  {
1502  /* reformat output */
1503  imap_qualify_path(buf, buflen, &adata->conn_account, completion);
1504  mutt_pretty_mailbox(buf, buflen);
1505  return 0;
1506  }
1507 
1508  return -1;
1509 }
1510 
1519 int imap_fast_trash(struct Mailbox *m, char *dest)
1520 {
1521  char prompt[1024];
1522  int rc = -1;
1523  bool triedcreate = false;
1524  enum QuadOption err_continue = MUTT_NO;
1525 
1526  struct ImapAccountData *adata = imap_adata_get(m);
1527  struct ImapAccountData *dest_adata = NULL;
1528  struct ImapMboxData *dest_mdata = NULL;
1529 
1530  if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1531  return -1;
1532 
1533  struct Buffer sync_cmd = mutt_buffer_make(0);
1534 
1535  /* check that the save-to folder is in the same account */
1536  if (!mutt_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1537  {
1538  mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1539  goto out;
1540  }
1541 
1542  for (int i = 0; i < m->msg_count; i++)
1543  {
1544  if (m->emails[i]->active && m->emails[i]->changed &&
1545  m->emails[i]->deleted && !m->emails[i]->purge)
1546  {
1547  rc = imap_sync_message_for_copy(m, m->emails[i], &sync_cmd, &err_continue);
1548  if (rc < 0)
1549  {
1550  mutt_debug(LL_DEBUG1, "could not sync\n");
1551  goto out;
1552  }
1553  }
1554  }
1555 
1556  /* loop in case of TRYCREATE */
1557  do
1558  {
1559  rc = imap_exec_msgset(m, "UID COPY", dest_mdata->munge_name, MUTT_TRASH, false, false);
1560  if (rc == 0)
1561  {
1562  mutt_debug(LL_DEBUG1, "No messages to trash\n");
1563  rc = -1;
1564  goto out;
1565  }
1566  else if (rc < 0)
1567  {
1568  mutt_debug(LL_DEBUG1, "could not queue copy\n");
1569  goto out;
1570  }
1571  else
1572  {
1573  mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1574  rc, dest_mdata->name);
1575  }
1576 
1577  /* let's get it on */
1578  rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1579  if (rc == IMAP_EXEC_ERROR)
1580  {
1581  if (triedcreate)
1582  {
1583  mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1584  break;
1585  }
1586  /* bail out if command failed for reasons other than nonexistent target */
1587  if (!mutt_str_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]", CASE_IGNORE))
1588  break;
1589  mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1590  snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1591  if (C_Confirmcreate && (mutt_yesorno(prompt, MUTT_YES) != MUTT_YES))
1592  {
1593  mutt_clear_error();
1594  goto out;
1595  }
1596  if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1597  break;
1598  triedcreate = true;
1599  }
1600  } while (rc == IMAP_EXEC_ERROR);
1601 
1602  if (rc != IMAP_EXEC_SUCCESS)
1603  {
1604  imap_error("imap_fast_trash", adata->buf);
1605  goto out;
1606  }
1607 
1608  rc = IMAP_EXEC_SUCCESS;
1609 
1610 out:
1611  mutt_buffer_dealloc(&sync_cmd);
1612  imap_mdata_free((void *) &dest_mdata);
1613 
1614  return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1615 }
1616 
1625 int imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
1626 {
1627  if (!m)
1628  return -1;
1629 
1630  struct Email *e = NULL;
1631  struct Email **emails = NULL;
1632  int oldsort;
1633  int rc;
1634 
1635  struct ImapAccountData *adata = imap_adata_get(m);
1636  struct ImapMboxData *mdata = imap_mdata_get(m);
1637 
1638  if (adata->state < IMAP_SELECTED)
1639  {
1640  mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1641  return -1;
1642  }
1643 
1644  /* This function is only called when the calling code expects the context
1645  * to be changed. */
1646  imap_allow_reopen(m);
1647 
1648  rc = imap_check_mailbox(m, false);
1649  if (rc < 0)
1650  return rc;
1651 
1652  /* if we are expunging anyway, we can do deleted messages very quickly... */
1653  if (expunge && (m->rights & MUTT_ACL_DELETE))
1654  {
1655  rc = imap_exec_msgset(m, "UID STORE", "+FLAGS.SILENT (\\Deleted)",
1656  MUTT_DELETED, true, false);
1657  if (rc < 0)
1658  {
1659  mutt_error(_("Expunge failed"));
1660  return rc;
1661  }
1662 
1663  if (rc > 0)
1664  {
1665  /* mark these messages as unchanged so second pass ignores them. Done
1666  * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1667  for (int i = 0; i < m->msg_count; i++)
1668  if (m->emails[i]->deleted && m->emails[i]->changed)
1669  m->emails[i]->active = false;
1670  mutt_message(ngettext("Marking %d message deleted...",
1671  "Marking %d messages deleted...", rc),
1672  rc);
1673  }
1674  }
1675 
1676 #ifdef USE_HCACHE
1677  mdata->hcache = imap_hcache_open(adata, mdata);
1678 #endif
1679 
1680  /* save messages with real (non-flag) changes */
1681  for (int i = 0; i < m->msg_count; i++)
1682  {
1683  e = m->emails[i];
1684 
1685  if (e->deleted)
1686  {
1687  imap_cache_del(m, e);
1688 #ifdef USE_HCACHE
1689  imap_hcache_del(mdata, imap_edata_get(e)->uid);
1690 #endif
1691  }
1692 
1693  if (e->active && e->changed)
1694  {
1695 #ifdef USE_HCACHE
1696  imap_hcache_put(mdata, e);
1697 #endif
1698  /* if the message has been rethreaded or attachments have been deleted
1699  * we delete the message and reupload it.
1700  * This works better if we're expunging, of course. */
1701  /* TODO: why the e->env check? */
1702  if ((e->env && e->env->changed) || e->attach_del)
1703  {
1704  /* L10N: The plural is chosen by the last %d, i.e. the total number */
1705  mutt_message(ngettext("Saving changed message... [%d/%d]",
1706  "Saving changed messages... [%d/%d]", m->msg_count),
1707  i + 1, m->msg_count);
1708  mutt_save_message_ctx(e, true, false, false, m);
1709  /* TODO: why the check for h->env? Is this possible? */
1710  if (e->env)
1711  e->env->changed = 0;
1712  }
1713  }
1714  }
1715 
1716 #ifdef USE_HCACHE
1717  imap_hcache_close(mdata);
1718 #endif
1719 
1720  /* presort here to avoid doing 10 resorts in imap_exec_msgset */
1721  oldsort = C_Sort;
1722  if (C_Sort != SORT_ORDER)
1723  {
1724  emails = m->emails;
1725  m->emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1726  memcpy(m->emails, emails, m->msg_count * sizeof(struct Email *));
1727 
1728  C_Sort = SORT_ORDER;
1729  qsort(m->emails, m->msg_count, sizeof(struct Email *), mutt_get_sort_func(SORT_ORDER));
1730  }
1731 
1732  rc = sync_helper(m, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1733  if (rc >= 0)
1734  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1735  if (rc >= 0)
1736  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1737  if (rc >= 0)
1738  rc |= sync_helper(m, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1739  if (rc >= 0)
1740  rc |= sync_helper(m, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1741 
1742  if (oldsort != C_Sort)
1743  {
1744  C_Sort = oldsort;
1745  FREE(&m->emails);
1746  m->emails = emails;
1747  }
1748 
1749  /* Flush the queued flags if any were changed in sync_helper. */
1750  if (rc > 0)
1751  if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1752  rc = -1;
1753 
1754  if (rc < 0)
1755  {
1756  if (close)
1757  {
1758  if (mutt_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1759  {
1760  adata->state = IMAP_AUTHENTICATED;
1761  return 0;
1762  }
1763  }
1764  else
1765  mutt_error(_("Error saving flags"));
1766  return -1;
1767  }
1768 
1769  /* Update local record of server state to reflect the synchronization just
1770  * completed. imap_read_headers always overwrites hcache-origin flags, so
1771  * there is no need to mutate the hcache after flag-only changes. */
1772  for (int i = 0; i < m->msg_count; i++)
1773  {
1774  struct ImapEmailData *edata = imap_edata_get(m->emails[i]);
1775  edata->deleted = m->emails[i]->deleted;
1776  edata->flagged = m->emails[i]->flagged;
1777  edata->old = m->emails[i]->old;
1778  edata->read = m->emails[i]->read;
1779  edata->replied = m->emails[i]->replied;
1780  m->emails[i]->changed = false;
1781  }
1782  m->changed = false;
1783 
1784  /* We must send an EXPUNGE command if we're not closing. */
1785  if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1786  {
1787  mutt_message(_("Expunging messages from server..."));
1788  /* Set expunge bit so we don't get spurious reopened messages */
1789  mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1790  if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1791  {
1792  mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1793  imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1794  return -1;
1795  }
1796  mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1797  }
1798 
1799  if (expunge && close)
1800  {
1801  adata->closing = true;
1802  imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
1803  adata->state = IMAP_AUTHENTICATED;
1804  }
1805 
1806  if (C_MessageCacheClean)
1807  imap_cache_clean(m);
1808 
1809  return 0;
1810 }
1811 
1815 struct Account *imap_ac_find(struct Account *a, const char *path)
1816 {
1817  if (!a || (a->magic != MUTT_IMAP) || !path)
1818  return NULL;
1819 
1820  struct Url *url = url_parse(path);
1821 
1822  struct ImapAccountData *adata = a->adata;
1823  struct ConnAccount *ac = &adata->conn_account;
1824 
1825  if ((mutt_str_strcasecmp(url->host, ac->host) != 0) ||
1826  (mutt_str_strcasecmp(url->user, ac->user) != 0))
1827  {
1828  a = NULL;
1829  }
1830 
1831  url_free(&url);
1832  return a;
1833 }
1834 
1838 int imap_ac_add(struct Account *a, struct Mailbox *m)
1839 {
1840  if (!a || !m || (m->magic != MUTT_IMAP))
1841  return -1;
1842 
1843  struct ImapAccountData *adata = a->adata;
1844 
1845  if (!adata)
1846  {
1847  struct ConnAccount conn_account;
1848  char mailbox[PATH_MAX];
1849 
1850  if (imap_parse_path(mailbox_path(m), &conn_account, mailbox, sizeof(mailbox)) < 0)
1851  return -1;
1852 
1853  adata = imap_adata_new();
1854  adata->conn_account = conn_account;
1855  adata->conn = mutt_conn_new(&conn_account);
1856  if (!adata->conn)
1857  return -1;
1858 
1860 
1861  if (imap_login(adata) < 0)
1862  return -1;
1863 
1864  a->adata = adata;
1866  }
1867 
1868  if (!m->mdata)
1869  {
1870  struct Url *url = url_parse(mailbox_path(m));
1871  struct ImapMboxData *mdata = imap_mdata_new(adata, url->path);
1872 
1873  /* fixup path and realpath, mainly to replace / by /INBOX */
1874  char buf[1024];
1875  imap_qualify_path(buf, sizeof(buf), &adata->conn_account, mdata->name);
1876  mutt_buffer_strcpy(&m->pathbuf, buf);
1878 
1879  m->mdata = mdata;
1881  url_free(&url);
1882  }
1883  return 0;
1884 }
1885 
1894 int imap_login(struct ImapAccountData *adata)
1895 {
1896  if (!adata)
1897  return -1;
1898 
1899  if (adata->state == IMAP_DISCONNECTED)
1900  imap_open_connection(adata);
1901  if (adata->state == IMAP_CONNECTED)
1902  {
1903  if (imap_authenticate(adata) == IMAP_AUTH_SUCCESS)
1904  {
1905  adata->state = IMAP_AUTHENTICATED;
1906  FREE(&adata->capstr);
1907  if (adata->conn->ssf)
1908  mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1909  adata->conn->ssf);
1910  }
1911  else
1913  }
1914  if (adata->state == IMAP_AUTHENTICATED)
1915  {
1916  /* capabilities may have changed */
1917  imap_exec(adata, "CAPABILITY", IMAP_CMD_QUEUE);
1918 
1919  /* enable RFC6855, if the server supports that */
1920  if (C_ImapRfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1921  imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1922 
1923  /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1924  * is supported (even if not advertised), so flip that bit. */
1925  if (adata->capabilities & IMAP_CAP_QRESYNC)
1926  {
1927  adata->capabilities |= IMAP_CAP_CONDSTORE;
1929  imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1930  }
1931 
1932  /* get root delimiter, '/' as default */
1933  adata->delim = '/';
1934  imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1935 
1936  /* we may need the root delimiter before we open a mailbox */
1937  imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1938  }
1939 
1940  if (adata->state < IMAP_AUTHENTICATED)
1941  return -1;
1942 
1943  return 0;
1944 }
1945 
1949 static int imap_mbox_open(struct Mailbox *m)
1950 {
1951  if (!m || !m->account || !m->mdata)
1952  return -1;
1953 
1954  char buf[PATH_MAX];
1955  int count = 0;
1956  int rc;
1957  const char *condstore = NULL;
1958 
1959  struct ImapAccountData *adata = imap_adata_get(m);
1960  struct ImapMboxData *mdata = imap_mdata_get(m);
1961 
1962  adata->mailbox = m;
1963 
1964  /* clear mailbox status */
1965  adata->status = 0;
1966  m->rights = 0;
1967  mdata->new_mail_count = 0;
1968 
1969  mutt_message(_("Selecting %s..."), mdata->name);
1970 
1971  /* pipeline ACL test */
1972  if (adata->capabilities & IMAP_CAP_ACL)
1973  {
1974  snprintf(buf, sizeof(buf), "MYRIGHTS %s", mdata->munge_name);
1975  imap_exec(adata, buf, IMAP_CMD_QUEUE);
1976  }
1977  /* assume we have all rights if ACL is unavailable */
1978  else
1979  {
1982  }
1983 
1984  /* pipeline the postponed count if possible */
1985  struct Mailbox *m_postponed = mx_mbox_find2(C_Postponed);
1986  struct ImapAccountData *postponed_adata = imap_adata_get(m_postponed);
1987  if (postponed_adata &&
1988  mutt_account_match(&postponed_adata->conn_account, &adata->conn_account))
1989  imap_mailbox_status(m_postponed, true);
1990 
1992  imap_exec(adata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE);
1993 
1994 #ifdef USE_HCACHE
1996  condstore = " (CONDSTORE)";
1997  else
1998 #endif
1999  condstore = "";
2000 
2001  snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
2002  mdata->munge_name, condstore);
2003 
2004  adata->state = IMAP_SELECTED;
2005 
2006  imap_cmd_start(adata, buf);
2007 
2008  do
2009  {
2010  char *pc = NULL;
2011 
2012  rc = imap_cmd_step(adata);
2013  if (rc != IMAP_RES_CONTINUE)
2014  break;
2015 
2016  pc = adata->buf + 2;
2017 
2018  /* Obtain list of available flags here, may be overridden by a
2019  * PERMANENTFLAGS tag in the OK response */
2020  if (mutt_str_startswith(pc, "FLAGS", CASE_IGNORE))
2021  {
2022  /* don't override PERMANENTFLAGS */
2023  if (STAILQ_EMPTY(&mdata->flags))
2024  {
2025  mutt_debug(LL_DEBUG3, "Getting mailbox FLAGS\n");
2026  pc = get_flags(&mdata->flags, pc);
2027  if (!pc)
2028  goto fail;
2029  }
2030  }
2031  /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
2032  else if (mutt_str_startswith(pc, "OK [PERMANENTFLAGS", CASE_IGNORE))
2033  {
2034  mutt_debug(LL_DEBUG3, "Getting mailbox PERMANENTFLAGS\n");
2035  /* safe to call on NULL */
2036  mutt_list_free(&mdata->flags);
2037  /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
2038  pc += 13;
2039  pc = get_flags(&(mdata->flags), pc);
2040  if (!pc)
2041  goto fail;
2042  }
2043  /* save UIDVALIDITY for the header cache */
2044  else if (mutt_str_startswith(pc, "OK [UIDVALIDITY", CASE_IGNORE))
2045  {
2046  mutt_debug(LL_DEBUG3, "Getting mailbox UIDVALIDITY\n");
2047  pc += 3;
2048  pc = imap_next_word(pc);
2049  if (mutt_str_atoui(pc, &mdata->uid_validity) < 0)
2050  goto fail;
2051  }
2052  else if (mutt_str_startswith(pc, "OK [UIDNEXT", CASE_IGNORE))
2053  {
2054  mutt_debug(LL_DEBUG3, "Getting mailbox UIDNEXT\n");
2055  pc += 3;
2056  pc = imap_next_word(pc);
2057  if (mutt_str_atoui(pc, &mdata->uid_next) < 0)
2058  goto fail;
2059  }
2060  else if (mutt_str_startswith(pc, "OK [HIGHESTMODSEQ", CASE_IGNORE))
2061  {
2062  mutt_debug(LL_DEBUG3, "Getting mailbox HIGHESTMODSEQ\n");
2063  pc += 3;
2064  pc = imap_next_word(pc);
2065  if (mutt_str_atoull(pc, &mdata->modseq) < 0)
2066  goto fail;
2067  }
2068  else if (mutt_str_startswith(pc, "OK [NOMODSEQ", CASE_IGNORE))
2069  {
2070  mutt_debug(LL_DEBUG3, "Mailbox has NOMODSEQ set\n");
2071  mdata->modseq = 0;
2072  }
2073  else
2074  {
2075  pc = imap_next_word(pc);
2076  if (mutt_str_startswith(pc, "EXISTS", CASE_IGNORE))
2077  {
2078  count = mdata->new_mail_count;
2079  mdata->new_mail_count = 0;
2080  }
2081  }
2082  } while (rc == IMAP_RES_CONTINUE);
2083 
2084  if (rc == IMAP_RES_NO)
2085  {
2086  char *s = imap_next_word(adata->buf); /* skip seq */
2087  s = imap_next_word(s); /* Skip response */
2088  mutt_error("%s", s);
2089  goto fail;
2090  }
2091 
2092  if (rc != IMAP_RES_OK)
2093  goto fail;
2094 
2095  /* check for READ-ONLY notification */
2096  if (mutt_str_startswith(imap_get_qualifier(adata->buf), "[READ-ONLY]", CASE_IGNORE) &&
2097  !(adata->capabilities & IMAP_CAP_ACL))
2098  {
2099  mutt_debug(LL_DEBUG2, "Mailbox is read-only\n");
2100  m->readonly = true;
2101  }
2102 
2103  /* dump the mailbox flags we've found */
2104  if (C_DebugLevel > LL_DEBUG2)
2105  {
2106  if (STAILQ_EMPTY(&mdata->flags))
2107  mutt_debug(LL_DEBUG3, "No folder flags found\n");
2108  else
2109  {
2110  struct ListNode *np = NULL;
2111  struct Buffer flag_buffer;
2112  mutt_buffer_init(&flag_buffer);
2113  mutt_buffer_printf(&flag_buffer, "Mailbox flags: ");
2114  STAILQ_FOREACH(np, &mdata->flags, entries)
2115  {
2116  mutt_buffer_add_printf(&flag_buffer, "[%s] ", np->data);
2117  }
2118  mutt_debug(LL_DEBUG3, "%s\n", flag_buffer.data);
2119  FREE(&flag_buffer.data);
2120  }
2121  }
2122 
2123  if (!((m->rights & MUTT_ACL_DELETE) || (m->rights & MUTT_ACL_SEEN) ||
2124  (m->rights & MUTT_ACL_WRITE) || (m->rights & MUTT_ACL_INSERT)))
2125  {
2126  m->readonly = true;
2127  }
2128 
2129  m->email_max = count;
2130  m->emails = mutt_mem_calloc(count, sizeof(struct Email *));
2131  m->v2r = mutt_mem_calloc(count, sizeof(int));
2132  m->msg_count = 0;
2133  m->msg_unread = 0;
2134  m->msg_flagged = 0;
2135  m->msg_new = 0;
2136  m->msg_deleted = 0;
2137  m->size = 0;
2138  m->vcount = 0;
2139 
2140  if (count && (imap_read_headers(m, 1, count, true) < 0))
2141  {
2142  mutt_error(_("Error opening mailbox"));
2143  goto fail;
2144  }
2145 
2146  mutt_debug(LL_DEBUG2, "msg_count is %d\n", m->msg_count);
2147  return 0;
2148 
2149 fail:
2150  if (adata->state == IMAP_SELECTED)
2151  adata->state = IMAP_AUTHENTICATED;
2152  return -1;
2153 }
2154 
2158 static int imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
2159 {
2160  if (!m || !m->account)
2161  return -1;
2162 
2163  /* in APPEND mode, we appear to hijack an existing IMAP connection -
2164  * ctx is brand new and mostly empty */
2165  struct ImapAccountData *adata = imap_adata_get(m);
2166  struct ImapMboxData *mdata = imap_mdata_get(m);
2167 
2168  int rc = imap_mailbox_status(m, false);
2169  if (rc >= 0)
2170  return 0;
2171  if (rc == -1)
2172  return -1;
2173 
2174  char buf[PATH_MAX + 64];
2175  snprintf(buf, sizeof(buf), _("Create %s?"), mdata->name);
2176  if (C_Confirmcreate && (mutt_yesorno(buf, MUTT_YES) != MUTT_YES))
2177  return -1;
2178 
2179  if (imap_create_mailbox(adata, mdata->name) < 0)
2180  return -1;
2181 
2182  return 0;
2183 }
2184 
2192 static int imap_mbox_check(struct Mailbox *m, int *index_hint)
2193 {
2194  if (!m)
2195  return -1;
2196 
2197  imap_allow_reopen(m);
2198  int rc = imap_check_mailbox(m, false);
2199  /* NOTE - ctx might have been changed at this point. In particular,
2200  * m could be NULL. Beware. */
2202 
2203  return rc;
2204 }
2205 
2209 static int imap_mbox_close(struct Mailbox *m)
2210 {
2211  if (!m)
2212  return -1;
2213 
2214  struct ImapAccountData *adata = imap_adata_get(m);
2215  struct ImapMboxData *mdata = imap_mdata_get(m);
2216 
2217  /* Check to see if the mailbox is actually open */
2218  if (!adata || !mdata)
2219  return 0;
2220 
2221  /* imap_mbox_open_append() borrows the struct ImapAccountData temporarily,
2222  * just for the connection.
2223  *
2224  * So when these are equal, it means we are actually closing the
2225  * mailbox and should clean up adata. Otherwise, we don't want to
2226  * touch adata - it's still being used. */
2227  if (m == adata->mailbox)
2228  {
2229  if ((adata->status != IMAP_FATAL) && (adata->state >= IMAP_SELECTED))
2230  {
2231  /* mx_mbox_close won't sync if there are no deleted messages
2232  * and the mailbox is unchanged, so we may have to close here */
2233  if (m->msg_deleted == 0)
2234  {
2235  adata->closing = true;
2236  imap_exec(adata, "CLOSE", IMAP_CMD_QUEUE);
2237  }
2238  adata->state = IMAP_AUTHENTICATED;
2239  }
2240 
2241  adata->mailbox = NULL;
2243  }
2244 
2245  return 0;
2246 }
2247 
2251 static int imap_msg_open_new(struct Mailbox *m, struct Message *msg, struct Email *e)
2252 {
2253  char tmp[PATH_MAX];
2254 
2255  mutt_mktemp(tmp, sizeof(tmp));
2256  msg->fp = mutt_file_fopen(tmp, "w");
2257  if (!msg->fp)
2258  {
2259  mutt_perror(tmp);
2260  return -1;
2261  }
2262  msg->path = mutt_str_strdup(tmp);
2263  return 0;
2264 }
2265 
2269 static int imap_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
2270 {
2271  struct ImapMboxData *mdata = imap_mdata_get(m);
2272  if (!mdata)
2273  return -1;
2274 
2275  char *new_tag = NULL;
2276  char *checker = NULL;
2277 
2278  /* Check for \* flags capability */
2279  if (!imap_has_flag(&mdata->flags, NULL))
2280  {
2281  mutt_error(_("IMAP server doesn't support custom flags"));
2282  return -1;
2283  }
2284 
2285  *buf = '\0';
2286  if (tags)
2287  mutt_str_strfcpy(buf, tags, buflen);
2288 
2289  if (mutt_get_field("Tags: ", buf, buflen, 0) != 0)
2290  return -1;
2291 
2292  /* each keyword must be atom defined by rfc822 as:
2293  *
2294  * atom = 1*<any CHAR except specials, SPACE and CTLs>
2295  * CHAR = ( 0.-127. )
2296  * specials = "(" / ")" / "<" / ">" / "@"
2297  * / "," / ";" / ":" / "\" / <">
2298  * / "." / "[" / "]"
2299  * SPACE = ( 32. )
2300  * CTLS = ( 0.-31., 127.)
2301  *
2302  * And must be separated by one space.
2303  */
2304 
2305  new_tag = buf;
2306  checker = buf;
2307  SKIPWS(checker);
2308  while (*checker != '\0')
2309  {
2310  if ((*checker < 32) || (*checker >= 127) || // We allow space because it's the separator
2311  (*checker == 40) || // (
2312  (*checker == 41) || // )
2313  (*checker == 60) || // <
2314  (*checker == 62) || // >
2315  (*checker == 64) || // @
2316  (*checker == 44) || // ,
2317  (*checker == 59) || // ;
2318  (*checker == 58) || // :
2319  (*checker == 92) || // backslash
2320  (*checker == 34) || // "
2321  (*checker == 46) || // .
2322  (*checker == 91) || // [
2323  (*checker == 93)) // ]
2324  {
2325  mutt_error(_("Invalid IMAP flags"));
2326  return 0;
2327  }
2328 
2329  /* Skip duplicate space */
2330  while ((checker[0] == ' ') && (checker[1] == ' '))
2331  checker++;
2332 
2333  /* copy char to new_tag and go the next one */
2334  *new_tag++ = *checker++;
2335  }
2336  *new_tag = '\0';
2337  new_tag = buf; /* rewind */
2338  mutt_str_remove_trailing_ws(new_tag);
2339 
2340  if (mutt_str_strcmp(tags, buf) == 0)
2341  return 0;
2342  return 1;
2343 }
2344 
2358 static int imap_tags_commit(struct Mailbox *m, struct Email *e, char *buf)
2359 {
2360  if (!m)
2361  return -1;
2362 
2363  char uid[11];
2364 
2365  struct ImapAccountData *adata = imap_adata_get(m);
2366 
2367  if (*buf == '\0')
2368  buf = NULL;
2369 
2370  if (!(adata->mailbox->rights & MUTT_ACL_WRITE))
2371  return 0;
2372 
2373  snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
2374 
2375  /* Remove old custom flags */
2376  if (imap_edata_get(e)->flags_remote)
2377  {
2378  struct Buffer cmd = mutt_buffer_make(128); // just a guess
2379  mutt_buffer_addstr(&cmd, "UID STORE ");
2380  mutt_buffer_addstr(&cmd, uid);
2381  mutt_buffer_addstr(&cmd, " -FLAGS.SILENT (");
2382  mutt_buffer_addstr(&cmd, imap_edata_get(e)->flags_remote);
2383  mutt_buffer_addstr(&cmd, ")");
2384 
2385  /* Should we return here, or we are fine and we could
2386  * continue to add new flags */
2387  int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2388  mutt_buffer_dealloc(&cmd);
2389  if (rc != IMAP_EXEC_SUCCESS)
2390  {
2391  return -1;
2392  }
2393  }
2394 
2395  /* Add new custom flags */
2396  if (buf)
2397  {
2398  struct Buffer cmd = mutt_buffer_make(128); // just a guess
2399  mutt_buffer_addstr(&cmd, "UID STORE ");
2400  mutt_buffer_addstr(&cmd, uid);
2401  mutt_buffer_addstr(&cmd, " +FLAGS.SILENT (");
2402  mutt_buffer_addstr(&cmd, buf);
2403  mutt_buffer_addstr(&cmd, ")");
2404 
2405  int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2406  mutt_buffer_dealloc(&cmd);
2407  if (rc != IMAP_EXEC_SUCCESS)
2408  {
2409  mutt_debug(LL_DEBUG1, "fail to add new flags\n");
2410  return -1;
2411  }
2412  }
2413 
2414  /* We are good sync them */
2415  mutt_debug(LL_DEBUG1, "NEW TAGS: %s\n", buf);
2416  driver_tags_replace(&e->tags, buf);
2417  FREE(&imap_edata_get(e)->flags_remote);
2419  return 0;
2420 }
2421 
2425 enum MailboxType imap_path_probe(const char *path, const struct stat *st)
2426 {
2427  if (!path)
2428  return MUTT_UNKNOWN;
2429 
2430  if (mutt_str_startswith(path, "imap://", CASE_IGNORE))
2431  return MUTT_IMAP;
2432 
2433  if (mutt_str_startswith(path, "imaps://", CASE_IGNORE))
2434  return MUTT_IMAP;
2435 
2436  return MUTT_UNKNOWN;
2437 }
2438 
2442 int imap_path_canon(char *buf, size_t buflen)
2443 {
2444  if (!buf)
2445  return -1;
2446 
2447  struct Url *url = url_parse(buf);
2448  if (!url)
2449  return 0;
2450 
2451  char tmp[PATH_MAX];
2452  char tmp2[PATH_MAX];
2453 
2454  imap_fix_path('\0', url->path, tmp, sizeof(tmp));
2455  url->path = tmp;
2456  url_tostring(url, tmp2, sizeof(tmp2), 0);
2457  mutt_str_strfcpy(buf, tmp2, buflen);
2458  url_free(&url);
2459 
2460  return 0;
2461 }
2462 
2471 int imap_expand_path(struct Buffer *buf)
2472 {
2474  return imap_path_canon(buf->data, PATH_MAX);
2475 }
2476 
2480 int imap_path_pretty(char *buf, size_t buflen, const char *folder)
2481 {
2482  if (!buf || !folder)
2483  return -1;
2484 
2485  imap_pretty_mailbox(buf, buflen, folder);
2486  return 0;
2487 }
2488 
2492 int imap_path_parent(char *buf, size_t buflen)
2493 {
2494  char tmp[PATH_MAX] = { 0 };
2495 
2496  imap_get_parent_path(buf, tmp, sizeof(tmp));
2497  mutt_str_strfcpy(buf, tmp, buflen);
2498  return 0;
2499 }
2500 
2501 // clang-format off
2505 struct MxOps MxImapOps = {
2506  .magic = MUTT_IMAP,
2507  .name = "imap",
2508  .ac_find = imap_ac_find,
2509  .ac_add = imap_ac_add,
2510  .mbox_open = imap_mbox_open,
2511  .mbox_open_append = imap_mbox_open_append,
2512  .mbox_check = imap_mbox_check,
2513  .mbox_check_stats = imap_mbox_check_stats,
2514  .mbox_sync = NULL, /* imap syncing is handled by imap_sync_mailbox */
2515  .mbox_close = imap_mbox_close,
2516  .msg_open = imap_msg_open,
2517  .msg_open_new = imap_msg_open_new,
2518  .msg_commit = imap_msg_commit,
2519  .msg_close = imap_msg_close,
2520  .msg_padding_size = NULL,
2521  .msg_save_hcache = imap_msg_save_hcache,
2522  .tags_edit = imap_tags_edit,
2523  .tags_commit = imap_tags_commit,
2524  .path_probe = imap_path_probe,
2525  .path_canon = imap_path_canon,
2526  .path_pretty = imap_path_pretty,
2527  .path_parent = imap_path_parent,
2528 };
2529 // clang-format on
struct Email ** emails
Array of Emails.
Definition: mailbox.h:110
int mutt_save_message_ctx(struct Email *e, bool delete_original, bool decode, bool decrypt, struct Mailbox *m)
Save a message to a given mailbox.
Definition: commands.c:965
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:410
struct PatternList * child
Arguments to logical operation.
Definition: pattern.h:62
Deleted messages.
Definition: mutt.h:107
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox&#39;s path string.
Definition: mailbox.h:194
WHERE bool C_ImapListSubscribed
Config: (imap) When browsing a mailbox, only display subscribed folders.
Definition: globals.h:231
Pattern matches email&#39;s header.
Definition: pattern.h:124
int imap_path_pretty(char *buf, size_t buflen, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty()
Definition: imap.c:2480
struct ImapCommand * cmds
Definition: imap_private.h:198
WHERE bool C_MessageCacheClean
Config: (imap/pop) Clean out obsolete entries from the message cache.
Definition: globals.h:250
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
header_cache_t * imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:415
int msg_count
Total number of messages.
Definition: mailbox.h:102
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
bool C_ImapRfc5161
Config: (imap) Use the IMAP ENABLE extension to select capabilities.
Definition: imap.c:69
off_t size
Size of the Mailbox.
Definition: mailbox.h:98
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1072
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
quote string according to IMAP rules
Definition: util.c:945
#define IMAP_CAP_ACL
RFC2086: IMAP4 ACL extension.
Definition: imap_private.h:123
The envelope/body of an email.
Definition: email.h:39
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:100
#define IMAP_LOG_LTRL
Definition: imap_private.h:48
void imap_disallow_reopen(struct Mailbox *m)
Disallow re-opening a folder upon expunge.
Definition: util.c:1137
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1309
#define mutt_perror(...)
Definition: logging.h:85
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: imap_private.h:66
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
struct ConnAccount account
Definition: connection.h:36
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:719
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:107
unsigned int ssf
security strength factor, in bits
Definition: connection.h:37
int msg_unread
Number of unread messages.
Definition: mailbox.h:103
GUI miscellaneous curses (window drawing) routines.
Structs that make up an email.
#define MUTT_ACL_READ
Read the mailbox.
Definition: mailbox.h:83
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:850
static int imap_tags_edit(struct Mailbox *m, const char *tags, char *buf, size_t buflen)
Prompt and validate new messages tags - Implements MxOps::tags_edit()
Definition: imap.c:2269
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:489
void mailbox_changed(struct Mailbox *m, enum MailboxNotification action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:160
WHERE short C_ImapPollTimeout
Config: (imap) Maximum time to wait for a server response.
Definition: globals.h:164
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: message.c:98
ImapExecResult
imap_exec return code
Definition: imap_private.h:79
Mailbox is selected.
Definition: imap_private.h:107
User aborted the question (with Ctrl-G)
Definition: quad.h:37
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: init.c:3331
#define mutt_message(...)
Definition: logging.h:83
static int sync_helper(struct Mailbox *m, AclFlags right, int flag, const char *name)
Sync flag changes to the server.
Definition: imap.c:302
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:104
int imap_delete_mailbox(struct Mailbox *m, char *path)
Delete a mailbox.
Definition: imap.c:642
unsigned int seqno
tag sequence number, e.g. &#39;{seqid}0001&#39;
Definition: imap_private.h:185
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:66
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:39
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:134
struct ImapAccountData * imap_adata_new(void)
Allocate and initialise a new ImapAccountData structure.
Definition: util.c:94
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
void imap_mdata_free(void **ptr)
Release and clear storage in an ImapMboxData structure.
Definition: util.c:224
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1894
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:781
#define MUTT_ACL_CREATE
Create a mailbox.
Definition: mailbox.h:76
struct ConnAccount conn_account
Definition: imap_private.h:169
static size_t plen
Length of cached packet.
Definition: pgppacket.c:38
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1252
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:51
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: imap_private.h:172
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:95
void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
Quote awkward characters in a mailbox name.
Definition: util.c:1019
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:111
void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target.
Definition: util.c:928
Match any Mailbox type.
Definition: mailbox.h:44
#define IMAP_CAP_ENABLE
RFC5161.
Definition: imap_private.h:133
ImapOpenFlags reopen
Flags, e.g.
Definition: imap_private.h:219
int imap_search(struct Mailbox *m, const struct PatternList *pat)
Find a matching mailbox.
Definition: imap.c:1350
void imap_adata_free(void **ptr)
Release and clear storage in an ImapAccountData structure.
Definition: util.c:68
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
A group of associated Mailboxes.
Definition: account.h:36
An open network connection (socket)
Definition: connection.h:34
String manipulation buffer.
Definition: buffer.h:33
#define MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition: mailbox.h:80
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition: imap.c:662
struct Connection * mutt_conn_new(const struct ConnAccount *account)
Create a new Connection.
Definition: mutt_socket.c:43
char user[128]
Definition: connaccount.h:33
static int imap_msg_open_new(struct Mailbox *m, struct Message *msg, struct Email *e)
Open a new message in a Mailbox - Implements MxOps::msg_open_new()
Definition: imap.c:2251
Flagged messages.
Definition: mutt.h:108
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: imap_private.h:68
IMAP authenticator multiplexor.
int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
Rename a mailbox.
Definition: imap.c:615
#define _(a)
Definition: message.h:28
Mailbox wasn&#39;t recognised.
Definition: mailbox.h:46
void imap_edata_free(void **ptr)
free ImapHeader structure
Definition: message.c:73
static int make_msg_set(struct Mailbox *m, struct Buffer *buf, int flag, bool changed, bool invert, int *pos)
Make a message set.
Definition: imap.c:180
struct Mailbox * mailbox
Definition: imap_private.h:205
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1204
bool changed
Email has been edited.
Definition: email.h:50
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:940
static int imap_mbox_open(struct Mailbox *m)
Open a mailbox - Implements MxOps::mbox_open()
Definition: imap.c:1949
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
int url_tostring(struct Url *u, char *dest, size_t len, int flags)
Output the URL string for a given Url object.
Definition: url.c:399
Items in an IMAP browser.
Definition: imap_private.h:144
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mx.h:49
#define SLIST_REMOVE_HEAD(head, field)
Definition: queue.h:291
int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
Delete an item from the header cache.
Definition: util.c:507
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1058
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: imap_private.h:74
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit()
Definition: message.c:2061
struct Mailbox * mx_mbox_find2(const char *path)
XXX.
Definition: mx.c:1516
WHERE bool C_SslForceTls
Config: (ssl) Require TLS encryption for all connections.
Definition: globals.h:241
WHERE bool C_Confirmcreate
Config: Confirm before creating a new mailbox.
Definition: globals.h:215
int imap_read_literal(FILE *fp, struct ImapAccountData *adata, unsigned long bytes, struct Progress *pbar)
Read bytes bytes from server into file.
Definition: imap.c:723
struct ImapMboxData * imap_mdata_new(struct ImapAccountData *adata, const char *name)
Allocate and initialise a new ImapMboxData structure.
Definition: util.c:163
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close()
Definition: message.c:2075
char delim
Definition: imap_private.h:147
Container for Accounts, Notifications.
Definition: neomutt.h:35
A progress bar.
Definition: progress.h:48
int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
Save message to the header cache - Implements MxOps::msg_save_hcache()
Definition: message.c:2083
void(* free_mdata)(void **)
Driver-specific data free function.
Definition: mailbox.h:148
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:77
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:123
static char * get_flags(struct ListHead *hflags, char *s)
Make a simple list out of a FLAGS response.
Definition: imap.c:104
#define mutt_get_field(field, buf, buflen, complete)
Definition: curs_lib.h:86
Messages that have been replied to.
Definition: mutt.h:101
int vcount
The number of virtual messages.
Definition: mailbox.h:113
A simple (non-regex) pattern.
Definition: pattern.h:49
Convenience wrapper for the config headers.
int mutt_socket_readchar(struct Connection *conn, char *c)
simple read buffering to speed things up
Definition: socket.c:206
Update internal tables.
Definition: mailbox.h:66
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:74
Hundreds of global variables to back the user variables.
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: imap_private.h:221
int imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
Sync all the changes to the server.
Definition: imap.c:1625
bool flagged
Definition: message.h:39
void imap_logout_all(void)
close all open connections
Definition: imap.c:687
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
Imap command executed or queued successfully.
Definition: imap_private.h:81
char host[128]
Definition: connaccount.h:36
Some miscellaneous functions.
Manage IMAP messages.
bool has_new
Mailbox has new mail.
Definition: mailbox.h:99
header_cache_t * hcache
Definition: imap_private.h:239
bool deleted
Definition: message.h:38
Connection is idle.
Definition: imap_private.h:110
size_t dsize
Length of data.
Definition: buffer.h:37
bool tagged
Email is tagged.
Definition: email.h:46
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: util.c:242
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:188
bool read
Email is read.
Definition: email.h:53
void mutt_progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:212
static int complete_hosts(char *buf, size_t buflen)
Look for completion matches for mailboxes.
Definition: imap.c:512
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
Parse and execute user-defined hooks.
Log at debug level 2.
Definition: logging.h:57
API for mailboxes.
bool old
Email is seen, but unread.
Definition: email.h:52
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1358
enum MailboxType magic
Mailbox type.
Definition: mailbox.h:116
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition: imap.c:963
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: imap_private.h:121
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition: util.c:129
bool readonly
Don&#39;t allow changes to the mailbox.
Definition: mailbox.h:130
int imap_authenticate(struct ImapAccountData *adata)
Authenticate to an IMAP server.
Definition: auth.c:67
const char * name
Definition: pgpmicalg.c:45
struct MailboxList neomutt_mailboxlist_get_all(struct NeoMutt *n, enum MailboxType magic)
Get a List of all Mailboxes.
Definition: neomutt.c:156
IMAP network mailbox.
struct Envelope * env
Envelope information.
Definition: email.h:91
enum MailboxType magic
Mailbox type, e.g. MUTT_IMAP.
Definition: mx.h:105
Convenience wrapper for the core headers.
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:299
ImapOpenFlags check_status
Flags, e.g.
Definition: imap_private.h:220
#define SKIPWS(ch)
Definition: string2.h:47
int imap_exec_msgset(struct Mailbox *m, const char *pre, const char *post, int flag, bool changed, bool invert)
Prepare commands for all messages matching conditions.
Definition: imap.c:1007
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:293
bool pat_not
Pattern should be inverted (not)
Definition: pattern.h:52
bool string_match
Check a string for a match.
Definition: pattern.h:54
IMAP command structure.
Definition: imap_private.h:155
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:443
WHERE short C_Sort
Config: Sort method for the index.
Definition: sort.h:58
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:109
Progress bar.
#define MUTT_ACL_POST
Post (submit messages to the server)
Definition: mailbox.h:82
#define IMAP_CAP_STARTTLS
RFC2595: STARTTLS.
Definition: imap_private.h:129
void * mdata
Driver specific data.
Definition: mailbox.h:147
uint16_t AclFlags
ACL Rights - These show permission to...
Definition: mailbox.h:73
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:85
struct TagList tags
For drivers that support server tagging.
Definition: email.h:108
unsigned long long modseq
Definition: imap_private.h:227
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: ssl.c:1444
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition: command.c:1242
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
#define IMAP_MAX_CMDLEN
Maximum length of command lines before they must be split (for lazy servers)
Definition: imap_private.h:60
char * flags_remote
Definition: message.h:48
int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e, struct Buffer *cmd, enum QuadOption *err_continue)
Update server to reflect the flags of a single message.
Definition: imap.c:1084
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:897
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:65
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:734
Old messages.
Definition: mutt.h:100
#define mutt_b2s(buf)
Definition: buffer.h:41
sort_t * mutt_get_sort_func(enum SortType method)
Get the sort function for a given sort id.
Definition: sort.c:322
WHERE short C_ImapKeepalive
Config: (imap) Time to wait before polling an open IMAP connection.
Definition: globals.h:163
Server-side pattern matches.
Definition: pattern.h:143
bool active
Message is not to be removed.
Definition: email.h:61
void mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:310
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:76
ConnAccount object used by POP and IMAP.
int imap_parse_path(const char *path, struct ConnAccount *account, char *mailbox, size_t mailboxlen)
Parse an IMAP mailbox name into ConnAccount, name.
Definition: util.c:586
short C_DebugLevel
Config: Logging level for debug logs.
Definition: mutt_logging.c:48
#define SLIST_FIRST(head)
Definition: queue.h:229
A local copy of an email.
Definition: mx.h:81
int email_max
Number of pointers in emails.
Definition: mailbox.h:111
unsigned int uid
32-bit Message UID
Definition: message.h:44
void mutt_account_hook(const char *url)
Perform an account hook.
Definition: hook.c:758
int imap_access(const char *path)
Check permissions on an IMAP mailbox with a new connection.
Definition: imap.c:599
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:335
A mailbox.
Definition: mailbox.h:92
#define PATH_MAX
Definition: mutt.h:52
char * user
Username.
Definition: url.h:69
enum MailboxType magic
Type of Mailboxes this Account contains.
Definition: account.h:38
User answered &#39;No&#39;, or assume &#39;No&#39;.
Definition: quad.h:38
#define IMAP_RES_NO
<tag> NO ...
Definition: imap_private.h:52
struct Hash * uid_hash
Definition: imap_private.h:233
struct ListHead flags
Definition: imap_private.h:224
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
WHERE bool C_ImapQresync
Config: (imap) Enable the QRESYNC extension.
Definition: globals.h:234
char * name
Definition: imap_private.h:146
Nondestructive flags change (IMAP)
Definition: mx.h:75
Either pattern can match.
Definition: pattern.h:107
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
Manage where the email is piped to external commands.
static int compile_search(struct Mailbox *m, const struct PatternList *pat, struct Buffer *buf)
Convert NeoMutt pattern to IMAP search.
Definition: imap.c:382
Email list needs resorting.
Definition: mailbox.h:65
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: imap_private.h:173
void imap_error(const char *where, const char *msg)
show an error and abort
Definition: util.c:760
Tagged messages.
Definition: mutt.h:109
char * data
Pointer to data.
Definition: buffer.h:35
WHERE short C_Timeout
Config: Time to wait for user input in menus.
Definition: globals.h:156
#define IMAP_OPEN_NO_FLAGS
No flags are set.
Definition: imap_private.h:63
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
#define IMAP_CAP_STATUS
Server supports STATUS command.
Definition: imap_private.h:122
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1295
Messages that have been read.
Definition: mutt.h:102
Connection is authenticated.
Definition: imap_private.h:106
bool replied
Definition: message.h:40
char * name
Mailbox name.
Definition: imap_private.h:215
Imap connection failure.
Definition: imap_private.h:83
bool old
Definition: message.h:37
bool mutt_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare account info (host/port/user)
Definition: mutt_account.c:61
int mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: string.c:292
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: util.c:114
char * host
Host.
Definition: url.h:71
Ignore case when comparing strings.
Definition: string2.h:68
short op
Operation, e.g. MUTT_PAT_SCORE.
Definition: pattern.h:51
int imap_msg_open(struct Mailbox *m, struct Message *msg, int msgno)
Open an email message in a Mailbox - Implements MxOps::msg_open()
Definition: message.c:1856
bool purge
Skip trash folder when deleting.
Definition: email.h:48
WHERE bool C_ImapCheckSubscribed
Config: (imap) When opening a mailbox, ask the server for a list of subscribed folders.
Definition: globals.h:229
int mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition: string.c:353
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1334
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:779
ImapCapFlags capabilities
Definition: imap_private.h:183
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:132
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:612
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
time_t lastread
last time we read a command for the server
Definition: imap_private.h:186
bool C_ImapIdle
Config: (imap) Use the IMAP IDLE extension to check for new mail.
Definition: imap.c:65
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:880
int imap_create_mailbox(struct ImapAccountData *adata, char *mailbox)
Create a new mailbox.
Definition: imap.c:573
static int imap_mbox_check(struct Mailbox *m, int *index_hint)
Check for new mail - Implements MxOps::mbox_check()
Definition: imap.c:2192
Login details for a remote server.
Definition: connaccount.h:31
int imap_complete(char *buf, size_t buflen, char *path)
Try to complete an IMAP folder path.
Definition: imap.c:1441
Imap Account.
Definition: mutt_account.h:52
WHERE bool C_ImapCondstore
Config: (imap) Enable the CONDSTORE extension.
Definition: globals.h:230
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:95
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:453
char * path
Path.
Definition: url.h:73
char * real_name
Original Mailbox name, e.g.
Definition: imap_private.h:217
unsigned int uid_validity
Definition: imap_private.h:225
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
#define IMAP_RES_CONTINUE
* ...
Definition: imap_private.h:55
#define IS_SPACE(ch)
Definition: string2.h:38
int * v2r
Mapping from virtual to real msgno.
Definition: mailbox.h:112
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: imap_private.h:134
char * mutt_str_strcat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:395
int imap_check_mailbox(struct Mailbox *m, bool force)
use the NOOP or IDLE command to poll for new mail
Definition: imap.c:1186
IMAP-specific Account data -.
Definition: imap_private.h:166
unsigned int uid_next
Definition: imap_private.h:226
static size_t longest_common_prefix(char *dest, const char *src, size_t start, size_t dlen)
Find longest prefix common to two strings.
Definition: imap.c:491
Pattern matches email&#39;s body.
Definition: pattern.h:123
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:154
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:277
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: imap_private.h:65
char * data
String.
Definition: list.h:35
MailboxType
Supported mailbox formats.
Definition: mailbox.h:42
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:142
union Pattern::@2 p
Shared constants/structs that are private to IMAP.
Log at debug level 1.
Definition: logging.h:56
bool flagged
Marked important?
Definition: email.h:45
static int do_search(const struct PatternList *search, bool allpats)
Perform a search of messages.
Definition: imap.c:340
IMAP-specific Mailbox data -.
Definition: imap_private.h:213
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
int mutt_str_strncmp(const char *a, const char *b, size_t l)
Compare two strings (to a maximum), safely.
Definition: string.c:642
int msg_new
Number of new messages.
Definition: mailbox.h:106
char * str
String, if string_match is set.
Definition: pattern.h:66
static int imap_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close()
Definition: imap.c:2209
bool deleted
Email is deleted.
Definition: email.h:47
void * edata
Driver-specific data.
Definition: email.h:112
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: imap_private.h:73
#define mutt_error(...)
Definition: logging.h:84
#define IMAP_CAP_IDLE
RFC2177: IDLE.
Definition: imap_private.h:131
bool replied
Email has been replied to.
Definition: email.h:56
int imap_read_headers(struct Mailbox *m, unsigned int msn_begin, unsigned int msn_end, bool initial_download)
Read headers from the server.
Definition: message.c:1265
int imap_path_parent(char *buf, size_t buflen)
Find the parent of a Mailbox path - Implements MxOps::path_parent()
Definition: imap.c:2492
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:628
static int imap_tags_commit(struct Mailbox *m, struct Email *e, char *buf)
Save the tags to a message - Implements MxOps::tags_commit()
Definition: imap.c:2358
Connection Library.
#define IMAP_CAP_X_GM_EXT_1
https://developers.google.com/gmail/imap/imap-extensions
Definition: imap_private.h:137
FILE * fp
pointer to the message data
Definition: mx.h:83
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition: util.c:1124
struct Account * imap_ac_find(struct Account *a, const char *path)
Find an Account that matches a Mailbox path - Implements MxOps::ac_find()
Definition: imap.c:1815
void url_free(struct Url **u)
Free the contents of a URL.
Definition: url.c:288
int index
The absolute (unsorted) message number.
Definition: email.h:87
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: imap_private.h:71
#define FREE(x)
Definition: memory.h:40
bool read
Definition: message.h:36
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: imap_private.h:67
#define MUTT_ACL_SEEN
Change the &#39;seen&#39; status of a message.
Definition: mailbox.h:84
#define STAILQ_EMPTY(head)
Definition: queue.h:346
void mutt_account_tourl(struct ConnAccount *account, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:145
#define SLIST_FOREACH(var, head, field)
Definition: queue.h:231
void mutt_hash_int_delete(struct Hash *table, unsigned int intkey, const void *data)
Remove an element from a Hash table.
Definition: hash.c:459
int imap_path_canon(char *buf, size_t buflen)
Canonicalise a Mailbox path - Implements MxOps::path_canon()
Definition: imap.c:2442
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:77
struct ImapList * cmdresult
Definition: imap_private.h:195
List of Mailboxes.
Definition: mailbox.h:156
Connected to server.
Definition: imap_private.h:105
bool changed
Mailbox has been modified.
Definition: mailbox.h:125
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
IMAP-specific Email data -.
Definition: message.h:33
bool driver_tags_replace(struct TagList *head, char *tags)
Replace all tags.
Definition: tags.c:182
New mail received in Mailbox.
Definition: mx.h:72
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
NeoMutt connections.
int imap_mbox_check_stats(struct Mailbox *m, int flags)
Check the Mailbox statistics - Implements MxOps::mbox_check_stats()
Definition: imap.c:1295
WHERE char * C_Postponed
Config: Folder to store postponed messages.
Definition: globals.h:139
Pattern matches raw email text.
Definition: pattern.h:126
struct Buffer pathbuf
Definition: mailbox.h:94
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:583
void(* free_adata)(void **)
Callback function to free private data.
Definition: account.h:44
A List node for strings.
Definition: list.h:33
static int compare_uid(const void *a, const void *b)
Compare two Emails by UID - Implements sort_t.
Definition: imap.c:984
Match patterns to emails.
char * munge_name
Munged version of the mailbox name.
Definition: imap_private.h:216
Unrecoverable error occurred.
Definition: imap_private.h:94
Mailbox was reopened.
Definition: mx.h:74
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: imap_private.h:120
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:52
int imap_fast_trash(struct Mailbox *m, char *dest)
Use server COPY command to copy deleted messages to trash.
Definition: imap.c:1519
Logged out from server.
Definition: imap_private.h:95
Sort by the order the messages appear in the mailbox.
Definition: sort.h:55
void imap_mdata_cache_reset(struct ImapMboxData *mdata)
Release and clear cache data of ImapMboxData structure.
Definition: util.c:211
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1748
Disconnected from server.
Definition: imap_private.h:104
Trashed messages.
Definition: mutt.h:114
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
enum QuadOption imap_continue(const char *msg, const char *resp)
display a message and ask the user if they want to go on
Definition: util.c:749
Authentication successful.
Definition: auth.h:37
Log at debug level 3.
Definition: logging.h:58
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
bool noselect
Definition: imap_private.h:148
#define MUTT_ACL_LOOKUP
Lookup mailbox (visible to &#39;list&#39;)
Definition: mailbox.h:81
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:688
unsigned int messages
Definition: imap_private.h:228
WHERE unsigned char C_SslStarttls
Config: (ssl) Use STARTTLS on servers advertising the capability.
Definition: globals.h:193
#define IMAP_CAP_QRESYNC
RFC7162.
Definition: imap_private.h:135
enum CommandResult mutt_parse_rc_line(char *line, struct Buffer *token, struct Buffer *err)
Parse a line of user config.
Definition: init.c:3224
char * path
path to temp file
Definition: mx.h:84
The Mailbox API.
Definition: mx.h:103
bool matched
Search matches this Email.
Definition: email.h:70
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1384
static int imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append()
Definition: imap.c:2158
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1767
static void set_flag(struct Mailbox *m, AclFlags aclflag, int flag, const char *str, char *flags, size_t flsize)
append str to flags if we currently have permission according to aclflag
Definition: imap.c:159
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
struct Connection * conn
Definition: imap_private.h:168
void mutt_account_unsetpass(struct ConnAccount *account)
Unset ConnAccount&#39;s password.
Definition: mutt_account.c:332
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:158
#define IMAP_RES_OK
<tag> OK ...
Definition: imap_private.h:54
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:161
Imap command failure.
Definition: imap_private.h:82
int imap_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add()
Definition: imap.c:1838