NeoMutt  2025-12-11-58-g09398d
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
imap.c
Go to the documentation of this file.
1
33
41
42#include "config.h"
43#include <limits.h>
44#include <stdbool.h>
45#include <stdint.h>
46#include <stdio.h>
47#include <string.h>
48#include "private.h"
49#include "mutt/lib.h"
50#include "config/lib.h"
51#include "email/lib.h"
52#include "core/lib.h"
53#include "conn/lib.h"
54#include "mutt.h"
55#include "lib.h"
56#include "editor/lib.h"
57#include "history/lib.h"
58#include "parse/lib.h"
59#include "progress/lib.h"
60#include "question/lib.h"
61#include "adata.h"
62#include "auth.h"
63#include "commands.h"
64#include "edata.h"
65#include "external.h"
66#include "hook.h"
67#include "mdata.h"
68#include "msg_set.h"
69#include "msn.h"
70#include "mutt_logging.h"
71#include "mutt_socket.h"
72#include "muttlib.h"
73#include "mx.h"
74#ifdef ENABLE_NLS
75#include <libintl.h>
76#endif
77
78struct Progress;
79struct stat;
80
84static const struct Command ImapCommands[] = {
85 // clang-format off
86 { "subscribe-to", parse_subscribe_to, 0,
87 N_("Subscribe to an IMAP mailbox"),
88 N_("subscribe-to <imap-folder-uri>"),
89 "optionalfeatures.html#imap" },
90 { "unsubscribe-from", parse_unsubscribe_from, 0,
91 N_("Unsubscribe from an IMAP mailbox"),
92 N_("unsubscribe-from <imap-folder-uri>"),
93 "optionalfeatures.html#imap" },
94
95 { NULL, NULL, 0, NULL, NULL, NULL, CF_NO_FLAGS },
96 // clang-format on
97};
98
106
113static int check_capabilities(struct ImapAccountData *adata)
114{
115 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
116 {
117 imap_error("check_capabilities", adata->buf);
118 return -1;
119 }
120
121 if (!((adata->capabilities & IMAP_CAP_IMAP4) || (adata->capabilities & IMAP_CAP_IMAP4REV1)))
122 {
123 mutt_error(_("This IMAP server is ancient. NeoMutt does not work with it."));
124 return -1;
125 }
126
127 return 0;
128}
129
139static char *get_flags(struct ListHead *hflags, char *s)
140{
141 /* sanity-check string */
142 const size_t plen = mutt_istr_startswith(s, "FLAGS");
143 if (plen == 0)
144 {
145 mutt_debug(LL_DEBUG1, "not a FLAGS response: %s\n", s);
146 return NULL;
147 }
148 s += plen;
149 SKIPWS(s);
150 if (*s != '(')
151 {
152 mutt_debug(LL_DEBUG1, "bogus FLAGS response: %s\n", s);
153 return NULL;
154 }
155
156 /* update caller's flags handle */
157 while (*s && (*s != ')'))
158 {
159 s++;
160 SKIPWS(s);
161 const char *flag_word = s;
162 while (*s && (*s != ')') && !mutt_isspace(*s))
163 s++;
164 const char ctmp = *s;
165 *s = '\0';
166 if (*flag_word)
167 mutt_list_insert_tail(hflags, mutt_str_dup(flag_word));
168 *s = ctmp;
169 }
170
171 /* note bad flags response */
172 if (*s != ')')
173 {
174 mutt_debug(LL_DEBUG1, "Unterminated FLAGS response: %s\n", s);
175 mutt_list_free(hflags);
176
177 return NULL;
178 }
179
180 s++;
181
182 return s;
183}
184
193static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag,
194 const char *str, struct Buffer *flags)
195{
196 struct ImapMboxData *mdata = imap_mdata_get(m);
197 if (!mdata)
198 return;
199
200 if (m->rights & aclflag)
201 if (flag && imap_has_flag(&mdata->flags, str))
202 buf_addstr(flags, str);
203}
204
213static bool compare_flags_for_copy(struct Email *e)
214{
215 struct ImapEmailData *edata = e->edata;
216
217 if (e->read != edata->read)
218 return true;
219 if (e->old != edata->old)
220 return true;
221 if (e->flagged != edata->flagged)
222 return true;
223 if (e->replied != edata->replied)
224 return true;
225
226 return false;
227}
228
240static int select_email_uids(struct Email **emails, int num_emails, enum MessageType flag,
241 bool changed, bool invert, struct UidArray *uida)
242{
243 if (!emails || !uida)
244 return -1;
245
246 for (int i = 0; i < num_emails; i++)
247 {
248 struct Email *e = emails[i];
249 if (changed && !e->changed)
250 continue;
251
252 /* don't include pending expunged messages.
253 *
254 * TODO: can we unset active in cmd_parse_expunge() and
255 * cmd_parse_vanished() instead of checking for index != INT_MAX. */
256 if (!e || !e->active || (e->index == INT_MAX))
257 continue;
258
260
261 bool match = false;
262 switch (flag)
263 {
264 case MUTT_DELETED:
265 if (e->deleted != edata->deleted)
266 match = invert ^ e->deleted;
267 break;
268 case MUTT_FLAG:
269 if (e->flagged != edata->flagged)
270 match = invert ^ e->flagged;
271 break;
272 case MUTT_OLD:
273 if (e->old != edata->old)
274 match = invert ^ e->old;
275 break;
276 case MUTT_READ:
277 if (e->read != edata->read)
278 match = invert ^ e->read;
279 break;
280 case MUTT_REPLIED:
281 if (e->replied != edata->replied)
282 match = invert ^ e->replied;
283 break;
284 case MUTT_TRASH:
285 if (e->deleted && !e->purge)
286 match = true;
287 break;
288 default:
289 break;
290 }
291
292 if (match)
293 ARRAY_ADD(uida, edata->uid);
294 }
295
296 return ARRAY_SIZE(uida);
297}
298
310static int sync_helper(struct Mailbox *m, struct Email **emails, int num_emails,
311 AclFlags right, enum MessageType flag, const char *name)
312{
314 struct ImapMboxData *mdata = imap_mdata_get(m);
315 if (!adata || !mdata)
316 return -1;
317
318 if ((m->rights & right) == 0)
319 return 0;
320
321 if ((right == MUTT_ACL_WRITE) && !imap_has_flag(&mdata->flags, name))
322 return 0;
323
324 int count = 0;
325 char buf[1024] = { 0 };
326
327 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
328
329 // Set the flag (+FLAGS) on matching emails
330 select_email_uids(emails, num_emails, flag, true, false, &uida);
331 snprintf(buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
332 int rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
333 if (rc < 0)
334 return rc;
335 count += rc;
336 ARRAY_FREE(&uida);
337
338 // Clear the flag (-FLAGS) on non-matching emails
339 select_email_uids(emails, num_emails, flag, true, true, &uida);
340 buf[0] = '-';
341 rc = imap_exec_msg_set(adata, "UID STORE", buf, &uida);
342 if (rc < 0)
343 return rc;
344 count += rc;
345 ARRAY_FREE(&uida);
346
347 return count;
348}
349
359static size_t longest_common_prefix(struct Buffer *buf, const char *src, size_t start)
360{
361 size_t pos = start;
362
363 size_t len = buf_len(buf);
364 while ((pos < len) && (buf_at(buf, pos) != '\0') && (buf_at(buf, pos) == src[pos]))
365 pos++;
366 buf->data[pos] = '\0';
367
368 buf_fix_dptr(buf);
369
370 return pos;
371}
372
382static int complete_hosts(struct Buffer *buf)
383{
384 int rc = -1;
385 size_t matchlen;
386
387 matchlen = buf_len(buf);
388 struct MailboxArray ma = neomutt_mailboxes_get(NeoMutt, MUTT_MAILBOX_ANY);
389 struct Mailbox **mp = NULL;
390 ARRAY_FOREACH(mp, &ma)
391 {
392 struct Mailbox *m = *mp;
393
395 continue;
396
397 if (rc)
398 {
399 buf_strcpy(buf, mailbox_path(m));
400 rc = 0;
401 }
402 else
403 {
404 longest_common_prefix(buf, mailbox_path(m), matchlen);
405 }
406 }
407 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
408
409#if 0
410 TAILQ_FOREACH(conn, mutt_socket_head(), entries)
411 {
412 struct Url url = { 0 };
413 char urlstr[1024] = { 0 };
414
415 if (conn->account.type != MUTT_ACCT_TYPE_IMAP)
416 continue;
417
418 account_to_url(&conn->account, &url);
419 /* FIXME: how to handle multiple users on the same host? */
420 url.user = NULL;
421 url.path = NULL;
422 url_tostring(&url, urlstr, sizeof(urlstr), U_NO_FLAGS);
423 if (mutt_strn_equal(buf, urlstr, matchlen))
424 {
425 if (rc)
426 {
427 mutt_str_copy(buf, urlstr, buflen);
428 rc = 0;
429 }
430 else
431 {
432 longest_common_prefix(buf, urlstr, matchlen);
433 }
434 }
435 }
436#endif
437
438 return rc;
439}
440
448int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
449{
450 char buf[2048] = { 0 };
451 char mbox[1024] = { 0 };
452
453 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), mailbox);
454 snprintf(buf, sizeof(buf), "CREATE %s", mbox);
455
457 {
458 mutt_error(_("CREATE failed: %s"), imap_cmd_trailer(adata));
459 return -1;
460 }
461
462 return 0;
463}
464
475int imap_access(const char *path)
476{
477 if (imap_path_status(path, false) >= 0)
478 return 0;
479 return -1;
480}
481
490int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
491{
492 char oldmbox[1024] = { 0 };
493 char newmbox[1024] = { 0 };
494 int rc = 0;
495
496 imap_munge_mbox_name(adata->unicode, oldmbox, sizeof(oldmbox), oldname);
497 imap_munge_mbox_name(adata->unicode, newmbox, sizeof(newmbox), newname);
498
499 struct Buffer *buf = buf_pool_get();
500 buf_printf(buf, "RENAME %s %s", oldmbox, newmbox);
501
503 rc = -1;
504
505 buf_pool_release(&buf);
506
507 return rc;
508}
509
517int imap_delete_mailbox(struct Mailbox *m, char *path)
518{
519 char buf[PATH_MAX + 7];
520 char mbox[PATH_MAX] = { 0 };
521
523 if (!adata)
524 return -1;
525
526 struct Url *url = url_parse(path);
527 if (!url)
528 return -1;
529
530 imap_munge_mbox_name(adata->unicode, mbox, sizeof(mbox), url->path);
531 url_free(&url);
532 snprintf(buf, sizeof(buf), "DELETE %s", mbox);
534 return -1;
535
536 return 0;
537}
538
543static void imap_logout(struct ImapAccountData *adata)
544{
545 if (adata->status != IMAP_FATAL)
546 {
547 /* we set status here to let imap_handle_untagged know we _expect_ to
548 * receive a bye response (so it doesn't freak out and close the conn) */
549 if (adata->state == IMAP_DISCONNECTED)
550 {
551 return;
552 }
553
554 adata->status = IMAP_BYE;
555 imap_cmd_start(adata, "LOGOUT");
556 const short c_imap_poll_timeout = cs_subset_number(NeoMutt->sub, "imap_poll_timeout");
557 if ((c_imap_poll_timeout <= 0) ||
558 (mutt_socket_poll(adata->conn, c_imap_poll_timeout) != 0))
559 {
560 while (imap_cmd_step(adata) == IMAP_RES_CONTINUE)
561 ; // do nothing
562 }
563 }
564 mutt_socket_close(adata->conn);
565 adata->state = IMAP_DISCONNECTED;
566}
567
574{
575 struct Account **ap = NULL;
577 {
578 struct Account *a = *ap;
579 if (a->type != MUTT_IMAP)
580 continue;
581
582 struct ImapAccountData *adata = a->adata;
583 if (!adata)
584 continue;
585
586 struct Connection *conn = adata->conn;
587 if (!conn || (conn->fd < 0))
588 continue;
589
590 mutt_message(_("Closing connection to %s..."), conn->account.host);
591 imap_logout(a->adata);
593 }
594}
595
610int imap_read_literal(FILE *fp, struct ImapAccountData *adata,
611 unsigned long bytes, struct Progress *progress)
612{
613 char c;
614 bool r = false;
615 struct Buffer buf = { 0 }; // Do not allocate, maybe it won't be used
616
617 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
618 if (c_debug_level >= IMAP_LOG_LTRL)
619 buf_alloc(&buf, bytes + 1);
620
621 mutt_debug(LL_DEBUG2, "reading %lu bytes\n", bytes);
622
623 for (unsigned long pos = 0; pos < bytes; pos++)
624 {
625 if (mutt_socket_readchar(adata->conn, &c) != 1)
626 {
627 mutt_debug(LL_DEBUG1, "error during read, %lu bytes read\n", pos);
628 adata->status = IMAP_FATAL;
629
630 buf_dealloc(&buf);
631 return -1;
632 }
633
634 if (r && (c != '\n'))
635 fputc('\r', fp);
636
637 if (c == '\r')
638 {
639 r = true;
640 continue;
641 }
642 else
643 {
644 r = false;
645 }
646
647 fputc(c, fp);
648
649 if ((pos % 1024) == 0)
650 progress_update(progress, pos, -1);
651 if (c_debug_level >= IMAP_LOG_LTRL)
652 buf_addch(&buf, c);
653 }
654
655 if (c_debug_level >= IMAP_LOG_LTRL)
656 {
657 mutt_debug(IMAP_LOG_LTRL, "\n%s", buf.data);
658 buf_dealloc(&buf);
659 }
660 return 0;
661}
662
668void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
669{
670 struct ImapMboxData *mdata = imap_mdata_get(m);
672
673 if (!mdata || !edata)
674 return;
675
676 imap_msn_remove(&mdata->msn, edata->msn - 1);
677 edata->msn = 0;
678}
679
689void imap_expunge_mailbox(struct Mailbox *m, bool resort)
690{
692 struct ImapMboxData *mdata = imap_mdata_get(m);
693 if (!adata || !mdata)
694 return;
695
696 struct Email *e = NULL;
697
698#ifdef USE_HCACHE
699 imap_hcache_open(adata, mdata, false);
700#endif
701
702 for (int i = 0; i < m->msg_count; i++)
703 {
704 e = m->emails[i];
705 if (!e)
706 break;
707
708 if (e->index == INT_MAX)
709 {
710 mutt_debug(LL_DEBUG2, "Expunging message UID %u\n", imap_edata_get(e)->uid);
711
712 e->deleted = true;
713
714 imap_cache_del(m, e);
715#ifdef USE_HCACHE
716 imap_hcache_del(mdata, imap_edata_get(e)->uid);
717#endif
718
719 mutt_hash_int_delete(mdata->uid_hash, imap_edata_get(e)->uid, e);
720
721 imap_edata_free((void **) &e->edata);
722 }
723 else
724 {
725 /* NeoMutt has several places where it turns off e->active as a
726 * hack. For example to avoid FLAG updates, or to exclude from
727 * imap_exec_msg_set.
728 *
729 * Unfortunately, when a reopen is allowed and the IMAP_EXPUNGE_PENDING
730 * flag becomes set (e.g. a flag update to a modified header),
731 * this function will be called by imap_cmd_finish().
732 *
733 * The ctx_update_tables() will free and remove these "inactive" headers,
734 * despite that an EXPUNGE was not received for them.
735 * This would result in memory leaks and segfaults due to dangling
736 * pointers in the msn_index and uid_hash.
737 *
738 * So this is another hack to work around the hacks. We don't want to
739 * remove the messages, so make sure active is on. */
740 e->active = true;
741 }
742 }
743
744#ifdef USE_HCACHE
745 imap_hcache_close(mdata);
746#endif
747
749 if (resort)
750 {
752 }
753}
754
762{
763 if (mutt_socket_open(adata->conn) < 0)
764 return -1;
765
766 adata->state = IMAP_CONNECTED;
767
768 if (imap_cmd_step(adata) != IMAP_RES_OK)
769 {
771 return -1;
772 }
773
774 if (mutt_istr_startswith(adata->buf, "* OK"))
775 {
776 if (!mutt_istr_startswith(adata->buf, "* OK [CAPABILITY") && check_capabilities(adata))
777 {
778 goto bail;
779 }
780#ifdef USE_SSL
781 /* Attempt STARTTLS if available and desired. */
782 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
783 if ((adata->conn->ssf == 0) &&
784 (c_ssl_force_tls || (adata->capabilities & IMAP_CAP_STARTTLS)))
785 {
786 enum QuadOption ans;
787
788 if (c_ssl_force_tls)
789 {
790 ans = MUTT_YES;
791 }
792 else if ((ans = query_quadoption(_("Secure connection with TLS?"),
793 NeoMutt->sub, "ssl_starttls")) == MUTT_ABORT)
794 {
795 goto bail;
796 }
797 if (ans == MUTT_YES)
798 {
799 enum ImapExecResult rc = imap_exec(adata, "STARTTLS", IMAP_CMD_SINGLE);
800 // Clear any data after the STARTTLS acknowledgement
801 mutt_socket_empty(adata->conn);
802
803 if (rc == IMAP_EXEC_FATAL)
804 goto bail;
805 if (rc != IMAP_EXEC_ERROR)
806 {
807 if (mutt_ssl_starttls(adata->conn))
808 {
809 mutt_error(_("Could not negotiate TLS connection"));
810 goto bail;
811 }
812 else
813 {
814 /* RFC2595 demands we recheck CAPABILITY after TLS completes. */
815 if (imap_exec(adata, "CAPABILITY", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
816 goto bail;
817 }
818 }
819 }
820 }
821
822 if (c_ssl_force_tls && (adata->conn->ssf == 0))
823 {
824 mutt_error(_("Encrypted connection unavailable"));
825 goto bail;
826 }
827#endif
828 }
829 else if (mutt_istr_startswith(adata->buf, "* PREAUTH"))
830 {
831#ifdef USE_SSL
832 /* Unless using a secure $tunnel, an unencrypted PREAUTH response may be a
833 * MITM attack. The only way to stop "STARTTLS" MITM attacks is via
834 * $ssl_force_tls: an attacker can easily spoof "* OK" and strip the
835 * STARTTLS capability. So consult $ssl_force_tls, not $ssl_starttls, to
836 * decide whether to abort. Note that if using $tunnel and
837 * $tunnel_is_secure, adata->conn->ssf will be set to 1. */
838 const bool c_ssl_force_tls = cs_subset_bool(NeoMutt->sub, "ssl_force_tls");
839 if ((adata->conn->ssf == 0) && c_ssl_force_tls)
840 {
841 mutt_error(_("Encrypted connection unavailable"));
842 goto bail;
843 }
844#endif
845
846 adata->state = IMAP_AUTHENTICATED;
847 if (check_capabilities(adata) != 0)
848 goto bail;
849 FREE(&adata->capstr);
850 }
851 else
852 {
853 imap_error("imap_open_connection()", adata->buf);
854 goto bail;
855 }
856
857 return 0;
858
859bail:
861 FREE(&adata->capstr);
862 return -1;
863}
864
870{
871 if (adata->state != IMAP_DISCONNECTED)
872 {
873 mutt_socket_close(adata->conn);
874 adata->state = IMAP_DISCONNECTED;
875 }
876 adata->seqno = 0;
877 adata->nextcmd = 0;
878 adata->lastcmd = 0;
879 adata->status = 0;
880 memset(adata->cmds, 0, sizeof(struct ImapCommand) * adata->cmdslots);
881}
882
894bool imap_has_flag(struct ListHead *flag_list, const char *flag)
895{
896 if (STAILQ_EMPTY(flag_list))
897 return false;
898
899 const size_t flaglen = mutt_str_len(flag);
900 struct ListNode *np = NULL;
901 STAILQ_FOREACH(np, flag_list, entries)
902 {
903 const size_t nplen = strlen(np->data);
904 if ((flaglen >= nplen) && ((flag[nplen] == '\0') || (flag[nplen] == ' ')) &&
905 mutt_istrn_equal(np->data, flag, nplen))
906 {
907 return true;
908 }
909
910 if (mutt_str_equal(np->data, "\\*"))
911 return true;
912 }
913
914 return false;
915}
916
920static int imap_sort_email_uid(const void *a, const void *b, void *sdata)
921{
922 const struct Email *ea = *(struct Email const *const *) a;
923 const struct Email *eb = *(struct Email const *const *) b;
924
925 const unsigned int ua = imap_edata_get((struct Email *) ea)->uid;
926 const unsigned int ub = imap_edata_get((struct Email *) eb)->uid;
927
928 return mutt_numeric_cmp(ua, ub);
929}
930
946int imap_sync_message_for_copy(struct Mailbox *m, struct Email *e,
947 struct Buffer *cmd, enum QuadOption *err_continue)
948{
951
952 if (!adata || (adata->mailbox != m) || !e)
953 return -1;
954
956 {
957 if (e->deleted == edata->deleted)
958 e->changed = false;
959 return 0;
960 }
961
962 buf_printf(cmd, "UID STORE %u", edata->uid);
963
964 struct Buffer *flags = buf_pool_get();
965
966 set_flag(m, MUTT_ACL_SEEN, e->read, "\\Seen ", flags);
967 set_flag(m, MUTT_ACL_WRITE, e->old, "Old ", flags);
968 set_flag(m, MUTT_ACL_WRITE, e->flagged, "\\Flagged ", flags);
969 set_flag(m, MUTT_ACL_WRITE, e->replied, "\\Answered ", flags);
970 set_flag(m, MUTT_ACL_DELETE, edata->deleted, "\\Deleted ", flags);
971
972 if (m->rights & MUTT_ACL_WRITE)
973 {
974 /* restore system flags */
975 if (edata->flags_system)
976 buf_addstr(flags, edata->flags_system);
977
978 /* set custom flags */
979 struct Buffer *tags = buf_pool_get();
981 if (!buf_is_empty(tags))
982 buf_addstr(flags, buf_string(tags));
983 buf_pool_release(&tags);
984 }
985
987 buf_fix_dptr(flags);
988
989 /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
990 * explicitly revoke all system flags (if we have permission) */
991 if (buf_is_empty(flags))
992 {
993 set_flag(m, MUTT_ACL_SEEN, true, "\\Seen ", flags);
994 set_flag(m, MUTT_ACL_WRITE, true, "Old ", flags);
995 set_flag(m, MUTT_ACL_WRITE, true, "\\Flagged ", flags);
996 set_flag(m, MUTT_ACL_WRITE, true, "\\Answered ", flags);
997 set_flag(m, MUTT_ACL_DELETE, !edata->deleted, "\\Deleted ", flags);
998
999 /* erase custom flags */
1000 if ((m->rights & MUTT_ACL_WRITE) && edata->flags_remote)
1001 buf_addstr(flags, edata->flags_remote);
1002
1004 buf_fix_dptr(flags);
1005
1006 buf_addstr(cmd, " -FLAGS.SILENT (");
1007 }
1008 else
1009 {
1010 buf_addstr(cmd, " FLAGS.SILENT (");
1011 }
1012
1013 buf_addstr(cmd, buf_string(flags));
1014 buf_addstr(cmd, ")");
1015
1016 int rc = -1;
1017
1018 /* after all this it's still possible to have no flags, if you
1019 * have no ACL rights */
1020 if (!buf_is_empty(flags) &&
1022 err_continue && (*err_continue != MUTT_YES))
1023 {
1024 *err_continue = imap_continue("imap_sync_message: STORE failed", adata->buf);
1025 if (*err_continue != MUTT_YES)
1026 goto done;
1027 }
1028
1029 /* server have now the updated flags */
1030 FREE(&edata->flags_remote);
1031 struct Buffer *flags_remote = buf_pool_get();
1032 driver_tags_get_with_hidden(&e->tags, flags_remote);
1033 edata->flags_remote = buf_strdup(flags_remote);
1034 buf_pool_release(&flags_remote);
1035
1036 if (e->deleted == edata->deleted)
1037 e->changed = false;
1038
1039 rc = 0;
1040
1041done:
1042 buf_pool_release(&flags);
1043 return rc;
1044}
1045
1052enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
1053{
1054 if (!m || !m->account)
1055 return MX_STATUS_ERROR;
1056
1058 struct ImapMboxData *mdata = imap_mdata_get(m);
1059 if (!adata || !mdata)
1060 return MX_STATUS_ERROR;
1061
1062 /* overload keyboard timeout to avoid many mailbox checks in a row.
1063 * Most users don't like having to wait exactly when they press a key. */
1064 int rc = 0;
1065
1066 /* try IDLE first, unless force is set */
1067 const bool c_imap_idle = cs_subset_bool(NeoMutt->sub, "imap_idle");
1068 const short c_imap_keep_alive = cs_subset_number(NeoMutt->sub, "imap_keep_alive");
1069 if (!force && c_imap_idle && (adata->capabilities & IMAP_CAP_IDLE) &&
1070 ((adata->state != IMAP_IDLE) || (mutt_date_now() >= adata->lastread + c_imap_keep_alive)))
1071 {
1072 if (imap_cmd_idle(adata) < 0)
1073 return MX_STATUS_ERROR;
1074 }
1075 if (adata->state == IMAP_IDLE)
1076 {
1077 while ((rc = mutt_socket_poll(adata->conn, 0)) > 0)
1078 {
1079 if (imap_cmd_step(adata) != IMAP_RES_CONTINUE)
1080 {
1081 mutt_debug(LL_DEBUG1, "Error reading IDLE response\n");
1082 return MX_STATUS_ERROR;
1083 }
1084 }
1085 if (rc < 0)
1086 {
1087 mutt_debug(LL_DEBUG1, "Poll failed, disabling IDLE\n");
1088 adata->capabilities &= ~IMAP_CAP_IDLE; // Clear the flag
1089 }
1090 }
1091
1092 const short c_timeout = cs_subset_number(NeoMutt->sub, "timeout");
1093 if ((force || ((adata->state != IMAP_IDLE) && (mutt_date_now() >= adata->lastread + c_timeout))) &&
1094 (imap_exec(adata, "NOOP", IMAP_CMD_POLL) != IMAP_EXEC_SUCCESS))
1095 {
1096 return MX_STATUS_ERROR;
1097 }
1098
1099 /* We call this even when we haven't run NOOP in case we have pending
1100 * changes to process, since we can reopen here. */
1101 imap_cmd_finish(adata);
1102
1103 enum MxStatus check = MX_STATUS_OK;
1104 if (mdata->check_status & IMAP_EXPUNGE_PENDING)
1105 check = MX_STATUS_REOPENED;
1106 else if (mdata->check_status & IMAP_NEWMAIL_PENDING)
1107 check = MX_STATUS_NEW_MAIL;
1108 else if (mdata->check_status & IMAP_FLAGS_PENDING)
1109 check = MX_STATUS_FLAGS;
1110 else if (rc < 0)
1111 check = MX_STATUS_ERROR;
1112
1113 mdata->check_status = IMAP_OPEN_NO_FLAGS;
1114
1115 if (force)
1116 m->last_checked = 0; // force a check on the next mx_mbox_check() call
1117 return check;
1118}
1119
1127static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
1128{
1129 char *uidvalidity_flag = NULL;
1130 char cmd[2048] = { 0 };
1131
1132 if (!adata || !mdata)
1133 return -1;
1134
1135 /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
1136 * IDLEd elsewhere.
1137 * adata->mailbox may be NULL for connections other than the current
1138 * mailbox's. */
1139 if (adata->mailbox && (adata->mailbox->mdata == mdata))
1140 {
1141 adata->mailbox->has_new = false;
1142 return mdata->messages;
1143 }
1144
1145 if (adata->mailbox && !adata->mailbox->poll_new_mail)
1146 return mdata->messages;
1147
1148 if (adata->capabilities & IMAP_CAP_IMAP4REV1)
1149 {
1150 uidvalidity_flag = "UIDVALIDITY";
1151 }
1152 else if (adata->capabilities & IMAP_CAP_STATUS)
1153 {
1154 uidvalidity_flag = "UID-VALIDITY";
1155 }
1156 else
1157 {
1158 mutt_debug(LL_DEBUG2, "Server doesn't support STATUS\n");
1159 return -1;
1160 }
1161
1162 snprintf(cmd, sizeof(cmd), "STATUS %s (UIDNEXT %s UNSEEN RECENT MESSAGES)",
1163 mdata->munge_name, uidvalidity_flag);
1164
1165 int rc = imap_exec(adata, cmd, queue ? IMAP_CMD_QUEUE : IMAP_CMD_POLL);
1166 if (rc != IMAP_EXEC_SUCCESS)
1167 {
1168 mutt_debug(LL_DEBUG1, "Error queueing command\n");
1169 return rc;
1170 }
1171 return mdata->messages;
1172}
1173
1177static enum MxStatus imap_mbox_check_stats(struct Mailbox *m, uint8_t flags)
1178{
1179 const bool queue = (flags & MUTT_MAILBOX_CHECK_IMMEDIATE) == 0;
1180 const int new_msgs = imap_mailbox_status(m, queue);
1181 if (new_msgs == -1)
1182 return MX_STATUS_ERROR;
1183 if (new_msgs == 0)
1184 return MX_STATUS_OK;
1185 return MX_STATUS_NEW_MAIL;
1186}
1187
1194int imap_path_status(const char *path, bool queue)
1195{
1196 struct Mailbox *m = mx_mbox_find2(path);
1197
1198 const bool is_temp = !m;
1199 if (is_temp)
1200 {
1201 m = mx_path_resolve(path);
1202 if (!mx_mbox_ac_link(m))
1203 {
1204 mailbox_free(&m);
1205 return 0;
1206 }
1207 }
1208
1209 int rc = imap_mailbox_status(m, queue);
1210
1211 if (is_temp)
1212 {
1213 mx_ac_remove(m, false);
1214 mailbox_free(&m);
1215 }
1216
1217 return rc;
1218}
1219
1229int imap_mailbox_status(struct Mailbox *m, bool queue)
1230{
1232 struct ImapMboxData *mdata = imap_mdata_get(m);
1233 if (!adata || !mdata)
1234 return -1;
1235 return imap_status(adata, mdata, queue);
1236}
1237
1245int imap_subscribe(const char *path, bool subscribe)
1246{
1247 struct ImapAccountData *adata = NULL;
1248 struct ImapMboxData *mdata = NULL;
1249
1250 if (imap_adata_find(path, &adata, &mdata) < 0)
1251 return -1;
1252
1253 if (subscribe)
1254 mutt_message(_("Subscribing to %s..."), mdata->name);
1255 else
1256 mutt_message(_("Unsubscribing from %s..."), mdata->name);
1257
1258 char buf[2048] = { 0 };
1259 snprintf(buf, sizeof(buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mdata->munge_name);
1260
1261 if (imap_exec(adata, buf, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1262 {
1263 imap_mdata_free((void *) &mdata);
1264 return -1;
1265 }
1266
1267 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1268 if (c_imap_check_subscribed)
1269 {
1270 struct Buffer *err = buf_pool_get();
1271 struct Buffer *mbox = buf_pool_get();
1272
1273 size_t len = buf_printf(mbox, "%smailboxes ", subscribe ? "" : "un");
1274 imap_quote_string(mbox->data + len, mbox->dsize - len, path, true);
1275
1276 if (parse_rc_line(mbox, err) != MUTT_CMD_SUCCESS)
1277 mutt_debug(LL_DEBUG1, "Error adding subscribed mailbox: %s\n", buf_string(err));
1278
1279 buf_pool_release(&mbox);
1280 buf_pool_release(&err);
1281 }
1282
1283 if (subscribe)
1284 mutt_message(_("Subscribed to %s"), mdata->name);
1285 else
1286 mutt_message(_("Unsubscribed from %s"), mdata->name);
1287 imap_mdata_free((void *) &mdata);
1288 return 0;
1289}
1290
1301int imap_complete(struct Buffer *buf, const char *path)
1302{
1303 struct ImapAccountData *adata = NULL;
1304 struct ImapMboxData *mdata = NULL;
1305 char tmp[2048] = { 0 };
1306 struct ImapList listresp = { 0 };
1307 struct Buffer *completion_buf = NULL;
1308 size_t clen;
1309 int completions = 0;
1310 int rc;
1311
1312 if (imap_adata_find(path, &adata, &mdata) < 0)
1313 {
1314 buf_strcpy(buf, path);
1315 return complete_hosts(buf);
1316 }
1317
1318 /* fire off command */
1319 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
1320 snprintf(tmp, sizeof(tmp), "%s \"\" \"%s%%\"",
1321 c_imap_list_subscribed ? "LSUB" : "LIST", mdata->real_name);
1322
1323 imap_cmd_start(adata, tmp);
1324
1325 /* and see what the results are */
1326 completion_buf = buf_pool_get();
1327 buf_strcpy(completion_buf, mdata->name);
1328 imap_mdata_free((void *) &mdata);
1329
1330 adata->cmdresult = &listresp;
1331 do
1332 {
1333 listresp.name = NULL;
1334 rc = imap_cmd_step(adata);
1335
1336 if ((rc == IMAP_RES_CONTINUE) && listresp.name)
1337 {
1338 /* if the folder isn't selectable, append delimiter to force browse
1339 * to enter it on second tab. */
1340 if (listresp.noselect)
1341 {
1342 clen = strlen(listresp.name);
1343 listresp.name[clen++] = listresp.delim;
1344 listresp.name[clen] = '\0';
1345 }
1346 /* copy in first word */
1347 if (!completions)
1348 {
1349 buf_strcpy(completion_buf, listresp.name);
1350 completions++;
1351 continue;
1352 }
1353
1354 longest_common_prefix(completion_buf, listresp.name, 0);
1355 completions++;
1356 }
1357 } while (rc == IMAP_RES_CONTINUE);
1358 adata->cmdresult = NULL;
1359
1360 if (completions)
1361 {
1362 /* reformat output */
1363 imap_buf_qualify_path(buf, &adata->conn->account, completion_buf->data);
1364 buf_pretty_mailbox(buf);
1365 buf_fix_dptr(buf);
1366 buf_pool_release(&completion_buf);
1367 return 0;
1368 }
1369
1370 buf_pool_release(&completion_buf);
1371 return -1;
1372}
1373
1382int imap_fast_trash(struct Mailbox *m, const char *dest)
1383{
1384 char prompt[1024] = { 0 };
1385 int rc = -1;
1386 bool triedcreate = false;
1387 enum QuadOption err_continue = MUTT_NO;
1388
1390 if (!adata)
1391 return -1;
1392
1393 struct ImapAccountData *dest_adata = NULL;
1394 struct ImapMboxData *dest_mdata = NULL;
1395
1396 if (imap_adata_find(dest, &dest_adata, &dest_mdata) < 0)
1397 return -1;
1398
1399 struct Buffer *sync_cmd = buf_pool_get();
1400
1401 /* check that the save-to folder is in the same account */
1402 if (!imap_account_match(&(adata->conn->account), &(dest_adata->conn->account)))
1403 {
1404 mutt_debug(LL_DEBUG3, "%s not same server as %s\n", dest, mailbox_path(m));
1405 goto out;
1406 }
1407
1408 for (int i = 0; i < m->msg_count; i++)
1409 {
1410 struct Email *e = m->emails[i];
1411 if (!e)
1412 break;
1413 if (e->active && e->changed && e->deleted && !e->purge)
1414 {
1415 rc = imap_sync_message_for_copy(m, e, sync_cmd, &err_continue);
1416 if (rc < 0)
1417 {
1418 mutt_debug(LL_DEBUG1, "could not sync\n");
1419 goto out;
1420 }
1421 }
1422 }
1423
1424 /* loop in case of TRYCREATE */
1425 do
1426 {
1427 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1428 select_email_uids(m->emails, m->msg_count, MUTT_TRASH, false, false, &uida);
1429 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1430 rc = imap_exec_msg_set(adata, "UID COPY", dest_mdata->munge_name, &uida);
1431 if (rc == 0)
1432 {
1433 mutt_debug(LL_DEBUG1, "No messages to trash\n");
1434 rc = -1;
1435 goto out;
1436 }
1437 else if (rc < 0)
1438 {
1439 mutt_debug(LL_DEBUG1, "could not queue copy\n");
1440 goto out;
1441 }
1442 else if (m->verbose)
1443 {
1444 mutt_message(ngettext("Copying %d message to %s...", "Copying %d messages to %s...", rc),
1445 rc, dest_mdata->name);
1446 }
1447 ARRAY_FREE(&uida);
1448
1449 /* let's get it on */
1450 rc = imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1451 if (rc == IMAP_EXEC_ERROR)
1452 {
1453 if (triedcreate)
1454 {
1455 mutt_debug(LL_DEBUG1, "Already tried to create mailbox %s\n", dest_mdata->name);
1456 break;
1457 }
1458 /* bail out if command failed for reasons other than nonexistent target */
1459 if (!mutt_istr_startswith(imap_get_qualifier(adata->buf), "[TRYCREATE]"))
1460 break;
1461 mutt_debug(LL_DEBUG3, "server suggests TRYCREATE\n");
1462 snprintf(prompt, sizeof(prompt), _("Create %s?"), dest_mdata->name);
1463 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
1464 if (c_confirm_create &&
1465 (query_yesorno_help(prompt, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
1466 {
1468 goto out;
1469 }
1470 if (imap_create_mailbox(adata, dest_mdata->name) < 0)
1471 break;
1472 triedcreate = true;
1473 }
1474 } while (rc == IMAP_EXEC_ERROR);
1475
1476 if (rc != IMAP_EXEC_SUCCESS)
1477 {
1478 imap_error("imap_fast_trash", adata->buf);
1479 goto out;
1480 }
1481
1482 rc = IMAP_EXEC_SUCCESS;
1483
1484out:
1485 buf_pool_release(&sync_cmd);
1486 imap_mdata_free((void *) &dest_mdata);
1487
1488 return ((rc == IMAP_EXEC_SUCCESS) ? 0 : -1);
1489}
1490
1500enum MxStatus imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
1501{
1502 if (!m)
1503 return -1;
1504
1505 struct Email **emails = NULL;
1506 int rc;
1507
1509 struct ImapMboxData *mdata = imap_mdata_get(m);
1510 if (!adata || !mdata)
1511 return MX_STATUS_ERROR;
1512
1513 if (adata->state < IMAP_SELECTED)
1514 {
1515 mutt_debug(LL_DEBUG2, "no mailbox selected\n");
1516 return -1;
1517 }
1518
1519 /* This function is only called when the calling code expects the context
1520 * to be changed. */
1522
1523 enum MxStatus check = imap_check_mailbox(m, false);
1524 if (check == MX_STATUS_ERROR)
1525 return check;
1526
1527 /* if we are expunging anyway, we can do deleted messages very quickly... */
1528 if (expunge && (m->rights & MUTT_ACL_DELETE))
1529 {
1530 struct UidArray uida = ARRAY_HEAD_INITIALIZER;
1531 select_email_uids(m->emails, m->msg_count, MUTT_DELETED, true, false, &uida);
1532 ARRAY_SORT(&uida, imap_sort_uid, NULL);
1533 rc = imap_exec_msg_set(adata, "UID STORE", "+FLAGS.SILENT (\\Deleted)", &uida);
1534 ARRAY_FREE(&uida);
1535 if (rc < 0)
1536 {
1537 mutt_error(_("Expunge failed"));
1538 return rc;
1539 }
1540
1541 if (rc > 0)
1542 {
1543 /* mark these messages as unchanged so second pass ignores them. Done
1544 * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
1545 for (int i = 0; i < m->msg_count; i++)
1546 {
1547 struct Email *e = m->emails[i];
1548 if (!e)
1549 break;
1550 if (e->deleted && e->changed)
1551 e->active = false;
1552 }
1553 if (m->verbose)
1554 {
1555 mutt_message(ngettext("Marking %d message deleted...",
1556 "Marking %d messages deleted...", rc),
1557 rc);
1558 }
1559 }
1560 }
1561
1562#ifdef USE_HCACHE
1563 imap_hcache_open(adata, mdata, true);
1564#endif
1565
1566 /* save messages with real (non-flag) changes */
1567 for (int i = 0; i < m->msg_count; i++)
1568 {
1569 struct Email *e = m->emails[i];
1570 if (!e)
1571 break;
1572
1573 if (e->deleted)
1574 {
1575 imap_cache_del(m, e);
1576#ifdef USE_HCACHE
1577 imap_hcache_del(mdata, imap_edata_get(e)->uid);
1578#endif
1579 }
1580
1581 if (e->active && e->changed)
1582 {
1583#ifdef USE_HCACHE
1584 imap_hcache_put(mdata, e);
1585#endif
1586 /* if the message has been rethreaded or attachments have been deleted
1587 * we delete the message and reupload it.
1588 * This works better if we're expunging, of course. */
1589 if (e->env->changed || e->attach_del)
1590 {
1591 /* L10N: The plural is chosen by the last %d, i.e. the total number */
1592 if (m->verbose)
1593 {
1594 mutt_message(ngettext("Saving changed message... [%d/%d]",
1595 "Saving changed messages... [%d/%d]", m->msg_count),
1596 i + 1, m->msg_count);
1597 }
1598 bool save_append = m->append;
1599 m->append = true;
1601 m->append = save_append;
1602 e->env->changed = false;
1603 }
1604 }
1605 }
1606
1607#ifdef USE_HCACHE
1608 imap_hcache_close(mdata);
1609#endif
1610
1611 /* presort here to avoid doing 10 resorts in imap_exec_msg_set */
1612 emails = MUTT_MEM_MALLOC(m->msg_count, struct Email *);
1613 memcpy(emails, m->emails, m->msg_count * sizeof(struct Email *));
1614 mutt_qsort_r(emails, m->msg_count, sizeof(struct Email *), imap_sort_email_uid, NULL);
1615
1616 rc = sync_helper(m, emails, m->msg_count, MUTT_ACL_DELETE, MUTT_DELETED, "\\Deleted");
1617 if (rc >= 0)
1618 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_FLAG, "\\Flagged");
1619 if (rc >= 0)
1620 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_OLD, "Old");
1621 if (rc >= 0)
1622 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_SEEN, MUTT_READ, "\\Seen");
1623 if (rc >= 0)
1624 rc |= sync_helper(m, emails, m->msg_count, MUTT_ACL_WRITE, MUTT_REPLIED, "\\Answered");
1625
1626 FREE(&emails);
1627
1628 /* Flush the queued flags if any were changed in sync_helper. */
1629 if (rc > 0)
1630 if (imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1631 rc = -1;
1632
1633 if (rc < 0)
1634 {
1635 if (close)
1636 {
1637 if (query_yesorno(_("Error saving flags. Close anyway?"), MUTT_NO) == MUTT_YES)
1638 {
1639 adata->state = IMAP_AUTHENTICATED;
1640 return 0;
1641 }
1642 }
1643 else
1644 {
1645 mutt_error(_("Error saving flags"));
1646 }
1647 return -1;
1648 }
1649
1650 /* Update local record of server state to reflect the synchronization just
1651 * completed. imap_read_headers always overwrites hcache-origin flags, so
1652 * there is no need to mutate the hcache after flag-only changes. */
1653 for (int i = 0; i < m->msg_count; i++)
1654 {
1655 struct Email *e = m->emails[i];
1656 if (!e)
1657 break;
1658 struct ImapEmailData *edata = imap_edata_get(e);
1659 edata->deleted = e->deleted;
1660 edata->flagged = e->flagged;
1661 edata->old = e->old;
1662 edata->read = e->read;
1663 edata->replied = e->replied;
1664 e->changed = false;
1665 }
1666 m->changed = false;
1667
1668 /* We must send an EXPUNGE command if we're not closing. */
1669 if (expunge && !close && (m->rights & MUTT_ACL_DELETE))
1670 {
1671 if (m->verbose)
1672 mutt_message(_("Expunging messages from server..."));
1673 /* Set expunge bit so we don't get spurious reopened messages */
1674 mdata->reopen |= IMAP_EXPUNGE_EXPECTED;
1675 if (imap_exec(adata, "EXPUNGE", IMAP_CMD_NO_FLAGS) != IMAP_EXEC_SUCCESS)
1676 {
1678 imap_error(_("imap_sync_mailbox: EXPUNGE failed"), adata->buf);
1679 return -1;
1680 }
1682 }
1683
1684 if (expunge && close)
1685 {
1686 adata->closing = true;
1687 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
1688 adata->state = IMAP_AUTHENTICATED;
1689 }
1690
1691 const bool c_message_cache_clean = cs_subset_bool(NeoMutt->sub, "message_cache_clean");
1692 if (c_message_cache_clean)
1694
1695 return check;
1696}
1697
1701static bool imap_ac_owns_path(struct Account *a, const char *path)
1702{
1703 struct Url *url = url_parse(path);
1704 if (!url)
1705 return false;
1706
1707 struct ImapAccountData *adata = a->adata;
1708 struct ConnAccount *cac = &adata->conn->account;
1709
1710 const bool rc = mutt_istr_equal(url->host, cac->host) &&
1711 (!url->user || mutt_istr_equal(url->user, cac->user));
1712 url_free(&url);
1713 return rc;
1714}
1715
1719static bool imap_ac_add(struct Account *a, struct Mailbox *m)
1720{
1721 struct ImapAccountData *adata = a->adata;
1722
1723 if (!adata)
1724 {
1725 struct ConnAccount cac = { { 0 } };
1726 char mailbox[PATH_MAX] = { 0 };
1727
1728 if (imap_parse_path(mailbox_path(m), &cac, mailbox, sizeof(mailbox)) < 0)
1729 return false;
1730
1731 adata = imap_adata_new(a);
1732 adata->conn = mutt_conn_new(&cac);
1733 if (!adata->conn)
1734 {
1735 imap_adata_free((void **) &adata);
1736 return false;
1737 }
1738
1740
1741 if (imap_login(adata) < 0)
1742 {
1743 imap_adata_free((void **) &adata);
1744 return false;
1745 }
1746
1747 a->adata = adata;
1749 }
1750
1751 if (!m->mdata)
1752 {
1753 struct Url *url = url_parse(mailbox_path(m));
1754 if (!url)
1755 return false;
1756 struct ImapMboxData *mdata = imap_mdata_new(adata, url->path);
1757
1758 /* fixup path and realpath, mainly to replace / by /INBOX */
1759 char buf[1024] = { 0 };
1760 imap_qualify_path(buf, sizeof(buf), &adata->conn->account, mdata->name);
1761 buf_strcpy(&m->pathbuf, buf);
1763
1764 m->mdata = mdata;
1766 url_free(&url);
1767 }
1768 return true;
1769}
1770
1775static void imap_mbox_select(struct Mailbox *m)
1776{
1778 struct ImapMboxData *mdata = imap_mdata_get(m);
1779 if (!adata || !mdata)
1780 return;
1781
1782 const char *condstore = NULL;
1783#ifdef USE_HCACHE
1784 const bool c_imap_condstore = cs_subset_bool(NeoMutt->sub, "imap_condstore");
1785 if ((adata->capabilities & IMAP_CAP_CONDSTORE) && c_imap_condstore)
1786 condstore = " (CONDSTORE)";
1787 else
1788#endif
1789 condstore = "";
1790
1791 char buf[PATH_MAX] = { 0 };
1792 snprintf(buf, sizeof(buf), "%s %s%s", m->readonly ? "EXAMINE" : "SELECT",
1793 mdata->munge_name, condstore);
1794
1795 adata->state = IMAP_SELECTED;
1796
1797 imap_cmd_start(adata, buf);
1798}
1799
1808int imap_login(struct ImapAccountData *adata)
1809{
1810 if (!adata)
1811 return -1;
1812
1813 if (adata->state == IMAP_DISCONNECTED)
1814 {
1815 buf_reset(&adata->cmdbuf); // purge outstanding queued commands
1816 imap_open_connection(adata);
1817 }
1818 if (adata->state == IMAP_CONNECTED)
1819 {
1821 {
1822 adata->state = IMAP_AUTHENTICATED;
1823 FREE(&adata->capstr);
1824 if (adata->conn->ssf != 0)
1825 {
1826 mutt_debug(LL_DEBUG2, "Communication encrypted at %d bits\n",
1827 adata->conn->ssf);
1828 }
1829 }
1830 else
1831 {
1833 }
1834 }
1835 if (adata->state == IMAP_AUTHENTICATED)
1836 {
1837 /* capabilities may have changed */
1838 imap_exec(adata, "CAPABILITY", IMAP_CMD_PASS);
1839
1840#ifdef USE_ZLIB
1841 /* RFC4978 */
1842 const bool c_imap_deflate = cs_subset_bool(NeoMutt->sub, "imap_deflate");
1843 if ((adata->capabilities & IMAP_CAP_COMPRESS) && c_imap_deflate &&
1844 (imap_exec(adata, "COMPRESS DEFLATE", IMAP_CMD_PASS) == IMAP_EXEC_SUCCESS))
1845 {
1846 mutt_debug(LL_DEBUG2, "IMAP compression is enabled on connection to %s\n",
1847 adata->conn->account.host);
1848 mutt_zstrm_wrap_conn(adata->conn);
1849 }
1850#endif
1851
1852 /* enable RFC2971, if the server supports that */
1853 const bool c_imap_send_id = cs_subset_bool(NeoMutt->sub, "imap_send_id");
1854 if (c_imap_send_id && (adata->capabilities & IMAP_CAP_ID))
1855 {
1856 imap_exec(adata, "ID (\"name\" \"NeoMutt\" \"version\" \"" PACKAGE_VERSION "\")",
1858 }
1859
1860 /* enable RFC6855, if the server supports that */
1861 const bool c_imap_rfc5161 = cs_subset_bool(NeoMutt->sub, "imap_rfc5161");
1862 if (c_imap_rfc5161 && (adata->capabilities & IMAP_CAP_ENABLE))
1863 imap_exec(adata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
1864
1865 /* enable QRESYNC. Advertising QRESYNC also means CONDSTORE
1866 * is supported (even if not advertised), so flip that bit. */
1867 if (adata->capabilities & IMAP_CAP_QRESYNC)
1868 {
1870 const bool c_imap_qresync = cs_subset_bool(NeoMutt->sub, "imap_qresync");
1871 if (c_imap_rfc5161 && c_imap_qresync)
1872 imap_exec(adata, "ENABLE QRESYNC", IMAP_CMD_QUEUE);
1873 }
1874
1875 /* get root delimiter, '/' as default */
1876 adata->delim = '/';
1877 imap_exec(adata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
1878
1879 /* we may need the root delimiter before we open a mailbox */
1880 imap_exec(adata, NULL, IMAP_CMD_NO_FLAGS);
1881
1882 /* select the mailbox that used to be open before disconnect */
1883 if (adata->mailbox)
1884 {
1885 imap_mbox_select(adata->mailbox);
1886 }
1887 }
1888
1889 if (adata->state < IMAP_AUTHENTICATED)
1890 return -1;
1891
1892 return 0;
1893}
1894
1899{
1900 if (!m->account || !m->mdata)
1901 return MX_OPEN_ERROR;
1902
1903 char buf[PATH_MAX] = { 0 };
1904 int count = 0;
1905 int rc;
1906
1908 struct ImapMboxData *mdata = imap_mdata_get(m);
1909 if (!adata || !mdata)
1910 return MX_OPEN_ERROR;
1911
1912 mutt_debug(LL_DEBUG3, "opening %s, saving %s\n", m->pathbuf.data,
1913 (adata->mailbox ? adata->mailbox->pathbuf.data : "(none)"));
1914 adata->prev_mailbox = adata->mailbox;
1915 adata->mailbox = m;
1916
1917 /* clear mailbox status */
1918 adata->status = 0;
1919 m->rights = 0;
1920 mdata->new_mail_count = 0;
1921
1922 if (m->verbose)
1923 mutt_message(_("Selecting %s..."), mdata->name);
1924
1925 /* pipeline ACL test */
1926 if (adata->capabilities & IMAP_CAP_ACL)
1927 {
1928 snprintf(buf, sizeof(buf), "MYRIGHTS %s", mdata->munge_name);
1929 imap_exec(adata, buf, IMAP_CMD_QUEUE);
1930 }
1931 else
1932 {
1933 /* assume we have all rights if ACL is unavailable */
1936 }
1937
1938 /* pipeline the postponed count if possible */
1939 const char *const c_postponed = cs_subset_string(NeoMutt->sub, "postponed");
1940 struct Mailbox *m_postponed = mx_mbox_find2(c_postponed);
1941 struct ImapAccountData *postponed_adata = imap_adata_get(m_postponed);
1942 if (postponed_adata &&
1943 imap_account_match(&postponed_adata->conn->account, &adata->conn->account))
1944 {
1945 imap_mailbox_status(m_postponed, true);
1946 }
1947
1948 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
1949 if (c_imap_check_subscribed)
1950 imap_exec(adata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE);
1951
1953
1954 do
1955 {
1956 char *pc = NULL;
1957
1958 rc = imap_cmd_step(adata);
1959 if (rc != IMAP_RES_CONTINUE)
1960 break;
1961
1962 if (!mutt_strn_equal(adata->buf, "* ", 2))
1963 continue;
1964 pc = imap_next_word(adata->buf);
1965
1966 /* Obtain list of available flags here, may be overridden by a
1967 * PERMANENTFLAGS tag in the OK response */
1968 if (mutt_istr_startswith(pc, "FLAGS"))
1969 {
1970 /* don't override PERMANENTFLAGS */
1971 if (STAILQ_EMPTY(&mdata->flags))
1972 {
1973 mutt_debug(LL_DEBUG3, "Getting mailbox FLAGS\n");
1974 pc = get_flags(&mdata->flags, pc);
1975 if (!pc)
1976 goto fail;
1977 }
1978 }
1979 else if (mutt_istr_startswith(pc, "OK [PERMANENTFLAGS"))
1980 {
1981 /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
1982 mutt_debug(LL_DEBUG3, "Getting mailbox PERMANENTFLAGS\n");
1983 /* safe to call on NULL */
1984 mutt_list_free(&mdata->flags);
1985 /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
1986 pc += 13;
1987 pc = get_flags(&(mdata->flags), pc);
1988 if (!pc)
1989 goto fail;
1990 }
1991 else if (mutt_istr_startswith(pc, "OK [UIDVALIDITY"))
1992 {
1993 /* save UIDVALIDITY for the header cache */
1994 mutt_debug(LL_DEBUG3, "Getting mailbox UIDVALIDITY\n");
1995 pc += 3;
1996 pc = imap_next_word(pc);
1997 if (!mutt_str_atoui(pc, &mdata->uidvalidity))
1998 goto fail;
1999 }
2000 else if (mutt_istr_startswith(pc, "OK [UIDNEXT"))
2001 {
2002 mutt_debug(LL_DEBUG3, "Getting mailbox UIDNEXT\n");
2003 pc += 3;
2004 pc = imap_next_word(pc);
2005 if (!mutt_str_atoui(pc, &mdata->uid_next))
2006 goto fail;
2007 }
2008 else if (mutt_istr_startswith(pc, "OK [HIGHESTMODSEQ"))
2009 {
2010 mutt_debug(LL_DEBUG3, "Getting mailbox HIGHESTMODSEQ\n");
2011 pc += 3;
2012 pc = imap_next_word(pc);
2013 if (!mutt_str_atoull(pc, &mdata->modseq))
2014 goto fail;
2015 }
2016 else if (mutt_istr_startswith(pc, "OK [NOMODSEQ"))
2017 {
2018 mutt_debug(LL_DEBUG3, "Mailbox has NOMODSEQ set\n");
2019 mdata->modseq = 0;
2020 }
2021 else
2022 {
2023 pc = imap_next_word(pc);
2024 if (mutt_istr_startswith(pc, "EXISTS"))
2025 {
2026 count = mdata->new_mail_count;
2027 mdata->new_mail_count = 0;
2028 }
2029 }
2030 } while (rc == IMAP_RES_CONTINUE);
2031
2032 if (rc == IMAP_RES_NO)
2033 {
2034 char *s = imap_next_word(adata->buf); /* skip seq */
2035 s = imap_next_word(s); /* Skip response */
2036 mutt_error("%s", s);
2037 goto fail;
2038 }
2039
2040 if (rc != IMAP_RES_OK)
2041 goto fail;
2042
2043 /* check for READ-ONLY notification */
2044 if (mutt_istr_startswith(imap_get_qualifier(adata->buf), "[READ-ONLY]") &&
2045 !(adata->capabilities & IMAP_CAP_ACL))
2046 {
2047 mutt_debug(LL_DEBUG2, "Mailbox is read-only\n");
2048 m->readonly = true;
2049 }
2050
2051 /* dump the mailbox flags we've found */
2052 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
2053 if (c_debug_level > LL_DEBUG2)
2054 {
2055 if (STAILQ_EMPTY(&mdata->flags))
2056 {
2057 mutt_debug(LL_DEBUG3, "No folder flags found\n");
2058 }
2059 else
2060 {
2061 struct ListNode *np = NULL;
2062 struct Buffer *flag_buffer = buf_pool_get();
2063 buf_printf(flag_buffer, "Mailbox flags: ");
2064 STAILQ_FOREACH(np, &mdata->flags, entries)
2065 {
2066 buf_add_printf(flag_buffer, "[%s] ", np->data);
2067 }
2068 mutt_debug(LL_DEBUG3, "%s\n", buf_string(flag_buffer));
2069 buf_pool_release(&flag_buffer);
2070 }
2071 }
2072
2073 if (!((m->rights & MUTT_ACL_DELETE) || (m->rights & MUTT_ACL_SEEN) ||
2074 (m->rights & MUTT_ACL_WRITE) || (m->rights & MUTT_ACL_INSERT)))
2075 {
2076 m->readonly = true;
2077 }
2078
2079 mx_alloc_memory(m, count);
2080
2081 m->msg_count = 0;
2082 m->msg_unread = 0;
2083 m->msg_flagged = 0;
2084 m->msg_new = 0;
2085 m->msg_deleted = 0;
2086 m->size = 0;
2087 m->vcount = 0;
2088
2089 if ((count > 0) && (imap_read_headers(m, 1, count, true) < 0))
2090 {
2091 mutt_error(_("Error opening mailbox"));
2092 goto fail;
2093 }
2094
2095 mutt_debug(LL_DEBUG2, "msg_count is %d\n", m->msg_count);
2096 return MX_OPEN_OK;
2097
2098fail:
2099 if (adata->state == IMAP_SELECTED)
2100 adata->state = IMAP_AUTHENTICATED;
2101 return MX_OPEN_ERROR;
2102}
2103
2108{
2109 if (!m->account)
2110 return false;
2111
2112 /* in APPEND mode, we appear to hijack an existing IMAP connection -
2113 * mailbox is brand new and mostly empty */
2115 struct ImapMboxData *mdata = imap_mdata_get(m);
2116 if (!adata || !mdata)
2117 return false;
2118
2119 int rc = imap_mailbox_status(m, false);
2120 if (rc >= 0)
2121 return true;
2122 if (rc == -1)
2123 return false;
2124
2125 char buf[PATH_MAX + 64];
2126 snprintf(buf, sizeof(buf), _("Create %s?"), mdata->name);
2127 const bool c_confirm_create = cs_subset_bool(NeoMutt->sub, "confirm_create");
2128 if (c_confirm_create &&
2129 (query_yesorno_help(buf, MUTT_YES, NeoMutt->sub, "confirm_create") != MUTT_YES))
2130 return false;
2131
2132 if (imap_create_mailbox(adata, mdata->name) < 0)
2133 return false;
2134
2135 return true;
2136}
2137
2144static enum MxStatus imap_mbox_check(struct Mailbox *m)
2145{
2147 enum MxStatus rc = imap_check_mailbox(m, false);
2148 /* NOTE - mv might have been changed at this point. In particular,
2149 * m could be NULL. Beware. */
2151
2152 return rc;
2153}
2154
2158static enum MxStatus imap_mbox_close(struct Mailbox *m)
2159{
2161 struct ImapMboxData *mdata = imap_mdata_get(m);
2162
2163 /* Check to see if the mailbox is actually open */
2164 if (!adata || !mdata)
2165 return MX_STATUS_OK;
2166
2167 /* imap_mbox_open_append() borrows the struct ImapAccountData temporarily,
2168 * just for the connection.
2169 *
2170 * So when these are equal, it means we are actually closing the
2171 * mailbox and should clean up adata. Otherwise, we don't want to
2172 * touch adata - it's still being used. */
2173 if (m == adata->mailbox)
2174 {
2175 if ((adata->status != IMAP_FATAL) && (adata->state >= IMAP_SELECTED))
2176 {
2177 /* mx_mbox_close won't sync if there are no deleted messages
2178 * and the mailbox is unchanged, so we may have to close here */
2179 if (m->msg_deleted == 0)
2180 {
2181 adata->closing = true;
2182 imap_exec(adata, "CLOSE", IMAP_CMD_NO_FLAGS);
2183 }
2184 adata->state = IMAP_AUTHENTICATED;
2185 }
2186
2187 mutt_debug(LL_DEBUG3, "closing %s, restoring %s\n", m->pathbuf.data,
2188 (adata->prev_mailbox ? adata->prev_mailbox->pathbuf.data : "(none)"));
2189 adata->mailbox = adata->prev_mailbox;
2192 }
2193
2194 return MX_STATUS_OK;
2195}
2196
2200static bool imap_msg_open_new(struct Mailbox *m, struct Message *msg, const struct Email *e)
2201{
2202 bool success = false;
2203
2204 struct Buffer *tempfile = buf_pool_get();
2205 buf_mktemp(tempfile);
2206
2207 msg->fp = mutt_file_fopen(buf_string(tempfile), "w");
2208 if (!msg->fp)
2209 {
2210 mutt_perror("%s", buf_string(tempfile));
2211 goto cleanup;
2212 }
2213
2214 msg->path = buf_strdup(tempfile);
2215 success = true;
2216
2217cleanup:
2218 buf_pool_release(&tempfile);
2219 return success;
2220}
2221
2225static int imap_tags_edit(struct Mailbox *m, const char *tags, struct Buffer *buf)
2226{
2227 struct ImapMboxData *mdata = imap_mdata_get(m);
2228 if (!mdata)
2229 return -1;
2230
2231 char *new_tag = NULL;
2232 char *checker = NULL;
2233
2234 /* Check for \* flags capability */
2235 if (!imap_has_flag(&mdata->flags, NULL))
2236 {
2237 mutt_error(_("IMAP server doesn't support custom flags"));
2238 return -1;
2239 }
2240
2241 buf_reset(buf);
2242 if (tags)
2243 buf_strcpy(buf, tags);
2244
2245 if (mw_get_field("Tags: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
2246 return -1;
2247
2248 /* each keyword must be atom defined by rfc822 as:
2249 *
2250 * atom = 1*<any CHAR except specials, SPACE and CTLs>
2251 * CHAR = ( 0.-127. )
2252 * specials = "(" / ")" / "<" / ">" / "@"
2253 * / "," / ";" / ":" / "\" / <">
2254 * / "." / "[" / "]"
2255 * SPACE = ( 32. )
2256 * CTLS = ( 0.-31., 127.)
2257 *
2258 * And must be separated by one space.
2259 */
2260
2261 new_tag = buf->data;
2262 checker = buf->data;
2263 SKIPWS(checker);
2264 while (*checker != '\0')
2265 {
2266 if ((*checker < 32) || (*checker >= 127) || // We allow space because it's the separator
2267 (*checker == 40) || // (
2268 (*checker == 41) || // )
2269 (*checker == 60) || // <
2270 (*checker == 62) || // >
2271 (*checker == 64) || // @
2272 (*checker == 44) || // ,
2273 (*checker == 59) || // ;
2274 (*checker == 58) || // :
2275 (*checker == 92) || // backslash
2276 (*checker == 34) || // "
2277 (*checker == 46) || // .
2278 (*checker == 91) || // [
2279 (*checker == 93)) // ]
2280 {
2281 mutt_error(_("Invalid IMAP flags"));
2282 return 0;
2283 }
2284
2285 /* Skip duplicate space */
2286 while ((checker[0] == ' ') && (checker[1] == ' '))
2287 checker++;
2288
2289 /* copy char to new_tag and go the next one */
2290 *new_tag++ = *checker++;
2291 }
2292 *new_tag = '\0';
2293 new_tag = buf->data; /* rewind */
2295
2296 return !mutt_str_equal(tags, buf_string(buf));
2297}
2298
2312static int imap_tags_commit(struct Mailbox *m, struct Email *e, const char *buf)
2313{
2314 char uid[11] = { 0 };
2315
2317 if (!adata)
2318 return -1;
2319
2320 if (*buf == '\0')
2321 buf = NULL;
2322
2323 if (!(adata->mailbox->rights & MUTT_ACL_WRITE))
2324 return 0;
2325
2326 snprintf(uid, sizeof(uid), "%u", imap_edata_get(e)->uid);
2327
2328 /* Remove old custom flags */
2329 if (imap_edata_get(e)->flags_remote)
2330 {
2331 struct Buffer *cmd = buf_pool_get();
2332 buf_addstr(cmd, "UID STORE ");
2333 buf_addstr(cmd, uid);
2334 buf_addstr(cmd, " -FLAGS.SILENT (");
2335 buf_addstr(cmd, imap_edata_get(e)->flags_remote);
2336 buf_addstr(cmd, ")");
2337
2338 /* Should we return here, or we are fine and we could
2339 * continue to add new flags */
2340 int rc = imap_exec(adata, buf_string(cmd), IMAP_CMD_NO_FLAGS);
2341 buf_pool_release(&cmd);
2342 if (rc != IMAP_EXEC_SUCCESS)
2343 {
2344 return -1;
2345 }
2346 }
2347
2348 /* Add new custom flags */
2349 if (buf)
2350 {
2351 struct Buffer *cmd = buf_pool_get();
2352 buf_addstr(cmd, "UID STORE ");
2353 buf_addstr(cmd, uid);
2354 buf_addstr(cmd, " +FLAGS.SILENT (");
2355 buf_addstr(cmd, buf);
2356 buf_addstr(cmd, ")");
2357
2358 int rc = imap_exec(adata, buf_string(cmd), IMAP_CMD_NO_FLAGS);
2359 buf_pool_release(&cmd);
2360 if (rc != IMAP_EXEC_SUCCESS)
2361 {
2362 mutt_debug(LL_DEBUG1, "fail to add new flags\n");
2363 return -1;
2364 }
2365 }
2366
2367 /* We are good sync them */
2368 mutt_debug(LL_DEBUG1, "NEW TAGS: %s\n", buf);
2369 driver_tags_replace(&e->tags, buf);
2370 FREE(&imap_edata_get(e)->flags_remote);
2371 struct Buffer *flags_remote = buf_pool_get();
2372 driver_tags_get_with_hidden(&e->tags, flags_remote);
2373 imap_edata_get(e)->flags_remote = buf_strdup(flags_remote);
2374 buf_pool_release(&flags_remote);
2376 return 0;
2377}
2378
2382enum MailboxType imap_path_probe(const char *path, const struct stat *st)
2383{
2384 if (mutt_istr_startswith(path, "imap://"))
2385 return MUTT_IMAP;
2386
2387 if (mutt_istr_startswith(path, "imaps://"))
2388 return MUTT_IMAP;
2389
2390 return MUTT_UNKNOWN;
2391}
2392
2396int imap_path_canon(struct Buffer *path)
2397{
2398 struct Url *url = url_parse(buf_string(path));
2399 if (!url)
2400 return 0;
2401
2402 char tmp[PATH_MAX] = { 0 };
2403 char tmp2[PATH_MAX] = { 0 };
2404 if (url->path)
2405 {
2406 struct ImapAccountData *adata = NULL;
2407 if (imap_adata_find(buf_string(path), &adata, NULL) == 0)
2408 {
2409 imap_fix_path_with_delim(adata->delim, url->path, tmp, sizeof(tmp));
2410 }
2411 else
2412 {
2413 imap_fix_path(url->path, tmp, sizeof(tmp));
2414 }
2415 url->path = tmp;
2416 }
2417 url_tostring(url, tmp2, sizeof(tmp2), U_NO_FLAGS);
2418 buf_strcpy(path, tmp2);
2419 url_free(&url);
2420
2421 return 0;
2422}
2423
2427static int imap_path_is_empty(struct Buffer *path)
2428{
2429 int rc = imap_path_status(buf_string(path), false);
2430 if (rc < 0)
2431 return -1;
2432 if (rc == 0)
2433 return 1;
2434 return 0;
2435}
2436
2440const struct MxOps MxImapOps = {
2441 // clang-format off
2442 .type = MUTT_IMAP,
2443 .name = "imap",
2444 .is_local = false,
2445 .ac_owns_path = imap_ac_owns_path,
2446 .ac_add = imap_ac_add,
2447 .mbox_open = imap_mbox_open,
2448 .mbox_open_append = imap_mbox_open_append,
2449 .mbox_check = imap_mbox_check,
2450 .mbox_check_stats = imap_mbox_check_stats,
2451 .mbox_sync = NULL, /* imap syncing is handled by imap_sync_mailbox */
2452 .mbox_close = imap_mbox_close,
2453 .msg_open = imap_msg_open,
2454 .msg_open_new = imap_msg_open_new,
2455 .msg_commit = imap_msg_commit,
2456 .msg_close = imap_msg_close,
2457 .msg_padding_size = NULL,
2458 .msg_save_hcache = imap_msg_save_hcache,
2459 .tags_edit = imap_tags_edit,
2460 .tags_commit = imap_tags_commit,
2461 .path_probe = imap_path_probe,
2462 .path_canon = imap_path_canon,
2463 .path_is_empty = imap_path_is_empty,
2464 // clang-format on
2465};
#define ARRAY_SORT(head, fn, sdata)
Sort an array.
Definition array.h:335
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:156
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:214
#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:295
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition atoi.c:217
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:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition buffer.c:204
size_t buf_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition buffer.c:491
void buf_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition buffer.c:377
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition buffer.c:76
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
void buf_fix_dptr(struct Buffer *buf)
Move the dptr to end of the Buffer.
Definition buffer.c:182
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:668
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition buffer.c:226
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
char * buf_strdup(const struct Buffer *buf)
Copy a Buffer's string.
Definition buffer.c:571
void buf_alloc(struct Buffer *buf, size_t new_size)
Make sure a buffer can store at least new_size bytes.
Definition buffer.c:337
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
#define CF_NO_FLAGS
No flags are set.
Definition command.h:46
@ MUTT_CMD_SUCCESS
Success: Command worked.
Definition command.h:38
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition helpers.c:291
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition helpers.c:143
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
Convenience wrapper for the config headers.
#define mutt_numeric_cmp(a, b)
Definition sort.h:26
Connection Library.
void mutt_account_unsetpass(struct ConnAccount *cac)
Unset ConnAccount's password.
bool commands_register(struct CommandArray *ca, const struct Command *cmds)
Add commands to Commands array.
Definition command.c:51
Convenience wrapper for the core headers.
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition mailbox.c:89
void mailbox_changed(struct Mailbox *m, enum NotifyMailbox action)
Notify observers of a change to a Mailbox.
Definition mailbox.c:231
#define MUTT_ACL_CREATE
Create a mailbox.
Definition mailbox.h:62
@ NT_MAILBOX_RESORT
Email list needs resorting.
Definition mailbox.h:181
@ NT_MAILBOX_UPDATE
Update internal tables.
Definition mailbox.h:182
#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:214
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
bool mutt_isspace(int arg)
Wrapper for isspace(3)
Definition ctype.c:95
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:740
Manage where the email is piped to external commands.
@ TRANSFORM_NONE
No transformation.
Definition external.h:43
@ SAVE_MOVE
Move message to another mailbox, removing the original.
Definition external.h:54
#define mutt_file_fopen(PATH, MODE)
Definition file.h:138
int mutt_ssl_starttls(struct Connection *conn)
Negotiate TLS over an already opened connection.
Definition gnutls.c:1171
void imap_adata_free(void **ptr)
Free the private Account data - Implements Account::adata_free() -.
Definition adata.c:69
enum CommandResult parse_unsubscribe_from(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse the 'unsubscribe-from' command - Implements Command::parse() -.
Definition commands.c:1844
enum CommandResult parse_subscribe_to(const struct Command *cmd, struct Buffer *line, struct Buffer *err)
Parse the 'subscribe-to' command - Implements Command::parse() -.
Definition commands.c:1414
void imap_edata_free(void **ptr)
Free the private Email data - Implements Email::edata_free() -.
Definition edata.c:39
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:272
#define mutt_error(...)
Definition logging2.h:93
#define mutt_message(...)
Definition logging2.h:92
#define mutt_debug(LEVEL,...)
Definition logging2.h:90
#define mutt_perror(...)
Definition logging2.h:94
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free() -.
Definition mdata.c:40
static bool imap_ac_add(struct Account *a, struct Mailbox *m)
Add a Mailbox to an Account - Implements MxOps::ac_add() -.
Definition imap.c:1719
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:1701
const struct MxOps MxImapOps
IMAP Mailbox - Implements MxOps -.
Definition imap.c:2440
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:1177
static enum MxStatus imap_mbox_check(struct Mailbox *m)
Check for new mail - Implements MxOps::mbox_check() -.
Definition imap.c:2144
static enum MxStatus imap_mbox_close(struct Mailbox *m)
Close a Mailbox - Implements MxOps::mbox_close() -.
Definition imap.c:2158
static bool imap_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
Open a Mailbox for appending - Implements MxOps::mbox_open_append() -.
Definition imap.c:2107
static enum MxOpenReturns imap_mbox_open(struct Mailbox *m)
Open a mailbox - Implements MxOps::mbox_open() -.
Definition imap.c:1898
int imap_msg_close(struct Mailbox *m, struct Message *msg)
Close an email - Implements MxOps::msg_close() -.
Definition message.c:2203
int imap_msg_commit(struct Mailbox *m, struct Message *msg)
Save changes to an email - Implements MxOps::msg_commit() -.
Definition message.c:2189
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:2200
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:1996
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:2211
int imap_path_canon(struct Buffer *path)
Canonicalise a Mailbox path - Implements MxOps::path_canon() -.
Definition imap.c:2396
static int imap_path_is_empty(struct Buffer *path)
Is the mailbox empty - Implements MxOps::path_is_empty() -.
Definition imap.c:2427
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox?
Definition imap.c:2382
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:2312
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:2225
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:920
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:59
void mutt_account_hook(const char *url)
Perform an account hook.
Definition hook.c:1547
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
Imap-specific Account data.
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:1133
const char * imap_cmd_trailer(struct ImapAccountData *adata)
Extra information after tagged command response if any.
Definition command.c:1285
int imap_cmd_idle(struct ImapAccountData *adata)
Enter the IDLE state.
Definition command.c:1455
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1147
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:1322
void imap_cmd_finish(struct ImapAccountData *adata)
Attempt to perform cleanup.
Definition command.c:1388
struct ImapEmailData * imap_edata_get(struct Email *e)
Get the private data for this Email.
Definition edata.c:66
Imap-specific Email data.
IMAP network mailbox.
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:477
struct ImapMboxData * imap_mdata_new(struct ImapAccountData *adata, const char *name)
Allocate and initialise a new ImapMboxData structure.
Definition mdata.c:74
struct ImapMboxData * imap_mdata_get(struct Mailbox *m)
Get the Mailbox data for this mailbox.
Definition mdata.c:61
Imap-specific Mailbox data.
int imap_cache_clean(struct Mailbox *m)
Delete all the entries in the message cache.
Definition message.c:1907
int imap_cache_del(struct Mailbox *m, struct Email *e)
Delete an email from the body cache.
Definition message.c:1888
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:1354
Shared constants/structs that are private to IMAP.
#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:855
#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:1067
void imap_disallow_reopen(struct Mailbox *m)
Disallow re-opening a folder upon expunge.
Definition util.c:1080
@ 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
void imap_hcache_open(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool create)
Open a header cache.
Definition util.c:302
#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:383
#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:109
#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:886
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:649
#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:869
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
#define IMAP_NEWMAIL_PENDING
New mail is waiting on the server.
Definition private.h:67
char * imap_fix_path(const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition util.c:681
void imap_error(const char *where, const char *msg)
Show an error and abort.
Definition util.c:660
#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:343
@ IMAP_BYE
Logged out from server.
Definition private.h:96
@ IMAP_FATAL
Unrecoverable error occurred.
Definition private.h:95
char * imap_fix_path_with_delim(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition util.c:713
#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:401
#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:1095
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:960
#define IMAP_CMD_SINGLE
Run a single command.
Definition private.h:75
#define IMAP_RES_CONTINUE
* ...
Definition private.h:56
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition util.c:824
#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:807
static void imap_logout(struct ImapAccountData *adata)
Gracefully log out of server.
Definition imap.c:543
int imap_mailbox_status(struct Mailbox *m, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1229
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1194
void imap_notify_delete_email(struct Mailbox *m, struct Email *e)
Inform IMAP that an Email has been deleted.
Definition imap.c:668
void imap_close_connection(struct ImapAccountData *adata)
Close an IMAP connection.
Definition imap.c:869
static int imap_status(struct ImapAccountData *adata, struct ImapMboxData *mdata, bool queue)
Refresh the number of total and new messages.
Definition imap.c:1127
int imap_complete(struct Buffer *buf, const char *path)
Try to complete an IMAP folder path.
Definition imap.c:1301
int imap_subscribe(const char *path, bool subscribe)
Subscribe to a mailbox.
Definition imap.c:1245
int imap_delete_mailbox(struct Mailbox *m, char *path)
Delete a mailbox.
Definition imap.c:517
void imap_expunge_mailbox(struct Mailbox *m, bool resort)
Purge messages from the server.
Definition imap.c:689
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:359
int imap_open_connection(struct ImapAccountData *adata)
Open an IMAP connection.
Definition imap.c:761
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:310
int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
Rename a mailbox.
Definition imap.c:490
static int complete_hosts(struct Buffer *buf)
Look for completion matches for mailboxes.
Definition imap.c:382
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition imap.c:448
static int check_capabilities(struct ImapAccountData *adata)
Make sure we can log in to this server.
Definition imap.c:113
int imap_fast_trash(struct Mailbox *m, const char *dest)
Use server COPY command to copy deleted messages to trash.
Definition imap.c:1382
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:946
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:240
enum MxStatus imap_sync_mailbox(struct Mailbox *m, bool expunge, bool close)
Sync all the changes to the server.
Definition imap.c:1500
int imap_access(const char *path)
Check permissions on an IMAP mailbox with a new connection.
Definition imap.c:475
void imap_logout_all(void)
Close all open connections.
Definition imap.c:573
enum MxStatus imap_check_mailbox(struct Mailbox *m, bool force)
Use the NOOP or IDLE command to poll for new mail.
Definition imap.c:1052
static char * get_flags(struct ListHead *hflags, char *s)
Make a simple list out of a FLAGS response.
Definition imap.c:139
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:610
bool imap_has_flag(struct ListHead *flag_list, const char *flag)
Does the flag exist in the list.
Definition imap.c:894
static void set_flag(struct Mailbox *m, AclFlags aclflag, bool flag, const char *str, struct Buffer *flags)
Append str to flags if we currently have permission according to aclflag.
Definition imap.c:193
static void imap_mbox_select(struct Mailbox *m)
Select a Mailbox.
Definition imap.c:1775
int imap_login(struct ImapAccountData *adata)
Open an IMAP connection.
Definition imap.c:1808
static const struct Command ImapCommands[]
Imap Commands.
Definition imap.c:84
void imap_init(void)
Setup feature commands.
Definition imap.c:102
static bool compare_flags_for_copy(struct Email *e)
Compare local flags against the server.
Definition imap.c:213
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition list.c:65
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition list.c:123
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:46
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:45
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:44
#define FREE(x)
Definition memory.h:62
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:48
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, int idx)
Remove an entry from the cache.
Definition msn.c:116
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 N_(a)
Definition message.h:32
#define _(a)
Definition message.h:28
void mutt_str_remove_trailing_ws(char *s)
Trim trailing whitespace from a string.
Definition string.c:565
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition string.c:672
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:255
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:660
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:427
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:232
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:498
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:581
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition string.c:244
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:455
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:282
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 account_to_url(struct ConnAccount *cac, struct Url *url)
Fill URL with info from account.
@ MUTT_ACCT_TYPE_IMAP
Imap Account.
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
struct Connection * mutt_conn_new(const struct ConnAccount *cac)
Create a new Connection.
Definition mutt_socket.c:47
NeoMutt connections.
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition muttlib.c:518
Some miscellaneous functions.
void mx_alloc_memory(struct Mailbox *m, int req_size)
Create storage for the emails.
Definition mx.c:1211
int mx_ac_remove(struct Mailbox *m, bool keep_account)
Remove a Mailbox from an Account and delete Account if empty.
Definition mx.c:1757
struct Mailbox * mx_mbox_find2(const char *path)
Find a Mailbox on an Account.
Definition mx.c:1618
bool mx_mbox_ac_link(struct Mailbox *m)
Link a Mailbox to an existing or new Account.
Definition mx.c:251
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition mx.c:1650
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:73
@ MX_OPEN_ERROR
Open failed with an error.
Definition mxapi.h:75
@ MX_OPEN_OK
Open succeeded.
Definition mxapi.h:74
#define MUTT_MAILBOX_CHECK_IMMEDIATE
Don't postpone the actual checking.
Definition mxapi.h:53
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_sync(), and mbox_close()
Definition mxapi.h:60
@ MX_STATUS_ERROR
An error occurred.
Definition mxapi.h:61
@ MX_STATUS_OK
No changes.
Definition mxapi.h:62
@ MX_STATUS_FLAGS
Nondestructive flags change (IMAP)
Definition mxapi.h:66
@ MX_STATUS_REOPENED
Mailbox was reopened.
Definition mxapi.h:65
@ MX_STATUS_NEW_MAIL
New mail received in Mailbox.
Definition mxapi.h:63
struct MailboxArray neomutt_mailboxes_get(struct NeoMutt *n, enum MailboxType type)
Get an Array of matching Mailboxes.
Definition neomutt.c:189
Text parsing functions.
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:96
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:353
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:377
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:325
#define TAILQ_FOREACH(var, head, field)
Definition queue.h:782
#define STAILQ_FOREACH(var, head, field)
Definition queue.h:390
#define STAILQ_EMPTY(head)
Definition queue.h:382
enum CommandResult parse_rc_line(struct Buffer *line, struct Buffer *err)
Parse a line of user config.
Definition rc.c:45
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
#define SKIPWS(ch)
Definition string2.h:51
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
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
CommandFlags flags
Command flags, e.g. CF_SYNONYM.
Definition command.h:83
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:99
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:110
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
bool deleted
Email has been deleted.
Definition edata.h:39
char * flags_system
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:37
char * data
String.
Definition list.h:38
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:88
Container for Accounts, Notifications.
Definition neomutt.h:43
struct AccountArray accounts
All Accounts.
Definition neomutt.h:48
struct CommandArray commands
NeoMutt commands.
Definition neomutt.h:51
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:47
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:238
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:123
int url_tostring(const struct Url *url, char *dest, size_t len, uint8_t flags)
Output the URL string for a given Url object.
Definition url.c:422
#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