NeoMutt  2019-12-07-168-gc45f47
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 <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include "imap_private.h"
41 #include "mutt/lib.h"
42 #include "email/lib.h"
43 #include "core/lib.h"
44 #include "conn/lib.h"
45 #include "mutt.h"
46 #include "globals.h"
47 #include "message.h"
48 #include "mutt_account.h"
49 #include "mutt_logging.h"
50 #include "mutt_menu.h"
51 #include "mutt_socket.h"
52 #include "mx.h"
53 
54 /* These Config Variables are only used in imap/command.c */
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_str_startswith(s, "OK", CASE_IGNORE))
241  return IMAP_RES_OK;
242  if (mutt_str_startswith(s, "NO", CASE_IGNORE))
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) || (exp_msn > mdata->max_msn))
266  return;
267 
268  e = mdata->msn_index[exp_msn - 1];
269  if (e)
270  {
271  /* imap_expunge_mailbox() will rewrite e->index.
272  * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
273  * makes the code simpler and possibly more efficient. */
274  e->index = INT_MAX;
275  imap_edata_get(e)->msn = 0;
276  }
277 
278  /* decrement seqno of those above. */
279  for (unsigned int cur = exp_msn; cur < mdata->max_msn; cur++)
280  {
281  e = mdata->msn_index[cur];
282  if (e)
283  imap_edata_get(e)->msn--;
284  mdata->msn_index[cur - 1] = e;
285  }
286 
287  mdata->msn_index[mdata->max_msn - 1] = NULL;
288  mdata->max_msn--;
289 
290  mdata->reopen |= IMAP_EXPUNGE_PENDING;
291 }
292 
301 static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
302 {
303  bool earlier = false;
304  int rc;
305  unsigned int uid = 0;
306 
307  struct ImapMboxData *mdata = adata->mailbox->mdata;
308 
309  mutt_debug(LL_DEBUG2, "Handling VANISHED\n");
310 
311  if (mutt_str_startswith(s, "(EARLIER)", CASE_IGNORE))
312  {
313  /* The RFC says we should not decrement msns with the VANISHED EARLIER tag.
314  * My experimentation says that's crap. */
315  earlier = true;
316  s = imap_next_word(s);
317  }
318 
319  char *end_of_seqset = s;
320  while (*end_of_seqset)
321  {
322  if (!strchr("0123456789:,", *end_of_seqset))
323  *end_of_seqset = '\0';
324  else
325  end_of_seqset++;
326  }
327 
328  struct SeqsetIterator *iter = mutt_seqset_iterator_new(s);
329  if (!iter)
330  {
331  mutt_debug(LL_DEBUG2, "VANISHED: empty seqset [%s]?\n", s);
332  return;
333  }
334 
335  while ((rc = mutt_seqset_iterator_next(iter, &uid)) == 0)
336  {
337  struct Email *e = mutt_hash_int_find(mdata->uid_hash, uid);
338  if (!e)
339  continue;
340 
341  unsigned int exp_msn = imap_edata_get(e)->msn;
342 
343  /* imap_expunge_mailbox() will rewrite e->index.
344  * It needs to resort using SORT_ORDER anyway, so setting to INT_MAX
345  * makes the code simpler and possibly more efficient. */
346  e->index = INT_MAX;
347  imap_edata_get(e)->msn = 0;
348 
349  if ((exp_msn < 1) || (exp_msn > mdata->max_msn))
350  {
351  mutt_debug(LL_DEBUG1, "VANISHED: msn for UID %u is incorrect\n", uid);
352  continue;
353  }
354  if (mdata->msn_index[exp_msn - 1] != e)
355  {
356  mutt_debug(LL_DEBUG1, "VANISHED: msn_index for UID %u is incorrect\n", uid);
357  continue;
358  }
359 
360  mdata->msn_index[exp_msn - 1] = NULL;
361 
362  if (!earlier)
363  {
364  /* decrement seqno of those above. */
365  for (unsigned int cur = exp_msn; cur < mdata->max_msn; cur++)
366  {
367  e = mdata->msn_index[cur];
368  if (e)
369  imap_edata_get(e)->msn--;
370  mdata->msn_index[cur - 1] = e;
371  }
372 
373  mdata->msn_index[mdata->max_msn - 1] = NULL;
374  mdata->max_msn--;
375  }
376  }
377 
378  if (rc < 0)
379  mutt_debug(LL_DEBUG1, "VANISHED: illegal seqset %s\n", s);
380 
381  mdata->reopen |= IMAP_EXPUNGE_PENDING;
382 
384 }
385 
395 static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
396 {
397  unsigned int msn, uid;
398  struct Email *e = NULL;
399  char *flags = NULL;
400  int uid_checked = 0;
401  bool server_changes = false;
402 
403  struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
404 
405  mutt_debug(LL_DEBUG3, "Handling FETCH\n");
406 
407  if (mutt_str_atoui(s, &msn) < 0)
408  {
409  mutt_debug(LL_DEBUG3, "Skipping FETCH response - illegal MSN\n");
410  return;
411  }
412 
413  if ((msn < 1) || (msn > mdata->max_msn))
414  {
415  mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u out of range\n", msn);
416  return;
417  }
418 
419  e = mdata->msn_index[msn - 1];
420  if (!e || !e->active)
421  {
422  mutt_debug(LL_DEBUG3, "Skipping FETCH response - MSN %u not in msn_index\n", msn);
423  return;
424  }
425 
426  mutt_debug(LL_DEBUG2, "Message UID %u updated\n", imap_edata_get(e)->uid);
427  /* skip FETCH */
428  s = imap_next_word(s);
429  s = imap_next_word(s);
430 
431  if (*s != '(')
432  {
433  mutt_debug(LL_DEBUG1, "Malformed FETCH response\n");
434  return;
435  }
436  s++;
437 
438  while (*s)
439  {
440  SKIPWS(s);
441  size_t plen = mutt_str_startswith(s, "FLAGS", CASE_IGNORE);
442  if (plen != 0)
443  {
444  flags = s;
445  if (uid_checked)
446  break;
447 
448  s += plen;
449  SKIPWS(s);
450  if (*s != '(')
451  {
452  mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
453  return;
454  }
455  s++;
456  while (*s && (*s != ')'))
457  s++;
458  if (*s == ')')
459  s++;
460  else
461  {
462  mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
463  return;
464  }
465  }
466  else if ((plen = mutt_str_startswith(s, "UID", CASE_IGNORE)))
467  {
468  s += plen;
469  SKIPWS(s);
470  if (mutt_str_atoui(s, &uid) < 0)
471  {
472  mutt_debug(LL_DEBUG1, "Illegal UID. Skipping update\n");
473  return;
474  }
475  if (uid != imap_edata_get(e)->uid)
476  {
477  mutt_debug(LL_DEBUG1, "UID vs MSN mismatch. Skipping update\n");
478  return;
479  }
480  uid_checked = 1;
481  if (flags)
482  break;
483  s = imap_next_word(s);
484  }
485  else if ((plen = mutt_str_startswith(s, "MODSEQ", CASE_IGNORE)))
486  {
487  s += plen;
488  SKIPWS(s);
489  if (*s != '(')
490  {
491  mutt_debug(LL_DEBUG1, "bogus MODSEQ response: %s\n", s);
492  return;
493  }
494  s++;
495  while (*s && (*s != ')'))
496  s++;
497  if (*s == ')')
498  s++;
499  else
500  {
501  mutt_debug(LL_DEBUG1, "Unterminated MODSEQ response: %s\n", s);
502  return;
503  }
504  }
505  else if (*s == ')')
506  break; /* end of request */
507  else if (*s)
508  {
509  mutt_debug(LL_DEBUG2, "Only handle FLAGS updates\n");
510  break;
511  }
512  }
513 
514  if (flags)
515  {
516  imap_set_flags(adata->mailbox, e, flags, &server_changes);
517  if (server_changes)
518  {
519  /* If server flags could conflict with NeoMutt's flags, reopen the mailbox. */
520  if (e->changed)
521  mdata->reopen |= IMAP_EXPUNGE_PENDING;
522  else
524  }
525  }
526 }
527 
533 static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
534 {
535  mutt_debug(LL_DEBUG3, "Handling CAPABILITY\n");
536 
537  s = imap_next_word(s);
538  char *bracket = strchr(s, ']');
539  if (bracket)
540  *bracket = '\0';
541  FREE(&adata->capstr);
542  adata->capstr = mutt_str_strdup(s);
543  adata->capabilities = 0;
544 
545  while (*s)
546  {
547  for (size_t i = 0; Capabilities[i]; i++)
548  {
549  if (mutt_str_word_casecmp(Capabilities[i], s) == 0)
550  {
551  adata->capabilities |= (1 << i);
552  mutt_debug(LL_DEBUG3, " Found capability \"%s\": %lu\n", Capabilities[i], i);
553  break;
554  }
555  }
556  s = imap_next_word(s);
557  }
558 }
559 
565 static void cmd_parse_list(struct ImapAccountData *adata, char *s)
566 {
567  struct ImapList *list = NULL;
568  struct ImapList lb = { 0 };
569  char delimbuf[5]; /* worst case: "\\"\0 */
570  unsigned int litlen;
571 
572  if (adata->cmdresult)
573  list = adata->cmdresult;
574  else
575  list = &lb;
576 
577  memset(list, 0, sizeof(struct ImapList));
578 
579  /* flags */
580  s = imap_next_word(s);
581  if (*s != '(')
582  {
583  mutt_debug(LL_DEBUG1, "Bad LIST response\n");
584  return;
585  }
586  s++;
587  while (*s)
588  {
589  if (mutt_str_startswith(s, "\\NoSelect", CASE_IGNORE))
590  list->noselect = true;
591  else if (mutt_str_startswith(s, "\\NonExistent", CASE_IGNORE)) /* rfc5258 */
592  list->noselect = true;
593  else if (mutt_str_startswith(s, "\\NoInferiors", CASE_IGNORE))
594  list->noinferiors = true;
595  else if (mutt_str_startswith(s, "\\HasNoChildren", CASE_IGNORE)) /* rfc5258*/
596  list->noinferiors = true;
597 
598  s = imap_next_word(s);
599  if (*(s - 2) == ')')
600  break;
601  }
602 
603  /* Delimiter */
604  if (!mutt_str_startswith(s, "NIL", CASE_IGNORE))
605  {
606  delimbuf[0] = '\0';
607  mutt_str_strcat(delimbuf, 5, s);
608  imap_unquote_string(delimbuf);
609  list->delim = delimbuf[0];
610  }
611 
612  /* Name */
613  s = imap_next_word(s);
614  /* Notes often responds with literals here. We need a real tokenizer. */
615  if (imap_get_literal_count(s, &litlen) == 0)
616  {
617  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
618  {
619  adata->status = IMAP_FATAL;
620  return;
621  }
622 
623  if (strlen(adata->buf) < litlen)
624  {
625  mutt_debug(LL_DEBUG1, "Error parsing LIST mailbox\n");
626  return;
627  }
628 
629  list->name = adata->buf;
630  s = list->name + litlen;
631  if (s[0] != '\0')
632  {
633  s[0] = '\0';
634  s++;
635  SKIPWS(s);
636  }
637  }
638  else
639  {
640  list->name = s;
641  /* Exclude rfc5258 RECURSIVEMATCH CHILDINFO suffix */
642  s = imap_next_word(s);
643  if (s[0] != '\0')
644  s[-1] = '\0';
645  imap_unmunge_mbox_name(adata->unicode, list->name);
646  }
647 
648  if (list->name[0] == '\0')
649  {
650  adata->delim = list->delim;
651  mutt_debug(LL_DEBUG3, "Root delimiter: %c\n", adata->delim);
652  }
653 }
654 
660 static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
661 {
662  char buf[256];
663  char errstr[256];
664  struct Buffer err, token;
665  struct Url url = { 0 };
666  struct ImapList list = { 0 };
667 
668  if (adata->cmdresult)
669  {
670  /* caller will handle response itself */
671  cmd_parse_list(adata, s);
672  return;
673  }
674 
676  return;
677 
678  adata->cmdresult = &list;
679  cmd_parse_list(adata, s);
680  adata->cmdresult = NULL;
681  /* noselect is for a gmail quirk */
682  if (!list.name || list.noselect)
683  return;
684 
685  mutt_debug(LL_DEBUG3, "Subscribing to %s\n", list.name);
686 
687  mutt_str_strfcpy(buf, "mailboxes \"", sizeof(buf));
688  mutt_account_tourl(&adata->conn->account, &url);
689  /* escape \ and " */
690  imap_quote_string(errstr, sizeof(errstr), list.name, true);
691  url.path = errstr + 1;
692  url.path[strlen(url.path) - 1] = '\0';
693  if (mutt_str_strcmp(url.user, C_ImapUser) == 0)
694  url.user = NULL;
695  url_tostring(&url, buf + 11, sizeof(buf) - 11, 0);
696  mutt_str_strcat(buf, sizeof(buf), "\"");
697  mutt_buffer_init(&token);
698  mutt_buffer_init(&err);
699  err.data = errstr;
700  err.dsize = sizeof(errstr);
701  if (mutt_parse_rc_line(buf, &token, &err))
702  mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", errstr);
703  FREE(&token.data);
704 }
705 
711 static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
712 {
713  mutt_debug(LL_DEBUG2, "Handling MYRIGHTS\n");
714 
715  s = imap_next_word((char *) s);
716  s = imap_next_word((char *) s);
717 
718  /* zero out current rights set */
719  adata->mailbox->rights = 0;
720 
721  while (*s && !isspace((unsigned char) *s))
722  {
723  switch (*s)
724  {
725  case 'a':
726  adata->mailbox->rights |= MUTT_ACL_ADMIN;
727  break;
728  case 'e':
729  adata->mailbox->rights |= MUTT_ACL_EXPUNGE;
730  break;
731  case 'i':
732  adata->mailbox->rights |= MUTT_ACL_INSERT;
733  break;
734  case 'k':
735  adata->mailbox->rights |= MUTT_ACL_CREATE;
736  break;
737  case 'l':
738  adata->mailbox->rights |= MUTT_ACL_LOOKUP;
739  break;
740  case 'p':
741  adata->mailbox->rights |= MUTT_ACL_POST;
742  break;
743  case 'r':
744  adata->mailbox->rights |= MUTT_ACL_READ;
745  break;
746  case 's':
747  adata->mailbox->rights |= MUTT_ACL_SEEN;
748  break;
749  case 't':
750  adata->mailbox->rights |= MUTT_ACL_DELETE;
751  break;
752  case 'w':
753  adata->mailbox->rights |= MUTT_ACL_WRITE;
754  break;
755  case 'x':
756  adata->mailbox->rights |= MUTT_ACL_DELMX;
757  break;
758 
759  /* obsolete rights */
760  case 'c':
762  break;
763  case 'd':
765  break;
766  default:
767  mutt_debug(LL_DEBUG1, "Unknown right: %c\n", *s);
768  }
769  s++;
770  }
771 }
772 
778 static void cmd_parse_search(struct ImapAccountData *adata, const char *s)
779 {
780  unsigned int uid;
781  struct Email *e = NULL;
782  struct ImapMboxData *mdata = adata->mailbox->mdata;
783 
784  mutt_debug(LL_DEBUG2, "Handling SEARCH\n");
785 
786  while ((s = imap_next_word((char *) s)) && (*s != '\0'))
787  {
788  if (mutt_str_atoui(s, &uid) < 0)
789  continue;
790  e = mutt_hash_int_find(mdata->uid_hash, uid);
791  if (e)
792  e->matched = true;
793  }
794 }
795 
802 static struct Mailbox *find_mailbox(struct ImapAccountData *adata, const char *name)
803 {
804  if (!adata || !adata->account || !name)
805  return NULL;
806 
807  struct MailboxNode *np = NULL;
808  STAILQ_FOREACH(np, &adata->account->mailboxes, entries)
809  {
810  struct ImapMboxData *mdata = imap_mdata_get(np->mailbox);
811  if (mutt_str_strcmp(name, mdata->name) == 0)
812  return np->mailbox;
813  }
814 
815  return NULL;
816 }
817 
826 static void cmd_parse_status(struct ImapAccountData *adata, char *s)
827 {
828  unsigned int litlen = 0;
829 
830  char *mailbox = imap_next_word(s);
831 
832  /* We need a real tokenizer. */
833  if (imap_get_literal_count(mailbox, &litlen) == 0)
834  {
835  if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
836  {
837  adata->status = IMAP_FATAL;
838  return;
839  }
840 
841  if (strlen(adata->buf) < litlen)
842  {
843  mutt_debug(LL_DEBUG1, "Error parsing STATUS mailbox\n");
844  return;
845  }
846 
847  mailbox = adata->buf;
848  s = mailbox + litlen;
849  s[0] = '\0';
850  s++;
851  SKIPWS(s);
852  }
853  else
854  {
855  s = imap_next_word(mailbox);
856  s[-1] = '\0';
857  imap_unmunge_mbox_name(adata->unicode, mailbox);
858  }
859 
860  struct Mailbox *m = find_mailbox(adata, mailbox);
861  struct ImapMboxData *mdata = imap_mdata_get(m);
862  if (!mdata)
863  {
864  mutt_debug(LL_DEBUG3, "Received status for an unexpected mailbox: %s\n", mailbox);
865  return;
866  }
867  unsigned int olduv = mdata->uid_validity;
868  unsigned int oldun = mdata->uid_next;
869 
870  if (*s++ != '(')
871  {
872  mutt_debug(LL_DEBUG1, "Error parsing STATUS\n");
873  return;
874  }
875  while ((s[0] != '\0') && (s[0] != ')'))
876  {
877  char *value = imap_next_word(s);
878 
879  errno = 0;
880  const unsigned long ulcount = strtoul(value, &value, 10);
881  if (((errno == ERANGE) && (ulcount == ULONG_MAX)) || ((unsigned int) ulcount != ulcount))
882  {
883  mutt_debug(LL_DEBUG1, "Error parsing STATUS number\n");
884  return;
885  }
886  const unsigned int count = (unsigned int) ulcount;
887 
888  if (mutt_str_startswith(s, "MESSAGES", CASE_MATCH))
889  mdata->messages = count;
890  else if (mutt_str_startswith(s, "RECENT", CASE_MATCH))
891  mdata->recent = count;
892  else if (mutt_str_startswith(s, "UIDNEXT", CASE_MATCH))
893  mdata->uid_next = count;
894  else if (mutt_str_startswith(s, "UIDVALIDITY", CASE_MATCH))
895  mdata->uid_validity = count;
896  else if (mutt_str_startswith(s, "UNSEEN", CASE_MATCH))
897  mdata->unseen = count;
898 
899  s = value;
900  if ((s[0] != '\0') && (*s != ')'))
901  s = imap_next_word(s);
902  }
903  mutt_debug(LL_DEBUG3, "%s (UIDVALIDITY: %u, UIDNEXT: %u) %d messages, %d recent, %d unseen\n",
904  mdata->name, mdata->uid_validity, mdata->uid_next, mdata->messages,
905  mdata->recent, mdata->unseen);
906 
907  mutt_debug(LL_DEBUG3, "Running default STATUS handler\n");
908 
909  mutt_debug(LL_DEBUG3, "Found %s in mailbox list (OV: %u ON: %u U: %d)\n",
910  mailbox, olduv, oldun, mdata->unseen);
911 
912  bool new_mail = false;
913  if (C_MailCheckRecent)
914  {
915  if ((olduv != 0) && (olduv == mdata->uid_validity))
916  {
917  if (oldun < mdata->uid_next)
918  new_mail = (mdata->unseen > 0);
919  }
920  else if ((olduv == 0) && (oldun == 0))
921  {
922  /* first check per session, use recent. might need a flag for this. */
923  new_mail = (mdata->recent > 0);
924  }
925  else
926  new_mail = (mdata->unseen > 0);
927  }
928  else
929  new_mail = (mdata->unseen > 0);
930 
931 #ifdef USE_SIDEBAR
932  if ((m->has_new != new_mail) || (m->msg_count != mdata->messages) ||
933  (m->msg_unread != mdata->unseen))
934  {
936  }
937 #endif
938 
939  m->has_new = new_mail;
940  m->msg_count = mdata->messages;
941  m->msg_unread = mdata->unseen;
942 
943  // force back to keep detecting new mail until the mailbox is opened
944  if (m->has_new)
945  mdata->uid_next = oldun;
946 }
947 
953 static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
954 {
955  mutt_debug(LL_DEBUG2, "Handling ENABLED\n");
956 
957  while ((s = imap_next_word((char *) s)) && (*s != '\0'))
958  {
959  if (mutt_str_startswith(s, "UTF8=ACCEPT", CASE_IGNORE) ||
960  mutt_str_startswith(s, "UTF8=ONLY", CASE_IGNORE))
961  {
962  adata->unicode = true;
963  }
964  if (mutt_str_startswith(s, "QRESYNC", CASE_IGNORE))
965  adata->qresync = true;
966  }
967 }
973 static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
974 {
975  unsigned int count = 0;
976  mutt_debug(LL_DEBUG2, "Handling EXISTS\n");
977 
978  if (mutt_str_atoui(pn, &count) < 0)
979  {
980  mutt_debug(LL_DEBUG1, "Malformed EXISTS: '%s'\n", pn);
981  return;
982  }
983 
984  struct ImapMboxData *mdata = adata->mailbox->mdata;
985 
986  /* new mail arrived */
987  if (count < mdata->max_msn)
988  {
989  /* Notes 6.0.3 has a tendency to report fewer messages exist than
990  * it should. */
991  mutt_debug(LL_DEBUG1, "Message count is out of sync\n");
992  }
993  /* at least the InterChange server sends EXISTS messages freely,
994  * even when there is no new mail */
995  else if (count == mdata->max_msn)
996  mutt_debug(LL_DEBUG3, "superfluous EXISTS message\n");
997  else
998  {
999  mutt_debug(LL_DEBUG2, "New mail in %s - %d messages total\n", mdata->name, count);
1000  mdata->reopen |= IMAP_NEWMAIL_PENDING;
1001  mdata->new_mail_count = count;
1002  }
1003 }
1004 
1011 static int cmd_handle_untagged(struct ImapAccountData *adata)
1012 {
1013  char *s = imap_next_word(adata->buf);
1014  char *pn = imap_next_word(s);
1015 
1016  if ((adata->state >= IMAP_SELECTED) && isdigit((unsigned char) *s))
1017  {
1018  /* pn vs. s: need initial seqno */
1019  pn = s;
1020  s = imap_next_word(s);
1021 
1022  /* EXISTS, EXPUNGE, FETCH are always related to the SELECTED mailbox */
1023  if (mutt_str_startswith(s, "EXISTS", CASE_IGNORE))
1024  cmd_parse_exists(adata, pn);
1025  else if (mutt_str_startswith(s, "EXPUNGE", CASE_IGNORE))
1026  cmd_parse_expunge(adata, pn);
1027  else if (mutt_str_startswith(s, "FETCH", CASE_IGNORE))
1028  cmd_parse_fetch(adata, pn);
1029  }
1030  else if ((adata->state >= IMAP_SELECTED) && mutt_str_startswith(s, "VANISHED", CASE_IGNORE))
1031  cmd_parse_vanished(adata, pn);
1032  else if (mutt_str_startswith(s, "CAPABILITY", CASE_IGNORE))
1033  cmd_parse_capability(adata, s);
1034  else if (mutt_str_startswith(s, "OK [CAPABILITY", CASE_IGNORE))
1035  cmd_parse_capability(adata, pn);
1036  else if (mutt_str_startswith(pn, "OK [CAPABILITY", CASE_IGNORE))
1038  else if (mutt_str_startswith(s, "LIST", CASE_IGNORE))
1039  cmd_parse_list(adata, s);
1040  else if (mutt_str_startswith(s, "LSUB", CASE_IGNORE))
1041  cmd_parse_lsub(adata, s);
1042  else if (mutt_str_startswith(s, "MYRIGHTS", CASE_IGNORE))
1043  cmd_parse_myrights(adata, s);
1044  else if (mutt_str_startswith(s, "SEARCH", CASE_IGNORE))
1045  cmd_parse_search(adata, s);
1046  else if (mutt_str_startswith(s, "STATUS", CASE_IGNORE))
1047  cmd_parse_status(adata, s);
1048  else if (mutt_str_startswith(s, "ENABLED", CASE_IGNORE))
1049  cmd_parse_enabled(adata, s);
1050  else if (mutt_str_startswith(s, "BYE", CASE_IGNORE))
1051  {
1052  mutt_debug(LL_DEBUG2, "Handling BYE\n");
1053 
1054  /* check if we're logging out */
1055  if (adata->status == IMAP_BYE)
1056  return 0;
1057 
1058  /* server shut down our connection */
1059  s += 3;
1060  SKIPWS(s);
1061  mutt_error("%s", s);
1062  cmd_handle_fatal(adata);
1063 
1064  return -1;
1065  }
1066  else if (C_ImapServernoise && mutt_str_startswith(s, "NO", CASE_IGNORE))
1067  {
1068  mutt_debug(LL_DEBUG2, "Handling untagged NO\n");
1069 
1070  /* Display the warning message from the server */
1071  mutt_error("%s", s + 2);
1072  }
1073 
1074  return 0;
1075 }
1076 
1086 int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
1087 {
1088  return cmd_start(adata, cmdstr, IMAP_CMD_NO_FLAGS);
1089 }
1090 
1100 int imap_cmd_step(struct ImapAccountData *adata)
1101 {
1102  if (!adata)
1103  return -1;
1104 
1105  size_t len = 0;
1106  int c;
1107  int rc;
1108  int stillrunning = 0;
1109  struct ImapCommand *cmd = NULL;
1110 
1111  if (adata->status == IMAP_FATAL)
1112  {
1113  cmd_handle_fatal(adata);
1114  return IMAP_RES_BAD;
1115  }
1116 
1117  /* read into buffer, expanding buffer as necessary until we have a full
1118  * line */
1119  do
1120  {
1121  if (len == adata->blen)
1122  {
1123  mutt_mem_realloc(&adata->buf, adata->blen + IMAP_CMD_BUFSIZE);
1124  adata->blen = adata->blen + IMAP_CMD_BUFSIZE;
1125  mutt_debug(LL_DEBUG3, "grew buffer to %lu bytes\n", adata->blen);
1126  }
1127 
1128  /* back up over '\0' */
1129  if (len)
1130  len--;
1131  c = mutt_socket_readln_d(adata->buf + len, adata->blen - len, adata->conn, MUTT_SOCK_LOG_FULL);
1132  if (c <= 0)
1133  {
1134  mutt_debug(LL_DEBUG1, "Error reading server response\n");
1135  cmd_handle_fatal(adata);
1136  return IMAP_RES_BAD;
1137  }
1138 
1139  len += c;
1140  }
1141  /* if we've read all the way to the end of the buffer, we haven't read a
1142  * full line (mutt_socket_readln strips the \r, so we always have at least
1143  * one character free when we've read a full line) */
1144  while (len == adata->blen);
1145 
1146  /* don't let one large string make cmd->buf hog memory forever */
1147  if ((adata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
1148  {
1150  adata->blen = IMAP_CMD_BUFSIZE;
1151  mutt_debug(LL_DEBUG3, "shrank buffer to %lu bytes\n", adata->blen);
1152  }
1153 
1154  adata->lastread = mutt_date_epoch();
1155 
1156  /* handle untagged messages. The caller still gets its shot afterwards. */
1157  if ((mutt_str_startswith(adata->buf, "* ", CASE_MATCH) ||
1158  mutt_str_startswith(imap_next_word(adata->buf), "OK [", CASE_MATCH)) &&
1159  cmd_handle_untagged(adata))
1160  {
1161  return IMAP_RES_BAD;
1162  }
1163 
1164  /* server demands a continuation response from us */
1165  if (adata->buf[0] == '+')
1166  return IMAP_RES_RESPOND;
1167 
1168  /* Look for tagged command completions.
1169  *
1170  * Some response handlers can end up recursively calling
1171  * imap_cmd_step() and end up handling all tagged command
1172  * completions.
1173  * (e.g. FETCH->set_flag->set_header_color->~h pattern match.)
1174  *
1175  * Other callers don't even create an adata->cmds entry.
1176  *
1177  * For both these cases, we default to returning OK */
1178  rc = IMAP_RES_OK;
1179  c = adata->lastcmd;
1180  do
1181  {
1182  cmd = &adata->cmds[c];
1183  if (cmd->state == IMAP_RES_NEW)
1184  {
1185  if (mutt_str_startswith(adata->buf, cmd->seq, CASE_MATCH))
1186  {
1187  if (!stillrunning)
1188  {
1189  /* first command in queue has finished - move queue pointer up */
1190  adata->lastcmd = (adata->lastcmd + 1) % adata->cmdslots;
1191  }
1192  cmd->state = cmd_status(adata->buf);
1193  /* bogus - we don't know which command result to return here. Caller
1194  * should provide a tag. */
1195  rc = cmd->state;
1196  }
1197  else
1198  stillrunning++;
1199  }
1200 
1201  c = (c + 1) % adata->cmdslots;
1202  } while (c != adata->nextcmd);
1203 
1204  if (stillrunning)
1205  rc = IMAP_RES_CONTINUE;
1206  else
1207  {
1208  mutt_debug(LL_DEBUG3, "IMAP queue drained\n");
1209  imap_cmd_finish(adata);
1210  }
1211 
1212  return rc;
1213 }
1214 
1221 bool imap_code(const char *s)
1222 {
1223  return cmd_status(s) == IMAP_RES_OK;
1224 }
1225 
1232 const char *imap_cmd_trailer(struct ImapAccountData *adata)
1233 {
1234  static const char *notrailer = "";
1235  const char *s = adata->buf;
1236 
1237  if (!s)
1238  {
1239  mutt_debug(LL_DEBUG2, "not a tagged response\n");
1240  return notrailer;
1241  }
1242 
1243  s = imap_next_word((char *) s);
1244  if (!s || (!mutt_str_startswith(s, "OK", CASE_IGNORE) &&
1245  !mutt_str_startswith(s, "NO", CASE_IGNORE) &&
1246  !mutt_str_startswith(s, "BAD", CASE_IGNORE)))
1247  {
1248  mutt_debug(LL_DEBUG2, "not a command completion: %s\n", adata->buf);
1249  return notrailer;
1250  }
1251 
1252  s = imap_next_word((char *) s);
1253  if (!s)
1254  return notrailer;
1255 
1256  return s;
1257 }
1258 
1270 int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
1271 {
1272  int rc;
1273 
1274  rc = cmd_start(adata, cmdstr, flags);
1275  if (rc < 0)
1276  {
1277  cmd_handle_fatal(adata);
1278  return IMAP_EXEC_FATAL;
1279  }
1280 
1281  if (flags & IMAP_CMD_QUEUE)
1282  return IMAP_EXEC_SUCCESS;
1283 
1284  if ((flags & IMAP_CMD_POLL) && (C_ImapPollTimeout > 0) &&
1285  ((mutt_socket_poll(adata->conn, C_ImapPollTimeout)) == 0))
1286  {
1287  mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1288  cmd_handle_fatal(adata);
1289  return IMAP_EXEC_FATAL;
1290  }
1291 
1292  /* Allow interruptions, particularly useful if there are network problems. */
1294  do
1295  {
1296  rc = imap_cmd_step(adata);
1297  } while (rc == IMAP_RES_CONTINUE);
1298  mutt_sig_allow_interrupt(false);
1299 
1300  if (rc == IMAP_RES_NO)
1301  return IMAP_EXEC_ERROR;
1302  if (rc != IMAP_RES_OK)
1303  {
1304  if (adata->status != IMAP_FATAL)
1305  return IMAP_EXEC_ERROR;
1306 
1307  mutt_debug(LL_DEBUG1, "command failed: %s\n", adata->buf);
1308  return IMAP_EXEC_FATAL;
1309  }
1310 
1311  return IMAP_EXEC_SUCCESS;
1312 }
1313 
1325 {
1326  if (!adata)
1327  return;
1328 
1329  if (adata->status == IMAP_FATAL)
1330  {
1331  adata->closing = false;
1332  cmd_handle_fatal(adata);
1333  return;
1334  }
1335 
1336  if (!(adata->state >= IMAP_SELECTED) || (adata->mailbox && adata->closing))
1337  {
1338  adata->closing = false;
1339  return;
1340  }
1341 
1342  adata->closing = false;
1343 
1344  struct ImapMboxData *mdata = imap_mdata_get(adata->mailbox);
1345 
1346  if (mdata && mdata->reopen & IMAP_REOPEN_ALLOW)
1347  {
1348  // First remove expunged emails from the msn_index
1349  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1350  {
1351  mutt_debug(LL_DEBUG2, "Expunging mailbox\n");
1352  imap_expunge_mailbox(adata->mailbox);
1353  /* Detect whether we've gotten unexpected EXPUNGE messages */
1354  if (!(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1357  }
1358 
1359  // Then add new emails to it
1360  if (mdata->reopen & IMAP_NEWMAIL_PENDING && (mdata->new_mail_count > mdata->max_msn))
1361  {
1362  if (!(mdata->reopen & IMAP_EXPUNGE_PENDING))
1364 
1365  mutt_debug(LL_DEBUG2, "Fetching new mails from %d to %d\n",
1366  mdata->max_msn + 1, mdata->new_mail_count);
1367  imap_read_headers(adata->mailbox, mdata->max_msn + 1, mdata->new_mail_count, false);
1368  }
1369 
1370  // And to finish inform about MUTT_REOPEN if needed
1371  if (mdata->reopen & IMAP_EXPUNGE_PENDING && !(mdata->reopen & IMAP_EXPUNGE_EXPECTED))
1373 
1374  if (mdata->reopen & IMAP_EXPUNGE_PENDING)
1376  }
1377 
1378  adata->status = 0;
1379 }
1380 
1387 int imap_cmd_idle(struct ImapAccountData *adata)
1388 {
1389  int rc;
1390 
1391  if (cmd_start(adata, "IDLE", IMAP_CMD_POLL) < 0)
1392  {
1393  cmd_handle_fatal(adata);
1394  return -1;
1395  }
1396 
1397  if ((C_ImapPollTimeout > 0) && ((mutt_socket_poll(adata->conn, C_ImapPollTimeout)) == 0))
1398  {
1399  mutt_error(_("Connection to %s timed out"), adata->conn->account.host);
1400  cmd_handle_fatal(adata);
1401  return -1;
1402  }
1403 
1404  do
1405  {
1406  rc = imap_cmd_step(adata);
1407  } while (rc == IMAP_RES_CONTINUE);
1408 
1409  if (rc == IMAP_RES_RESPOND)
1410  {
1411  /* successfully entered IDLE state */
1412  adata->state = IMAP_IDLE;
1413  /* queue automatic exit when next command is issued */
1414  mutt_buffer_addstr(&adata->cmdbuf, "DONE\r\n");
1415  rc = IMAP_RES_OK;
1416  }
1417  if (rc != IMAP_RES_OK)
1418  {
1419  mutt_debug(LL_DEBUG1, "error starting IDLE\n");
1420  return -1;
1421  }
1422 
1423  return 0;
1424 }
time_t mutt_date_epoch(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:411
struct ImapCommand * cmds
Definition: imap_private.h:198
int mutt_str_word_casecmp(const char *a, const char *b)
Find word a in word list b.
Definition: string.c:996
int msg_count
Total number of messages.
Definition: mailbox.h:90
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1100
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
quote string according to IMAP rules
Definition: util.c:985
#define IMAP_LOG_PASS
Definition: imap_private.h:49
WHERE bool C_MailCheckRecent
Config: Notify the user about new mail since the last time the mailbox was opened.
Definition: globals.h:239
The envelope/body of an email.
Definition: email.h:37
struct Email ** msn_index
look up headers by (MSN-1)
Definition: imap_private.h:235
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: imap_private.h:66
struct ConnAccount account
Definition: connection.h:36
int msg_unread
Number of unread messages.
Definition: mailbox.h:91
Structs that make up an email.
#define MUTT_ACL_READ
Read the mailbox.
Definition: mailbox.h:71
WHERE short C_ImapPollTimeout
Config: (imap) Maximum time to wait for a server response.
Definition: globals.h:156
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: message.c:100
Mailbox is selected.
Definition: imap_private.h:107
static void cmd_parse_capability(struct ImapAccountData *adata, char *s)
set capability bits according to CAPABILITY response
Definition: command.c:533
unsigned int seqno
tag sequence number, e.g. &#39;{seqid}0001&#39;
Definition: imap_private.h:185
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:66
void imap_unmunge_mbox_name(bool unicode, char *s)
Remove quoting from a mailbox name.
Definition: util.c:1076
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1962
void imap_expunge_mailbox(struct Mailbox *m)
Purge messages from the server.
Definition: imap.c:788
#define MUTT_ACL_CREATE
Create a mailbox.
Definition: mailbox.h:64
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
#define MUTT_ACL_DELMX
Delete a mailbox.
Definition: mailbox.h:66
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: imap_private.h:172
NeoMutt Logging.
struct MailboxList mailboxes
List of Mailboxes.
Definition: account.h:41
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:1810
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: imap_private.h:220
struct SeqsetIterator * mutt_seqset_iterator_new(const char *seqset)
Create a new Sequence Set Iterator.
Definition: util.c:1223
String manipulation buffer.
Definition: buffer.h:33
#define MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition: mailbox.h:68
#define IMAP_RES_NEW
ImapCommand.state additions.
Definition: imap_private.h:57
char user[128]
Definition: connaccount.h:60
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: imap_private.h:68
#define _(a)
Definition: message.h:28
struct Mailbox * mailbox
Definition: imap_private.h:205
WHERE char * C_ImapUser
Config: (imap) Username for the IMAP server.
Definition: globals.h:115
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1232
bool changed
Email has been edited.
Definition: email.h:48
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:949
Match case when comparing strings.
Definition: string2.h:67
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: imap_private.h:145
#define MUTT_ACL_EXPUNGE
Expunge messages.
Definition: mailbox.h:67
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1086
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: imap_private.h:74
char delim
Definition: imap_private.h:148
#define MUTT_SOCK_LOG_FULL
Definition: mutt_socket.h:32
unsigned int msn
Message Sequence Number.
Definition: message.h:45
void * mutt_hash_int_find(const struct Hash *table, unsigned int intkey)
Find the HashElem data in a Hash table element using a key.
Definition: hash.c:408
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:65
void mutt_sig_allow_interrupt(bool allow)
Allow/disallow Ctrl-C (SIGINT)
Definition: signal.c:238
#define REDRAW_SIDEBAR
Redraw the sidebar.
Definition: mutt_menu.h:51
unsigned int max_msn
the largest MSN fetched so far
Definition: imap_private.h:237
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:85
unsigned char seqid
Definition: imap_private.h:184
Hundreds of global variables to back the user variables.
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: imap_private.h:222
Imap command executed or queued successfully.
Definition: imap_private.h:81
char host[128]
Definition: connaccount.h:63
Manage IMAP messages.
bool has_new
Mailbox has new mail.
Definition: mailbox.h:87
Connection is idle.
Definition: imap_private.h:110
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:802
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: util.c:251
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:190
static struct ImapCommand * cmd_new(struct ImapAccountData *adata)
Create and queue a new command control block.
Definition: command.c:106
Many unsorted constants and some structs.
Log at debug level 2.
Definition: logging.h:41
API for mailboxes.
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1387
const char * name
Definition: pgpmicalg.c:46
struct Buffer cmdbuf
Definition: imap_private.h:202
Convenience wrapper for the core headers.
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:412
ImapOpenFlags check_status
Flags, e.g. IMAP_NEWMAIL_PENDING.
Definition: imap_private.h:221
#define SKIPWS(ch)
Definition: string2.h:47
static void cmd_parse_search(struct ImapAccountData *adata, const char *s)
store SEARCH response for later use
Definition: command.c:778
#define mutt_socket_send_d(conn, buf, dbg)
Definition: mutt_socket.h:39
IMAP command structure.
Definition: imap_private.h:156
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
#define MUTT_ACL_POST
Post (submit messages to the server)
Definition: mailbox.h:70
void * mdata
Driver specific data.
Definition: mailbox.h:135
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:73
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:1270
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
void mutt_seqset_iterator_free(struct SeqsetIterator **ptr)
Free a Sequence Set Iterator.
Definition: util.c:1303
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:937
bool active
Message is not to be removed.
Definition: email.h:59
struct Account * account
Parent Account.
Definition: imap_private.h:206
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.
bool C_ImapServernoise
Config: (imap) Display server warnings as error messages.
Definition: command.c:55
A mailbox.
Definition: mailbox.h:80
char * user
Username.
Definition: url.h:69
#define IMAP_RES_NO
<tag> NO ...
Definition: imap_private.h:52
struct Hash * uid_hash
Definition: imap_private.h:234
struct ListHead flags
Definition: imap_private.h:225
char * name
Definition: imap_private.h:147
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: imap_private.h:173
char * data
Pointer to data.
Definition: buffer.h:35
void imap_unquote_string(char *s)
equally stupid unquoting routine
Definition: util.c:1022
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:773
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1324
char * name
Mailbox name.
Definition: imap_private.h:216
GUI present the user with a selectable list.
Imap connection failure.
Definition: imap_private.h:83
int mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: string.c:292
Ignore case when comparing strings.
Definition: string2.h:68
#define IMAP_LOG_CMD
Definition: imap_private.h:47
WHERE bool C_ImapCheckSubscribed
Config: (imap) When opening a mailbox, ask the server for a list of subscribed folders.
Definition: globals.h:222
#define IMAP_CMD_BUFSIZE
Definition: command.c:57
char seq[SEQ_LEN+1]
Command tag, e.g. &#39;a0001&#39;.
Definition: imap_private.h:158
static void cmd_parse_enabled(struct ImapAccountData *adata, const char *s)
Record what the server has enabled.
Definition: command.c:953
ImapCapFlags capabilities
Definition: imap_private.h:183
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:120
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:349
time_t lastread
last time we read a command for the server
Definition: imap_private.h:186
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: imap_private.h:72
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:95
bool noinferiors
Definition: imap_private.h:150
char * path
Path.
Definition: url.h:73
unsigned int uid_validity
Definition: imap_private.h:226
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
#define IMAP_RES_CONTINUE
* ...
Definition: imap_private.h:55
static void cmd_parse_myrights(struct ImapAccountData *adata, const char *s)
Set rights bits according to MYRIGHTS response.
Definition: command.c:711
char * mutt_str_strcat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:395
static void cmd_parse_lsub(struct ImapAccountData *adata, char *s)
Parse a server LSUB (list subscribed mailboxes)
Definition: command.c:660
IMAP-specific Account data -.
Definition: imap_private.h:167
unsigned int uid_next
Definition: imap_private.h:227
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: imap_private.h:65
Shared constants/structs that are private to IMAP.
Log at debug level 1.
Definition: logging.h:40
IMAP-specific Mailbox data -.
Definition: imap_private.h:214
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
int state
Command state, e.g. IMAP_RES_NEW.
Definition: imap_private.h:159
int imap_get_literal_count(const char *buf, unsigned int *bytes)
write number of bytes in an IMAP literal into bytes
Definition: util.c:893
static void cmd_parse_fetch(struct ImapAccountData *adata, char *s)
Load fetch response into ImapAccountData.
Definition: command.c:395
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: imap_private.h:73
#define mutt_error(...)
Definition: logging.h:84
static void cmd_parse_expunge(struct ImapAccountData *adata, const char *s)
Parse expunge command.
Definition: command.c:256
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:1274
static int cmd_start(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Start a new IMAP command.
Definition: command.c:200
int mutt_seqset_iterator_next(struct SeqsetIterator *iter, unsigned int *next)
Get the next UID from a Sequence Set.
Definition: util.c:1244
Connection Library.
static int cmd_handle_untagged(struct ImapAccountData *adata)
fallback parser for otherwise unhandled messages
Definition: command.c:1011
int index
The absolute (unsorted) message number.
Definition: email.h:85
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: imap_private.h:71
#define FREE(x)
Definition: memory.h:40
#define IMAP_RES_BAD
<tag> BAD ...
Definition: imap_private.h:53
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:423
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: imap_private.h:67
#define MUTT_ACL_ADMIN
Administer the account (get/set permissions)
Definition: mailbox.h:63
int mutt_socket_readln_d(char *buf, size_t buflen, struct Connection *conn, int dbg)
Read a line from a socket.
Definition: socket.c:244
static void cmd_parse_vanished(struct ImapAccountData *adata, char *s)
Parse vanished command.
Definition: command.c:301
#define MUTT_ACL_SEEN
Change the &#39;seen&#39; status of a message.
Definition: mailbox.h:72
struct ImapList * cmdresult
Definition: imap_private.h:195
List of Mailboxes.
Definition: mailbox.h:144
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
unsigned int unseen
Definition: imap_private.h:231
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
NeoMutt connections.
unsigned int recent
Definition: imap_private.h:230
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:565
uint8_t ImapCmdFlags
Flags for imap_exec(), e.g. IMAP_CMD_PASS.
Definition: imap_private.h:70
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
Unrecoverable error occurred.
Definition: imap_private.h:94
Logged out from server.
Definition: imap_private.h:95
Disconnected from server.
Definition: imap_private.h:104
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:638
bool imap_code(const char *s)
Was the command successful.
Definition: command.c:1221
Log at debug level 3.
Definition: logging.h:42
bool noselect
Definition: imap_private.h:149
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:69
unsigned int messages
Definition: imap_private.h:229
UID Sequence Set Iterator.
Definition: imap_private.h:246
static bool cmd_queue_full(struct ImapAccountData *adata)
Is the IMAP command queue full?
Definition: command.c:92
enum CommandResult mutt_parse_rc_line(char *line, struct Buffer *token, struct Buffer *err)
Parse a line of user config.
Definition: init.c:1001
#define IMAP_REOPEN_ALLOW
Allow re-opening a folder upon expunge.
Definition: imap_private.h:64
bool matched
Search matches this Email.
Definition: email.h:68
static void cmd_parse_exists(struct ImapAccountData *adata, const char *pn)
Parse EXISTS message from serer.
Definition: command.c:973
struct Connection * conn
Definition: imap_private.h:169
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:146
#define IMAP_RES_OK
<tag> OK ...
Definition: imap_private.h:54
Imap command failure.
Definition: imap_private.h:82
#define IMAP_RES_RESPOND
+
Definition: imap_private.h:56
static void cmd_parse_status(struct ImapAccountData *adata, char *s)
Parse status from server.
Definition: command.c:826
static int cmd_status(const char *s)
parse response line for tagged OK/NO/BAD
Definition: command.c:236