1
/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */
3
#include "imap-common.h"
7
#include "str-sanitize.h"
8
#include "imap-resp-code.h"
9
#include "imap-parser.h"
10
#include "imap-sync.h"
11
#include "imap-utf7.h"
12
#include "imap-util.h"
13
#include "mail-storage.h"
14
#include "mail-namespace.h"
15
#include "imap-commands-util.h"
17
struct mail_namespace *
18
client_find_namespace(struct client_command_context *cmd, const char **mailbox)
20
struct mail_namespace *namespaces = cmd->client->user->namespaces;
21
struct mail_namespace *ns;
22
unsigned int name_len;
25
ns = mail_namespace_find(namespaces, *mailbox);
27
client_send_tagline(cmd, t_strdup_printf(
28
"NO Client tried to access nonexistent namespace. "
29
"(Mailbox name should probably be prefixed with: %s)",
30
mail_namespace_find_inbox(namespaces)->prefix));
34
name_len = strlen(*mailbox);
35
if ((cmd->client->set->parsed_workarounds &
36
WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
38
(*mailbox)[name_len-1] == mail_namespace_get_sep(ns)) {
39
/* drop the extra trailing hierarchy separator */
40
*mailbox = t_strndup(*mailbox, name_len-1);
43
utf8_name = t_str_new(64);
44
if (imap_utf7_to_utf8(*mailbox, utf8_name) < 0) {
45
client_send_tagline(cmd, "NO Mailbox name is not valid mUTF-7");
48
*mailbox = str_c(utf8_name);
52
bool client_verify_open_mailbox(struct client_command_context *cmd)
54
if (cmd->client->mailbox != NULL)
57
client_send_tagline(cmd, "BAD No mailbox selected.");
62
int client_open_save_dest_box(struct client_command_context *cmd,
63
const char *name, struct mailbox **destbox_r)
65
struct mail_namespace *ns;
67
const char *error_string;
68
enum mail_error error;
70
ns = client_find_namespace(cmd, &name);
74
if (cmd->client->mailbox != NULL &&
75
mailbox_equals(cmd->client->mailbox, ns, name)) {
76
*destbox_r = cmd->client->mailbox;
79
box = mailbox_alloc(ns->list, name, MAILBOX_FLAG_SAVEONLY);
80
if (mailbox_open(box) < 0) {
81
error_string = mailbox_get_last_error(box, &error);
82
if (error == MAIL_ERROR_NOTFOUND) {
83
client_send_tagline(cmd, t_strdup_printf(
84
"NO [TRYCREATE] %s", error_string));
86
client_send_storage_error(cmd, mailbox_get_storage(box));
91
if (cmd->client->enabled_features != 0) {
92
if (mailbox_enable(box, cmd->client->enabled_features) < 0) {
93
client_send_storage_error(cmd, mailbox_get_storage(box));
103
imap_get_error_string(struct client_command_context *cmd,
104
const char *error_string, enum mail_error error)
106
const char *resp_code = NULL;
109
case MAIL_ERROR_NONE:
111
case MAIL_ERROR_TEMP:
112
resp_code = IMAP_RESP_CODE_SERVERBUG;
114
case MAIL_ERROR_NOTPOSSIBLE:
115
case MAIL_ERROR_PARAMS:
116
resp_code = IMAP_RESP_CODE_CANNOT;
118
case MAIL_ERROR_PERM:
119
resp_code = IMAP_RESP_CODE_NOPERM;
121
case MAIL_ERROR_NOSPACE:
122
resp_code = IMAP_RESP_CODE_OVERQUOTA;
124
case MAIL_ERROR_NOTFOUND:
125
if ((cmd->cmd_flags & COMMAND_FLAG_USE_NONEXISTENT) != 0)
126
resp_code = IMAP_RESP_CODE_NONEXISTENT;
128
case MAIL_ERROR_EXISTS:
129
resp_code = IMAP_RESP_CODE_ALREADYEXISTS;
131
case MAIL_ERROR_EXPUNGED:
132
resp_code = IMAP_RESP_CODE_EXPUNGEISSUED;
134
case MAIL_ERROR_INUSE:
135
resp_code = IMAP_RESP_CODE_INUSE;
138
if (resp_code == NULL || *error_string == '[')
139
return t_strconcat("NO ", error_string, NULL);
141
return t_strdup_printf("NO [%s] %s", resp_code, error_string);
144
void client_send_list_error(struct client_command_context *cmd,
145
struct mailbox_list *list)
147
const char *error_string;
148
enum mail_error error;
150
error_string = mailbox_list_get_last_error(list, &error);
151
client_send_tagline(cmd, imap_get_error_string(cmd, error_string,
155
void client_send_storage_error(struct client_command_context *cmd,
156
struct mail_storage *storage)
158
const char *error_string;
159
enum mail_error error;
161
if (cmd->client->mailbox != NULL &&
162
mailbox_is_inconsistent(cmd->client->mailbox)) {
163
/* we can't do forced CLOSE, so have to disconnect */
164
client_disconnect_with_error(cmd->client,
165
"IMAP session state is inconsistent, please relogin.");
169
error_string = mail_storage_get_last_error(storage, &error);
170
client_send_tagline(cmd, imap_get_error_string(cmd, error_string,
174
void client_send_untagged_storage_error(struct client *client,
175
struct mail_storage *storage)
177
const char *error_string;
178
enum mail_error error;
180
if (client->mailbox != NULL &&
181
mailbox_is_inconsistent(client->mailbox)) {
182
/* we can't do forced CLOSE, so have to disconnect */
183
client_disconnect_with_error(client,
184
"IMAP session state is inconsistent, please relogin.");
188
error_string = mail_storage_get_last_error(storage, &error);
189
client_send_line(client, t_strconcat("* NO ", error_string, NULL));
192
bool client_parse_mail_flags(struct client_command_context *cmd,
193
const struct imap_arg *args,
194
enum mail_flags *flags_r,
195
const char *const **keywords_r)
198
enum mail_flags flag;
199
ARRAY_DEFINE(keywords, const char *);
203
p_array_init(&keywords, cmd->pool, 16);
205
while (!IMAP_ARG_IS_EOL(args)) {
206
if (!imap_arg_get_atom(args, &atom)) {
207
client_send_command_error(cmd,
208
"Flags list contains non-atoms.");
214
atom = t_str_ucase(atom);
215
flag = imap_parse_system_flag(atom);
216
if (flag != 0 && flag != MAIL_RECENT)
219
client_send_tagline(cmd, t_strconcat(
220
"BAD Invalid system flag ",
225
/* keyword validity checks are done by lib-storage */
226
array_append(&keywords, &atom, 1);
232
if (array_count(&keywords) == 0)
235
(void)array_append_space(&keywords); /* NULL-terminate */
236
*keywords_r = array_idx(&keywords, 0);
241
void client_send_mailbox_flags(struct client *client, bool selecting)
243
struct mailbox_status status;
244
unsigned int count = array_count(client->keywords.names);
245
const char *const *keywords;
248
if (!selecting && count == client->keywords.announce_count) {
249
/* no changes to keywords and we're not selecting a mailbox */
253
client->keywords.announce_count = count;
254
mailbox_get_open_status(client->mailbox, STATUS_PERMANENT_FLAGS,
257
keywords = count == 0 ? NULL :
258
array_idx(client->keywords.names, 0);
259
str = t_str_new(128);
260
str_append(str, "* FLAGS (");
261
imap_write_flags(str, MAIL_FLAGS_NONRECENT, keywords);
262
str_append_c(str, ')');
263
client_send_line(client, str_c(str));
265
if (!status.permanent_keywords)
268
str_truncate(str, 0);
269
str_append(str, "* OK [PERMANENTFLAGS (");
270
imap_write_flags(str, status.permanent_flags, keywords);
271
if (status.allow_new_keywords) {
272
if (status.permanent_flags != 0 || keywords != NULL)
273
str_append_c(str, ' ');
274
str_append(str, "\\*");
276
str_append(str, ")] ");
278
if (mailbox_is_readonly(client->mailbox))
279
str_append(str, "Read-only mailbox.");
281
str_append(str, "Flags permitted.");
282
client_send_line(client, str_c(str));
285
void client_update_mailbox_flags(struct client *client,
286
const ARRAY_TYPE(keywords) *keywords)
288
client->keywords.names = keywords;
289
client->keywords.announce_count = 0;
293
client_get_keyword_names(struct client *client, ARRAY_TYPE(keywords) *dest,
294
const ARRAY_TYPE(keyword_indexes) *src)
296
const unsigned int *kw_indexes;
297
const char *const *all_names;
298
unsigned int all_count;
300
client_send_mailbox_flags(client, FALSE);
302
/* convert indexes to names */
303
all_names = array_get(client->keywords.names, &all_count);
305
array_foreach(src, kw_indexes) {
306
unsigned int kw_index = *kw_indexes;
308
i_assert(kw_index < all_count);
309
array_append(dest, &all_names[kw_index], 1);
312
(void)array_append_space(dest);
313
return array_idx(dest, 0);
316
bool mailbox_equals(const struct mailbox *box1,
317
const struct mail_namespace *ns2, const char *name2)
319
struct mail_namespace *ns1 = mailbox_get_namespace(box1);
325
name1 = mailbox_get_vname(box1);
326
if (strcmp(name1, name2) == 0)
329
return strcasecmp(name1, "INBOX") == 0 &&
330
strcasecmp(name2, "INBOX") == 0;
333
void msgset_generator_init(struct msgset_generator_context *ctx, string_t *str)
335
memset(ctx, 0, sizeof(*ctx));
337
ctx->last_uid = (uint32_t)-1;
340
void msgset_generator_next(struct msgset_generator_context *ctx, uint32_t uid)
342
if (uid != ctx->last_uid+1) {
343
if (ctx->first_uid == 0)
345
else if (ctx->first_uid == ctx->last_uid)
346
str_printfa(ctx->str, "%u,", ctx->first_uid);
348
str_printfa(ctx->str, "%u:%u,",
349
ctx->first_uid, ctx->last_uid);
351
ctx->first_uid = uid;
356
void msgset_generator_finish(struct msgset_generator_context *ctx)
358
if (ctx->first_uid == ctx->last_uid)
359
str_printfa(ctx->str, "%u", ctx->first_uid);
361
str_printfa(ctx->str, "%u:%u", ctx->first_uid, ctx->last_uid);