NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
command.c
Go to the documentation of this file.
1 
32 #include "config.h"
33 #include <ctype.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include "private.h"
42 #include "mutt/lib.h"
43 #include "config/lib.h"
44 #include "email/lib.h"
45 #include "core/lib.h"
46 #include "conn/lib.h"
47 #include "adata.h"
48 #include "edata.h"
49 #include "init.h"
50 #include "mdata.h"
51 #include "msn.h"
52 #include "mutt_account.h"
53 #include "mutt_logging.h"
54 #include "mutt_socket.h"
55 #include "mx.h"
56 
57 #define IMAP_CMD_BUFSIZE 512
58 
64 static const char *const Capabilities[] = {
65  "IMAP4",
66  "IMAP4rev1",
67  "STATUS",
68  "ACL",
69  "NAMESPACE",
70  "AUTH=CRAM-MD5",
71  "AUTH=GSSAPI",
72  "AUTH=ANONYMOUS",
73  "AUTH=OAUTHBEARER",
74  "STARTTLS",
75  "LOGINDISABLED",
76  "IDLE",
77  "SASL-IR",
78  "ENABLE",
79  "CONDSTORE",
80  "QRESYNC",
81  "LIST-EXTENDED",
82  "COMPRESS=DEFLATE",
83  "X-GM-EXT-1",
84  NULL,
85 };
86 
92 static bool cmd_queue_full(struct ImapAccountData *adata)
93 {
94  if (((adata->nextcmd + 1) % adata->cmdslots) == adata->lastcmd)
95  return true;
96 
97  return false;
98 }
99 
106 static struct ImapCommand *cmd_new(struct ImapAccountData *adata)
107 {
108  struct ImapCommand *cmd = NULL;
109 
110  if (cmd_queue_full(adata))
111  {
112  mutt_debug(LL_DEBUG3, "IMAP command queue full\n");
113  return NULL;
114  }
115 
116  cmd = adata->cmds + adata->nextcmd;
117  adata->nextcmd = (adata->nextcmd + 1) % adata->cmdslots;
118 
119  snprintf(cmd->seq, sizeof(cmd->seq), "%c%04u", adata->seqid, adata->seqno++);
120  if (adata->seqno > 9999)
121  adata->seqno = 0;
122 
123  cmd->state = IMAP_RES_NEW;
124 
125  return cmd;
126 }
127 
138 static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
139 {
140  if (cmd_queue_full(adata))
141  {
142  mutt_debug(LL_DEBUG3, "Draining IMAP command pipeline\n");
143 
144  const int rc = imap_exec(adata, NULL, flags & IMAP_CMD_POLL);
145 
146  if (rc == IMAP_EXEC_ERROR)
147  return IMAP_RES_BAD;
148  }
149 
150  struct ImapCommand *cmd = cmd_new(adata);
151  if (!cmd)
152  return IMAP_RES_BAD;
153 
154  if (mutt_buffer_add_printf(&adata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
155  return IMAP_RES_BAD;
156 
157  return 0;
158 }
159 
164 static void cmd_handle_fatal(struct ImapAccountData *adata)
165 {
166  adata->status = IMAP_FATAL;
167 
168  if (!adata->mailbox)
169  return;
170 
171  struct ImapMboxData *mdata = adata->mailbox->mdata;
172 
173  if ((adata->state >= IMAP_SELECTED) && (mdata->reopen & IMAP_REOPEN_ALLOW))
174  {
176  mutt_socket_close(adata->conn);
177  mutt_error(_("Mailbox %s@%s closed"), adata->conn->account.user,
178  adata->conn->account.host);
179  adata->state = IMAP_DISCONNECTED;
180  }
181 
182  imap_close_connection(adata);
183  if (!adata->recovering)
184  {
185  adata->recovering = true;
186  if (imap_login(adata))
188  adata->recovering = false;
189  }
190 }
191 
200 static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
201 {
202  int rc;
203 
204  if (adata->status == IMAP_FATAL)
205  {
206  cmd_handle_fatal(adata);
207  return -1;
208  }
209 
210  if (cmdstr && ((rc = cmd_queue(adata, cmdstr, flags)) < 0))
211  return rc;
212 
213  if (flags & IMAP_CMD_QUEUE)
214  return 0;
215 
216  if (mutt_buffer_is_empty(&adata->cmdbuf))
217  return IMAP_RES_BAD;
218 
219  rc = mutt_socket_send_d(adata->conn, adata->cmdbuf.data,
221  mutt_buffer_reset(&adata->cmdbuf);
222 
223  /* unidle when command queue is flushed */
224  if (adata->state == IMAP_IDLE)
225  adata->state = IMAP_SELECTED;
226 
227  return (rc < 0) ? IMAP_RES_BAD : 0;
228 }
229 
236 static int cmd_status(const char *s)
237 {
238  s = imap_next_word((char *) s);
239 
240  if (mutt_istr_startswith(s, "OK"))
241  return IMAP_RES_OK;
242  if (mutt_istr_startswith(s, "NO"))
243  return IMAP_RES_NO;
244 
245  return IMAP_RES_BAD;
246 }
247 
256 static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
257 {
258  unsigned int exp_msn;
259  struct Email *e = NULL;
260 
261  mutt_debug(LL_DEBUG2, "Handling EXPUNGE\n");
262 
263  struct ImapMboxData *mdata = adata->mailbox->mdata;
264 
265  if ((mutt_str_atoui(s, &exp_msn) < 0) || (exp_msn < 1) ||
266  (exp_msn > imap_msn_highest(&mdata->msn)))
267  {
268  return;
269  }
270 
271  e = imap_msn_get(&mdata->msn, exp_msn - 1);
272  if (e)
273  {
274  /* imap_expunge_mailbox() will rewrite e->index.
275  * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
276  * makes the code simpler and possibly more efficient. */
277  e->index = INT_MAX;
278  imap_edata_get(e)->msn = 0;
279  }
280 
281  /* decrement seqno of those above. */
282  const size_t max_msn = imap_msn_highest(&mdata->msn);
283  for (unsigned int cur = exp_msn; cur < max_msn; cur++)
284  {
285  e = imap_msn_get(&mdata->msn, cur);
286  if (e)
287  imap_edata_get(e)->msn--;
288  imap_msn_set(&mdata->msn, cur - 1, e);
289  }
290  imap_msn_shrink(&mdata->msn, 1);
291 
292  mdata->reopen |= IMAP_EXPUNGE_PENDING;
293 }
294 
303 static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
304 {
305  bool earlier = false;
306  int rc;
307  unsigned int uid = 0;
308 
309  struct ImapMboxData *mdata = adata->mailbox->mdata;
310 
311  mutt_debug(LL_DEBUG2, "Handling VANISHED\n");
312 
313  if (mutt_istr_startswith(s, "(EARLIER)"))
314  {
315  /* The RFC says we should not decrement msns with the VANISHED EARLIER tag.
316  * My experimentation says that's crap. */
317  earlier = true;
318  s = imap_next_word(s);
319  }
320 
321  char *end_of_seqset = s;
322  while (*end_of_seqset)
323  {
324  if (!strchr("0123456789:,", *end_of_seqset))
325  *end_of_seqset = '\0';
326  else
327  end_of_seqset++;
328  }
329 
330  struct SeqsetIterator *iter = mutt_seqset_iterator_new(s);
331  if (!iter)
332  {
333  mutt_debug(LL_DEBUG2, "VANISHED: empty seqset [%s]?\n", s);
334  return;
335  }
336 
337  while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
338  {
339  struct Email *e = mutt_hash_int_find(mdata->uid_hash, uid);
340  if (!e)
341  continue;
342 
343  unsigned int exp_msn = imap_edata_get(e)->msn;
344 
345  /* imap_expunge_mailbox() will rewrite e->index.
346  * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
347  * makes the code simpler and possibly more efficient. */
348  e->index = INT_MAX;
349  imap_edata_get(e)->msn = 0;
350 
351  if ((exp_msn < 1) || (exp_msn > imap_msn_highest(&mdata->msn)))
352  {
353  mutt_debug(LL_DEBUG1, "VANISHED: msn for UID %u is incorrect\n", uid);
354  continue;
355  }
356  if (imap_msn_get(&mdata->msn, exp_msn - 1) != e)
357  {
358  mutt_debug(LL_DEBUG1, "VANISHED: msn_index for UID %u is incorrect\n", uid);
359  continue;
360  }
361 
362  imap_msn_remove(&mdata->msn, exp_msn - 1);
363 
364  if (!earlier)
365  {
366  /* decrement seqno of those above. */
367  const size_t max_msn = imap_msn_highest(&mdata->msn);
368  for (unsigned int cur = exp_msn; cur < max_msn; cur++)
369  {
370  e = imap_msn_get(&mdata->msn, cur);
371  if (e)
372  imap_edata_get(e)->msn--;
373  imap_msn_set(&mdata->msn, cur - 1, e);
374  }
375 
376  imap_msn_shrink(&mdata->msn, 1);
377  }
378  }
379 
380  if (rc < 0)
381  mutt_debug(LL_DEBUG1, "VANISHED: illegal seqset %s\n", s);
382 
383  mdata->reopen |= IMAP_EXPUNGE_PENDING;
384 
386 }
387 
397 static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
398 {
399  unsigned int msn, uid;
400  struct Email *e = NULL;
401  char *flags = NULL;
402  int uid_checked = 0;
403  bool server_changes = false;
404 
405  struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
406 
407  mutt_debug(LL_DEBUG3, "Handling FETCH\n");
408 
409  if (mutt_str_atoui(s, &msn) < 0)
410  {
411  mutt_debug(LL_DEBUG3, "Skipping FETCH response - illegal MSN\n");
412  return;
413  }
414 
415  if ((msn < 1) || (msn > imap_msn_highest(&mdata->msn)))
416  {
417  mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u out of range\n", msn);
418  return;
419  }
420 
421  e = imap_msn_get(&mdata->msn, msn - 1);
422  if (!e || !e->active)
423  {
424  mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u not in msn_index\n", msn);
425  return;
426  }
427 
428  mutt_debug(LL_DEBUG2, "Message UID %u updated\n", imap_edata_get(e)->uid);
429  /* skip FETCH */
430  s = imap_next_word(s);
431  s = imap_next_word(s);
432 
433  if (*s != '(')
434  {
435  mutt_debug(LL_DEBUG1, "Malformed FETCH response\n");
436  return;
437  }
438  s++;
439 
440  while (*s)
441  {
442  SKIPWS(s);
443  size_t plen = mutt_istr_startswith(s, "FLAGS");
444  if (plen != 0)
445  {
446  flags = s;
447  if (uid_checked)
448  break;
449 
450  s += plen;
451  SKIPWS(s);
452  if (*s != '(')
453  {
454  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
455  return;
456  }
457  s++;
458  while (*s && (*s != ')'))
459  s++;
460  if (*s == ')')
461  s++;
462  else
463  {
464  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
465  return;
466  }
467  }
468  else if ((plen = mutt_istr_startswith(s, "UID")))
469  {
470  s += plen;
471  SKIPWS(s);
472  if (mutt_str_atoui(s, &uid) < 0)
473  {
474  mutt_debug(LL_DEBUG1, "Illegal UID. Skipping update\n");
475  return;
476  }
477  if (uid != imap_edata_get(e)->uid)
478  {
479  mutt_debug(LL_DEBUG1, "UID vs MSN mismatch. Skipping update\n");
480  return;
481  }
482  uid_checked = 1;
483  if (flags)
484  break;
485  s = imap_next_word(s);
486  }
487  else if ((plen = mutt_istr_startswith(s, "MODSEQ")))
488  {
489  s += plen;
490  SKIPWS(s);
491  if (*s != '(')
492  {
493  mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
494  return;
495  }
496  s++;
497  while (*s && (*s != ')'))
498  s++;
499  if (*s == ')')
500  s++;
501  else
502  {
503  mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
504  return;
505  }
506  }
507  else if (*s == ')')
508  break; /* end of request */
509  else if (*s)
510  {
511  mutt_debug(LL_DEBUG2, "Only handle FLAGS updates\n");
512  break;
513  }
514  }
515 
516  if (flags)
517  {
518  imap_set_flags(adata->mailbox, e, flags, &server_changes);
519  if (server_changes)
520  {
521  /* If server flags could conflict with NeoMutt's flags, reopen the mailbox. */
522  if (e->changed)
523  mdata->reopen |= IMAP_EXPUNGE_PENDING;
524  else
526  }
527  }
528 }
529 
535 static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
536 {
537  mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
538 
539  s = imap_next_word(s);
540  char *bracket = strchr(s, ']');
541  if (bracket)
542  *bracket = '\0';
543  FREE(&adata->capstr);
544  adata->capstr = mutt_str_dup(s);
545  adata->capabilities = 0;
546 
547  while (*s)
548  {
549  for (size_t i = 0; Capabilities[i]; i++)
550  {
551  size_t len = mutt_istr_startswith(s, Capabilities[i]);
552  if (len != 0 && ((s[len] == '\0') || IS_SPACE(s[len])))
553  {
554  adata->capabilities |= (1 << i);
555  mutt_debug(LL_DEBUG3, " Found capability \"%s\": %lu\n", Capabilities[i], i);
556  break;
557  }
558  }
559  s = imap_next_word(s);
560  }
561 }
562 
568 static void cmd_parse_list(struct ImapAccountData *adata, char *s)
569 {
570  struct ImapList *list = NULL;
571  struct ImapList lb = { 0 };
572  char delimbuf[5]; /* worst case: "\\"\0 */
573  unsigned int litlen;
574 
575  if (adata->cmdresult)
576  list = adata->cmdresult;
577  else
578  list = &lb;
579 
580  memset(list, 0, sizeof(struct ImapList));
581 
582  /* flags */
583  s = imap_next_word(s);
584  if (*s != '(')
585  {
586  mutt_debug(LL_DEBUG1, "Bad LIST response\n");
587  return;
588  }
589  s++;
590  while (*s)
591  {
592  if (mutt_istr_startswith(s, "\\NoSelect"))
593  list->noselect = true;
594  else if (mutt_istr_startswith(s, "\\NonExistent")) /* rfc5258 */
595  list->noselect = true;
596  else if (mutt_istr_startswith(s, "\\NoInferiors"))
597  list->noinferiors = true;
598  else if (mutt_istr_startswith(s, "\\HasNoChildren")) /* rfc5258*/
599  list->noinferiors = true;
600 
601  s = imap_next_word(s);
602  if (*(s - 2) == ')')
603  break;
604  }
605 
606  /* Delimiter */
607  if (!mutt_istr_startswith(s, "NIL"))
608  {
609  delimbuf[0] = '\0';
610  mutt_str_cat(delimbuf, 5, s);
611  imap_unquote_string(delimbuf);
612  list->delim = delimbuf[0];
613  }
614 
615  /* Name */
616  s = imap_next_word(s);
617  /* Notes often responds with literals here. We need a real tokenizer. */
618  if (imap_get_literal_count(s, &litlen) == 0)
619  {
620  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
621  {
622  adata->status = IMAP_FATAL;
623  return;
624  }
625 
626  if (strlen(adata->buf) < litlen)
627  {
628  mutt_debug(LL_DEBUG1, "Error parsing LIST mailbox\n");
629  return;
630  }
631 
632  list->name = adata->buf;
633  s = list->name + litlen;
634  if (s[0] != '\0')
635  {
636  s[0] = '\0';
637  s++;
638  SKIPWS(s);
639  }
640  }
641  else
642  {
643  list->name = s;
644  /* Exclude rfc5258 RECURSIVEMATCH CHILDINFO suffix */
645  s = imap_next_word(s);
646  if (s[0] != '\0')
647  s[-1] = '\0';
648  imap_unmunge_mbox_name(adata->unicode, list->name);
649  }
650 
651  if (list->name[0] == '\0')
652  {
653  adata->delim = list->delim;
654  mutt_debug(LL_DEBUG3, "Root delimiter: %c\n", adata->delim);
655  }
656 }
657 
663 static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
664 {
665  char buf[256];
666  char quoted_name[256];
667  struct Buffer err;
668  struct Url url = { 0 };
669  struct ImapList list = { 0 };
670 
671  if (adata->cmdresult)
672  {
673  /* caller will handle response itself */
674  cmd_parse_list(adata, s);
675  return;
676  }
677 
678  const bool c_imap_check_subscribed =
679  cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
680  if (!c_imap_check_subscribed)
681  return;
682 
683  adata->cmdresult = &list;
684  cmd_parse_list(adata, s);
685  adata->cmdresult = NULL;
686  /* noselect is for a gmail quirk */
687  if (!list.name || list.noselect)
688  return;
689 
690  mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
691 
692  mutt_str_copy(buf, "mailboxes \"", sizeof(buf));
693  mutt_account_tourl(&adata->conn->account, &url);
694  /* escape \ and " */
695  imap_quote_string(quoted_name, sizeof(quoted_name), list.name, true);
696  url.path = quoted_name + 1;
697  url.path[strlen(url.path) - 1] = '\0';
698  const char *const c_imap_user = cs_subset_string(NeoMutt->sub, "imap_user");
699  if (mutt_str_equal(url.user, c_imap_user))
700  url.user = NULL;
701  url_tostring(&url, buf + 11, sizeof(buf) - 11, U_NO_FLAGS);
702  mutt_str_cat(buf, sizeof(buf), "\"");
703  mutt_buffer_init(&err);
704  err.dsize = 256;
705  err.data = mutt_mem_malloc(err.dsize);
706  if (mutt_parse_rc_line(buf, &err))
707  mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
708  FREE(&err.data);
709 }
710 
716 static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
717 {
718  mutt_debug(LL_DEBUG2, "Handling MYRIGHTS\n");
719 
720  s = imap_next_word((char *) s);
721  s = imap_next_word((char *) s);
722 
723  /* zero out current rights set */
724  adata->mailbox->rights = 0;
725 
726  while (*s && !isspace((unsigned char) *s))
727  {
728  switch (*s)
729  {
730  case 'a':
731  adata->mailbox->rights |= MUTT_ACL_ADMIN;
732  break;
733  case 'e':
734  adata->mailbox->rights |= MUTT_ACL_EXPUNGE;
735  break;
736  case 'i':
737  adata->mailbox->rights |= MUTT_ACL_INSERT;
738  break;
739  case 'k':
740  adata->mailbox->rights |= MUTT_ACL_CREATE;
741  break;
742  case 'l':
743  adata->mailbox->rights |= MUTT_ACL_LOOKUP;
744  break;
745  case 'p':
746  adata->mailbox->rights |= MUTT_ACL_POST;
747  break;
748  case 'r':
749  adata->mailbox->rights |= MUTT_ACL_READ;
750  break;
751  case 's':
752  adata->mailbox->rights |= MUTT_ACL_SEEN;
753  break;
754  case 't':
755  adata->mailbox->rights |= MUTT_ACL_DELETE;
756  break;
757  case 'w':
758  adata->mailbox->rights |= MUTT_ACL_WRITE;
759  break;
760  case 'x':
761  adata->mailbox->rights |= MUTT_ACL_DELMX;
762  break;
763 
764  /* obsolete rights */
765  case 'c':
767  break;
768  case 'd':
770  break;
771  default:
772  mutt_debug(LL_DEBUG1, "Unknown right: %c\n", *s);
773  }
774  s++;
775  }
776 }
777 
784 static struct Mailbox *find_mailbox(struct ImapAccountData *adata, const char *name)
785 {
786  if (!adata || !adata->account || !name)
787  return NULL;
788 
789  struct MailboxNode *np = NULL;
790  STAILQ_FOREACH(np, &adata->account->mailboxes, entries)
791  {
792  struct ImapMboxData *mdata = imap_mdata_get(np->mailbox);
793  if (mutt_str_equal(name, mdata->name))
794  return np->mailbox;
795  }
796 
797  return NULL;
798 }
799 
808 static void cmd_parse_status(struct ImapAccountData *adata, char *s)
809 {
810  unsigned int litlen = 0;
811 
812  char *mailbox = imap_next_word(s);
813 
814  /* We need a real tokenizer. */
815  if (imap_get_literal_count(mailbox, &litlen) == 0)
816  {
817  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
818  {
819  adata->status = IMAP_FATAL;
820  return;
821  }
822 
823  if (strlen(adata->buf) < litlen)
824  {
825  mutt_debug(LL_DEBUG1, "Error parsing STATUS mailbox\n");
826  return;
827  }
828 
829  mailbox = adata->buf;
830  s = mailbox + litlen;
831  s[0] = '\0';
832  s++;
833  SKIPWS(s);
834  }
835  else
836  {
837  s = imap_next_word(mailbox);
838  s[-1] = '\0';
839  imap_unmunge_mbox_name(adata->unicode, mailbox);
840  }
841 
842  struct Mailbox *m = find_mailbox(adata, mailbox);
843  struct ImapMboxData *mdata = imap_mdata_get(m);
844  if (!mdata)
845  {
846  mutt_debug(LL_DEBUG3, "Received status for an unexpected mailbox: %s\n", mailbox);
847  return;
848  }
849  uint32_t olduv = mdata->uidvalidity;
850  unsigned int oldun = mdata->uid_next;
851 
852  if (*s++ != '(')
853  {
854  mutt_debug(LL_DEBUG1, "Error parsing STATUS\n");
855  return;
856  }
857  while ((s[0] != '\0') && (s[0] != ')'))
858  {
859  char *value = imap_next_word(s);
860 
861  errno = 0;
862  const unsigned long ulcount = strtoul(value, &value, 10);
863  if (((errno == ERANGE) && (ulcount == ULONG_MAX)) || ((unsigned int) ulcount != ulcount))
864  {
865  mutt_debug(LL_DEBUG1, "Error parsing STATUS number\n");
866  return;
867  }
868  const unsigned int count = (unsigned int) ulcount;
869 
870  if (mutt_str_startswith(s, "MESSAGES"))
871  mdata->messages = count;
872  else if (mutt_str_startswith(s, "RECENT"))
873  mdata->recent = count;
874  else if (mutt_str_startswith(s, "UIDNEXT"))
875  mdata->uid_next = count;
876  else if (mutt_str_startswith(s, "UIDVALIDITY"))
877  mdata->uidvalidity = count;
878  else if (mutt_str_startswith(s, "UNSEEN"))
879  mdata->unseen = count;
880 
881  s = value;
882  if ((s[0] != '\0') && (*s != ')'))
883  s = imap_next_word(s);
884  }
885  mutt_debug(LL_DEBUG3, "%s (UIDVALIDITY: %u, UIDNEXT: %u) %d messages, %d recent, %d unseen\n",
886  mdata->name, mdata->uidvalidity, mdata->uid_next, mdata->messages,
887  mdata->recent, mdata->unseen);
888 
889  mutt_debug(LL_DEBUG3, "Running default STATUS handler\n");
890 
891  mutt_debug(LL_DEBUG3, "Found %s in mailbox list (OV: %u ON: %u U: %d)\n",
892  mailbox, olduv, oldun, mdata->unseen);
893 
894  bool new_mail = false;
895  const bool c_mail_check_recent =
896  cs_subset_bool(NeoMutt->sub, "mail_check_recent");
897  if (c_mail_check_recent)
898  {
899  if ((olduv != 0) && (olduv == mdata->uidvalidity))
900  {
901  if (oldun < mdata->uid_next)
902  new_mail = (mdata->unseen > 0);
903  }
904  else if ((olduv == 0) && (oldun == 0))
905  {
906  /* first check per session, use recent. might need a flag for this. */
907  new_mail = (mdata->recent > 0);
908  }
909  else
910  new_mail = (mdata->unseen > 0);
911  }
912  else
913  new_mail = (mdata->unseen > 0);
914 
915  m->has_new = new_mail;
916  m->msg_count = mdata->messages;
917  m->msg_unread = mdata->unseen;
918 
919  // force back to keep detecting new mail until the mailbox is opened
920  if (m->has_new)
921  mdata->uid_next = oldun;
922 }
923 
929 static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
930 {
931  mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
932 
933  while ((s = imap_next_word((char *) s)) && (*s != '\0'))
934  {
935  if (mutt_istr_startswith(s, "UTF8=ACCEPT") ||
936  mutt_istr_startswith(s, "UTF8=ONLY"))
937  {
938  adata->unicode = true;
939  }
940  if (mutt_istr_startswith(s, "QRESYNC"))
941  adata->qresync = true;
942  }
943 }
949 static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
950 {
951  unsigned int count = 0;
952  mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
953 
954  if (mutt_str_atoui(pn, &count) < 0)
955  {
956  mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
957  return;
958  }
959 
960  struct ImapMboxData *mdata = adata->mailbox->mdata;
961 
962  /* new mail arrived */
963  if (count < imap_msn_highest(&mdata->msn))
964  {
965  /* Notes 6.0.3 has a tendency to report fewer messages exist than
966  * it should. */
967  mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
968  }
969  /* at least the InterChange server sends EXISTS messages freely,
970  * even when there is no new mail */
971  else if (count == imap_msn_highest(&mdata->msn))
972  mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
973  else
974  {
975  mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
976  mdata->reopen |= IMAP_NEWMAIL_PENDING;
977  mdata->new_mail_count = count;
978  }
979 }
980 
987 static int cmd_handle_untagged(struct ImapAccountData *adata)
988 {
989  char *s = imap_next_word(adata->buf);
990  char *pn = imap_next_word(s);
991 
992  const bool c_imap_server_noise =
993  cs_subset_bool(NeoMutt->sub, "imap_server_noise");
994  if ((adata->state >= IMAP_SELECTED) && isdigit((unsigned char) *s))
995  {
996  /* pn vs. s: need initial seqno */
997  pn = s;
998  s = imap_next_word(s);
999 
1000  /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1001  if (mutt_istr_startswith(s, "EXISTS"))
1002  cmd_parse_exists(adata, pn);
1003  else if (mutt_istr_startswith(s, "EXPUNGE"))
1004  cmd_parse_expunge(adata, pn);
1005  else if (mutt_istr_startswith(s, "FETCH"))
1006  cmd_parse_fetch(adata, pn);
1007  }
1008  else if ((adata->state >= IMAP_SELECTED) &&
1009  mutt_istr_startswith(s, "VANISHED"))
1010  cmd_parse_vanished(adata, pn);
1011  else if (mutt_istr_startswith(s, "CAPABILITY"))
1012  cmd_parse_capability(adata, s);
1013  else if (mutt_istr_startswith(s, "OK [CAPABILITY"))
1014  cmd_parse_capability(adata, pn);
1015  else if (mutt_istr_startswith(pn, "OK [CAPABILITY"))
1017  else if (mutt_istr_startswith(s, "LIST"))
1018  cmd_parse_list(adata, s);
1019  else if (mutt_istr_startswith(s, "LSUB"))
1020  cmd_parse_lsub(adata, s);
1021  else if (mutt_istr_startswith(s, "MYRIGHTS"))
1022  cmd_parse_myrights(adata, s);
1023  else if (mutt_istr_startswith(s, "SEARCH"))
1024  cmd_parse_search(adata, s);
1025  else if (mutt_istr_startswith(s, "STATUS"))
1026  cmd_parse_status(adata, s);
1027  else if (mutt_istr_startswith(s, "ENABLED"))
1028  cmd_parse_enabled(adata, s);
1029  else if (mutt_istr_startswith(s, "BYE"))
1030  {
1031  mutt_debug(LL_DEBUG2, "Handling BYE\n");
1032 
1033  /* check if we're logging out */
1034  if (adata->status == IMAP_BYE)
1035  return 0;
1036 
1037  /* server shut down our connection */
1038  s += 3;
1039  SKIPWS(s);
1040  mutt_error("%s", s);
1041  cmd_handle_fatal(adata);
1042 
1043  return -1;
1044  }
1045  else if (c_imap_server_noise && mutt_istr_startswith(s, "NO"))
1046  {
1047  mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1048 
1049  /* Display the warning message from the server */
1050  mutt_error("%s", s + 2);
1051  }
1052 
1053  return 0;
1054 }
1055 
1065 int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
1066 {
1067  return cmd_start(adata, cmdstr, IMAP_CMD_NO_FLAGS);
1068 }
1069 
1079 int imap_cmd_step(struct ImapAccountData *adata)
1080 {
1081  if (!adata)
1082  return -1;
1083 
1084  size_t len = 0;
1085  int c;
1086  int rc;
1087  int stillrunning = 0;
1088  struct ImapCommand *cmd = NULL;
1089 
1090  if (adata->status == IMAP_FATAL)
1091  {
1092  cmd_handle_fatal(adata);
1093  return IMAP_RES_BAD;
1094  }
1095 
1096  /* read into buffer, expanding buffer as necessary until we have a full
1097  * line */
1098  do
1099  {
1100  if (len == adata->blen)
1101  {
1102  mutt_mem_realloc(&adata->buf, adata->blen + IMAP_CMD_BUFSIZE);
1103  adata->blen = adata->blen + IMAP_CMD_BUFSIZE;
1104  mutt_debug(LL_DEBUG3, "grew buffer to %lu bytes\n", adata->blen);
1105  }
1106 
1107  /* back up over '\0' */
1108  if (len)
1109  len--;
1110  c = mutt_socket_readln_d(adata->buf + len, adata->blen - len, adata->conn, MUTT_SOCK_LOG_FULL);
1111  if (c <= 0)
1112  {
1113  mutt_debug(LL_DEBUG1, "Error reading server response\n");
1114  cmd_handle_fatal(adata);
1115  return IMAP_RES_BAD;
1116  }
1117 
1118  len += c;
1119  }
1120  /* if we've read all the way to the end of the buffer, we haven't read a
1121  * full line (mutt_socket_readln strips the \r, so we always have at least
1122  * one character free when we've read a full line) */
1123  while (len == adata->blen);
1124 
1125  /* don't let one large string make cmd->buf hog memory forever */
1126  if ((adata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
1127  {
1129  adata->blen = IMAP_CMD_BUFSIZE;
1130  mutt_debug(LL_DEBUG3, "shrank buffer to %lu bytes\n", adata->blen);
1131  }
1132 
1133  adata->lastread = mutt_date_epoch();
1134 
1135  /* handle untagged messages. The caller still gets its shot afterwards. */
1136  if ((mutt_str_startswith(adata->buf, "* ") ||
1137  mutt_str_startswith(imap_next_word(adata->buf), "OK [")) &&
1138  cmd_handle_untagged(adata))
1139  {
1140  return IMAP_RES_BAD;
1141  }
1142 
1143  /* server demands a continuation response from us */
1144  if (adata->buf[0] == '+')
1145  return IMAP_RES_RESPOND;
1146 
1147  /* Look for tagged command completions.
1148  *
1149  * Some response handlers can end up recursively calling
1150  * imap_cmd_step() and end up handling all tagged command
1151  * completions.
1152  * (e.g. FETCH->set_flag->set_header_color->~h pattern match.)
1153  *
1154  * Other callers don't even create an adata->cmds entry.
1155  *
1156  * For both these cases, we default to returning OK */
1157  rc = IMAP_RES_OK;
1158  c = adata->lastcmd;
1159  do
1160  {
1161  cmd = &adata->cmds[c];
1162  if (cmd->state == IMAP_RES_NEW)
1163  {
1164  if (mutt_str_startswith(adata->buf, cmd->seq))
1165  {
1166  if (!stillrunning)
1167  {
1168  /* first command in queue has finished - move queue pointer up */
1169  adata->lastcmd = (adata->lastcmd + 1) % adata->cmdslots;
1170  }
1171  cmd->state = cmd_status(adata->buf);
1172  rc = cmd->state;
1173  if (cmd->state == IMAP_RES_NO || cmd->state == IMAP_RES_BAD)
1174  {
1175  mutt_message(_("IMAP command failed: %s"), adata->buf);
1176  }
1177  }
1178  else
1179  stillrunning++;
1180  }
1181 
1182  c = (c + 1) % adata->cmdslots;
1183  } while (c != adata->nextcmd);
1184 
1185  if (stillrunning)
1186  rc = IMAP_RES_CONTINUE;
1187  else
1188  {
1189  mutt_debug(LL_DEBUG3, "IMAP queue drained\n");
1190  imap_cmd_finish(adata);
1191  }
1192 
1193  return rc;
1194 }
1195 
1202 bool imap_code(const char *s)
1203 {
1204  return cmd_status(s) == IMAP_RES_OK;
1205 }
1206 
1213 const char *imap_cmd_trailer(struct ImapAccountData *adata)
1214 {
1215  static const char *notrailer = "";
1216  const char *s = adata->buf;
1217 
1218  if (!s)
1219  {
1220  mutt_debug(LL_DEBUG2, "not a tagged response\n");
1221  return notrailer;
1222  }
1223 
1224  s = imap_next_word((char *) s);
1225  if (!s || (!mutt_istr_startswith(s, "OK") && !mutt_istr_startswith(s, "NO") &&
1226  !mutt_istr_startswith(s, "BAD")))
1227  {
1228  mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1229  return notrailer;
1230  }
1231 
1232  s = imap_next_word((char *) s);
1233  if (!s)
1234  return notrailer;
1235 
1236  return s;
1237 }
1238 
1250 int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
1251 {
1252  int rc;
1253 
1254  if (flags & IMAP_CMD_SINGLE)
1255  {
1256  // Process any existing commands
1257  if (adata->nextcmd != adata->lastcmd)
1258  imap_exec(adata, NULL, IMAP_CMD_POLL);
1259  }
1260 
1261  rc = cmd_start(adata, cmdstr, flags);
1262  if (rc < 0)
1263  {
1264  cmd_handle_fatal(adata);
1265  return IMAP_EXEC_FATAL;
1266  }
1267 
1268  if (flags & IMAP_CMD_QUEUE)
1269  return IMAP_EXEC_SUCCESS;
1270 
1271  const short c_imap_poll_timeout =
1272  cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1273  if ((flags & IMAP_CMD_POLL) && (c_imap_poll_timeout > 0) &&
1274  ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1275  {
1276  mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1277  cmd_handle_fatal(adata);
1278  return IMAP_EXEC_FATAL;
1279  }
1280 
1281  /* Allow interruptions, particularly useful if there are network problems. */
1283  do
1284  {
1285  rc = imap_cmd_step(adata);
1286  // The queue is empty, so the single command has been processed
1287  if ((flags & IMAP_CMD_SINGLE) && (adata->nextcmd == adata->lastcmd))
1288  break;
1289  } while (rc == IMAP_RES_CONTINUE);
1290  mutt_sig_allow_interrupt(false);
1291 
1292  if (rc == IMAP_RES_NO)
1293  return IMAP_EXEC_ERROR;
1294  if (rc != IMAP_RES_OK)
1295  {
1296  if (adata->status != IMAP_FATAL)
1297  return IMAP_EXEC_ERROR;
1298 
1299  mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1300  return IMAP_EXEC_FATAL;
1301  }
1302 
1303  return IMAP_EXEC_SUCCESS;
1304 }
1305 
1317 {
1318  if (!adata)
1319  return;
1320 
1321  if (adata->status == IMAP_FATAL)
1322  {
1323  adata->closing = false;
1324  cmd_handle_fatal(adata);
1325  return;
1326  }
1327 
1328  if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1329  {
1330  adata->closing = false;
1331  return;
1332  }
1333 
1334  adata->closing = false;
1335 
1336  struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1337 
1338  if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1339  {
1340  // First remove expunged emails from the msn_index
1341  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1342  {
1343  mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1344  imap_expunge_mailbox(adata->mailbox);
1345  /* Detect whether we've gotten unexpected EXPUNGE messages */
1346  if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1349  }
1350 
1351  // Then add new emails to it
1352  if (mdata->reopen & IMAP_NEWMAIL_PENDING)
1353  {
1354  const size_t max_msn = imap_msn_highest(&mdata->msn);
1355  if (mdata->new_mail_count > max_msn)
1356  {
1357  if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1359 
1360  mutt_debug(LL_DEBUG2, "Fetching new mails from %d to %d\n", max_msn + 1,
1361  mdata->new_mail_count);
1362  imap_read_headers(adata->mailbox, max_msn + 1, mdata->new_mail_count, false);
1363  }
1364  }
1365 
1366  // And to finish inform about MUTT_REOPEN if needed
1367  if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1369 
1370  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1372  }
1373 
1374  adata->status = 0;
1375 }
1376 
1383 int imap_cmd_idle(struct ImapAccountData *adata)
1384 {
1385  int rc;
1386 
1387  if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1388  {
1389  cmd_handle_fatal(adata);
1390  return -1;
1391  }
1392 
1393  const short c_imap_poll_timeout =
1394  cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
1395  if ((c_imap_poll_timeout > 0) &&
1396  ((mutt_socket_poll(adata->conn, c_imap_poll_timeout)) == 0))
1397  {
1398  mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1399  cmd_handle_fatal(adata);
1400  return -1;
1401  }
1402 
1403  do
1404  {
1405  rc = imap_cmd_step(adata);
1406  } while (rc == IMAP_RES_CONTINUE);
1407 
1408  if (rc == IMAP_RES_RESPOND)
1409  {
1410  /* successfully entered IDLE state */
1411  adata->state = IMAP_IDLE;
1412  /* queue automatic exit when next command is issued */
1413  mutt_buffer_addstr(&adata->cmdbuf, "DONE\r\n");
1414  rc = IMAP_RES_OK;
1415  }
1416  if (rc != IMAP_RES_OK)
1417  {
1418  mutt_debug(LL_DEBUG1, "error starting IDLE\n");
1419  return -1;
1420  }
1421 
1422  return 0;
1423 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:427
struct ImapCommand * cmds
Definition: adata.h:69
#define IMAP_RES_RESPOND
+
Definition: private.h:58
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 cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:56
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1079
The envelope/body of an email.
Definition: email.h:37
Imap-specific Email data.
Logged out from server.
Definition: private.h:98
Config/command parsing.
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:40
uint32_t uidvalidity
Definition: mdata.h:50
Imap connection failure.
Definition: private.h:86
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
#define mutt_error(...)
Definition: logging.h:88
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
set capability bits according to CAPABILITY response
Definition: command.c:535
#define IMAP_RES_NEW
ImapCommand.state additions.
Definition: private.h:59
unsigned int seqno
tag sequence number, e.g. &#39;{seqid}0001&#39;
Definition: adata.h:57
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:68
Unrecoverable error occurred.
Definition: private.h:97
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1849
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:676
#define MUTT_ACL_CREATE
Create a mailbox.
Definition: mailbox.h:65
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition: private.h:66
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
#define MUTT_ACL_DELMX
Delete a mailbox.
Definition: mailbox.h:67
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:70
NeoMutt Logging.
int lastcmd
Definition: adata.h:72
struct MailboxList mailboxes
List of Mailboxes.
Definition: account.h:41
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:44
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1081
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
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:76
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:1039
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
#define _(a)
Definition: message.h:28
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1213
bool changed
Email has been edited.
Definition: email.h:48
#define IMAP_LOG_PASS
Definition: private.h:51
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:64
#define U_NO_FLAGS
Definition: url.h:49
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:853
char * capstr
Definition: adata.h:54
static void cmd_handle_fatal(struct ImapAccountData *adata)
When ImapAccountData is in fatal state, do what we can.
Definition: command.c:164
Items in an IMAP browser.
Definition: private.h:148
#define MUTT_ACL_EXPUNGE
Expunge messages.
Definition: mailbox.h:68
Imap-specific Account data.
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1065
size_t blen
Definition: adata.h:60
IMAP MSN helper functions.
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
char delim
Definition: private.h:151
#define MUTT_SOCK_LOG_FULL
Definition: mutt_socket.h:31
unsigned int msn
Message Sequence Number.
Definition: edata.h:45
Container for Accounts, Notifications.
Definition: neomutt.h:36
void * mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:384
void cmd_parse_search(struct ImapAccountData *adata, const char *s)
store SEARCH response for later use
Definition: search.c:259
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:66
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
Imap command executed or queued successfully.
Definition: private.h:84
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
unsigned char seqid
tag sequence prefix
Definition: adata.h:56
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:46
char host[128]
Server to login to.
Definition: connaccount.h:53
bool has_new
Mailbox has new mail.
Definition: mailbox.h:88
Imap-specific Mailbox data.
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
size_t dsize
Length of data.
Definition: buffer.h:37
static struct Mailbox * find_mailbox(struct ImapAccountData *adata, const char *name)
Find a Mailbox by its name.
Definition: command.c:784
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:192
int nextcmd
Definition: adata.h:71
char * name
A short name for the Mailbox.
Definition: mailbox.h:85
Disconnected from server.
Definition: private.h:107
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition: command.c:106
void imap_msn_remove(struct MSN *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:112
Log at debug level 2.
Definition: logging.h:41
API for mailboxes.
struct Email * imap_msn_get(const struct MSN *msn, size_t idx)
Return the Email associated with an msn.
Definition: msn.c:79
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1383
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:1299
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:59
struct Buffer cmdbuf
Definition: adata.h:73
Convenience wrapper for the core headers.
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:429
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition: mdata.h:45
#define SKIPWS(ch)
Definition: string2.h:46
#define mutt_socket_send_d(conn, buf, dbg)
Definition: mutt_socket.h:38
Mailbox is selected.
Definition: private.h:110
IMAP command structure.
Definition: private.h:159
int mutt_buffer_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
char delim
Definition: adata.h:75
Shared constants/structs that are private to IMAP.
#define MUTT_ACL_POST
Post (submit messages to the server)
Definition: mailbox.h:71
void * mdata
Driver specific data.
Definition: mailbox.h:136
void imap_unquote_string(char *s)
equally stupid unquoting routine
Definition: util.c:877
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:74
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:1250
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
struct HashTable * uid_hash
Definition: mdata.h:58
bool closing
If true, we are waiting for CLOSE completion.
Definition: adata.h:43
bool active
Message is not to be removed.
Definition: email.h:59
struct Account * account
Parent Account.
Definition: adata.h:78
static int cmd_queue(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Add a IMAP command to the queue.
Definition: command.c:138
ConnAccount object used by POP and IMAP.
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
A mailbox.
Definition: mailbox.h:81
char * user
Username.
Definition: url.h:71
struct ListHead flags
Definition: mdata.h:49
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:74
size_t imap_msn_shrink(struct MSN *msn, size_t num)
Remove a number of entries from the end of the cache.
Definition: msn.c:102
char * name
Definition: private.h:150
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1159
int cmdslots
Definition: adata.h:70
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
char * data
Pointer to data.
Definition: buffer.h:35
int url_tostring(struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:418
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1316
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: private.h:67
bool qresync
true, if QRESYNC is successfully ENABLE&#39;d
Definition: adata.h:63
char * name
Mailbox name.
Definition: mdata.h:40
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
int mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: string.c:282
#define IMAP_CMD_BUFSIZE
Definition: command.c:57
char seq[SEQ_LEN+1]
Command tag, e.g. &#39;a0001&#39;.
Definition: private.h:161
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition: command.c:929
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
ImapCapFlags capabilities
Definition: adata.h:55
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:121
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:54
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
time_t lastread
last time we read a command for the server
Definition: adata.h:58
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:97
bool noinferiors
Definition: private.h:153
char * path
Path.
Definition: url.h:75
Connection is idle.
Definition: private.h:113
Imap command failure.
Definition: private.h:85
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:385
#define IS_SPACE(ch)
Definition: string2.h:38
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition: command.c:716
size_t imap_msn_highest(const struct MSN *msn)
Return the highest MSN in use.
Definition: msn.c:68
int imap_get_literal_count(const char *buf, unsigned int *bytes)
write number of bytes in an IMAP literal into bytes
Definition: util.c:748
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition: command.c:663
IMAP-specific Account data -.
Definition: adata.h:39
unsigned int uid_next
Definition: mdata.h:51
#define IMAP_LOG_CMD
Definition: private.h:49
Log at debug level 1.
Definition: logging.h:40
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:749
IMAP-specific Mailbox data -.
Definition: mdata.h:38
int state
Command state, e.g. IMAP_RES_NEW.
Definition: private.h:162
char * buf
Definition: adata.h:59
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition: command.c:397
uint8_t ImapCmdFlags
Flags for imap_exec(), e.g. IMAP_CMD_PASS.
Definition: private.h:72
static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
Parse expunge command.
Definition: command.c:256
bool recovering
Definition: adata.h:42
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:69
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition: command.c:200
Connection Library.
static int cmd_handle_untagged(struct ImapAccountData *adata)
fallback parser for otherwise unhandled messages
Definition: command.c:987
int index
The absolute (unsorted) message number.
Definition: email.h:86
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
#define MUTT_ACL_ADMIN
Administer the account (get/set permissions)
Definition: mailbox.h:64
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition: socket.c:246
char * imap_set_flags(struct Mailbox *m, struct Email *e, char *s, bool *server_changes)
fill the message header according to the server flags
Definition: message.c:1870
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition: command.c:303
#define MUTT_ACL_SEEN
Change the &#39;seen&#39; status of a message.
Definition: mailbox.h:73
struct ImapList * cmdresult
Definition: adata.h:66
List of Mailboxes.
Definition: mailbox.h:156
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:75
unsigned int unseen
Definition: mdata.h:55
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
NeoMutt connections.
unsigned int recent
Definition: mdata.h:54
void imap_msn_set(struct MSN *msn, size_t idx, struct Email *e)
Cache an Email into a given position.
Definition: msn.c:91
Convenience wrapper for the library headers.
static void cmd_parse_list(struct ImapAccountData *adata, char *s)
Parse a server LIST command (list mailboxes)
Definition: command.c:568
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:57
#define IMAP_CMD_SINGLE
Run a single command.
Definition: private.h:77
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:68
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1102
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition: util.c:931
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1202
Log at debug level 3.
Definition: logging.h:42
bool noselect
Definition: private.h:152
static const char *const Capabilities[]
Server capabilities strings that we understand.
Definition: command.c:64
#define MUTT_ACL_LOOKUP
Lookup mailbox (visible to &#39;list&#39;)
Definition: mailbox.h:70
unsigned int messages
Definition: mdata.h:53
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
quote string according to IMAP rules
Definition: util.c:840
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition: adata.h:62
UID Sequence Set Iterator.
Definition: private.h:168
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition: command.c:92
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition: command.c:949
struct Connection * conn
Definition: adata.h:41
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:158
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition: command.c:808
#define IMAP_RES_BAD
<tag> BAD ...
Definition: private.h:55
static int cmd_status(const char *s)
parse response line for tagged OK/NO/BAD
Definition: command.c:236