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