NeoMutt  2024-02-01-23-g345d7b
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
imap.c
Go to the documentation of this file.
1
42#include "config.h"
43#include <ctype.h>
44#include <limits.h>
45#include <stdbool.h>
46#include <stdint.h>
47#include <stdio.h>
48#include <string.h>
49#include "private.h"
50#include "mutt/lib.h"
51#include "config/lib.h"
52#include "email/lib.h"
53#include "core/lib.h"
54#include "conn/lib.h"
55#include "mutt.h"
56#include "lib.h"
57#include "editor/lib.h"
58#include "history/lib.h"
59#include "parse/lib.h"
60#include "progress/lib.h"
61#include "question/lib.h"
62#include "adata.h"
63#include "auth.h"
64#include "commands.h"
65#include "edata.h"
66#include "external.h"
67#include "hook.h"
68#include "mdata.h"
69#include "msg_set.h"
70#include "msn.h"
71#include "mutt_logging.h"
72#include "mutt_socket.h"
73#include "muttlib.h"
74#include "mx.h"
75#include "sort.h"
76#ifdef ENABLE_NLS
77#include <libintl.h>
78#endif
79
80struct Progress;
81struct stat;
82
86static const struct Command ImapCommands[] = {
87 // clang-format off
88 { "subscribe-to", parse_subscribe_to, 0 },
89 { "unsubscribe-from", parse_unsubscribe_from, 0 },
90 // clang-format on
91};
92
96void imap_init(void)
97{
99}
100
107static int check_capabilities(struct ImapAccountData *adata)
108{
109 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
110 {
111 imap_error("check_capabilities", adata->buf);
112 return -1;
113 }
114
115 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
116 {
117 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
118 return -1;
119 }
120
121 return 0;
122}
123
133static char *get_flags(struct ListHead *hflags, char *s)
134{
135 /* sanity-check string */
136 const size_t plen = mutt_istr_startswith(s, "FLAGS");
137 if (plen == 0)
138 {
139 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
140 return NULL;
141 }
142 s += plen;
143 SKIPWS(s);
144 if (*s != '(')
145 {
146 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
147 return NULL;
148 }
149
150 /* update caller's flags handle */
151 while (*s && (*s != ')'))
152 {
153 s++;
154 SKIPWS(s);
155 const char *flag_word = s;
156 while (*s && (*s != ')') && !isspace(*s))
157 s++;
158 const char ctmp = *s;
159 *s = '\0';
160 if (*flag_word)
161 mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
162 *s = ctmp;
163 }
164
165 /* note bad flags response */
166 if (*s != ')')
167 {
168 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
169 mutt_list_free(hflags);
170
171 return NULL;
172 }
173
174 s++;
175
176 return s;
177}
178
188static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag,
189 const char *str, char *flags, size_t flsize)
190{
191 if (m->rights & aclflag)
192 if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
193 mutt_str_cat(flags, flsize, str);
194}
195
204static bool compare_flags_for_copy(struct Email *e)
205{
206 struct ImapEmailData *edata = e->edata;
207
208 if (e->read != edata->read)
209 return true;
210 if (e->old != edata->old)
211 return true;
212 if (e->flagged != edata->flagged)
213 return true;
214 if (e->replied != edata->replied)
215 return true;
216
217 return false;
218}
219
231static int select_email_uids(struct Email **emails, int num_emails, enum MessageType flag,
232 bool changed, bool invert, struct UidArray *uida)
233{
234 if (!emails || !uida)
235 return -1;
236
237 for (int i = 0; i < num_emails; i++)
238 {
239 struct Email *e = emails[i];
240 if (changed && !e->changed)
241 continue;
242
243 /* don't include pending expunged messages.
244 *
245 * TODO: can we unset active in cmd_parse_expunge() and
246 * cmd_parse_vanished() instead of checking for index != INT_MAX. */
247 if (!e || !e->active || (e->index == INT_MAX))
248 continue;
249
251
252 bool match = false;
253 switch (flag)
254 {
255 case MUTT_DELETED:
256 if (e->deleted != edata->deleted)
257 match = invert ^ e->deleted;
258 break;
259 case MUTT_FLAG:
260 if (e->flagged != edata->flagged)
261 match = invert ^ e->flagged;
262 break;
263 case MUTT_OLD:
264 if (e->old != edata->old)
265 match = invert ^ e->old;
266 break;
267 case MUTT_READ:
268 if (e->read != edata->read)
269 match = invert ^ e->read;
270 break;
271 case MUTT_REPLIED:
272 if (e->replied != edata->replied)
273 match = invert ^ e->replied;
274 break;
275 case MUTT_TRASH:
276 if (e->deleted && !e->purge)
277 match = true;
278 break;
279 default:
280 break;
281 }
282
283 if (match)
284 ARRAY_ADD(uida, edata->uid);
285 }
286
287 return ARRAY_SIZE(uida);
288}
289
301static int sync_helper(struct Mailbox *m, struct Email **emails, int num_emails,
302 AclFlags right, enum MessageType flag, const char *name)
303{
305 if (!adata)
306 return -1;
307
308 if ((m->rights & right) == 0)
309 return 0;
310
311 if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
312 return 0;
313
314 int count = 0;
315 char buf[1024] = { 0 };
316
317 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
318
319 // Set the flag (+FLAGS) on matching emails
320 select_email_uids(emails, num_emails, flag, true, false, &uida);
321 snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
322 int rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
323 if (rc < 0)
324 return rc;
325 count += rc;
326 ARRAY_FREE(&uida);
327
328 // Clear the flag (-FLAGS) on non-matching emails
329 select_email_uids(emails, num_emails, flag, true, true, &uida);
330 buf[0] = '-';
331 rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
332 if (rc < 0)
333 return rc;
334 count += rc;
335 ARRAY_FREE(&uida);
336
337 return count;
338}
339
349static size_t longest_common_prefix(struct Buffer *buf, const char *src, size_t start)
350{
351 size_t pos = start;
352
353 size_t len = buf_len(buf);
354 while ((pos < len) && buf->data[pos] && (buf->data[pos] == src[pos]))
355 pos++;
356 buf->data[pos] = '\0';
357
358 buf_fix_dptr(buf);
359
360 return pos;
361}
362
372static int complete_hosts(struct Buffer *buf)
373{
374 int rc = -1;
375 size_t matchlen;
376
377 matchlen = buf_len(buf);
378 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
380 struct MailboxNode *np = NULL;
381 STAILQ_FOREACH(np, &ml, entries)
382 {
384 continue;
385
386 if (rc)
387 {
389 rc = 0;
390 }
391 else
392 {
393 longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen);
394 }
395 }
397
398#if 0
399 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
400 {
401 struct Url url = { 0 };
402 char urlstr[1024] = { 0 };
403
404 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
405 continue;
406
407 mutt_account_tourl(&conn->account, &url);
408 /* FIXME: how to handle multiple users on the same host? */
409 url.user = NULL;
410 url.path = NULL;
411 url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
412 if (mutt_strn_equal(buf, urlstr, matchlen))
413 {
414 if (rc)
415 {
416 mutt_str_copy(buf, urlstr, buflen);
417 rc = 0;
418 }
419 else
420 {
421 longest_common_prefix(buf, urlstr, matchlen);
422 }
423 }
424 }
425#endif
426
427 return rc;
428}
429
437int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
438{
439 char buf[2048] = { 0 };
440 char mbox[1024] = { 0 };
441
442 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
443 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
444
446 {
447 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
448 return -1;
449 }
450
451 return 0;
452}
453
464int imap_access(const char *path)
465{
466 if (imap_path_status(path, false) >= 0)
467 return 0;
468 return -1;
469}
470
479int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
480{
481 char oldmbox[1024] = { 0 };
482 char newmbox[1024] = { 0 };
483 int rc = 0;
484
485 imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
486 imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
487
488 struct Buffer *buf = buf_pool_get();
489 buf_printf(buf, "RENAME %s %s", oldmbox, newmbox);
490
492 rc = -1;
493
494 buf_pool_release(&buf);
495
496 return rc;
497}
498
506int imap_delete_mailbox(struct Mailbox *m, char *path)
507{
508 char buf[PATH_MAX + 7];
509 char mbox[PATH_MAX] = { 0 };
510 struct Url *url = url_parse(path);
511 if (!url)
512 return -1;
513
515 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
516 url_free(&url);
517 snprintf(buf, sizeof(buf), "DELETE %s", mbox);
519 return -1;
520
521 return 0;
522}
523
529{
530 /* we set status here to let imap_handle_untagged know we _expect_ to
531 * receive a bye response (so it doesn't freak out and close the conn) */
532 if (adata->state == IMAP_DISCONNECTED)
533 {
534 return;
535 }
536
537 adata->status = IMAP_BYE;
538 imap_cmd_start(adata, "LOGOUT");
539 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
540 if ((c_imap_poll_timeout <= 0) ||
541 (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
542 {
544 ; // do nothing
545 }
547 adata->state = IMAP_DISCONNECTED;
548}
549
556{
557 struct Account *np = NULL;
558 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
559 {
560 if (np->type != MUTT_IMAP)
561 continue;
562
563 struct ImapAccountData *adata = np->adata;
564 if (!adata)
565 continue;
566
567 struct Connection *conn = adata->conn;
568 if (!conn || (conn->fd < 0))
569 continue;
570
571 mutt_message(_("Closing connection to %s..."), conn->account.host);
572 imap_logout(np->adata);
574 }
575}
576
591int imap_read_literal(FILE *fp, struct ImapAccountData *adata,
592 unsigned long bytes, struct Progress *progress)
593{
594 char c;
595 bool r = false;
596 struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
597
598 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
599 if (c_debug_level >= IMAP_LOG_LTRL)
600 buf_alloc(&buf, bytes + 1);
601
602 mutt_debug(LL_DEBUG2, "reading %lu bytes\n", bytes);
603
604 for (unsigned long pos = 0; pos < bytes; pos++)
605 {
606 if (mutt_socket_readchar(adata->conn, &c) != 1)
607 {
608 mutt_debug(LL_DEBUG1, "error during read, %lu bytes read\n", pos);
609 adata->status = IMAP_FATAL;
610
611 buf_dealloc(&buf);
612 return -1;
613 }
614
615 if (r && (c != '\n'))
616 fputc('\r', fp);
617
618 if (c == '\r')
619 {
620 r = true;
621 continue;
622 }
623 else
624 {
625 r = false;
626 }
627
628 fputc(c, fp);
629
630 if ((pos % 1024) == 0)
631 progress_update(progress, pos, -1);
632 if (c_debug_level >= IMAP_LOG_LTRL)
633 buf_addch(&buf, c);
634 }
635
636 if (c_debug_level >= IMAP_LOG_LTRL)
637 {
638 mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
639 buf_dealloc(&buf);
640 }
641 return 0;
642}
643
649void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
650{
651 struct ImapMboxData *mdata = imap_mdata_get(m);
653
654 if (!mdata || !edata)
655 return;
656
657 imap_msn_remove(&mdata->msn, edata->msn - 1);
658 edata->msn = 0;
659}
660
670void imap_expunge_mailbox(struct Mailbox *m, bool resort)
671{
673 struct ImapMboxData *mdata = imap_mdata_get(m);
674 if (!adata || !mdata)
675 return;
676
677 struct Email *e = NULL;
678
679#ifdef USE_HCACHE
680 imap_hcache_open(adata, mdata);
681#endif
682
683 for (int i = 0; i < m->msg_count; i++)
684 {
685 e = m->emails[i];
686 if (!e)
687 break;
688
689 if (e->index == INT_MAX)
690 {
691 mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
692
693 e->deleted = true;
694
695 imap_cache_del(m, e);
696#ifdef USE_HCACHE
697 imap_hcache_del(mdata, imap_edata_get(e)->uid);
698#endif
699
700 mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
701
702 imap_edata_free((void **) &e->edata);
703 }
704 else
705 {
706 /* NeoMutt has several places where it turns off e->active as a
707 * hack. For example to avoid FLAG updates, or to exclude from
708 * imap_exec_msg_set.
709 *
710 * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
711 * flag becomes set (e.g. a flag update to a modified header),
712 * this function will be called by imap_cmd_finish().
713 *
714 * The ctx_update_tables() will free and remove these "inactive" headers,
715 * despite that an EXPUNGE was not received for them.
716 * This would result in memory leaks and segfaults due to dangling
717 * pointers in the msn_index and uid_hash.
718 *
719 * So this is another hack to work around the hacks. We don't want to
720 * remove the messages, so make sure active is on. */
721 e->active = true;
722 }
723 }
724
725#ifdef USE_HCACHE
726 imap_hcache_close(mdata);
727#endif
728
730 if (resort)
731 {
733 }
734}
735
743{
744 if (mutt_socket_open(adata->conn) < 0)
745 return -1;
746
747 adata->state = IMAP_CONNECTED;
748
749 if (imap_cmd_step(adata) != IMAP_RES_OK)
750 {
752 return -1;
753 }
754
755 if (mutt_istr_startswith(adata->buf, "* OK"))
756 {
757 if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
758 {
759 goto bail;
760 }
761#ifdef USE_SSL
762 /* Attempt STARTTLS if available and desired. */
763 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
764 if ((adata->conn->ssf == 0) &&
765 (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
766 {
767 enum QuadOption ans;
768
769 if (c_ssl_force_tls)
770 {
771 ans = MUTT_YES;
772 }
773 else if ((ans = query_quadoption(_("Secure connection with TLS?"),
774 NeoMutt->sub, "ssl_starttls")) == MUTT_ABORT)
775 {
776 goto bail;
777 }
778 if (ans == MUTT_YES)
779 {
780 enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
781 // Clear any data after the STARTTLS acknowledgement
782 mutt_socket_empty(adata->conn);
783
784 if (rc == IMAP_EXEC_FATAL)
785 goto bail;
786 if (rc != IMAP_EXEC_ERROR)
787 {
788 if (mutt_ssl_starttls(adata->conn))
789 {
790 mutt_error(_("Could not negotiate TLS connection"));
791 goto bail;
792 }
793 else
794 {
795 /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
796 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
797 goto bail;
798 }
799 }
800 }
801 }
802
803 if (c_ssl_force_tls && (adata->conn->ssf == 0))
804 {
805 mutt_error(_("Encrypted connection unavailable"));
806 goto bail;
807 }
808#endif
809 }
810 else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
811 {
812#ifdef USE_SSL
813 /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
814 * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
815 * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
816 * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
817 * decide whether to abort. Note that if using $tunnel and
818 * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
819 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
820 if ((adata->conn->ssf == 0) && c_ssl_force_tls)
821 {
822 mutt_error(_("Encrypted connection unavailable"));
823 goto bail;
824 }
825#endif
826
827 adata->state = IMAP_AUTHENTICATED;
828 if (check_capabilities(adata) != 0)
829 goto bail;
830 FREE(&adata->capstr);
831 }
832 else
833 {
834 imap_error("imap_open_connection()", adata->buf);
835 goto bail;
836 }
837
838 return 0;
839
840bail:
842 FREE(&adata->capstr);
843 return -1;
844}
845
851{
852 if (adata->state != IMAP_DISCONNECTED)
853 {
854 mutt_socket_close(adata->conn);
855 adata->state = IMAP_DISCONNECTED;
856 }
857 adata->seqno = 0;
858 adata->nextcmd = 0;
859 adata->lastcmd = 0;
860 adata->status = 0;
861 memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
862}
863
875bool imap_has_flag(struct ListHead *flag_list, const char *flag)
876{
877 if (STAILQ_EMPTY(flag_list))
878 return false;
879
880 const size_t flaglen = mutt_str_len(flag);
881 struct ListNode *np = NULL;
882 STAILQ_FOREACH(np, flag_list, entries)
883 {
884 const size_t nplen = strlen(np->data);
885 if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
886 mutt_istrn_equal(np->data, flag, nplen))
887 {
888 return true;
889 }
890
891 if (mutt_str_equal(np->data, "\\*"))
892 return true;
893 }
894
895 return false;
896}
897
901static int imap_sort_email_uid(const void *a, const void *b, void *sdata)
902{
903 const struct Email *ea = *(struct Email const *const *) a;
904 const struct Email *eb = *(struct Email const *const *) b;
905
906 const unsigned int ua = imap_edata_get((struct Email *) ea)->uid;
907 const unsigned int ub = imap_edata_get((struct Email *) eb)->uid;
908
909 return mutt_numeric_cmp(ua, ub);
910}
911
927int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e,
928 struct Buffer *cmd, enum QuadOption *err_continue)
929{
931 if (!adata || (adata->mailbox != m))
932 return -1;
933
934 char flags[1024] = { 0 };
935 char uid[11] = { 0 };
936
938 {
939 if (e->deleted == imap_edata_get(e)->deleted)
940 e->changed = false;
941 return 0;
942 }
943
944 snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
945 buf_reset(cmd);
946 buf_addstr(cmd, "UID STORE ");
947 buf_addstr(cmd, uid);
948
949 flags[0] = '\0';
950
951 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags, sizeof(flags));
952 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags, sizeof(flags));
953 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags, sizeof(flags));
954 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags, sizeof(flags));
955 set_flag(m, MUTT_ACL_DELETE, imap_edata_get(e)->deleted, "\\Deleted ", flags,
956 sizeof(flags));
957
958 if (m->rights & MUTT_ACL_WRITE)
959 {
960 /* restore system flags */
961 if (imap_edata_get(e)->flags_system)
962 mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_system);
963 /* set custom flags */
964 struct Buffer *tags = buf_pool_get();
966 if (!buf_is_empty(tags))
967 {
968 mutt_str_cat(flags, sizeof(flags), buf_string(tags));
969 }
970 buf_pool_release(&tags);
971 }
972
974
975 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
976 * explicitly revoke all system flags (if we have permission) */
977 if (*flags == '\0')
978 {
979 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags, sizeof(flags));
980 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags, sizeof(flags));
981 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags, sizeof(flags));
982 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags, sizeof(flags));
983 set_flag(m, MUTT_ACL_DELETE, !imap_edata_get(e)->deleted, "\\Deleted ",
984 flags, sizeof(flags));
985
986 /* erase custom flags */
987 if ((m->rights & MUTT_ACL_WRITE) && imap_edata_get(e)->flags_remote)
988 mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_remote);
989
991
992 buf_addstr(cmd, " -FLAGS.SILENT (");
993 }
994 else
995 {
996 buf_addstr(cmd, " FLAGS.SILENT (");
997 }
998
999 buf_addstr(cmd, flags);
1000 buf_addstr(cmd, ")");
1001
1002 /* after all this it's still possible to have no flags, if you
1003 * have no ACL rights */
1004 if (*flags && (imap_exec(adata, cmd->data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS) &&
1005 err_continue && (*err_continue != MUTT_YES))
1006 {
1007 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1008 if (*err_continue != MUTT_YES)
1009 return -1;
1010 }
1011
1012 /* server have now the updated flags */
1013 FREE(&imap_edata_get(e)->flags_remote);
1014 struct Buffer *flags_remote = buf_pool_get();
1015 driver_tags_get_with_hidden(&e->tags, flags_remote);
1016 imap_edata_get(e)->flags_remote = buf_strdup(flags_remote);
1017 buf_pool_release(&flags_remote);
1018
1019 if (e->deleted == imap_edata_get(e)->deleted)
1020 e->changed = false;
1021
1022 return 0;
1023}
1024
1031enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
1032{
1033 if (!m || !m->account)
1034 return MX_STATUS_ERROR;
1035
1037 struct ImapMboxData *mdata = imap_mdata_get(m);
1038
1039 /* overload keyboard timeout to avoid many mailbox checks in a row.
1040 * Most users don't like having to wait exactly when they press a key. */
1041 int rc = 0;
1042
1043 /* try IDLE first, unless force is set */
1044 const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1045 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
1046 if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1047 ((adata->state != IMAP_IDLE) || (mutt_date_now() >= adata->lastread + c_imap_keep_alive)))
1048 {
1049 if (imap_cmd_idle(adata) < 0)
1050 return MX_STATUS_ERROR;
1051 }
1052 if (adata->state == IMAP_IDLE)
1053 {
1054 while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1055 {
1056 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1057 {
1058 mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1059 return MX_STATUS_ERROR;
1060 }
1061 }
1062 if (rc < 0)
1063 {
1064 mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1065 adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1066 }
1067 }
1068
1069 const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1070 if ((force || ((adata->state != IMAP_IDLE) && (mutt_date_now() >= adata->lastread + c_timeout))) &&
1071 (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1072 {
1073 return MX_STATUS_ERROR;
1074 }
1075
1076 /* We call this even when we haven't run NOOP in case we have pending
1077 * changes to process, since we can reopen here. */
1078 imap_cmd_finish(adata);
1079
1080 enum MxStatus check = MX_STATUS_OK;
1081 if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1082 check = MX_STATUS_REOPENED;
1083 else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1084 check = MX_STATUS_NEW_MAIL;
1085 else if (mdata->check_status & IMAP_FLAGS_PENDING)
1086 check = MX_STATUS_FLAGS;
1087 else if (rc < 0)
1088 check = MX_STATUS_ERROR;
1089
1090 mdata->check_status = IMAP_OPEN_NO_FLAGS;
1091
1092 if (force)
1093 m->last_checked = 0; // force a check on the next mx_mbox_check() call
1094 return check;
1095}
1096
1104static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
1105{
1106 char *uidvalidity_flag = NULL;
1107 char cmd[2048] = { 0 };
1108
1109 if (!adata || !mdata)
1110 return -1;
1111
1112 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1113 * IDLEd elsewhere.
1114 * adata->mailbox may be NULL for connections other than the current
1115 * mailbox's. */
1116 if (adata->mailbox && (adata->mailbox->mdata == mdata))
1117 {
1118 adata->mailbox->has_new = false;
1119 return mdata->messages;
1120 }
1121
1122 if (adata->mailbox && !adata->mailbox->poll_new_mail)
1123 return mdata->messages;
1124
1125 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1126 {
1127 uidvalidity_flag = "UIDVALIDITY";
1128 }
1129 else if (adata->capabilities & IMAP_CAP_STATUS)
1130 {
1131 uidvalidity_flag = "UID-VALIDITY";
1132 }
1133 else
1134 {
1135 mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1136 return -1;
1137 }
1138
1139 snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1140 mdata->munge_name, uidvalidity_flag);
1141
1142 int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_POLL);
1143 if (rc != IMAP_EXEC_SUCCESS)
1144 {
1145 mutt_debug(LL_DEBUG1, "Error queueing command\n");
1146 return rc;
1147 }
1148 return mdata->messages;
1149}
1150
1154static enum MxStatus imap_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1155{
1156 const bool queue = (flags & MUTT_MAILBOX_CHECK_IMMEDIATE) == 0;
1157 const int new_msgs = imap_mailbox_status(m, queue);
1158 if (new_msgs == -1)
1159 return MX_STATUS_ERROR;
1160 if (new_msgs == 0)
1161 return MX_STATUS_OK;
1162 return MX_STATUS_NEW_MAIL;
1163}
1164
1171int imap_path_status(const char *path, bool queue)
1172{
1173 struct Mailbox *m = mx_mbox_find2(path);
1174
1175 const bool is_temp = !m;
1176 if (is_temp)
1177 {
1178 m = mx_path_resolve(path);
1179 if (!mx_mbox_ac_link(m))
1180 {
1181 mailbox_free(&m);
1182 return 0;
1183 }
1184 }
1185
1186 int rc = imap_mailbox_status(m, queue);
1187
1188 if (is_temp)
1189 {
1190 mx_ac_remove(m, false);
1191 mailbox_free(&m);
1192 }
1193
1194 return rc;
1195}
1196
1206int imap_mailbox_status(struct Mailbox *m, bool queue)
1207{
1209 struct ImapMboxData *mdata = imap_mdata_get(m);
1210 if (!adata || !mdata)
1211 return -1;
1212 return imap_status(adata, mdata, queue);
1213}
1214
1222int imap_subscribe(char *path, bool subscribe)
1223{
1224 struct ImapAccountData *adata = NULL;
1225 struct ImapMboxData *mdata = NULL;
1226
1227 if (imap_adata_find(path, &adata, &mdata) < 0)
1228 return -1;
1229
1230 if (subscribe)
1231 mutt_message(_("Subscribing to %s..."), mdata->name);
1232 else
1233 mutt_message(_("Unsubscribing from %s..."), mdata->name);
1234
1235 char buf[2048] = { 0 };
1236 snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1237
1238 if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1239 {
1240 imap_mdata_free((void *) &mdata);
1241 return -1;
1242 }
1243
1244 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1245 if (c_imap_check_subscribed)
1246 {
1247 char mbox[1024] = { 0 };
1248 size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1249 imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1250 struct Buffer *err = buf_pool_get();
1251 if (parse_rc_line(mbox, err))
1252 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
1253 buf_pool_release(&err);
1254 }
1255
1256 if (subscribe)
1257 mutt_message(_("Subscribed to %s"), mdata->name);
1258 else
1259 mutt_message(_("Unsubscribed from %s"), mdata->name);
1260 imap_mdata_free((void *) &mdata);
1261 return 0;
1262}
1263
1274int imap_complete(struct Buffer *buf, const char *path)
1275{
1276 struct ImapAccountData *adata = NULL;
1277 struct ImapMboxData *mdata = NULL;
1278 char tmp[2048] = { 0 };
1279 struct ImapList listresp = { 0 };
1280 struct Buffer *completion_buf = NULL;
1281 size_t clen;
1282 int completions = 0;
1283 int rc;
1284
1285 if (imap_adata_find(path, &adata, &mdata) < 0)
1286 {
1287 buf_strcpy(buf, path);
1288 return complete_hosts(buf);
1289 }
1290
1291 /* fire off command */
1292 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1293 snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1294 c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1295
1296 imap_cmd_start(adata, tmp);
1297
1298 /* and see what the results are */
1299 completion_buf = buf_pool_get();
1300 buf_strcpy(completion_buf, mdata->name);
1301 imap_mdata_free((void *) &mdata);
1302
1303 adata->cmdresult = &listresp;
1304 do
1305 {
1306 listresp.name = NULL;
1307 rc = imap_cmd_step(adata);
1308
1309 if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1310 {
1311 /* if the folder isn't selectable, append delimiter to force browse
1312 * to enter it on second tab. */
1313 if (listresp.noselect)
1314 {
1315 clen = strlen(listresp.name);
1316 listresp.name[clen++] = listresp.delim;
1317 listresp.name[clen] = '\0';
1318 }
1319 /* copy in first word */
1320 if (!completions)
1321 {
1322 buf_strcpy(completion_buf, listresp.name);
1323 completions++;
1324 continue;
1325 }
1326
1327 longest_common_prefix(completion_buf, listresp.name, 0);
1328 completions++;
1329 }
1330 } while (rc == IMAP_RES_CONTINUE);
1331 adata->cmdresult = NULL;
1332
1333 if (completions)
1334 {
1335 /* reformat output */
1336 imap_buf_qualify_path(buf, &adata->conn->account, completion_buf->data);
1337 buf_pretty_mailbox(buf);
1338 buf_fix_dptr(buf);
1339 buf_pool_release(&completion_buf);
1340 return 0;
1341 }
1342
1343 buf_pool_release(&completion_buf);
1344 return -1;
1345}
1346
1355int imap_fast_trash(struct Mailbox *m, const char *dest)
1356{
1357 char prompt[1024] = { 0 };
1358 int rc = -1;
1359 bool triedcreate = false;
1360 enum QuadOption err_continue = MUTT_NO;
1361
1363 struct ImapAccountData *dest_adata = NULL;
1364 struct ImapMboxData *dest_mdata = NULL;
1365
1366 if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1367 return -1;
1368
1369 struct Buffer sync_cmd = buf_make(0);
1370
1371 /* check that the save-to folder is in the same account */
1372 if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1373 {
1374 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1375 goto out;
1376 }
1377
1378 for (int i = 0; i < m->msg_count; i++)
1379 {
1380 struct Email *e = m->emails[i];
1381 if (!e)
1382 break;
1383 if (e->active && e->changed && e->deleted && !e->purge)
1384 {
1385 rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
1386 if (rc < 0)
1387 {
1388 mutt_debug(LL_DEBUG1, "could not sync\n");
1389 goto out;
1390 }
1391 }
1392 }
1393
1394 /* loop in case of TRYCREATE */
1395 do
1396 {
1397 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1398 select_email_uids(m->emails, m->msg_count, MUTT_TRASH, false, false, &uida);
1399 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1400 rc = imap_exec_msg_set(adata, "UID COPY", dest_mdata->munge_name, &uida);
1401 if (rc == 0)
1402 {
1403 mutt_debug(LL_DEBUG1, "No messages to trash\n");
1404 rc = -1;
1405 goto out;
1406 }
1407 else if (rc < 0)
1408 {
1409 mutt_debug(LL_DEBUG1, "could not queue copy\n");
1410 goto out;
1411 }
1412 else if (m->verbose)
1413 {
1414 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1415 rc, dest_mdata->name);
1416 }
1417 ARRAY_FREE(&uida);
1418
1419 /* let's get it on */
1420 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1421 if (rc == IMAP_EXEC_ERROR)
1422 {
1423 if (triedcreate)
1424 {
1425 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1426 break;
1427 }
1428 /* bail out if command failed for reasons other than nonexistent target */
1429 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1430 break;
1431 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1432 snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1433 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1434 if (c_confirm_create &&
1435 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1436 {
1438 goto out;
1439 }
1440 if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1441 break;
1442 triedcreate = true;
1443 }
1444 } while (rc == IMAP_EXEC_ERROR);
1445
1446 if (rc != IMAP_EXEC_SUCCESS)
1447 {
1448 imap_error("imap_fast_trash", adata->buf);
1449 goto out;
1450 }
1451
1452 rc = IMAP_EXEC_SUCCESS;
1453
1454out:
1455 buf_dealloc(&sync_cmd);
1456 imap_mdata_free((void *) &dest_mdata);
1457
1458 return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1459}
1460
1470enum MxStatus imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
1471{
1472 if (!m)
1473 return -1;
1474
1475 struct Email **emails = NULL;
1476 int rc;
1477
1479 struct ImapMboxData *mdata = imap_mdata_get(m);
1480
1481 if (adata->state < IMAP_SELECTED)
1482 {
1483 mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1484 return -1;
1485 }
1486
1487 /* This function is only called when the calling code expects the context
1488 * to be changed. */
1490
1491 enum MxStatus check = imap_check_mailbox(m, false);
1492 if (check == MX_STATUS_ERROR)
1493 return check;
1494
1495 /* if we are expunging anyway, we can do deleted messages very quickly... */
1496 if (expunge && (m->rights & MUTT_ACL_DELETE))
1497 {
1498 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1499 select_email_uids(m->emails, m->msg_count, MUTT_DELETED, true, false, &uida);
1500 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1501 rc = imap_exec_msg_set(adata, "UID STORE", "+FLAGS.SILENT (\\Deleted)", &uida);
1502 ARRAY_FREE(&uida);
1503 if (rc < 0)
1504 {
1505 mutt_error(_("Expunge failed"));
1506 return rc;
1507 }
1508
1509 if (rc > 0)
1510 {
1511 /* mark these messages as unchanged so second pass ignores them. Done
1512 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1513 for (int i = 0; i < m->msg_count; i++)
1514 {
1515 struct Email *e = m->emails[i];
1516 if (!e)
1517 break;
1518 if (e->deleted && e->changed)
1519 e->active = false;
1520 }
1521 if (m->verbose)
1522 {
1523 mutt_message(ngettext("Marking %d message deleted...",
1524 "Marking %d messages deleted...", rc),
1525 rc);
1526 }
1527 }
1528 }
1529
1530#ifdef USE_HCACHE
1531 imap_hcache_open(adata, mdata);
1532#endif
1533
1534 /* save messages with real (non-flag) changes */
1535 for (int i = 0; i < m->msg_count; i++)
1536 {
1537 struct Email *e = m->emails[i];
1538 if (!e)
1539 break;
1540
1541 if (e->deleted)
1542 {
1543 imap_cache_del(m, e);
1544#ifdef USE_HCACHE
1545 imap_hcache_del(mdata, imap_edata_get(e)->uid);
1546#endif
1547 }
1548
1549 if (e->active && e->changed)
1550 {
1551#ifdef USE_HCACHE
1552 imap_hcache_put(mdata, e);
1553#endif
1554 /* if the message has been rethreaded or attachments have been deleted
1555 * we delete the message and reupload it.
1556 * This works better if we're expunging, of course. */
1557 if (e->env->changed || e->attach_del)
1558 {
1559 /* L10N: The plural is chosen by the last %d, i.e. the total number */
1560 if (m->verbose)
1561 {
1562 mutt_message(ngettext("Saving changed message... [%d/%d]",
1563 "Saving changed messages... [%d/%d]", m->msg_count),
1564 i + 1, m->msg_count);
1565 }
1566 bool save_append = m->append;
1567 m->append = true;
1569 m->append = save_append;
1570 e->env->changed = false;
1571 }
1572 }
1573 }
1574
1575#ifdef USE_HCACHE
1576 imap_hcache_close(mdata);
1577#endif
1578
1579 /* presort here to avoid doing 10 resorts in imap_exec_msg_set */
1580 emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1581 memcpy(emails, m->emails, m->msg_count * sizeof(struct Email *));
1582 mutt_qsort_r(emails, m->msg_count, sizeof(struct Email *), imap_sort_email_uid, NULL);
1583
1584 rc = sync_helper(m, emails, m->msg_count, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1585 if (rc >= 0)
1586 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1587 if (rc >= 0)
1588 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1589 if (rc >= 0)
1590 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1591 if (rc >= 0)
1592 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1593
1594 FREE(&emails);
1595
1596 /* Flush the queued flags if any were changed in sync_helper. */
1597 if (rc > 0)
1598 if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1599 rc = -1;
1600
1601 if (rc < 0)
1602 {
1603 if (close)
1604 {
1605 if (query_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1606 {
1607 adata->state = IMAP_AUTHENTICATED;
1608 return 0;
1609 }
1610 }
1611 else
1612 {
1613 mutt_error(_("Error saving flags"));
1614 }
1615 return -1;
1616 }
1617
1618 /* Update local record of server state to reflect the synchronization just
1619 * completed. imap_read_headers always overwrites hcache-origin flags, so
1620 * there is no need to mutate the hcache after flag-only changes. */
1621 for (int i = 0; i < m->msg_count; i++)
1622 {
1623 struct Email *e = m->emails[i];
1624 if (!e)
1625 break;
1626 struct ImapEmailData *edata = imap_edata_get(e);
1627 edata->deleted = e->deleted;
1628 edata->flagged = e->flagged;
1629 edata->old = e->old;
1630 edata->read = e->read;
1631 edata->replied = e->replied;
1632 e->changed = false;
1633 }
1634 m->changed = false;
1635
1636 /* We must send an EXPUNGE command if we're not closing. */
1637 if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1638 {
1639 if (m->verbose)
1640 mutt_message(_("Expunging messages from server..."));
1641 /* Set expunge bit so we don't get spurious reopened messages */
1642 mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1643 if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1644 {
1645 mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1646 imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1647 return -1;
1648 }
1649 mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1650 }
1651
1652 if (expunge && close)
1653 {
1654 adata->closing = true;
1655 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
1656 adata->state = IMAP_AUTHENTICATED;
1657 }
1658
1659 const bool c_message_cache_clean = cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1660 if (c_message_cache_clean)
1662
1663 return check;
1664}
1665
1669static bool imap_ac_owns_path(struct Account *a, const char *path)
1670{
1671 struct Url *url = url_parse(path);
1672 if (!url)
1673 return false;
1674
1675 struct ImapAccountData *adata = a->adata;
1676 struct ConnAccount *cac = &adata->conn->account;
1677
1678 const bool rc = mutt_istr_equal(url->host, cac->host) &&
1679 (!url->user || mutt_istr_equal(url->user, cac->user));
1680 url_free(&url);
1681 return rc;
1682}
1683
1687static bool imap_ac_add(struct Account *a, struct Mailbox *m)
1688{
1689 struct ImapAccountData *adata = a->adata;
1690
1691 if (!adata)
1692 {
1693 struct ConnAccount cac = { { 0 } };
1694 char mailbox[PATH_MAX] = { 0 };
1695
1696 if (imap_parse_path(mailbox_path(m), &cac, mailbox, sizeof(mailbox)) < 0)
1697 return false;
1698
1699 adata = imap_adata_new(a);
1700 adata->conn = mutt_conn_new(&cac);
1701 if (!adata->conn)
1702 {
1703 imap_adata_free((void **) &adata);
1704 return false;
1705 }
1706
1708
1709 if (imap_login(adata) < 0)
1710 {
1711 imap_adata_free((void **) &adata);
1712 return false;
1713 }
1714
1715 a->adata = adata;
1717 }
1718
1719 if (!m->mdata)
1720 {
1721 struct Url *url = url_parse(mailbox_path(m));
1722 if (!url)
1723 return false;
1724 struct ImapMboxData *mdata = imap_mdata_new(adata, url->path);
1725
1726 /* fixup path and realpath, mainly to replace / by /INBOX */
1727 char buf[1024] = { 0 };
1728 imap_qualify_path(buf, sizeof(buf), &adata->conn->account, mdata->name);
1729 buf_strcpy(&m->pathbuf, buf);
1731
1732 m->mdata = mdata;
1734 url_free(&url);
1735 }
1736 return true;
1737}
1738
1743static void imap_mbox_select(struct Mailbox *m)
1744{
1746 struct ImapMboxData *mdata = imap_mdata_get(m);
1747 if (!adata || !mdata)
1748 return;
1749
1750 const char *condstore = NULL;
1751#ifdef USE_HCACHE
1752 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1753 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1754 condstore = " (CONDSTORE)";
1755 else
1756#endif
1757 condstore = "";
1758
1759 char buf[PATH_MAX] = { 0 };
1760 snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1761 mdata->munge_name, condstore);
1762
1763 adata->state = IMAP_SELECTED;
1764
1765 imap_cmd_start(adata, buf);
1766}
1767
1776int imap_login(struct ImapAccountData *adata)
1777{
1778 if (!adata)
1779 return -1;
1780
1781 if (adata->state == IMAP_DISCONNECTED)
1782 {
1783 buf_reset(&adata->cmdbuf); // purge outstanding queued commands
1784 imap_open_connection(adata);
1785 }
1786 if (adata->state == IMAP_CONNECTED)
1787 {
1789 {
1790 adata->state = IMAP_AUTHENTICATED;
1791 FREE(&adata->capstr);
1792 if (adata->conn->ssf != 0)
1793 {
1794 mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1795 adata->conn->ssf);
1796 }
1797 }
1798 else
1799 {
1801 }
1802 }
1803 if (adata->state == IMAP_AUTHENTICATED)
1804 {
1805 /* capabilities may have changed */
1806 imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1807
1808#ifdef USE_ZLIB
1809 /* RFC4978 */
1810 const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1811 if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1812 (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1813 {
1814 mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1815 adata->conn->account.host);
1816 mutt_zstrm_wrap_conn(adata->conn);
1817 }
1818#endif
1819
1820 /* enable RFC2971, if the server supports that */
1821 const bool c_imap_send_id = cs_subset_bool(NeoMutt->sub, "imap_send_id");
1822 if (c_imap_send_id && (adata->capabilities & IMAP_CAP_ID))
1823 {
1824 imap_exec(adata, "ID (\"name\" \"NeoMutt\" \"version\" \"" PACKAGE_VERSION "\")",
1826 }
1827
1828 /* enable RFC6855, if the server supports that */
1829 const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1830 if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1831 imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1832
1833 /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1834 * is supported (even if not advertised), so flip that bit. */
1835 if (adata->capabilities & IMAP_CAP_QRESYNC)
1836 {
1838 const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1839 if (c_imap_rfc5161 && c_imap_qresync)
1840 imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1841 }
1842
1843 /* get root delimiter, '/' as default */
1844 adata->delim = '/';
1845 imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1846
1847 /* we may need the root delimiter before we open a mailbox */
1848 imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1849
1850 /* select the mailbox that used to be open before disconnect */
1851 if (adata->mailbox)
1852 {
1853 imap_mbox_select(adata->mailbox);
1854 }
1855 }
1856
1857 if (adata->state < IMAP_AUTHENTICATED)
1858 return -1;
1859
1860 return 0;
1861}
1862
1867{
1868 if (!m->account || !m->mdata)
1869 return MX_OPEN_ERROR;
1870
1871 char buf[PATH_MAX] = { 0 };
1872 int count = 0;
1873 int rc;
1874
1876 struct ImapMboxData *mdata = imap_mdata_get(m);
1877
1878 mutt_debug(LL_DEBUG3, "opening %s, saving %s\n", m->pathbuf.data,
1879 (adata->mailbox ? adata->mailbox->pathbuf.data : "(none)"));
1880 adata->prev_mailbox = adata->mailbox;
1881 adata->mailbox = m;
1882
1883 /* clear mailbox status */
1884 adata->status = 0;
1885 m->rights = 0;
1886 mdata->new_mail_count = 0;
1887
1888 if (m->verbose)
1889 mutt_message(_("Selecting %s..."), mdata->name);
1890
1891 /* pipeline ACL test */
1892 if (adata->capabilities & IMAP_CAP_ACL)
1893 {
1894 snprintf(buf, sizeof(buf), "MYRIGHTS %s", mdata->munge_name);
1895 imap_exec(adata, buf, IMAP_CMD_QUEUE);
1896 }
1897 else
1898 {
1899 /* assume we have all rights if ACL is unavailable */
1902 }
1903
1904 /* pipeline the postponed count if possible */
1905 const char *const c_postponed = cs_subset_string(NeoMutt->sub, "postponed");
1906 struct Mailbox *m_postponed = mx_mbox_find2(c_postponed);
1907 struct ImapAccountData *postponed_adata = imap_adata_get(m_postponed);
1908 if (postponed_adata &&
1909 imap_account_match(&postponed_adata->conn->account, &adata->conn->account))
1910 {
1911 imap_mailbox_status(m_postponed, true);
1912 }
1913
1914 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1915 if (c_imap_check_subscribed)
1916 imap_exec(adata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE);
1917
1919
1920 do
1921 {
1922 char *pc = NULL;
1923
1924 rc = imap_cmd_step(adata);
1925 if (rc != IMAP_RES_CONTINUE)
1926 break;
1927
1928 pc = adata->buf + 2;
1929
1930 /* Obtain list of available flags here, may be overridden by a
1931 * PERMANENTFLAGS tag in the OK response */
1932 if (mutt_istr_startswith(pc, "FLAGS"))
1933 {
1934 /* don't override PERMANENTFLAGS */
1935 if (STAILQ_EMPTY(&mdata->flags))
1936 {
1937 mutt_debug(LL_DEBUG3, "Getting mailbox FLAGS\n");
1938 pc = get_flags(&mdata->flags, pc);
1939 if (!pc)
1940 goto fail;
1941 }
1942 }
1943 else if (mutt_istr_startswith(pc, "OK [PERMANENTFLAGS"))
1944 {
1945 /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
1946 mutt_debug(LL_DEBUG3, "Getting mailbox PERMANENTFLAGS\n");
1947 /* safe to call on NULL */
1948 mutt_list_free(&mdata->flags);
1949 /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
1950 pc += 13;
1951 pc = get_flags(&(mdata->flags), pc);
1952 if (!pc)
1953 goto fail;
1954 }
1955 else if (mutt_istr_startswith(pc, "OK [UIDVALIDITY"))
1956 {
1957 /* save UIDVALIDITY for the header cache */
1958 mutt_debug(LL_DEBUG3, "Getting mailbox UIDVALIDITY\n");
1959 pc += 3;
1960 pc = imap_next_word(pc);
1961 if (!mutt_str_atoui(pc, &mdata->uidvalidity))
1962 goto fail;
1963 }
1964 else if (mutt_istr_startswith(pc, "OK [UIDNEXT"))
1965 {
1966 mutt_debug(LL_DEBUG3, "Getting mailbox UIDNEXT\n");
1967 pc += 3;
1968 pc = imap_next_word(pc);
1969 if (!mutt_str_atoui(pc, &mdata->uid_next))
1970 goto fail;
1971 }
1972 else if (mutt_istr_startswith(pc, "OK [HIGHESTMODSEQ"))
1973 {
1974 mutt_debug(LL_DEBUG3, "Getting mailbox HIGHESTMODSEQ\n");
1975 pc += 3;
1976 pc = imap_next_word(pc);
1977 if (!mutt_str_atoull(pc, &mdata->modseq))
1978 goto fail;
1979 }
1980 else if (mutt_istr_startswith(pc, "OK [NOMODSEQ"))
1981 {
1982 mutt_debug(LL_DEBUG3, "Mailbox has NOMODSEQ set\n");
1983 mdata->modseq = 0;
1984 }
1985 else
1986 {
1987 pc = imap_next_word(pc);
1988 if (mutt_istr_startswith(pc, "EXISTS"))
1989 {
1990 count = mdata->new_mail_count;
1991 mdata->new_mail_count = 0;
1992 }
1993 }
1994 } while (rc == IMAP_RES_CONTINUE);
1995
1996 if (rc == IMAP_RES_NO)
1997 {
1998 char *s = imap_next_word(adata->buf); /* skip seq */
1999 s = imap_next_word(s); /* Skip response */
2000 mutt_error("%s", s);
2001 goto fail;
2002 }
2003
2004 if (rc != IMAP_RES_OK)
2005 goto fail;
2006
2007 /* check for READ-ONLY notification */
2008 if (mutt_istr_startswith(imap_get_qualifier(adata->buf), "[READ-ONLY]") &&
2009 !(adata->capabilities & IMAP_CAP_ACL))
2010 {
2011 mutt_debug(LL_DEBUG2, "Mailbox is read-only\n");
2012 m->readonly = true;
2013 }
2014
2015 /* dump the mailbox flags we've found */
2016 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
2017 if (c_debug_level > LL_DEBUG2)
2018 {
2019 if (STAILQ_EMPTY(&mdata->flags))
2020 {
2021 mutt_debug(LL_DEBUG3, "No folder flags found\n");
2022 }
2023 else
2024 {
2025 struct ListNode *np = NULL;
2026 struct Buffer *flag_buffer = buf_pool_get();
2027 buf_printf(flag_buffer, "Mailbox flags: ");
2028 STAILQ_FOREACH(np, &mdata->flags, entries)
2029 {
2030 buf_add_printf(flag_buffer, "[%s] ", np->data);
2031 }
2032 mutt_debug(LL_DEBUG3, "%s\n", buf_string(flag_buffer));
2033 buf_pool_release(&flag_buffer);
2034 }
2035 }
2036
2037 if (!((m->rights & MUTT_ACL_DELETE) || (m->rights & MUTT_ACL_SEEN) ||
2038 (m->rights & MUTT_ACL_WRITE) || (m->rights & MUTT_ACL_INSERT)))
2039 {
2040 m->readonly = true;
2041 }
2042
2043 mx_alloc_memory(m, count);
2044
2045 m->msg_count = 0;
2046 m->msg_unread = 0;
2047 m->msg_flagged = 0;
2048 m->msg_new = 0;
2049 m->msg_deleted = 0;
2050 m->size = 0;
2051 m->vcount = 0;
2052
2053 if ((count > 0) && (imap_read_headers(m, 1, count, true) < 0))
2054 {
2055 mutt_error(_("Error opening mailbox"));
2056 goto fail;
2057 }
2058
2059 mutt_debug(LL_DEBUG2, "msg_count is %d\n", m->msg_count);
2060 return MX_OPEN_OK;
2061
2062fail:
2063 if (adata->state == IMAP_SELECTED)
2064 adata->state = IMAP_AUTHENTICATED;
2065 return MX_OPEN_ERROR;
2066}
2067
2072{
2073 if (!m->account)
2074 return false;
2075
2076 /* in APPEND mode, we appear to hijack an existing IMAP connection -
2077 * mailbox is brand new and mostly empty */
2079 struct ImapMboxData *mdata = imap_mdata_get(m);
2080
2081 int rc = imap_mailbox_status(m, false);
2082 if (rc >= 0)
2083 return true;
2084 if (rc == -1)
2085 return false;
2086
2087 char buf[PATH_MAX + 64];
2088 snprintf(buf, sizeof(buf), _("Create %s?"), mdata->name);
2089 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
2090 if (c_confirm_create &&
2091 (query_yesorno_help(buf, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
2092 return false;
2093
2094 if (imap_create_mailbox(adata, mdata->name) < 0)
2095 return false;
2096
2097 return true;
2098}
2099
2106static enum MxStatus imap_mbox_check(struct Mailbox *m)
2107{
2109 enum MxStatus rc = imap_check_mailbox(m, false);
2110 /* NOTE - mv might have been changed at this point. In particular,
2111 * m could be NULL. Beware. */
2113
2114 return rc;
2115}
2116
2120static enum MxStatus imap_mbox_close(struct Mailbox *m)
2121{
2123 struct ImapMboxData *mdata = imap_mdata_get(m);
2124
2125 /* Check to see if the mailbox is actually open */
2126 if (!adata || !mdata)
2127 return MX_STATUS_OK;
2128
2129 /* imap_mbox_open_append() borrows the struct ImapAccountData temporarily,
2130 * just for the connection.
2131 *
2132 * So when these are equal, it means we are actually closing the
2133 * mailbox and should clean up adata. Otherwise, we don't want to
2134 * touch adata - it's still being used. */
2135 if (m == adata->mailbox)
2136 {
2137 if ((adata->status != IMAP_FATAL) && (adata->state >= IMAP_SELECTED))
2138 {
2139 /* mx_mbox_close won't sync if there are no deleted messages
2140 * and the mailbox is unchanged, so we may have to close here */
2141 if (m->msg_deleted == 0)
2142 {
2143 adata->closing = true;
2144 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
2145 }
2146 adata->state = IMAP_AUTHENTICATED;
2147 }
2148
2149 mutt_debug(LL_DEBUG3, "closing %s, restoring %s\n", m->pathbuf.data,
2150 (adata->prev_mailbox ? adata->prev_mailbox->pathbuf.data : "(none)"));
2151 adata->mailbox = adata->prev_mailbox;
2154 }
2155
2156 return MX_STATUS_OK;
2157}
2158
2162static bool imap_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
2163{
2164 bool success = false;
2165
2166 struct Buffer *tmp = buf_pool_get();
2167 buf_mktemp(tmp);
2168
2169 msg->fp = mutt_file_fopen(buf_string(tmp), "w");
2170 if (!msg->fp)
2171 {
2172 mutt_perror("%s", buf_string(tmp));
2173 goto cleanup;
2174 }
2175
2176 msg->path = buf_strdup(tmp);
2177 success = true;
2178
2179cleanup:
2180 buf_pool_release(&tmp);
2181 return success;
2182}
2183
2187static int imap_tags_edit(struct Mailbox *m, const char *tags, struct Buffer *buf)
2188{
2189 struct ImapMboxData *mdata = imap_mdata_get(m);
2190 if (!mdata)
2191 return -1;
2192
2193 char *new_tag = NULL;
2194 char *checker = NULL;
2195
2196 /* Check for \* flags capability */
2197 if (!imap_has_flag(&mdata->flags, NULL))
2198 {
2199 mutt_error(_("IMAP server doesn't support custom flags"));
2200 return -1;
2201 }
2202
2203 buf_reset(buf);
2204 if (tags)
2205 buf_strcpy(buf, tags);
2206
2207 if (mw_get_field("Tags: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
2208 return -1;
2209
2210 /* each keyword must be atom defined by rfc822 as:
2211 *
2212 * atom = 1*<any CHAR except specials, SPACE and CTLs>
2213 * CHAR = ( 0.-127. )
2214 * specials = "(" / ")" / "<" / ">" / "@"
2215 * / "," / ";" / ":" / "\" / <">
2216 * / "." / "[" / "]"
2217 * SPACE = ( 32. )
2218 * CTLS = ( 0.-31., 127.)
2219 *
2220 * And must be separated by one space.
2221 */
2222
2223 new_tag = buf->data;
2224 checker = buf->data;
2225 SKIPWS(checker);
2226 while (*checker != '\0')
2227 {
2228 if ((*checker < 32) || (*checker >= 127) || // We allow space because it's the separator
2229 (*checker == 40) || // (
2230 (*checker == 41) || // )
2231 (*checker == 60) || // <
2232 (*checker == 62) || // >
2233 (*checker == 64) || // @
2234 (*checker == 44) || // ,
2235 (*checker == 59) || // ;
2236 (*checker == 58) || // :
2237 (*checker == 92) || // backslash
2238 (*checker == 34) || // "
2239 (*checker == 46) || // .
2240 (*checker == 91) || // [
2241 (*checker == 93)) // ]
2242 {
2243 mutt_error(_("Invalid IMAP flags"));
2244 return 0;
2245 }
2246
2247 /* Skip duplicate space */
2248 while ((checker[0] == ' ') && (checker[1] == ' '))
2249 checker++;
2250
2251 /* copy char to new_tag and go the next one */
2252 *new_tag++ = *checker++;
2253 }
2254 *new_tag = '\0';
2255 new_tag = buf->data; /* rewind */
2257
2258 return !mutt_str_equal(tags, buf_string(buf));
2259}
2260
2274static int imap_tags_commit(struct Mailbox *m, struct Email *e, const char *buf)
2275{
2276 char uid[11] = { 0 };
2277
2279
2280 if (*buf == '\0')
2281 buf = NULL;
2282
2283 if (!(adata->mailbox->rights & MUTT_ACL_WRITE))
2284 return 0;
2285
2286 snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
2287
2288 /* Remove old custom flags */
2289 if (imap_edata_get(e)->flags_remote)
2290 {
2291 struct Buffer cmd = buf_make(128); // just a guess
2292 buf_addstr(&cmd, "UID STORE ");
2293 buf_addstr(&cmd, uid);
2294 buf_addstr(&cmd, " -FLAGS.SILENT (");
2295 buf_addstr(&cmd, imap_edata_get(e)->flags_remote);
2296 buf_addstr(&cmd, ")");
2297
2298 /* Should we return here, or we are fine and we could
2299 * continue to add new flags */
2300 int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2301 buf_dealloc(&cmd);
2302 if (rc != IMAP_EXEC_SUCCESS)
2303 {
2304 return -1;
2305 }
2306 }
2307
2308 /* Add new custom flags */
2309 if (buf)
2310 {
2311 struct Buffer cmd = buf_make(128); // just a guess
2312 buf_addstr(&cmd, "UID STORE ");
2313 buf_addstr(&cmd, uid);
2314 buf_addstr(&cmd, " +FLAGS.SILENT (");
2315 buf_addstr(&cmd, buf);
2316 buf_addstr(&cmd, ")");
2317
2318 int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2319 buf_dealloc(&cmd);
2320 if (rc != IMAP_EXEC_SUCCESS)
2321 {
2322 mutt_debug(LL_DEBUG1, "fail to add new flags\n");
2323 return -1;
2324 }
2325 }
2326
2327 /* We are good sync them */
2328 mutt_debug(LL_DEBUG1, "NEW TAGS: %s\n", buf);
2329 driver_tags_replace(&e->tags, buf);
2330 FREE(&imap_edata_get(e)->flags_remote);
2331 struct Buffer *flags_remote = buf_pool_get();
2332 driver_tags_get_with_hidden(&e->tags, flags_remote);
2333 imap_edata_get(e)->flags_remote = buf_strdup(flags_remote);
2334 buf_pool_release(&flags_remote);
2336 return 0;
2337}
2338
2342enum MailboxType imap_path_probe(const char *path, const struct stat *st)
2343{
2344 if (mutt_istr_startswith(path, "imap://"))
2345 return MUTT_IMAP;
2346
2347 if (mutt_istr_startswith(path, "imaps://"))
2348 return MUTT_IMAP;
2349
2350 return MUTT_UNKNOWN;
2351}
2352
2356int imap_path_canon(struct Buffer *path)
2357{
2358 struct Url *url = url_parse(buf_string(path));
2359 if (!url)
2360 return 0;
2361
2362 char tmp[PATH_MAX] = { 0 };
2363 char tmp2[PATH_MAX];
2364
2365 imap_fix_path('\0', url->path, tmp, sizeof(tmp));
2366 url->path = tmp;
2367 url_tostring(url, tmp2, sizeof(tmp2), U_NO_FLAGS);
2368 buf_strcpy(path, tmp2);
2369 url_free(&url);
2370
2371 return 0;
2372}
2373
2383{
2385 return imap_path_canon(path);
2386}
2387
2391static int imap_path_is_empty(struct Buffer *path)
2392{
2393 int rc = imap_path_status(buf_string(path), false);
2394 if (rc < 0)
2395 return -1;
2396 if (rc == 0)
2397 return 1;
2398 return 0;
2399}
2400
2404const struct MxOps MxImapOps = {
2405 // clang-format off
2406 .type = MUTT_IMAP,
2407 .name = "imap",
2408 .is_local = false,
2409 .ac_owns_path = imap_ac_owns_path,
2410 .ac_add = imap_ac_add,
2411 .mbox_open = imap_mbox_open,
2412 .mbox_open_append = imap_mbox_open_append,
2413 .mbox_check = imap_mbox_check,
2414 .mbox_check_stats = imap_mbox_check_stats,
2415 .mbox_sync = NULL, /* imap syncing is handled by imap_sync_mailbox */
2416 .mbox_close = imap_mbox_close,
2417 .msg_open = imap_msg_open,
2418 .msg_open_new = imap_msg_open_new,
2419 .msg_commit = imap_msg_commit,
2420 .msg_close = imap_msg_close,
2421 .msg_padding_size = NULL,
2422 .msg_save_hcache = imap_msg_save_hcache,
2423 .tags_edit = imap_tags_edit,
2424 .tags_commit = imap_tags_commit,
2425 .path_probe = imap_path_probe,
2426 .path_canon = imap_path_canon,
2427 .path_is_empty = imap_path_is_empty,
2428 // clang-format on
2429};
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:279
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:156
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:87
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:204
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:58
const char * mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition: atoi.c:292
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:214
IMAP authenticator multiplexor.
@ IMAP_AUTH_SUCCESS
Authentication successful.
Definition: auth.h:40
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:178
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:221
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:508
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:394
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:93
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:308
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:75
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:199
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:258
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:243
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:412
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:588
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:354
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:97
Functions to parse commands in a config file.
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:292
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:144
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:48
Convenience wrapper for the config headers.
Connection Library.
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
Definition: connaccount.c:178
void commands_register(const struct Command *cmds, const size_t num_cmds)
Add commands to Commands array.
Definition: command.c:53
Convenience wrapper for the core headers.
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:90
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition: mailbox.c:226
#define MUTT_ACL_CREATE
Create a mailbox.
Definition: mailbox.h:62
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition: mailbox.h:178
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:179
#define MUTT_ACL_POST
Post (submit messages to the server)
Definition: mailbox.h:68
#define MUTT_ACL_LOOKUP
Lookup mailbox (visible to 'list')
Definition: mailbox.h:67
#define MUTT_ACL_INSERT
Add/copy into the mailbox (used when editing a message)
Definition: mailbox.h:66
#define MUTT_ACL_DELETE
Delete a message.
Definition: mailbox.h:63
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition: mailbox.h:211
uint16_t AclFlags
ACL Rights - These show permission to...
Definition: mailbox.h:59
#define MUTT_ACL_WRITE
Write to a message (for flagging or linking threads)
Definition: mailbox.h:71
MailboxType
Supported mailbox formats.
Definition: mailbox.h:41
@ MUTT_IMAP
'IMAP' Mailbox type
Definition: mailbox.h:50
@ MUTT_MAILBOX_ANY
Match any Mailbox type.
Definition: mailbox.h:42
@ MUTT_UNKNOWN
Mailbox wasn't recognised.
Definition: mailbox.h:44
#define MUTT_ACL_SEEN
Change the 'seen' status of a message.
Definition: mailbox.h:70
#define MUTT_ACL_READ
Read the mailbox.
Definition: mailbox.h:69
Edit a string.
Structs that make up an email.
int mutt_save_message_mbox(struct Mailbox *m_src, struct Email *e, enum MessageSaveOpt save_opt, enum MessageTransformOpt transform_opt, struct Mailbox *m_dst)
Save a message to a given mailbox.
Definition: external.c:738
Manage where the email is piped to external commands.
@ TRANSFORM_NONE
No transformation.
Definition: external.h:42
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: external.h:53
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:147
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1144
void imap_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free() -.
Definition: adata.c:69
enum CommandResult parse_unsubscribe_from(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'unsubscribe-from' command - Implements Command::parse() -.
Definition: commands.c:1547
enum CommandResult parse_subscribe_to(struct Buffer *buf, struct Buffer *s, intptr_t data, struct Buffer *err)
Parse the 'subscribe-to' command - Implements Command::parse() -.
Definition: commands.c:1210
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free() -.
Definition: edata.c:40
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition: window.c:274
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_message(...)
Definition: logging2.h:91
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free() -.
Definition: mdata.c:39
static bool imap_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add() -.
Definition: imap.c:1687
static bool imap_ac_owns_path(struct Account *a, const char *path)
Check whether an Account owns a Mailbox path - Implements MxOps::ac_owns_path() -.
Definition: imap.c:1669
const struct MxOps MxImapOps
IMAP Mailbox - Implements MxOps -.
Definition: imap.c:2404
static enum MxStatus imap_mbox_check_stats(struct Mailbox *m, uint8_t flags)
Check the Mailbox statistics - Implements MxOps::mbox_check_stats() -.
Definition: imap.c:1154
static enum MxStatus imap_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition: imap.c:2106
static enum MxStatus imap_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: imap.c:2120
static bool imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: imap.c:2071
static enum MxOpenReturns imap_mbox_open(struct Mailbox *m)
Open a mailbox - Implements MxOps::mbox_open() -.
Definition: imap.c:1866
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: message.c:2182
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: message.c:2168
static bool imap_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
Open a new message in a Mailbox - Implements MxOps::msg_open_new() -.
Definition: imap.c:2162
bool imap_msg_open(struct Mailbox *m, struct Message *msg, struct Email *e)
Open an email message in a Mailbox - Implements MxOps::msg_open() -.
Definition: message.c:1975
int imap_msg_save_hcache(struct Mailbox *m, struct Email *e)
Save message to the header cache - Implements MxOps::msg_save_hcache() -.
Definition: message.c:2190
int imap_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: imap.c:2356
static int imap_path_is_empty(struct Buffer *path)
Is the mailbox empty - Implements MxOps::path_is_empty() -.
Definition: imap.c:2391
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2342
static int imap_tags_commit(struct Mailbox *m, struct Email *e, const char *buf)
Save the tags to a message - Implements MxOps::tags_commit() -.
Definition: imap.c:2274
static int imap_tags_edit(struct Mailbox *m, const char *tags, struct Buffer *buf)
Prompt and validate new messages tags - Implements MxOps::tags_edit() -.
Definition: imap.c:2187
int imap_sort_uid(const void *a, const void *b, void *sdata)
Compare two UIDs - Implements sort_t -.
Definition: msg_set.c:55
static int imap_sort_email_uid(const void *a, const void *b, void *sdata)
Compare two Emails by UID - Implements sort_t -.
Definition: imap.c:901
void mutt_hash_int_delete(struct HashTable *table, unsigned int intkey, const void *data)
Remove an element from a Hash Table.
Definition: hash.c:444
Read/write command history from/to a file.
@ HC_OTHER
Miscellaneous strings.
Definition: lib.h:56
void mutt_account_hook(const char *url)
Perform an account hook.
Definition: hook.c:857
Parse and execute user-defined hooks.
struct ImapAccountData * imap_adata_new(struct Account *a)
Allocate and initialise a new ImapAccountData structure.
Definition: adata.c:98
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:123
int imap_authenticate(struct ImapAccountData *adata)
Authenticate to an IMAP server.
Definition: auth.c:115
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1118
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1270
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1439
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1132
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:1307
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1372
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:67
int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
Parse an IMAP mailbox name into ConnAccount, name.
Definition: util.c:474
struct ImapMboxData * imap_mdata_new(struct ImapAccountData *adata, const char *name)
Allocate and initialise a new ImapMboxData structure.
Definition: mdata.c:73
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition: mdata.c:60
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1886
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1867
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:1341
#define IMAP_CAP_ENABLE
RFC5161.
Definition: private.h:135
#define IMAP_CAP_IDLE
RFC2177: IDLE.
Definition: private.h:133
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:71
void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target.
Definition: util.c:816
#define IMAP_CAP_ID
RFC2971: IMAP4 ID extension.
Definition: private.h:141
void imap_allow_reopen(struct Mailbox *m)
Allow re-opening a folder upon expunge.
Definition: util.c:1026
void imap_disallow_reopen(struct Mailbox *m)
Disallow re-opening a folder upon expunge.
Definition: util.c:1039
@ IMAP_DISCONNECTED
Disconnected from server.
Definition: private.h:105
@ IMAP_IDLE
Connection is idle.
Definition: private.h:111
@ IMAP_AUTHENTICATED
Connection is authenticated.
Definition: private.h:107
@ IMAP_SELECTED
Mailbox is selected.
Definition: private.h:108
@ IMAP_CONNECTED
Connected to server.
Definition: private.h:106
#define IMAP_EXPUNGE_PENDING
Messages on the server have been expunged.
Definition: private.h:66
#define IMAP_RES_OK
<tag> OK ...
Definition: private.h:55
#define IMAP_OPEN_NO_FLAGS
No flags are set.
Definition: private.h:63
#define IMAP_EXPUNGE_EXPECTED
Messages will be expunged from the server.
Definition: private.h:65
int imap_hcache_put(struct ImapMboxData *mdata, struct Email *e)
Add an entry to the header cache.
Definition: util.c:380
#define IMAP_LOG_LTRL
Definition: private.h:49
#define IMAP_CMD_POLL
Poll the tcp connection before running the imap command.
Definition: private.h:74
void imap_mdata_cache_reset(struct ImapMboxData *mdata)
Release and clear cache data of ImapMboxData structure.
Definition: util.c:107
#define IMAP_CAP_IMAP4
Server supports IMAP4.
Definition: private.h:121
#define IMAP_CAP_STARTTLS
RFC2595: STARTTLS.
Definition: private.h:131
#define IMAP_CAP_IMAP4REV1
Server supports IMAP4rev1.
Definition: private.h:122
#define IMAP_CAP_STATUS
Server supports STATUS command.
Definition: private.h:123
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:847
enum QuadOption imap_continue(const char *msg, const char *resp)
Display a message and ask the user if they want to go on.
Definition: util.c:646
#define IMAP_CMD_PASS
Command contains a password. Suppress logging.
Definition: private.h:72
void imap_buf_qualify_path(struct Buffer *buf, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target to a buffer.
Definition: util.c:830
ImapExecResult
Imap_exec return code.
Definition: private.h:81
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:82
@ IMAP_EXEC_ERROR
Imap command failure.
Definition: private.h:83
@ IMAP_EXEC_FATAL
Imap connection failure.
Definition: private.h:84
#define IMAP_CAP_ACL
RFC2086: IMAP4 ACL extension.
Definition: private.h:124
#define IMAP_CAP_QRESYNC
RFC7162.
Definition: private.h:137
char * imap_fix_path(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition: util.c:679
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition: private.h:67
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition: util.c:657
#define IMAP_FLAGS_PENDING
Flags have changed on the server.
Definition: private.h:68
void imap_hcache_close(struct ImapMboxData *mdata)
Close the header cache.
Definition: util.c:340
@ IMAP_BYE
Logged out from server.
Definition: private.h:96
@ IMAP_FATAL
Unrecoverable error occurred.
Definition: private.h:95
#define IMAP_CAP_COMPRESS
RFC4978: COMPRESS=DEFLATE.
Definition: private.h:139
int imap_hcache_del(struct ImapMboxData *mdata, unsigned int uid)
Delete an item from the header cache.
Definition: util.c:398
#define IMAP_RES_NO
<tag> NO ...
Definition: private.h:53
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition: util.c:73
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1054
void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
Quote awkward characters in a mailbox name.
Definition: util.c:921
#define IMAP_CMD_SINGLE
Run a single command.
Definition: private.h:75
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata)
Open a header cache.
Definition: util.c:299
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:56
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:785
#define IMAP_CAP_CONDSTORE
RFC7162.
Definition: private.h:136
#define IMAP_CMD_QUEUE
Queue a command, do not execute.
Definition: private.h:73
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition: util.c:768
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition: imap.c:528
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1206
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1171
void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
Inform IMAP that an Email has been deleted.
Definition: imap.c:649
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:850
static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, char *flags, size_t flsize)
Append str to flags if we currently have permission according to aclflag.
Definition: imap.c:188
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1222
int imap_expand_path(struct Buffer *path)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2382
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1104
int imap_complete(struct Buffer *buf, const char *path)
Try to complete an IMAP folder path.
Definition: imap.c:1274
int imap_delete_mailbox(struct Mailbox *m, char *path)
Delete a mailbox.
Definition: imap.c:506
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition: imap.c:670
static size_t longest_common_prefix(struct Buffer *buf, const char *src, size_t start)
Find longest prefix common to two strings.
Definition: imap.c:349
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:742
static int sync_helper(struct Mailbox *m, struct Email **emails, int num_emails, AclFlags right, enum MessageType flag, const char *name)
Sync flag changes to the server.
Definition: imap.c:301
int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
Rename a mailbox.
Definition: imap.c:479
static int complete_hosts(struct Buffer *buf)
Look for completion matches for mailboxes.
Definition: imap.c:372
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:437
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:107
int imap_fast_trash(struct Mailbox *m, const char *dest)
Use server COPY command to copy deleted messages to trash.
Definition: imap.c:1355
int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e, struct Buffer *cmd, enum QuadOption *err_continue)
Update server to reflect the flags of a single message.
Definition: imap.c:927
static int select_email_uids(struct Email **emails, int num_emails, enum MessageType flag, bool changed, bool invert, struct UidArray *uida)
Create a list of Email UIDs by type.
Definition: imap.c:231
enum MxStatus imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
Sync all the changes to the server.
Definition: imap.c:1470
int imap_access(const char *path)
Check permissions on an IMAP mailbox with a new connection.
Definition: imap.c:464
void imap_logout_all(void)
Close all open connections.
Definition: imap.c:555
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition: imap.c:1031
static char * get_flags(struct ListHead *hflags, char *s)
Make a simple list out of a FLAGS response.
Definition: imap.c:133
int imap_read_literal(FILE *fp, struct ImapAccountData *adata, unsigned long bytes, struct Progress *progress)
Read bytes bytes from server into file.
Definition: imap.c:591
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition: imap.c:875
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition: imap.c:1743
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1776
static const struct Command ImapCommands[]
Imap Commands.
Definition: imap.c:86
void imap_init(void)
Setup feature commands.
Definition: imap.c:96
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:204
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
@ LL_DEBUG3
Log at debug level 3.
Definition: logging2.h:45
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define FREE(x)
Definition: memory.h:45
#define mutt_array_size(x)
Definition: memory.h:38
int imap_exec_msg_set(struct ImapAccountData *adata, const char *pre, const char *post, struct UidArray *uida)
Execute a command using a set of UIDs.
Definition: msg_set.c:133
IMAP Message Sets.
void imap_msn_remove(struct MSNArray *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:114
IMAP MSN helper functions.
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:455
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition: string.c:614
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:721
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:709
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:474
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:230
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:545
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:630
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:242
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:502
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:329
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:268
Many unsorted constants and some structs.
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:56
MessageType
To set flags or match patterns.
Definition: mutt.h:67
@ MUTT_TRASH
Trashed messages.
Definition: mutt.h:85
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:73
@ MUTT_OLD
Old messages.
Definition: mutt.h:71
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:79
@ MUTT_DELETED
Deleted messages.
Definition: mutt.h:78
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:72
#define PATH_MAX
Definition: mutt.h:42
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:80
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
Definition: mutt_account.h:38
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:74
NeoMutt Logging.
struct Connection * mutt_conn_new(const struct ConnAccount *cac)
Create a new Connection.
Definition: mutt_socket.c:49
NeoMutt connections.
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:557
Some miscellaneous functions.
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition: mx.c:1204
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1738
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1603
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:249
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1634
API for mailboxes.
uint8_t OpenMailboxFlags
Flags for mutt_open_mailbox(), e.g. MUTT_NOSORT.
Definition: mxapi.h:39
MxOpenReturns
Return values for mbox_open()
Definition: mxapi.h:76
@ MX_OPEN_ERROR
Open failed with an error.
Definition: mxapi.h:78
@ MX_OPEN_OK
Open succeeded.
Definition: mxapi.h:77
#define MUTT_MAILBOX_CHECK_IMMEDIATE
Don't postpone the actual checking.
Definition: mxapi.h:56
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_sync(), and mbox_close()
Definition: mxapi.h:63
@ MX_STATUS_ERROR
An error occurred.
Definition: mxapi.h:64
@ MX_STATUS_OK
No changes.
Definition: mxapi.h:65
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition: mxapi.h:69
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition: mxapi.h:68
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition: mxapi.h:66
void neomutt_mailboxlist_clear(struct MailboxList *ml)
Free a Mailbox List.
Definition: neomutt.c:163
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:186
Notmuch-specific Mailbox data.
Text parsing functions.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
Pop-specific Account data.
Pop-specific Email data.
Progress Bar.
bool progress_update(struct Progress *progress, size_t pos, int percent)
Update the state of the progress bar.
Definition: progress.c:80
void mutt_qsort_r(void *base, size_t nmemb, size_t size, sort_t compar, void *sdata)
Sort an array, where the comparator has access to opaque data rather than requiring global variables.
Definition: qsort_r.c:67
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition: quad.h:37
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
Ask the user a question.
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition: question.c:349
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:373
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:334
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define STAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:324
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
#define STAILQ_EMPTY(head)
Definition: queue.h:348
enum CommandResult parse_rc_line(const char *line, struct Buffer *err)
Parse a line of user config.
Definition: rc.c:104
GUI display the mailboxes in a side panel.
int mutt_socket_close(struct Connection *conn)
Close a socket.
Definition: socket.c:100
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:182
int mutt_socket_readchar(struct Connection *conn, char *c)
Simple read buffering to speed things up.
Definition: socket.c:200
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:306
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:76
Assorted sorting methods.
#define mutt_numeric_cmp(a, b)
Definition: sort.h:35
Key value store.
#define SKIPWS(ch)
Definition: string2.h:45
A group of associated Mailboxes.
Definition: account.h:36
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:37
char * name
Name of Account.
Definition: account.h:38
void(* adata_free)(void **ptr)
Definition: account.h:53
void * adata
Private data (for Mailbox backends)
Definition: account.h:42
String manipulation buffer.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:37
Login details for a remote server.
Definition: connaccount.h:53
char user[128]
Username.
Definition: connaccount.h:56
char host[128]
Server to login to.
Definition: connaccount.h:54
unsigned int ssf
Security strength factor, in bits (see notes)
Definition: connection.h:50
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:49
int fd
Socket file descriptor.
Definition: connection.h:53
The envelope/body of an email.
Definition: email.h:39
bool read
Email is read.
Definition: email.h:50
bool purge
Skip trash folder when deleting.
Definition: email.h:79
struct Envelope * env
Envelope information.
Definition: email.h:68
void * edata
Driver-specific data.
Definition: email.h:74
bool active
Message is not to be removed.
Definition: email.h:76
bool old
Email is seen, but unread.
Definition: email.h:49
bool changed
Email has been edited.
Definition: email.h:77
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:102
bool flagged
Marked important?
Definition: email.h:47
bool replied
Email has been replied to.
Definition: email.h:51
struct TagList tags
For drivers that support server tagging.
Definition: email.h:72
char * path
Path of Email (for local Mailboxes)
Definition: email.h:70
bool deleted
Email is deleted.
Definition: email.h:78
int index
The absolute (unsorted) message number.
Definition: email.h:113
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:90
IMAP-specific Account data -.
Definition: adata.h:40
char delim
Path delimiter.
Definition: adata.h:75
struct Mailbox * prev_mailbox
Previously selected mailbox.
Definition: adata.h:77
struct ImapList * cmdresult
Definition: adata.h:66
int lastcmd
Last command in the queue.
Definition: adata.h:72
bool closing
If true, we are waiting for CLOSE completion.
Definition: adata.h:43
time_t lastread
last time we read a command for the server
Definition: adata.h:58
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition: adata.h:62
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
int nextcmd
Next command to be sent.
Definition: adata.h:71
unsigned char state
ImapState, e.g. IMAP_AUTHENTICATED.
Definition: adata.h:44
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
char * capstr
Capability string from the server.
Definition: adata.h:54
struct ImapCommand * cmds
Queue of commands for the server.
Definition: adata.h:69
unsigned char status
ImapFlags, e.g. IMAP_FATAL.
Definition: adata.h:45
int cmdslots
Size of the command queue.
Definition: adata.h:70
char * buf
Definition: adata.h:59
unsigned int seqno
tag sequence number, e.g. '{seqid}0001'
Definition: adata.h:57
struct Connection * conn
Connection to IMAP server.
Definition: adata.h:41
struct Buffer cmdbuf
Definition: adata.h:73
IMAP command structure.
Definition: private.h:160
IMAP-specific Email data -.
Definition: edata.h:35
unsigned int uid
32-bit Message UID
Definition: edata.h:45
char * flags_remote
Definition: edata.h:49
Items in an IMAP browser.
Definition: private.h:149
bool noselect
Definition: private.h:152
char * name
Definition: private.h:150
char delim
Definition: private.h:151
IMAP-specific Mailbox data -.
Definition: mdata.h:40
ImapOpenFlags reopen
Flags, e.g. IMAP_REOPEN_ALLOW.
Definition: mdata.h:45
unsigned int uid_next
Definition: mdata.h:52
struct ListHead flags
Definition: mdata.h:50
char * real_name
Original Mailbox name, e.g.: INBOX can be just \0.
Definition: mdata.h:43
unsigned int new_mail_count
Set when EXISTS notifies of new mail.
Definition: mdata.h:47
struct HashTable * uid_hash
Hash Table: "uid" -> Email.
Definition: mdata.h:59
unsigned long long modseq
Definition: mdata.h:53
char * munge_name
Munged version of the mailbox name.
Definition: mdata.h:42
uint32_t uidvalidity
Definition: mdata.h:51
char * name
Mailbox name.
Definition: mdata.h:41
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
List of Mailboxes.
Definition: mailbox.h:154
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:155
A mailbox.
Definition: mailbox.h:79
void(* mdata_free)(void **ptr)
Definition: mailbox.h:143
int vcount
The number of virtual messages.
Definition: mailbox.h:99
bool changed
Mailbox has been modified.
Definition: mailbox.h:110
bool has_new
Mailbox has new mail.
Definition: mailbox.h:85
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:81
bool append
Mailbox is opened in append mode.
Definition: mailbox.h:109
int msg_new
Number of new messages.
Definition: mailbox.h:92
time_t last_checked
Last time we checked this mailbox for new mail.
Definition: mailbox.h:105
int msg_count
Total number of messages.
Definition: mailbox.h:88
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:119
bool poll_new_mail
Check for new mail.
Definition: mailbox.h:115
void * mdata
Driver specific data.
Definition: mailbox.h:132
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
struct Buffer pathbuf
Path of the Mailbox.
Definition: mailbox.h:80
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:93
struct Account * account
Account that owns this Mailbox.
Definition: mailbox.h:127
off_t size
Size of the Mailbox.
Definition: mailbox.h:84
int msg_flagged
Number of flagged messages.
Definition: mailbox.h:90
bool readonly
Don't allow changes to the mailbox.
Definition: mailbox.h:116
bool verbose
Display status messages?
Definition: mailbox.h:117
int msg_unread
Number of unread messages.
Definition: mailbox.h:89
A local copy of an email.
Definition: message.h:34
FILE * fp
pointer to the message data
Definition: message.h:35
char * path
path to temp file
Definition: message.h:36
Definition: mxapi.h:91
enum MailboxType type
Mailbox type, e.g. MUTT_IMAP.
Definition: mxapi.h:92
Container for Accounts, Notifications.
Definition: neomutt.h:41
struct AccountList accounts
List of all Accounts.
Definition: neomutt.h:46
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:45
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition: url.h:69
char * user
Username.
Definition: url.h:71
char * host
Host.
Definition: url.h:73
char * path
Path.
Definition: url.h:75
bool driver_tags_replace(struct TagList *tl, const char *tags)
Replace all tags.
Definition: tags.c:201
void driver_tags_get_with_hidden(struct TagList *tl, struct Buffer *tags)
Get all tags, also hidden ones, separated by space.
Definition: tags.c:174
#define buf_mktemp(buf)
Definition: tmp.h:33
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:239
int url_tostring(struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition: url.c:423
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:124
#define U_NO_FLAGS
Definition: url.h:49
void mutt_zstrm_wrap_conn(struct Connection *conn)
Wrap a compression layer around a Connection.
Definition: zstrm.c:291