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