1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "imap4d.h"
18
19 static int select_flags;
20
21 /* select ::= "SELECT" SPACE mailbox */
22
23 int
imap4d_select(struct imap4d_session * session,struct imap4d_command * command,imap4d_tokbuf_t tok)24 imap4d_select (struct imap4d_session *session,
25 struct imap4d_command *command, imap4d_tokbuf_t tok)
26 {
27 if (imap4d_tokbuf_argc (tok) != 3)
28 return io_completion_response (command, RESP_BAD, "Invalid arguments");
29 return imap4d_select0 (command, imap4d_tokbuf_getarg (tok, IMAP4_ARG_1),
30 MU_STREAM_RDWR);
31 }
32
33 /* This code is shared with EXAMINE. */
34 int
imap4d_select0(struct imap4d_command * command,const char * mboxname,int flags)35 imap4d_select0 (struct imap4d_command *command, const char *mboxname,
36 int flags)
37 {
38 int status;
39 char *mailbox_name;
40 mu_record_t record;
41
42 /* FIXME: Check state. */
43
44 /* Even if a mailbox is selected, a SELECT EXAMINE or LOGOUT
45 command MAY be issued without previously issuing a CLOSE command.
46 The SELECT, EXAMINE, and LOGUT commands implictly close the
47 currently selected mailbox without doing an expunge. */
48 if (mbox)
49 {
50 imap4d_enter_critical ();
51 mu_mailbox_sync (mbox);
52 mu_mailbox_close (mbox);
53 manlock_unlock (mbox);
54 imap4d_leave_critical ();
55 mu_mailbox_destroy (&mbox);
56 /* Destroy the old uid table. */
57 imap4d_sync ();
58 }
59
60 if (mu_c_strcasecmp (mboxname, "INBOX") == 0)
61 flags |= MU_STREAM_CREAT;
62 mailbox_name = namespace_get_name (mboxname, &record, NULL);
63
64 if (!mailbox_name)
65 return io_completion_response (command, RESP_NO, "Couldn't open mailbox");
66
67 if (flags & MU_STREAM_WRITE)
68 {
69 status = manlock_open_mailbox_from_record (&mbox, record,
70 mailbox_name, flags);
71 if (status)
72 flags &= ~MU_STREAM_WRITE;
73 }
74
75 if (!(flags & MU_STREAM_WRITE))
76 {
77 status = mu_mailbox_create_from_record (&mbox, record, mailbox_name);
78
79 if (status)
80 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_create_from_record",
81 mailbox_name,
82 status);
83 else
84 {
85 status = mu_mailbox_open (mbox, flags);
86 if (status)
87 {
88 mu_diag_funcall (MU_DIAG_ERROR, "mu_mailbox_open",
89 mailbox_name,
90 status);
91 mu_mailbox_destroy (&mbox);
92 }
93 }
94 }
95
96 if (status == 0)
97 {
98 select_flags = flags;
99 state = STATE_SEL;
100
101 imap4d_set_observer (mbox);
102
103 if ((status = imap4d_select_status ()) == 0)
104 {
105 free (mailbox_name);
106 /* Need to set the state explicitely for select. */
107 return io_sendf ("%s OK [%s] %s Completed\n", command->tag,
108 ((flags & MU_STREAM_RDWR) == MU_STREAM_RDWR) ?
109 "READ-WRITE" : "READ-ONLY", command->name);
110 }
111 }
112
113 mu_mailbox_destroy (&mbox);
114 status = io_completion_response (command, RESP_NO, "Could not open %s: %s",
115 mboxname, mu_strerror (status));
116 free (mailbox_name);
117 return status;
118 }
119
120 /* The code is shared between select and noop */
121 int
imap4d_select_status()122 imap4d_select_status ()
123 {
124 const char *mflags = "\\Answered \\Flagged \\Deleted \\Seen \\Draft";
125 unsigned long uidvalidity = 0;
126 size_t count = 0, recent = 0, unseen = 0, uidnext = 0;
127 int status = 0;
128
129 if (state != STATE_SEL)
130 return 0; /* FIXME: this should be something! */
131
132 if ((status = util_uidvalidity (mbox, &uidvalidity))
133 || (status = mu_mailbox_uidnext (mbox, &uidnext))
134 || (status = mu_mailbox_messages_count (mbox, &count))
135 || (status = mu_mailbox_messages_recent (mbox, &recent))
136 || (status = mu_mailbox_message_unseen (mbox, &unseen)))
137 return status;
138
139 /* This outputs EXISTS and RECENT responses */
140 imap4d_sync();
141 io_untagged_response (RESP_OK, "[UIDVALIDITY %lu] UID valididy status",
142 uidvalidity);
143 io_untagged_response (RESP_OK, "[UIDNEXT %lu] Predicted next uid",
144 (unsigned long) uidnext);
145 if (unseen)
146 io_untagged_response (RESP_OK, "[UNSEEN %lu] first unseen message",
147 (unsigned long) unseen);
148 io_untagged_response (RESP_NONE, "FLAGS (%s)", mflags);
149 /* FIXME:
150 - '\*' can be supported if we use the attribute_set userflag()
151 - Answered is still not set in the mailbox code. */
152 if (!(select_flags & MU_STREAM_WRITE))
153 io_untagged_response (RESP_OK, "[PERMANENTFLAGS ()] No permanent flags");
154 else
155 io_untagged_response (RESP_OK, "[PERMANENTFLAGS (%s)] Permanent flags",
156 mflags);
157
158 return 0;
159 }
160
161