NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
imap.c
Go to the documentation of this file.
1
34#include "config.h"
35#include <ctype.h>
36#include <limits.h>
37#include <stdbool.h>
38#include <stdint.h>
39#include <stdio.h>
40#include <string.h>
41#include "private.h"
42#include "mutt/lib.h"
43#include "config/lib.h"
44#include "email/lib.h"
45#include "core/lib.h"
46#include "conn/lib.h"
47#include "mutt.h"
48#include "lib.h"
49#include "editor/lib.h"
50#include "history/lib.h"
51#include "parse/lib.h"
52#include "progress/lib.h"
53#include "question/lib.h"
54#include "adata.h"
55#include "auth.h"
56#include "commands.h"
57#include "edata.h"
58#include "external.h"
59#include "hook.h"
60#include "mdata.h"
61#include "msg_set.h"
62#include "msn.h"
63#include "mutt_logging.h"
64#include "mutt_socket.h"
65#include "muttlib.h"
66#include "mx.h"
67#include "sort.h"
68#ifdef ENABLE_NLS
69#include <libintl.h>
70#endif
71
72struct Progress;
73struct stat;
74
78static const struct Command ImapCommands[] = {
79 // clang-format off
80 { "subscribe-to", parse_subscribe_to, 0 },
81 { "unsubscribe-from", parse_unsubscribe_from, 0 },
82 // clang-format on
83};
84
88void imap_init(void)
89{
91}
92
99static int check_capabilities(struct ImapAccountData *adata)
100{
101 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
102 {
103 imap_error("check_capabilities", adata->buf);
104 return -1;
105 }
106
107 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
108 {
109 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
110 return -1;
111 }
112
113 return 0;
114}
115
125static char *get_flags(struct ListHead *hflags, char *s)
126{
127 /* sanity-check string */
128 const size_t plen = mutt_istr_startswith(s, "FLAGS");
129 if (plen == 0)
130 {
131 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
132 return NULL;
133 }
134 s += plen;
135 SKIPWS(s);
136 if (*s != '(')
137 {
138 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
139 return NULL;
140 }
141
142 /* update caller's flags handle */
143 while (*s && (*s != ')'))
144 {
145 s++;
146 SKIPWS(s);
147 const char *flag_word = s;
148 while (*s && (*s != ')') && !isspace(*s))
149 s++;
150 const char ctmp = *s;
151 *s = '\0';
152 if (*flag_word)
153 mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
154 *s = ctmp;
155 }
156
157 /* note bad flags response */
158 if (*s != ')')
159 {
160 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
161 mutt_list_free(hflags);
162
163 return NULL;
164 }
165
166 s++;
167
168 return s;
169}
170
180static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag,
181 const char *str, char *flags, size_t flsize)
182{
183 if (m->rights & aclflag)
184 if (flag && imap_has_flag(&imap_mdata_get(m)->flags, str))
185 mutt_str_cat(flags, flsize, str);
186}
187
196static bool compare_flags_for_copy(struct Email *e)
197{
198 struct ImapEmailData *edata = e->edata;
199
200 if (e->read != edata->read)
201 return true;
202 if (e->old != edata->old)
203 return true;
204 if (e->flagged != edata->flagged)
205 return true;
206 if (e->replied != edata->replied)
207 return true;
208
209 return false;
210}
211
223static int select_email_uids(struct Email **emails, int num_emails, enum MessageType flag,
224 bool changed, bool invert, struct UidArray *uida)
225{
226 if (!emails || !uida)
227 return -1;
228
229 for (int i = 0; i < num_emails; i++)
230 {
231 struct Email *e = emails[i];
232 if (changed && !e->changed)
233 continue;
234
235 /* don't include pending expunged messages.
236 *
237 * TODO: can we unset active in cmd_parse_expunge() and
238 * cmd_parse_vanished() instead of checking for index != INT_MAX. */
239 if (!e || !e->active || (e->index == INT_MAX))
240 continue;
241
243
244 bool match = false;
245 switch (flag)
246 {
247 case MUTT_DELETED:
248 if (e->deleted != edata->deleted)
249 match = invert ^ e->deleted;
250 break;
251 case MUTT_FLAG:
252 if (e->flagged != edata->flagged)
253 match = invert ^ e->flagged;
254 break;
255 case MUTT_OLD:
256 if (e->old != edata->old)
257 match = invert ^ e->old;
258 break;
259 case MUTT_READ:
260 if (e->read != edata->read)
261 match = invert ^ e->read;
262 break;
263 case MUTT_REPLIED:
264 if (e->replied != edata->replied)
265 match = invert ^ e->replied;
266 break;
267 case MUTT_TRASH:
268 if (e->deleted && !e->purge)
269 match = true;
270 break;
271 default:
272 break;
273 }
274
275 if (match)
276 ARRAY_ADD(uida, edata->uid);
277 }
278
279 return ARRAY_SIZE(uida);
280}
281
293static int sync_helper(struct Mailbox *m, struct Email **emails, int num_emails,
294 AclFlags right, enum MessageType flag, const char *name)
295{
297 if (!adata)
298 return -1;
299
300 if ((m->rights & right) == 0)
301 return 0;
302
303 if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&imap_mdata_get(m)->flags, name))
304 return 0;
305
306 int count = 0;
307 char buf[1024] = { 0 };
308
309 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
310
311 // Set the flag (+FLAGS) on matching emails
312 select_email_uids(emails, num_emails, flag, true, false, &uida);
313 snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
314 int rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
315 if (rc < 0)
316 return rc;
317 count += rc;
318 ARRAY_FREE(&uida);
319
320 // Clear the flag (-FLAGS) on non-matching emails
321 select_email_uids(emails, num_emails, flag, true, true, &uida);
322 buf[0] = '-';
323 rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
324 if (rc < 0)
325 return rc;
326 count += rc;
327 ARRAY_FREE(&uida);
328
329 return count;
330}
331
341static size_t longest_common_prefix(struct Buffer *buf, const char *src, size_t start)
342{
343 size_t pos = start;
344
345 size_t len = buf_len(buf);
346 while ((pos < len) && buf->data[pos] && (buf->data[pos] == src[pos]))
347 pos++;
348 buf->data[pos] = '\0';
349
350 buf_fix_dptr(buf);
351
352 return pos;
353}
354
364static int complete_hosts(struct Buffer *buf)
365{
366 int rc = -1;
367 size_t matchlen;
368
369 matchlen = buf_len(buf);
370 struct MailboxList ml = STAILQ_HEAD_INITIALIZER(ml);
372 struct MailboxNode *np = NULL;
373 STAILQ_FOREACH(np, &ml, entries)
374 {
376 continue;
377
378 if (rc)
379 {
381 rc = 0;
382 }
383 else
384 {
385 longest_common_prefix(buf, mailbox_path(np->mailbox), matchlen);
386 }
387 }
389
390#if 0
391 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
392 {
393 struct Url url = { 0 };
394 char urlstr[1024] = { 0 };
395
396 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
397 continue;
398
399 mutt_account_tourl(&conn->account, &url);
400 /* FIXME: how to handle multiple users on the same host? */
401 url.user = NULL;
402 url.path = NULL;
403 url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
404 if (mutt_strn_equal(buf, urlstr, matchlen))
405 {
406 if (rc)
407 {
408 mutt_str_copy(buf, urlstr, buflen);
409 rc = 0;
410 }
411 else
412 {
413 longest_common_prefix(buf, urlstr, matchlen);
414 }
415 }
416 }
417#endif
418
419 return rc;
420}
421
429int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
430{
431 char buf[2048], mbox[1024];
432
433 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
434 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
435
437 {
438 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
439 return -1;
440 }
441
442 return 0;
443}
444
455int imap_access(const char *path)
456{
457 if (imap_path_status(path, false) >= 0)
458 return 0;
459 return -1;
460}
461
470int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
471{
472 char oldmbox[1024] = { 0 };
473 char newmbox[1024] = { 0 };
474 int rc = 0;
475
476 imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
477 imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
478
479 struct Buffer *buf = buf_pool_get();
480 buf_printf(buf, "RENAME %s %s", oldmbox, newmbox);
481
483 rc = -1;
484
485 buf_pool_release(&buf);
486
487 return rc;
488}
489
497int imap_delete_mailbox(struct Mailbox *m, char *path)
498{
499 char buf[PATH_MAX + 7];
500 char mbox[PATH_MAX] = { 0 };
501 struct Url *url = url_parse(path);
502 if (!url)
503 return -1;
504
506 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
507 url_free(&url);
508 snprintf(buf, sizeof(buf), "DELETE %s", mbox);
510 return -1;
511
512 return 0;
513}
514
520{
521 /* we set status here to let imap_handle_untagged know we _expect_ to
522 * receive a bye response (so it doesn't freak out and close the conn) */
523 if (adata->state == IMAP_DISCONNECTED)
524 {
525 return;
526 }
527
528 adata->status = IMAP_BYE;
529 imap_cmd_start(adata, "LOGOUT");
530 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
531 if ((c_imap_poll_timeout <= 0) ||
532 (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
533 {
535 ; // do nothing
536 }
538 adata->state = IMAP_DISCONNECTED;
539}
540
547{
548 struct Account *np = NULL;
549 TAILQ_FOREACH(np, &NeoMutt->accounts, entries)
550 {
551 if (np->type != MUTT_IMAP)
552 continue;
553
554 struct ImapAccountData *adata = np->adata;
555 if (!adata)
556 continue;
557
558 struct Connection *conn = adata->conn;
559 if (!conn || (conn->fd < 0))
560 continue;
561
562 mutt_message(_("Closing connection to %s..."), conn->account.host);
563 imap_logout(np->adata);
565 }
566}
567
582int imap_read_literal(FILE *fp, struct ImapAccountData *adata,
583 unsigned long bytes, struct Progress *progress)
584{
585 char c;
586 bool r = false;
587 struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
588
589 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
590 if (c_debug_level >= IMAP_LOG_LTRL)
591 buf_alloc(&buf, bytes + 1);
592
593 mutt_debug(LL_DEBUG2, "reading %lu bytes\n", bytes);
594
595 for (unsigned long pos = 0; pos < bytes; pos++)
596 {
597 if (mutt_socket_readchar(adata->conn, &c) != 1)
598 {
599 mutt_debug(LL_DEBUG1, "error during read, %lu bytes read\n", pos);
600 adata->status = IMAP_FATAL;
601
602 buf_dealloc(&buf);
603 return -1;
604 }
605
606 if (r && (c != '\n'))
607 fputc('\r', fp);
608
609 if (c == '\r')
610 {
611 r = true;
612 continue;
613 }
614 else
615 {
616 r = false;
617 }
618
619 fputc(c, fp);
620
621 if ((pos % 1024) == 0)
622 progress_update(progress, pos, -1);
623 if (c_debug_level >= IMAP_LOG_LTRL)
624 buf_addch(&buf, c);
625 }
626
627 if (c_debug_level >= IMAP_LOG_LTRL)
628 {
629 mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
630 buf_dealloc(&buf);
631 }
632 return 0;
633}
634
640void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
641{
642 struct ImapMboxData *mdata = imap_mdata_get(m);
644
645 if (!mdata || !edata)
646 return;
647
648 imap_msn_remove(&mdata->msn, edata->msn - 1);
649 edata->msn = 0;
650}
651
661void imap_expunge_mailbox(struct Mailbox *m, bool resort)
662{
664 struct ImapMboxData *mdata = imap_mdata_get(m);
665 if (!adata || !mdata)
666 return;
667
668 struct Email *e = NULL;
669
670#ifdef USE_HCACHE
671 imap_hcache_open(adata, mdata);
672#endif
673
674 for (int i = 0; i < m->msg_count; i++)
675 {
676 e = m->emails[i];
677 if (!e)
678 break;
679
680 if (e->index == INT_MAX)
681 {
682 mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
683
684 e->deleted = true;
685
686 imap_cache_del(m, e);
687#ifdef USE_HCACHE
688 imap_hcache_del(mdata, imap_edata_get(e)->uid);
689#endif
690
691 mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
692
693 imap_edata_free((void **) &e->edata);
694 }
695 else
696 {
697 /* NeoMutt has several places where it turns off e->active as a
698 * hack. For example to avoid FLAG updates, or to exclude from
699 * imap_exec_msg_set.
700 *
701 * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
702 * flag becomes set (e.g. a flag update to a modified header),
703 * this function will be called by imap_cmd_finish().
704 *
705 * The ctx_update_tables() will free and remove these "inactive" headers,
706 * despite that an EXPUNGE was not received for them.
707 * This would result in memory leaks and segfaults due to dangling
708 * pointers in the msn_index and uid_hash.
709 *
710 * So this is another hack to work around the hacks. We don't want to
711 * remove the messages, so make sure active is on. */
712 e->active = true;
713 }
714 }
715
716#ifdef USE_HCACHE
717 imap_hcache_close(mdata);
718#endif
719
721 if (resort)
722 {
724 }
725}
726
734{
735 if (mutt_socket_open(adata->conn) < 0)
736 return -1;
737
738 adata->state = IMAP_CONNECTED;
739
740 if (imap_cmd_step(adata) != IMAP_RES_OK)
741 {
743 return -1;
744 }
745
746 if (mutt_istr_startswith(adata->buf, "* OK"))
747 {
748 if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
749 {
750 goto bail;
751 }
752#ifdef USE_SSL
753 /* Attempt STARTTLS if available and desired. */
754 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
755 if ((adata->conn->ssf == 0) &&
756 (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
757 {
758 enum QuadOption ans;
759
760 if (c_ssl_force_tls)
761 {
762 ans = MUTT_YES;
763 }
764 else if ((ans = query_quadoption(_("Secure connection with TLS?"),
765 NeoMutt->sub, "ssl_starttls")) == MUTT_ABORT)
766 {
767 goto bail;
768 }
769 if (ans == MUTT_YES)
770 {
771 enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
772 // Clear any data after the STARTTLS acknowledgement
773 mutt_socket_empty(adata->conn);
774
775 if (rc == IMAP_EXEC_FATAL)
776 goto bail;
777 if (rc != IMAP_EXEC_ERROR)
778 {
779 if (mutt_ssl_starttls(adata->conn))
780 {
781 mutt_error(_("Could not negotiate TLS connection"));
782 goto bail;
783 }
784 else
785 {
786 /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
787 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
788 goto bail;
789 }
790 }
791 }
792 }
793
794 if (c_ssl_force_tls && (adata->conn->ssf == 0))
795 {
796 mutt_error(_("Encrypted connection unavailable"));
797 goto bail;
798 }
799#endif
800 }
801 else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
802 {
803#ifdef USE_SSL
804 /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
805 * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
806 * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
807 * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
808 * decide whether to abort. Note that if using $tunnel and
809 * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
810 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
811 if ((adata->conn->ssf == 0) && c_ssl_force_tls)
812 {
813 mutt_error(_("Encrypted connection unavailable"));
814 goto bail;
815 }
816#endif
817
818 adata->state = IMAP_AUTHENTICATED;
819 if (check_capabilities(adata) != 0)
820 goto bail;
821 FREE(&adata->capstr);
822 }
823 else
824 {
825 imap_error("imap_open_connection()", adata->buf);
826 goto bail;
827 }
828
829 return 0;
830
831bail:
833 FREE(&adata->capstr);
834 return -1;
835}
836
842{
843 if (adata->state != IMAP_DISCONNECTED)
844 {
845 mutt_socket_close(adata->conn);
846 adata->state = IMAP_DISCONNECTED;
847 }
848 adata->seqno = 0;
849 adata->nextcmd = 0;
850 adata->lastcmd = 0;
851 adata->status = 0;
852 memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
853}
854
866bool imap_has_flag(struct ListHead *flag_list, const char *flag)
867{
868 if (STAILQ_EMPTY(flag_list))
869 return false;
870
871 const size_t flaglen = mutt_str_len(flag);
872 struct ListNode *np = NULL;
873 STAILQ_FOREACH(np, flag_list, entries)
874 {
875 const size_t nplen = strlen(np->data);
876 if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
877 mutt_istrn_equal(np->data, flag, nplen))
878 {
879 return true;
880 }
881
882 if (mutt_str_equal(np->data, "\\*"))
883 return true;
884 }
885
886 return false;
887}
888
892static int imap_sort_email_uid(const void *a, const void *b, void *sdata)
893{
894 const struct Email *ea = *(struct Email const *const *) a;
895 const struct Email *eb = *(struct Email const *const *) b;
896
897 const unsigned int ua = imap_edata_get((struct Email *) ea)->uid;
898 const unsigned int ub = imap_edata_get((struct Email *) eb)->uid;
899
900 return mutt_numeric_cmp(ua, ub);
901}
902
918int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e,
919 struct Buffer *cmd, enum QuadOption *err_continue)
920{
922 if (!adata || (adata->mailbox != m))
923 return -1;
924
925 char flags[1024] = { 0 };
926 char *tags = NULL;
927 char uid[11] = { 0 };
928
930 {
931 if (e->deleted == imap_edata_get(e)->deleted)
932 e->changed = false;
933 return 0;
934 }
935
936 snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
937 buf_reset(cmd);
938 buf_addstr(cmd, "UID STORE ");
939 buf_addstr(cmd, uid);
940
941 flags[0] = '\0';
942
943 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags, sizeof(flags));
944 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags, sizeof(flags));
945 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags, sizeof(flags));
946 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags, sizeof(flags));
947 set_flag(m, MUTT_ACL_DELETE, imap_edata_get(e)->deleted, "\\Deleted ", flags,
948 sizeof(flags));
949
950 if (m->rights & MUTT_ACL_WRITE)
951 {
952 /* restore system flags */
953 if (imap_edata_get(e)->flags_system)
954 mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_system);
955 /* set custom flags */
957 if (tags)
958 {
959 mutt_str_cat(flags, sizeof(flags), tags);
960 FREE(&tags);
961 }
962 }
963
965
966 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
967 * explicitly revoke all system flags (if we have permission) */
968 if (*flags == '\0')
969 {
970 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags, sizeof(flags));
971 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags, sizeof(flags));
972 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags, sizeof(flags));
973 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags, sizeof(flags));
974 set_flag(m, MUTT_ACL_DELETE, !imap_edata_get(e)->deleted, "\\Deleted ",
975 flags, sizeof(flags));
976
977 /* erase custom flags */
978 if ((m->rights & MUTT_ACL_WRITE) && imap_edata_get(e)->flags_remote)
979 mutt_str_cat(flags, sizeof(flags), imap_edata_get(e)->flags_remote);
980
982
983 buf_addstr(cmd, " -FLAGS.SILENT (");
984 }
985 else
986 {
987 buf_addstr(cmd, " FLAGS.SILENT (");
988 }
989
990 buf_addstr(cmd, flags);
991 buf_addstr(cmd, ")");
992
993 /* after all this it's still possible to have no flags, if you
994 * have no ACL rights */
995 if (*flags && (imap_exec(adata, cmd->data, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS) &&
996 err_continue && (*err_continue != MUTT_YES))
997 {
998 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
999 if (*err_continue != MUTT_YES)
1000 return -1;
1001 }
1002
1003 /* server have now the updated flags */
1004 FREE(&imap_edata_get(e)->flags_remote);
1006
1007 if (e->deleted == imap_edata_get(e)->deleted)
1008 e->changed = false;
1009
1010 return 0;
1011}
1012
1019enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
1020{
1021 if (!m || !m->account)
1022 return MX_STATUS_ERROR;
1023
1025 struct ImapMboxData *mdata = imap_mdata_get(m);
1026
1027 /* overload keyboard timeout to avoid many mailbox checks in a row.
1028 * Most users don't like having to wait exactly when they press a key. */
1029 int rc = 0;
1030
1031 /* try IDLE first, unless force is set */
1032 const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1033 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
1034 if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1035 ((adata->state != IMAP_IDLE) || (mutt_date_now() >= adata->lastread + c_imap_keep_alive)))
1036 {
1037 if (imap_cmd_idle(adata) < 0)
1038 return MX_STATUS_ERROR;
1039 }
1040 if (adata->state == IMAP_IDLE)
1041 {
1042 while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1043 {
1044 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1045 {
1046 mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1047 return MX_STATUS_ERROR;
1048 }
1049 }
1050 if (rc < 0)
1051 {
1052 mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1053 adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1054 }
1055 }
1056
1057 const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1058 if ((force || ((adata->state != IMAP_IDLE) && (mutt_date_now() >= adata->lastread + c_timeout))) &&
1059 (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1060 {
1061 return MX_STATUS_ERROR;
1062 }
1063
1064 /* We call this even when we haven't run NOOP in case we have pending
1065 * changes to process, since we can reopen here. */
1066 imap_cmd_finish(adata);
1067
1068 enum MxStatus check = MX_STATUS_OK;
1069 if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1070 check = MX_STATUS_REOPENED;
1071 else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1072 check = MX_STATUS_NEW_MAIL;
1073 else if (mdata->check_status & IMAP_FLAGS_PENDING)
1074 check = MX_STATUS_FLAGS;
1075 else if (rc < 0)
1076 check = MX_STATUS_ERROR;
1077
1078 mdata->check_status = IMAP_OPEN_NO_FLAGS;
1079
1080 return check;
1081}
1082
1090static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
1091{
1092 char *uidvalidity_flag = NULL;
1093 char cmd[2048] = { 0 };
1094
1095 if (!adata || !mdata)
1096 return -1;
1097
1098 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1099 * IDLEd elsewhere.
1100 * adata->mailbox may be NULL for connections other than the current
1101 * mailbox's. */
1102 if (adata->mailbox && (adata->mailbox->mdata == mdata))
1103 {
1104 adata->mailbox->has_new = false;
1105 return mdata->messages;
1106 }
1107
1108 if (adata->mailbox && !adata->mailbox->poll_new_mail)
1109 return mdata->messages;
1110
1111 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1112 {
1113 uidvalidity_flag = "UIDVALIDITY";
1114 }
1115 else if (adata->capabilities & IMAP_CAP_STATUS)
1116 {
1117 uidvalidity_flag = "UID-VALIDITY";
1118 }
1119 else
1120 {
1121 mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1122 return -1;
1123 }
1124
1125 snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1126 mdata->munge_name, uidvalidity_flag);
1127
1128 int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_POLL);
1129 if (rc != IMAP_EXEC_SUCCESS)
1130 {
1131 mutt_debug(LL_DEBUG1, "Error queueing command\n");
1132 return rc;
1133 }
1134 return mdata->messages;
1135}
1136
1140static enum MxStatus imap_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1141{
1142 const bool queue = (flags & MUTT_MAILBOX_CHECK_IMMEDIATE) == 0;
1143 const int new_msgs = imap_mailbox_status(m, queue);
1144 if (new_msgs == -1)
1145 return MX_STATUS_ERROR;
1146 if (new_msgs == 0)
1147 return MX_STATUS_OK;
1148 return MX_STATUS_NEW_MAIL;
1149}
1150
1157int imap_path_status(const char *path, bool queue)
1158{
1159 struct Mailbox *m = mx_mbox_find2(path);
1160
1161 const bool is_temp = !m;
1162 if (is_temp)
1163 {
1164 m = mx_path_resolve(path);
1165 if (!mx_mbox_ac_link(m))
1166 {
1167 mailbox_free(&m);
1168 return 0;
1169 }
1170 }
1171
1172 int rc = imap_mailbox_status(m, queue);
1173
1174 if (is_temp)
1175 {
1176 mx_ac_remove(m, false);
1177 mailbox_free(&m);
1178 }
1179
1180 return rc;
1181}
1182
1192int imap_mailbox_status(struct Mailbox *m, bool queue)
1193{
1195 struct ImapMboxData *mdata = imap_mdata_get(m);
1196 if (!adata || !mdata)
1197 return -1;
1198 return imap_status(adata, mdata, queue);
1199}
1200
1208int imap_subscribe(char *path, bool subscribe)
1209{
1210 struct ImapAccountData *adata = NULL;
1211 struct ImapMboxData *mdata = NULL;
1212 struct Buffer err;
1213
1214 if (imap_adata_find(path, &adata, &mdata) < 0)
1215 return -1;
1216
1217 if (subscribe)
1218 mutt_message(_("Subscribing to %s..."), mdata->name);
1219 else
1220 mutt_message(_("Unsubscribing from %s..."), mdata->name);
1221
1222 char buf[2048] = { 0 };
1223 snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1224
1225 if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1226 {
1227 imap_mdata_free((void *) &mdata);
1228 return -1;
1229 }
1230
1231 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1232 if (c_imap_check_subscribed)
1233 {
1234 char mbox[1024] = { 0 };
1235 buf_init(&err);
1236 err.dsize = 256;
1237 err.data = mutt_mem_malloc(err.dsize);
1238 size_t len = snprintf(mbox, sizeof(mbox), "%smailboxes ", subscribe ? "" : "un");
1239 imap_quote_string(mbox + len, sizeof(mbox) - len, path, true);
1240 if (parse_rc_line(mbox, &err))
1241 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", err.data);
1242 FREE(&err.data);
1243 }
1244
1245 if (subscribe)
1246 mutt_message(_("Subscribed to %s"), mdata->name);
1247 else
1248 mutt_message(_("Unsubscribed from %s"), mdata->name);
1249 imap_mdata_free((void *) &mdata);
1250 return 0;
1251}
1252
1263int imap_complete(struct Buffer *buf, const char *path)
1264{
1265 struct ImapAccountData *adata = NULL;
1266 struct ImapMboxData *mdata = NULL;
1267 char tmp[2048] = { 0 };
1268 struct ImapList listresp = { 0 };
1269 struct Buffer *completion_buf = NULL;
1270 size_t clen;
1271 int completions = 0;
1272 int rc;
1273
1274 if (imap_adata_find(path, &adata, &mdata) < 0)
1275 {
1276 buf_strcpy(buf, path);
1277 return complete_hosts(buf);
1278 }
1279
1280 /* fire off command */
1281 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1282 snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1283 c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1284
1285 imap_cmd_start(adata, tmp);
1286
1287 /* and see what the results are */
1288 completion_buf = buf_pool_get();
1289 buf_strcpy(completion_buf, mdata->name);
1290 imap_mdata_free((void *) &mdata);
1291
1292 adata->cmdresult = &listresp;
1293 do
1294 {
1295 listresp.name = NULL;
1296 rc = imap_cmd_step(adata);
1297
1298 if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1299 {
1300 /* if the folder isn't selectable, append delimiter to force browse
1301 * to enter it on second tab. */
1302 if (listresp.noselect)
1303 {
1304 clen = strlen(listresp.name);
1305 listresp.name[clen++] = listresp.delim;
1306 listresp.name[clen] = '\0';
1307 }
1308 /* copy in first word */
1309 if (!completions)
1310 {
1311 buf_strcpy(completion_buf, listresp.name);
1312 completions++;
1313 continue;
1314 }
1315
1316 longest_common_prefix(completion_buf, listresp.name, 0);
1317 completions++;
1318 }
1319 } while (rc == IMAP_RES_CONTINUE);
1320 adata->cmdresult = NULL;
1321
1322 if (completions)
1323 {
1324 /* reformat output */
1325 imap_buf_qualify_path(buf, &adata->conn->account, completion_buf->data);
1326 buf_pretty_mailbox(buf);
1327 buf_fix_dptr(buf);
1328 buf_pool_release(&completion_buf);
1329 return 0;
1330 }
1331
1332 buf_pool_release(&completion_buf);
1333 return -1;
1334}
1335
1344int imap_fast_trash(struct Mailbox *m, const char *dest)
1345{
1346 char prompt[1024] = { 0 };
1347 int rc = -1;
1348 bool triedcreate = false;
1349 enum QuadOption err_continue = MUTT_NO;
1350
1352 struct ImapAccountData *dest_adata = NULL;
1353 struct ImapMboxData *dest_mdata = NULL;
1354
1355 if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1356 return -1;
1357
1358 struct Buffer sync_cmd = buf_make(0);
1359
1360 /* check that the save-to folder is in the same account */
1361 if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1362 {
1363 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1364 goto out;
1365 }
1366
1367 for (int i = 0; i < m->msg_count; i++)
1368 {
1369 struct Email *e = m->emails[i];
1370 if (!e)
1371 break;
1372 if (e->active && e->changed && e->deleted && !e->purge)
1373 {
1374 rc = imap_sync_message_for_copy(m, e, &sync_cmd, &err_continue);
1375 if (rc < 0)
1376 {
1377 mutt_debug(LL_DEBUG1, "could not sync\n");
1378 goto out;
1379 }
1380 }
1381 }
1382
1383 /* loop in case of TRYCREATE */
1384 do
1385 {
1386 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1387 select_email_uids(m->emails, m->msg_count, MUTT_TRASH, false, false, &uida);
1388 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1389 rc = imap_exec_msg_set(adata, "UID COPY", dest_mdata->munge_name, &uida);
1390 if (rc == 0)
1391 {
1392 mutt_debug(LL_DEBUG1, "No messages to trash\n");
1393 rc = -1;
1394 goto out;
1395 }
1396 else if (rc < 0)
1397 {
1398 mutt_debug(LL_DEBUG1, "could not queue copy\n");
1399 goto out;
1400 }
1401 else if (m->verbose)
1402 {
1403 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1404 rc, dest_mdata->name);
1405 }
1406 ARRAY_FREE(&uida);
1407
1408 /* let's get it on */
1409 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1410 if (rc == IMAP_EXEC_ERROR)
1411 {
1412 if (triedcreate)
1413 {
1414 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1415 break;
1416 }
1417 /* bail out if command failed for reasons other than nonexistent target */
1418 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1419 break;
1420 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1421 snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1422 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1423 if (c_confirm_create &&
1424 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1425 {
1427 goto out;
1428 }
1429 if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1430 break;
1431 triedcreate = true;
1432 }
1433 } while (rc == IMAP_EXEC_ERROR);
1434
1435 if (rc != IMAP_EXEC_SUCCESS)
1436 {
1437 imap_error("imap_fast_trash", adata->buf);
1438 goto out;
1439 }
1440
1441 rc = IMAP_EXEC_SUCCESS;
1442
1443out:
1444 buf_dealloc(&sync_cmd);
1445 imap_mdata_free((void *) &dest_mdata);
1446
1447 return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1448}
1449
1459enum MxStatus imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
1460{
1461 if (!m)
1462 return -1;
1463
1464 struct Email **emails = NULL;
1465 int rc;
1466
1468 struct ImapMboxData *mdata = imap_mdata_get(m);
1469
1470 if (adata->state < IMAP_SELECTED)
1471 {
1472 mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1473 return -1;
1474 }
1475
1476 /* This function is only called when the calling code expects the context
1477 * to be changed. */
1479
1480 enum MxStatus check = imap_check_mailbox(m, false);
1481 if (check == MX_STATUS_ERROR)
1482 return check;
1483
1484 /* if we are expunging anyway, we can do deleted messages very quickly... */
1485 if (expunge && (m->rights & MUTT_ACL_DELETE))
1486 {
1487 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1488 select_email_uids(m->emails, m->msg_count, MUTT_DELETED, true, false, &uida);
1489 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1490 rc = imap_exec_msg_set(adata, "UID STORE", "+FLAGS.SILENT (\\Deleted)", &uida);
1491 ARRAY_FREE(&uida);
1492 if (rc < 0)
1493 {
1494 mutt_error(_("Expunge failed"));
1495 return rc;
1496 }
1497
1498 if (rc > 0)
1499 {
1500 /* mark these messages as unchanged so second pass ignores them. Done
1501 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1502 for (int i = 0; i < m->msg_count; i++)
1503 {
1504 struct Email *e = m->emails[i];
1505 if (!e)
1506 break;
1507 if (e->deleted && e->changed)
1508 e->active = false;
1509 }
1510 if (m->verbose)
1511 {
1512 mutt_message(ngettext("Marking %d message deleted...",
1513 "Marking %d messages deleted...", rc),
1514 rc);
1515 }
1516 }
1517 }
1518
1519#ifdef USE_HCACHE
1520 imap_hcache_open(adata, mdata);
1521#endif
1522
1523 /* save messages with real (non-flag) changes */
1524 for (int i = 0; i < m->msg_count; i++)
1525 {
1526 struct Email *e = m->emails[i];
1527 if (!e)
1528 break;
1529
1530 if (e->deleted)
1531 {
1532 imap_cache_del(m, e);
1533#ifdef USE_HCACHE
1534 imap_hcache_del(mdata, imap_edata_get(e)->uid);
1535#endif
1536 }
1537
1538 if (e->active && e->changed)
1539 {
1540#ifdef USE_HCACHE
1541 imap_hcache_put(mdata, e);
1542#endif
1543 /* if the message has been rethreaded or attachments have been deleted
1544 * we delete the message and reupload it.
1545 * This works better if we're expunging, of course. */
1546 if (e->env->changed || e->attach_del)
1547 {
1548 /* L10N: The plural is chosen by the last %d, i.e. the total number */
1549 if (m->verbose)
1550 {
1551 mutt_message(ngettext("Saving changed message... [%d/%d]",
1552 "Saving changed messages... [%d/%d]", m->msg_count),
1553 i + 1, m->msg_count);
1554 }
1555 bool save_append = m->append;
1556 m->append = true;
1558 m->append = save_append;
1559 e->env->changed = false;
1560 }
1561 }
1562 }
1563
1564#ifdef USE_HCACHE
1565 imap_hcache_close(mdata);
1566#endif
1567
1568 /* presort here to avoid doing 10 resorts in imap_exec_msg_set */
1569 emails = mutt_mem_malloc(m->msg_count * sizeof(struct Email *));
1570 memcpy(emails, m->emails, m->msg_count * sizeof(struct Email *));
1571 mutt_qsort_r(emails, m->msg_count, sizeof(struct Email *), imap_sort_email_uid, NULL);
1572
1573 rc = sync_helper(m, emails, m->msg_count, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1574 if (rc >= 0)
1575 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1576 if (rc >= 0)
1577 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1578 if (rc >= 0)
1579 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1580 if (rc >= 0)
1581 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1582
1583 FREE(&emails);
1584
1585 /* Flush the queued flags if any were changed in sync_helper. */
1586 if (rc > 0)
1587 if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1588 rc = -1;
1589
1590 if (rc < 0)
1591 {
1592 if (close)
1593 {
1594 if (query_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1595 {
1596 adata->state = IMAP_AUTHENTICATED;
1597 return 0;
1598 }
1599 }
1600 else
1601 {
1602 mutt_error(_("Error saving flags"));
1603 }
1604 return -1;
1605 }
1606
1607 /* Update local record of server state to reflect the synchronization just
1608 * completed. imap_read_headers always overwrites hcache-origin flags, so
1609 * there is no need to mutate the hcache after flag-only changes. */
1610 for (int i = 0; i < m->msg_count; i++)
1611 {
1612 struct Email *e = m->emails[i];
1613 if (!e)
1614 break;
1615 struct ImapEmailData *edata = imap_edata_get(e);
1616 edata->deleted = e->deleted;
1617 edata->flagged = e->flagged;
1618 edata->old = e->old;
1619 edata->read = e->read;
1620 edata->replied = e->replied;
1621 e->changed = false;
1622 }
1623 m->changed = false;
1624
1625 /* We must send an EXPUNGE command if we're not closing. */
1626 if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1627 {
1628 if (m->verbose)
1629 mutt_message(_("Expunging messages from server..."));
1630 /* Set expunge bit so we don't get spurious reopened messages */
1631 mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1632 if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1633 {
1634 mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1635 imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1636 return -1;
1637 }
1638 mdata->reopen &= ~IMAP_EXPUNGE_EXPECTED;
1639 }
1640
1641 if (expunge && close)
1642 {
1643 adata->closing = true;
1644 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
1645 adata->state = IMAP_AUTHENTICATED;
1646 }
1647
1648 const bool c_message_cache_clean = cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1649 if (c_message_cache_clean)
1651
1652 return check;
1653}
1654
1658static bool imap_ac_owns_path(struct Account *a, const char *path)
1659{
1660 struct Url *url = url_parse(path);
1661 if (!url)
1662 return false;
1663
1664 struct ImapAccountData *adata = a->adata;
1665 struct ConnAccount *cac = &adata->conn->account;
1666
1667 const bool rc = mutt_istr_equal(url->host, cac->host) &&
1668 (!url->user || mutt_istr_equal(url->user, cac->user));
1669 url_free(&url);
1670 return rc;
1671}
1672
1676static bool imap_ac_add(struct Account *a, struct Mailbox *m)
1677{
1678 struct ImapAccountData *adata = a->adata;
1679
1680 if (!adata)
1681 {
1682 struct ConnAccount cac = { { 0 } };
1683 char mailbox[PATH_MAX] = { 0 };
1684
1685 if (imap_parse_path(mailbox_path(m), &cac, mailbox, sizeof(mailbox)) < 0)
1686 return false;
1687
1688 adata = imap_adata_new(a);
1689 adata->conn = mutt_conn_new(&cac);
1690 if (!adata->conn)
1691 {
1692 imap_adata_free((void **) &adata);
1693 return false;
1694 }
1695
1697
1698 if (imap_login(adata) < 0)
1699 {
1700 imap_adata_free((void **) &adata);
1701 return false;
1702 }
1703
1704 a->adata = adata;
1706 }
1707
1708 if (!m->mdata)
1709 {
1710 struct Url *url = url_parse(mailbox_path(m));
1711 if (!url)
1712 return false;
1713 struct ImapMboxData *mdata = imap_mdata_new(adata, url->path);
1714
1715 /* fixup path and realpath, mainly to replace / by /INBOX */
1716 char buf[1024] = { 0 };
1717 imap_qualify_path(buf, sizeof(buf), &adata->conn->account, mdata->name);
1718 buf_strcpy(&m->pathbuf, buf);
1720
1721 m->mdata = mdata;
1723 url_free(&url);
1724 }
1725 return true;
1726}
1727
1732static void imap_mbox_select(struct Mailbox *m)
1733{
1735 struct ImapMboxData *mdata = imap_mdata_get(m);
1736 if (!adata || !mdata)
1737 return;
1738
1739 const char *condstore = NULL;
1740#ifdef USE_HCACHE
1741 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1742 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1743 condstore = " (CONDSTORE)";
1744 else
1745#endif
1746 condstore = "";
1747
1748 char buf[PATH_MAX] = { 0 };
1749 snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1750 mdata->munge_name, condstore);
1751
1752 adata->state = IMAP_SELECTED;
1753
1754 imap_cmd_start(adata, buf);
1755}
1756
1765int imap_login(struct ImapAccountData *adata)
1766{
1767 if (!adata)
1768 return -1;
1769
1770 if (adata->state == IMAP_DISCONNECTED)
1771 {
1772 buf_reset(&adata->cmdbuf); // purge outstanding queued commands
1773 imap_open_connection(adata);
1774 }
1775 if (adata->state == IMAP_CONNECTED)
1776 {
1778 {
1779 adata->state = IMAP_AUTHENTICATED;
1780 FREE(&adata->capstr);
1781 if (adata->conn->ssf != 0)
1782 {
1783 mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1784 adata->conn->ssf);
1785 }
1786 }
1787 else
1788 {
1790 }
1791 }
1792 if (adata->state == IMAP_AUTHENTICATED)
1793 {
1794 /* capabilities may have changed */
1795 imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1796
1797#ifdef USE_ZLIB
1798 /* RFC4978 */
1799 const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1800 if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1801 (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1802 {
1803 mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1804 adata->conn->account.host);
1805 mutt_zstrm_wrap_conn(adata->conn);
1806 }
1807#endif
1808
1809 /* enable RFC2971, if the server supports that */
1810 const bool c_imap_send_id = cs_subset_bool(NeoMutt->sub, "imap_send_id");
1811 if (c_imap_send_id && (adata->capabilities & IMAP_CAP_ID))
1812 {
1813 imap_exec(adata, "ID (\"name\" \"NeoMutt\" \"version\" \"" PACKAGE_VERSION "\")",
1815 }
1816
1817 /* enable RFC6855, if the server supports that */
1818 const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1819 if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1820 imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1821
1822 /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1823 * is supported (even if not advertised), so flip that bit. */
1824 if (adata->capabilities & IMAP_CAP_QRESYNC)
1825 {
1827 const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1828 if (c_imap_rfc5161 && c_imap_qresync)
1829 imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1830 }
1831
1832 /* get root delimiter, '/' as default */
1833 adata->delim = '/';
1834 imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1835
1836 /* we may need the root delimiter before we open a mailbox */
1837 imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1838
1839 /* select the mailbox that used to be open before disconnect */
1840 if (adata->mailbox)
1841 {
1842 imap_mbox_select(adata->mailbox);
1843 }
1844 }
1845
1846 if (adata->state < IMAP_AUTHENTICATED)
1847 return -1;
1848
1849 return 0;
1850}
1851
1856{
1857 if (!m->account || !m->mdata)
1858 return MX_OPEN_ERROR;
1859
1860 char buf[PATH_MAX] = { 0 };
1861 int count = 0;
1862 int rc;
1863
1865 struct ImapMboxData *mdata = imap_mdata_get(m);
1866
1867 mutt_debug(LL_DEBUG3, "opening %s, saving %s\n", m->pathbuf.data,
1868 (adata->mailbox ? adata->mailbox->pathbuf.data : "(none)"));
1869 adata->prev_mailbox = adata->mailbox;
1870 adata->mailbox = m;
1871
1872 /* clear mailbox status */
1873 adata->status = 0;
1874 m->rights = 0;
1875 mdata->new_mail_count = 0;
1876
1877 if (m->verbose)
1878 mutt_message(_("Selecting %s..."), mdata->name);
1879
1880 /* pipeline ACL test */
1881 if (adata->capabilities & IMAP_CAP_ACL)
1882 {
1883 snprintf(buf, sizeof(buf), "MYRIGHTS %s", mdata->munge_name);
1884 imap_exec(adata, buf, IMAP_CMD_QUEUE);
1885 }
1886 else
1887 {
1888 /* assume we have all rights if ACL is unavailable */
1891 }
1892
1893 /* pipeline the postponed count if possible */
1894 const char *const c_postponed = cs_subset_string(NeoMutt->sub, "postponed");
1895 struct Mailbox *m_postponed = mx_mbox_find2(c_postponed);
1896 struct ImapAccountData *postponed_adata = imap_adata_get(m_postponed);
1897 if (postponed_adata &&
1898 imap_account_match(&postponed_adata->conn->account, &adata->conn->account))
1899 {
1900 imap_mailbox_status(m_postponed, true);
1901 }
1902
1903 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1904 if (c_imap_check_subscribed)
1905 imap_exec(adata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE);
1906
1908
1909 do
1910 {
1911 char *pc = NULL;
1912
1913 rc = imap_cmd_step(adata);
1914 if (rc != IMAP_RES_CONTINUE)
1915 break;
1916
1917 pc = adata->buf + 2;
1918
1919 /* Obtain list of available flags here, may be overridden by a
1920 * PERMANENTFLAGS tag in the OK response */
1921 if (mutt_istr_startswith(pc, "FLAGS"))
1922 {
1923 /* don't override PERMANENTFLAGS */
1924 if (STAILQ_EMPTY(&mdata->flags))
1925 {
1926 mutt_debug(LL_DEBUG3, "Getting mailbox FLAGS\n");
1927 pc = get_flags(&mdata->flags, pc);
1928 if (!pc)
1929 goto fail;
1930 }
1931 }
1932 else if (mutt_istr_startswith(pc, "OK [PERMANENTFLAGS"))
1933 {
1934 /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
1935 mutt_debug(LL_DEBUG3, "Getting mailbox PERMANENTFLAGS\n");
1936 /* safe to call on NULL */
1937 mutt_list_free(&mdata->flags);
1938 /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
1939 pc += 13;
1940 pc = get_flags(&(mdata->flags), pc);
1941 if (!pc)
1942 goto fail;
1943 }
1944 else if (mutt_istr_startswith(pc, "OK [UIDVALIDITY"))
1945 {
1946 /* save UIDVALIDITY for the header cache */
1947 mutt_debug(LL_DEBUG3, "Getting mailbox UIDVALIDITY\n");
1948 pc += 3;
1949 pc = imap_next_word(pc);
1950 if (!mutt_str_atoui(pc, &mdata->uidvalidity))
1951 goto fail;
1952 }
1953 else if (mutt_istr_startswith(pc, "OK [UIDNEXT"))
1954 {
1955 mutt_debug(LL_DEBUG3, "Getting mailbox UIDNEXT\n");
1956 pc += 3;
1957 pc = imap_next_word(pc);
1958 if (!mutt_str_atoui(pc, &mdata->uid_next))
1959 goto fail;
1960 }
1961 else if (mutt_istr_startswith(pc, "OK [HIGHESTMODSEQ"))
1962 {
1963 mutt_debug(LL_DEBUG3, "Getting mailbox HIGHESTMODSEQ\n");
1964 pc += 3;
1965 pc = imap_next_word(pc);
1966 if (!mutt_str_atoull(pc, &mdata->modseq))
1967 goto fail;
1968 }
1969 else if (mutt_istr_startswith(pc, "OK [NOMODSEQ"))
1970 {
1971 mutt_debug(LL_DEBUG3, "Mailbox has NOMODSEQ set\n");
1972 mdata->modseq = 0;
1973 }
1974 else
1975 {
1976 pc = imap_next_word(pc);
1977 if (mutt_istr_startswith(pc, "EXISTS"))
1978 {
1979 count = mdata->new_mail_count;
1980 mdata->new_mail_count = 0;
1981 }
1982 }
1983 } while (rc == IMAP_RES_CONTINUE);
1984
1985 if (rc == IMAP_RES_NO)
1986 {
1987 char *s = imap_next_word(adata->buf); /* skip seq */
1988 s = imap_next_word(s); /* Skip response */
1989 mutt_error("%s", s);
1990 goto fail;
1991 }
1992
1993 if (rc != IMAP_RES_OK)
1994 goto fail;
1995
1996 /* check for READ-ONLY notification */
1997 if (mutt_istr_startswith(imap_get_qualifier(adata->buf), "[READ-ONLY]") &&
1998 !(adata->capabilities & IMAP_CAP_ACL))
1999 {
2000 mutt_debug(LL_DEBUG2, "Mailbox is read-only\n");
2001 m->readonly = true;
2002 }
2003
2004 /* dump the mailbox flags we've found */
2005 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
2006 if (c_debug_level > LL_DEBUG2)
2007 {
2008 if (STAILQ_EMPTY(&mdata->flags))
2009 {
2010 mutt_debug(LL_DEBUG3, "No folder flags found\n");
2011 }
2012 else
2013 {
2014 struct ListNode *np = NULL;
2015 struct Buffer flag_buffer;
2016 buf_init(&flag_buffer);
2017 buf_printf(&flag_buffer, "Mailbox flags: ");
2018 STAILQ_FOREACH(np, &mdata->flags, entries)
2019 {
2020 buf_add_printf(&flag_buffer, "[%s] ", np->data);
2021 }
2022 mutt_debug(LL_DEBUG3, "%s\n", flag_buffer.data);
2023 FREE(&flag_buffer.data);
2024 }
2025 }
2026
2027 if (!((m->rights & MUTT_ACL_DELETE) || (m->rights & MUTT_ACL_SEEN) ||
2028 (m->rights & MUTT_ACL_WRITE) || (m->rights & MUTT_ACL_INSERT)))
2029 {
2030 m->readonly = true;
2031 }
2032
2033 mx_alloc_memory(m, count);
2034
2035 m->msg_count = 0;
2036 m->msg_unread = 0;
2037 m->msg_flagged = 0;
2038 m->msg_new = 0;
2039 m->msg_deleted = 0;
2040 m->size = 0;
2041 m->vcount = 0;
2042
2043 if ((count > 0) && (imap_read_headers(m, 1, count, true) < 0))
2044 {
2045 mutt_error(_("Error opening mailbox"));
2046 goto fail;
2047 }
2048
2049 mutt_debug(LL_DEBUG2, "msg_count is %d\n", m->msg_count);
2050 return MX_OPEN_OK;
2051
2052fail:
2053 if (adata->state == IMAP_SELECTED)
2054 adata->state = IMAP_AUTHENTICATED;
2055 return MX_OPEN_ERROR;
2056}
2057
2062{
2063 if (!m->account)
2064 return false;
2065
2066 /* in APPEND mode, we appear to hijack an existing IMAP connection -
2067 * mailbox is brand new and mostly empty */
2069 struct ImapMboxData *mdata = imap_mdata_get(m);
2070
2071 int rc = imap_mailbox_status(m, false);
2072 if (rc >= 0)
2073 return true;
2074 if (rc == -1)
2075 return false;
2076
2077 char buf[PATH_MAX + 64];
2078 snprintf(buf, sizeof(buf), _("Create %s?"), mdata->name);
2079 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
2080 if (c_confirm_create &&
2081 (query_yesorno_help(buf, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
2082 return false;
2083
2084 if (imap_create_mailbox(adata, mdata->name) < 0)
2085 return false;
2086
2087 return true;
2088}
2089
2096static enum MxStatus imap_mbox_check(struct Mailbox *m)
2097{
2099 enum MxStatus rc = imap_check_mailbox(m, false);
2100 /* NOTE - mv might have been changed at this point. In particular,
2101 * m could be NULL. Beware. */
2103
2104 return rc;
2105}
2106
2110static enum MxStatus imap_mbox_close(struct Mailbox *m)
2111{
2113 struct ImapMboxData *mdata = imap_mdata_get(m);
2114
2115 /* Check to see if the mailbox is actually open */
2116 if (!adata || !mdata)
2117 return MX_STATUS_OK;
2118
2119 /* imap_mbox_open_append() borrows the struct ImapAccountData temporarily,
2120 * just for the connection.
2121 *
2122 * So when these are equal, it means we are actually closing the
2123 * mailbox and should clean up adata. Otherwise, we don't want to
2124 * touch adata - it's still being used. */
2125 if (m == adata->mailbox)
2126 {
2127 if ((adata->status != IMAP_FATAL) && (adata->state >= IMAP_SELECTED))
2128 {
2129 /* mx_mbox_close won't sync if there are no deleted messages
2130 * and the mailbox is unchanged, so we may have to close here */
2131 if (m->msg_deleted == 0)
2132 {
2133 adata->closing = true;
2134 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
2135 }
2136 adata->state = IMAP_AUTHENTICATED;
2137 }
2138
2139 mutt_debug(LL_DEBUG3, "closing %s, restoring %s\n", m->pathbuf.data,
2140 (adata->prev_mailbox ? adata->prev_mailbox->pathbuf.data : "(none)"));
2141 adata->mailbox = adata->prev_mailbox;
2144 }
2145
2146 return MX_STATUS_OK;
2147}
2148
2152static bool imap_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
2153{
2154 bool success = false;
2155
2156 struct Buffer *tmp = buf_pool_get();
2157 buf_mktemp(tmp);
2158
2159 msg->fp = mutt_file_fopen(buf_string(tmp), "w");
2160 if (!msg->fp)
2161 {
2162 mutt_perror("%s", buf_string(tmp));
2163 goto cleanup;
2164 }
2165
2166 msg->path = buf_strdup(tmp);
2167 success = true;
2168
2169cleanup:
2170 buf_pool_release(&tmp);
2171 return success;
2172}
2173
2177static int imap_tags_edit(struct Mailbox *m, const char *tags, struct Buffer *buf)
2178{
2179 struct ImapMboxData *mdata = imap_mdata_get(m);
2180 if (!mdata)
2181 return -1;
2182
2183 char *new_tag = NULL;
2184 char *checker = NULL;
2185
2186 /* Check for \* flags capability */
2187 if (!imap_has_flag(&mdata->flags, NULL))
2188 {
2189 mutt_error(_("IMAP server doesn't support custom flags"));
2190 return -1;
2191 }
2192
2193 buf_reset(buf);
2194 if (tags)
2195 buf_strcpy(buf, tags);
2196
2197 if (mw_get_field("Tags: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
2198 return -1;
2199
2200 /* each keyword must be atom defined by rfc822 as:
2201 *
2202 * atom = 1*<any CHAR except specials, SPACE and CTLs>
2203 * CHAR = ( 0.-127. )
2204 * specials = "(" / ")" / "<" / ">" / "@"
2205 * / "," / ";" / ":" / "\" / <">
2206 * / "." / "[" / "]"
2207 * SPACE = ( 32. )
2208 * CTLS = ( 0.-31., 127.)
2209 *
2210 * And must be separated by one space.
2211 */
2212
2213 new_tag = buf->data;
2214 checker = buf->data;
2215 SKIPWS(checker);
2216 while (*checker != '\0')
2217 {
2218 if ((*checker < 32) || (*checker >= 127) || // We allow space because it's the separator
2219 (*checker == 40) || // (
2220 (*checker == 41) || // )
2221 (*checker == 60) || // <
2222 (*checker == 62) || // >
2223 (*checker == 64) || // @
2224 (*checker == 44) || // ,
2225 (*checker == 59) || // ;
2226 (*checker == 58) || // :
2227 (*checker == 92) || // backslash
2228 (*checker == 34) || // "
2229 (*checker == 46) || // .
2230 (*checker == 91) || // [
2231 (*checker == 93)) // ]
2232 {
2233 mutt_error(_("Invalid IMAP flags"));
2234 return 0;
2235 }
2236
2237 /* Skip duplicate space */
2238 while ((checker[0] == ' ') && (checker[1] == ' '))
2239 checker++;
2240
2241 /* copy char to new_tag and go the next one */
2242 *new_tag++ = *checker++;
2243 }
2244 *new_tag = '\0';
2245 new_tag = buf->data; /* rewind */
2247
2248 return !mutt_str_equal(tags, buf_string(buf));
2249}
2250
2264static int imap_tags_commit(struct Mailbox *m, struct Email *e, const char *buf)
2265{
2266 char uid[11] = { 0 };
2267
2269
2270 if (*buf == '\0')
2271 buf = NULL;
2272
2273 if (!(adata->mailbox->rights & MUTT_ACL_WRITE))
2274 return 0;
2275
2276 snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
2277
2278 /* Remove old custom flags */
2279 if (imap_edata_get(e)->flags_remote)
2280 {
2281 struct Buffer cmd = buf_make(128); // just a guess
2282 buf_addstr(&cmd, "UID STORE ");
2283 buf_addstr(&cmd, uid);
2284 buf_addstr(&cmd, " -FLAGS.SILENT (");
2285 buf_addstr(&cmd, imap_edata_get(e)->flags_remote);
2286 buf_addstr(&cmd, ")");
2287
2288 /* Should we return here, or we are fine and we could
2289 * continue to add new flags */
2290 int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2291 buf_dealloc(&cmd);
2292 if (rc != IMAP_EXEC_SUCCESS)
2293 {
2294 return -1;
2295 }
2296 }
2297
2298 /* Add new custom flags */
2299 if (buf)
2300 {
2301 struct Buffer cmd = buf_make(128); // just a guess
2302 buf_addstr(&cmd, "UID STORE ");
2303 buf_addstr(&cmd, uid);
2304 buf_addstr(&cmd, " +FLAGS.SILENT (");
2305 buf_addstr(&cmd, buf);
2306 buf_addstr(&cmd, ")");
2307
2308 int rc = imap_exec(adata, cmd.data, IMAP_CMD_NO_FLAGS);
2309 buf_dealloc(&cmd);
2310 if (rc != IMAP_EXEC_SUCCESS)
2311 {
2312 mutt_debug(LL_DEBUG1, "fail to add new flags\n");
2313 return -1;
2314 }
2315 }
2316
2317 /* We are good sync them */
2318 mutt_debug(LL_DEBUG1, "NEW TAGS: %s\n", buf);
2319 driver_tags_replace(&e->tags, buf);
2320 FREE(&imap_edata_get(e)->flags_remote);
2323 return 0;
2324}
2325
2329enum MailboxType imap_path_probe(const char *path, const struct stat *st)
2330{
2331 if (mutt_istr_startswith(path, "imap://"))
2332 return MUTT_IMAP;
2333
2334 if (mutt_istr_startswith(path, "imaps://"))
2335 return MUTT_IMAP;
2336
2337 return MUTT_UNKNOWN;
2338}
2339
2343int imap_path_canon(struct Buffer *path)
2344{
2345 struct Url *url = url_parse(buf_string(path));
2346 if (!url)
2347 return 0;
2348
2349 char tmp[PATH_MAX] = { 0 };
2350 char tmp2[PATH_MAX];
2351
2352 imap_fix_path('\0', url->path, tmp, sizeof(tmp));
2353 url->path = tmp;
2354 url_tostring(url, tmp2, sizeof(tmp2), U_NO_FLAGS);
2355 buf_strcpy(path, tmp2);
2356 url_free(&url);
2357
2358 return 0;
2359}
2360
2370{
2372 return imap_path_canon(path);
2373}
2374
2378static int imap_path_pretty(struct Buffer *path, const char *folder)
2379{
2380 if (!folder)
2381 return -1;
2382
2383 imap_pretty_mailbox(path->data, path->dsize, folder);
2384 return 0;
2385}
2386
2390static int imap_path_parent(struct Buffer *path)
2391{
2392 char tmp[PATH_MAX] = { 0 };
2393
2394 imap_get_parent_path(buf_string(path), tmp, sizeof(tmp));
2395 buf_strcpy(path, tmp);
2396 return 0;
2397}
2398
2402static int imap_path_is_empty(struct Buffer *path)
2403{
2404 int rc = imap_path_status(buf_string(path), false);
2405 if (rc < 0)
2406 return -1;
2407 if (rc == 0)
2408 return 1;
2409 return 0;
2410}
2411
2415const struct MxOps MxImapOps = {
2416 // clang-format off
2417 .type = MUTT_IMAP,
2418 .name = "imap",
2419 .is_local = false,
2420 .ac_owns_path = imap_ac_owns_path,
2421 .ac_add = imap_ac_add,
2422 .mbox_open = imap_mbox_open,
2423 .mbox_open_append = imap_mbox_open_append,
2424 .mbox_check = imap_mbox_check,
2425 .mbox_check_stats = imap_mbox_check_stats,
2426 .mbox_sync = NULL, /* imap syncing is handled by imap_sync_mailbox */
2427 .mbox_close = imap_mbox_close,
2428 .msg_open = imap_msg_open,
2429 .msg_open_new = imap_msg_open_new,
2430 .msg_commit = imap_msg_commit,
2431 .msg_close = imap_msg_close,
2432 .msg_padding_size = NULL,
2433 .msg_save_hcache = imap_msg_save_hcache,
2434 .tags_edit = imap_tags_edit,
2435 .tags_commit = imap_tags_commit,
2436 .path_probe = imap_path_probe,
2437 .path_canon = imap_path_canon,
2438 .path_pretty = imap_path_pretty,
2439 .path_parent = imap_path_parent,
2440 .path_is_empty = imap_path_is_empty,
2441 // clang-format on
2442};
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition: array.h:278
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition: array.h:155
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:86
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
const char * mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition: atoi.c:291
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:213
IMAP authenticator multiplexor.
@ IMAP_AUTH_SUCCESS
Authentication successful.
Definition: auth.h:39
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:216
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:466
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:389
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:88
struct Buffer buf_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:70
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition: buffer.c:194
struct Buffer * buf_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:55
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:253
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:238
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:407
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition: buffer.c:542
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition: buffer.c:349
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
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:176
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.
Enter 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:40
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition: external.h:51
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:636
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition: gnutls.c:1144
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:1545
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:1206
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:275
#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
static bool imap_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add() -.
Definition: imap.c:1676
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:1658
const struct MxOps MxImapOps
IMAP Mailbox - Implements MxOps -.
Definition: imap.c:2415
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:1140
static enum MxStatus imap_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition: imap.c:2096
static enum MxStatus imap_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition: imap.c:2110
static bool imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition: imap.c:2061
static enum MxOpenReturns imap_mbox_open(struct Mailbox *m)
Open a mailbox - Implements MxOps::mbox_open() -.
Definition: imap.c:1855
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition: message.c:2163
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition: message.c:2149
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:2152
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:1956
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:2171
int imap_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition: imap.c:2343
static int imap_path_is_empty(struct Buffer *path)
Is the mailbox empty - Implements MxOps::path_is_empty() -.
Definition: imap.c:2402
static int imap_path_parent(struct Buffer *path)
Find the parent of a Mailbox path - Implements MxOps::path_parent() -.
Definition: imap.c:2390
static int imap_path_pretty(struct Buffer *path, const char *folder)
Abbreviate a Mailbox path - Implements MxOps::path_pretty() -.
Definition: imap.c:2378
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2329
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:2264
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:2177
int imap_sort_uid(const void *a, const void *b, void *sdata)
Compare two UIDs - Implements sort_t -.
Definition: msg_set.c:54
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:892
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:54
void mutt_account_hook(const char *url)
Perform an account hook.
Definition: hook.c:846
Parse and execute user-defined hooks.
void imap_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free()
Definition: adata.c:69
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:110
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition: command.c:1115
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition: command.c:1267
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition: command.c:1436
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition: command.c:1129
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:1304
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition: command.c:1369
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition: edata.c:66
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free()
Definition: edata.c:39
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:471
void imap_get_parent_path(const char *path, char *buf, size_t buflen)
Get the path of the parent folder.
Definition: util.c:160
void imap_pretty_mailbox(char *path, size_t pathlen, const char *folder)
Prettify an IMAP mailbox name.
Definition: util.c:578
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
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free()
Definition: mdata.c:39
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition: message.c:1867
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition: message.c:1848
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:1326
#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:813
#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:1023
void imap_disallow_reopen(struct Mailbox *m)
Disallow re-opening a folder upon expunge.
Definition: util.c:1036
@ 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:377
#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:105
#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:844
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:643
#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:827
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:676
#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:654
#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:337
@ 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:395
#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:71
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition: util.c:1051
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:918
#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:296
#define IMAP_RES_CONTINUE
* ...
Definition: private.h:56
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:782
#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:765
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition: imap.c:519
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1192
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1157
void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
Inform IMAP that an Email has been deleted.
Definition: imap.c:640
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition: imap.c:841
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:180
int imap_subscribe(char *path, bool subscribe)
Subscribe to a mailbox.
Definition: imap.c:1208
int imap_expand_path(struct Buffer *path)
Buffer wrapper around imap_path_canon()
Definition: imap.c:2369
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1090
int imap_complete(struct Buffer *buf, const char *path)
Try to complete an IMAP folder path.
Definition: imap.c:1263
int imap_delete_mailbox(struct Mailbox *m, char *path)
Delete a mailbox.
Definition: imap.c:497
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition: imap.c:661
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:341
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:733
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:293
int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
Rename a mailbox.
Definition: imap.c:470
static int complete_hosts(struct Buffer *buf)
Look for completion matches for mailboxes.
Definition: imap.c:364
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition: imap.c:429
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition: imap.c:99
int imap_fast_trash(struct Mailbox *m, const char *dest)
Use server COPY command to copy deleted messages to trash.
Definition: imap.c:1344
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:918
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:223
enum MxStatus imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
Sync all the changes to the server.
Definition: imap.c:1459
int imap_access(const char *path)
Check permissions on an IMAP mailbox with a new connection.
Definition: imap.c:455
void imap_logout_all(void)
Close all open connections.
Definition: imap.c:546
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition: imap.c:1019
static char * get_flags(struct ListHead *hflags, char *s)
Make a simple list out of a FLAGS response.
Definition: imap.c:125
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:582
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition: imap.c:866
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition: imap.c:1732
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition: imap.c:1765
static const struct Command ImapCommands[]
Imap Commands.
Definition: imap.c:78
void imap_init(void)
Setup feature commands.
Definition: imap.c:88
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition: imap.c:196
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 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:177
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition: mailbox.h:178
#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:210
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
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:132
IMAP Message Sets.
void imap_msn_remove(struct MSNArray *msn, size_t idx)
Remove an entry from the cache.
Definition: msn.c:113
IMAP MSN helper functions.
time_t mutt_date_now(void)
Return the number of seconds since the Unix epoch.
Definition: date.c:446
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:637
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:810
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:251
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:798
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:497
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:228
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
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:653
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:240
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:525
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:327
char * mutt_str_cat(char *buf, size_t buflen, const char *s)
Concatenate two strings.
Definition: string.c:266
Many unsorted constants and some structs.
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:55
MessageType
To set flags or match patterns.
Definition: mutt.h:66
@ MUTT_TRASH
Trashed messages.
Definition: mutt.h:84
@ MUTT_READ
Messages that have been read.
Definition: mutt.h:72
@ MUTT_OLD
Old messages.
Definition: mutt.h:70
@ MUTT_FLAG
Flagged messages.
Definition: mutt.h:78
@ MUTT_DELETED
Deleted messages.
Definition: mutt.h:77
@ MUTT_REPLIED
Messages that have been replied to.
Definition: mutt.h:71
#define PATH_MAX
Definition: mutt.h:41
void mutt_account_tourl(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
Definition: mutt_account.c:79
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
Definition: mutt_account.h:37
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
NeoMutt Logging.
struct Connection * mutt_conn_new(const struct ConnAccount *cac)
Create a new Connection.
Definition: mutt_socket.c:48
NeoMutt connections.
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:562
Some miscellaneous functions.
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition: mx.c:1232
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition: mx.c:1801
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition: mx.c:1666
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition: mx.c:267
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1697
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_snc(), 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:162
size_t neomutt_mailboxlist_get_all(struct MailboxList *head, struct NeoMutt *n, enum MailboxType type)
Get a List of all Mailboxes.
Definition: neomutt.c:185
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:73
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:66
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:345
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:369
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:330
#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:101
int mutt_socket_poll(struct Connection *conn, time_t wait_secs)
Checks whether reads would block.
Definition: socket.c:196
int mutt_socket_readchar(struct Connection *conn, char *c)
Simple read buffering to speed things up.
Definition: socket.c:214
void mutt_socket_empty(struct Connection *conn)
Clear out any queued data.
Definition: socket.c:320
int mutt_socket_open(struct Connection *conn)
Simple wrapper.
Definition: socket.c:77
Assorted sorting methods.
#define mutt_numeric_cmp(a, b)
Definition: sort.h:34
Key value store.
#define SKIPWS(ch)
Definition: string2.h:45
A group of associated Mailboxes.
Definition: account.h:37
enum MailboxType type
Type of Mailboxes this Account contains.
Definition: account.h:38
char * name
Name of Account.
Definition: account.h:39
void(* adata_free)(void **ptr)
Free the private data attached to the Account.
Definition: account.h:52
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
String manipulation buffer.
Definition: buffer.h:34
size_t dsize
Length of data.
Definition: buffer.h:37
char * data
Pointer to data.
Definition: buffer.h:35
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:51
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
int fd
Socket file descriptor.
Definition: connection.h:54
The envelope/body of an email.
Definition: email.h:37
bool read
Email is read.
Definition: email.h:48
bool purge
Skip trash folder when deleting.
Definition: email.h:77
struct Envelope * env
Envelope information.
Definition: email.h:66
void * edata
Driver-specific data.
Definition: email.h:72
bool active
Message is not to be removed.
Definition: email.h:74
bool old
Email is seen, but unread.
Definition: email.h:47
bool changed
Email has been edited.
Definition: email.h:75
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:98
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
char * path
Path of Email (for local Mailboxes)
Definition: email.h:68
bool deleted
Email is deleted.
Definition: email.h:76
int index
The absolute (unsorted) message number.
Definition: email.h:109
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:92
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:34
unsigned int uid
32-bit Message UID
Definition: edata.h:44
char * flags_remote
Definition: edata.h:48
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:153
struct Mailbox * mailbox
Mailbox in the list.
Definition: mailbox.h:154
A mailbox.
Definition: mailbox.h:79
void(* mdata_free)(void **ptr)
Free the private data attached to the Mailbox.
Definition: mailbox.h:142
int vcount
The number of virtual messages.
Definition: mailbox.h:99
bool changed
Mailbox has been modified.
Definition: mailbox.h:109
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:108
int msg_new
Number of new messages.
Definition: mailbox.h:92
int msg_count
Total number of messages.
Definition: mailbox.h:88
AclFlags rights
ACL bits, see AclFlags.
Definition: mailbox.h:118
bool poll_new_mail
Check for new mail.
Definition: mailbox.h:114
void * mdata
Driver specific data.
Definition: mailbox.h:133
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:128
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:115
bool verbose
Display status messages?
Definition: mailbox.h:116
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 *head, const char *tags)
Replace all tags.
Definition: tags.c:186
char * driver_tags_get_with_hidden(struct TagList *list)
Get tags with hiddens.
Definition: tags.c:158
#define buf_mktemp(buf)
Definition: tmp.h:33
struct Url * url_parse(const char *src)
Fill in Url.
Definition: url.c:238
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:422
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition: url.c:123
#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:288