# or ~user/.
#mail_full_filesystem_access = no
+# Maximum allowed length for custom flag name. It's only forced when trying
+# to create new flags.
+#mail_max_flag_length = 50
+
# Save mails with CR+LF instead of plain LF. This makes sending those mails
# take less CPU, especially with sendfile() syscall with Linux and FreeBSD.
# But it also creates a bit more disk I/O which may just make it slower.
MAX_IMAP_ARG_ELEMENTS);
client->last_input = ioloop_time;
+ client->mailbox_flags.pool =
+ pool_alloconly_create("mailbox_custom_flags", 512);
client->storage = storage;
storage->set_callbacks(storage, &mail_storage_callbacks, client);
i_stream_unref(client->input);
o_stream_unref(client->output);
+ pool_unref(client->mailbox_flags.pool);
i_free(client);
/* quit the program */
typedef int client_command_func_t(struct client *client);
+struct mailbox_custom_flags {
+ pool_t pool; /* will be p_clear()ed when changed */
+
+ char **custom_flags;
+ unsigned int custom_flags_count;
+};
+
struct client {
int socket;
struct io *io;
struct mail_storage *storage;
struct mailbox *mailbox;
+ struct mailbox_custom_flags mailbox_flags;
unsigned int select_counter; /* increased when mailbox is changed */
time_t last_input;
int cmd_append(struct client *client)
{
struct mailbox *box;
+ struct mailbox_status status;
struct mail_save_context *ctx;
struct imap_parser *save_parser;
struct imap_arg *args;
struct imap_arg_list *flags_list;
+ struct mailbox_custom_flags old_flags;
struct mail_full_flags flags;
time_t internal_date;
const char *mailbox, *internal_date_str, *error;
return TRUE;
}
+ if (!box->get_status(box, STATUS_CUSTOM_FLAGS, &status)) {
+ client_send_storage_error(client);
+ box->close(box);
+ return TRUE;
+ }
+ memset(&old_flags, 0, sizeof(old_flags));
+ old_flags.pool = data_stack_pool;
+ client_save_custom_flags(&old_flags, status.custom_flags,
+ status.custom_flags_count);
+
ctx = box->save_init(box, TRUE);
if (ctx == NULL) {
client_send_storage_error(client);
if (flags_list != NULL) {
if (!client_parse_mail_flags(client, flags_list->args,
- &flags))
+ &old_flags, &flags))
break;
} else {
memset(&flags, 0, sizeof(flags));
return TRUE;
}
+ client_save_custom_flags(&client->mailbox_flags, status.custom_flags,
+ status.custom_flags_count);
+
/* set client's mailbox only after getting status to make sure
we're not sending any expunge/exists replies too early to client */
client->mailbox = box;
if (args[2].type == IMAP_ARG_LIST) {
if (!client_parse_mail_flags(client,
IMAP_ARG_LIST(&args[2])->args,
- &flags))
+ &client->mailbox_flags, &flags))
return TRUE;
} else {
- if (!client_parse_mail_flags(client, args+2, &flags))
+ if (!client_parse_mail_flags(client, args+2,
+ &client->mailbox_flags, &flags))
return TRUE;
}
t_strconcat(syntax ? "* BAD " : "* NO ", error, NULL));
}
+static int is_valid_custom_flag(struct client *client,
+ const struct mailbox_custom_flags *old_flags,
+ const char *flag)
+{
+ size_t i;
+
+ /* if it already exists, skip validity checks */
+ for (i = 0; i < old_flags->custom_flags_count; i++) {
+ if (old_flags->custom_flags[i] != NULL &&
+ strcasecmp(old_flags->custom_flags[i], flag) == 0)
+ return TRUE;
+ }
+
+ if (strlen(flag) > max_custom_flag_length) {
+ client_send_tagline(client,
+ t_strdup_printf("BAD Invalid flag name '%s': "
+ "Maximum length is %u characters",
+ flag, max_custom_flag_length));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
int client_parse_mail_flags(struct client *client, struct imap_arg *args,
+ const struct mailbox_custom_flags *old_flags,
struct mail_full_flags *flags)
{
/* @UNSAFE */
}
if (i == flags->custom_flags_count) {
+ if (!is_valid_custom_flag(client, old_flags,
+ atom))
+ return FALSE;
flags->flags |= 1 << (flag_pos +
MAIL_CUSTOM_FLAG_1_BIT);
flags->custom_flags[flag_pos++] = atom;
")] Flags permitted.", NULL));
}
}
+
+void client_save_custom_flags(struct mailbox_custom_flags *dest,
+ const char *custom_flags[],
+ unsigned int custom_flags_count)
+{
+ unsigned int i;
+
+ p_clear(dest->pool);
+
+ dest->custom_flags =
+ p_new(dest->pool, char *, custom_flags_count);
+ dest->custom_flags_count = custom_flags_count;
+
+ for (i = 0; i < custom_flags_count; i++)
+ dest->custom_flags[i] = p_strdup(dest->pool, custom_flags[i]);
+}
/* Parse flags. Returns TRUE if successful, if not sends an error message to
client. */
int client_parse_mail_flags(struct client *client, struct imap_arg *args,
+ const struct mailbox_custom_flags *old_flags,
struct mail_full_flags *flags);
/* Send FLAGS + PERMANENTFLAGS to client. */
const char *custom_flags[],
unsigned int custom_flags_count);
+/* Copy custom flags into dest. dest must have been initialized. */
+void client_save_custom_flags(struct mailbox_custom_flags *dest,
+ const char *custom_flags[],
+ unsigned int custom_flags_count);
+
#endif
for command from user is around MAX_INBUF_SIZE * MAX_IMAP_ARG_ELEMENTS */
#define MAX_IMAP_ARG_ELEMENTS 128
+#define DEFAULT_MAX_CUSTOM_FLAG_LENGTH 50
+
extern struct ioloop *ioloop;
+extern unsigned int max_custom_flag_length;
#endif
if (client->mailbox != mailbox)
return;
+ client_save_custom_flags(&client->mailbox_flags, custom_flags,
+ custom_flags_count);
+
client_send_mailbox_flags(client, mailbox, custom_flags,
custom_flags_count);
}
(getenv("LOGGED_IN") == NULL)
struct ioloop *ioloop;
+unsigned int max_custom_flag_length;
+
static char log_prefix[128]; /* syslog() needs this to be permanent */
static void sig_quit(int signo __attr_unused__)
{
struct client *client;
struct mail_storage *storage;
- const char *mail;
+ const char *mail, *str;
int hin, hout;
lib_init_signals(sig_quit);
}
}
+ str = getenv("MAIL_MAX_FLAG_LENGTH");
+ max_custom_flag_length = str != NULL ?
+ (unsigned int)strtoul(str, NULL, 10) :
+ DEFAULT_MAX_CUSTOM_FLAG_LENGTH;
+
client = client_create(hin, hout, storage);
if (IS_STANDALONE()) {
set->mailbox_check_interval));
env_put(t_strconcat("CLIENT_WORKAROUNDS=",
set->client_workarounds, NULL));
+ env_put(t_strdup_printf("MAIL_MAX_FLAG_LENGTH=%u",
+ set->mail_max_flag_length));
if (set->mail_save_crlf)
env_put("MAIL_SAVE_CRLF=1");
DEF(SET_STR, client_workarounds),
DEF(SET_INT, mailbox_check_interval),
DEF(SET_BOOL, mail_full_filesystem_access),
+ DEF(SET_INT, mail_max_flag_length),
DEF(SET_BOOL, mail_save_crlf),
DEF(SET_BOOL, mail_read_mmaped),
DEF(SET_BOOL, maildir_copy_with_hardlinks),
MEMBER(client_workarounds) NULL,
MEMBER(mailbox_check_interval) 0,
MEMBER(mail_full_filesystem_access) FALSE,
+ MEMBER(mail_max_flag_length) 50,
MEMBER(mail_save_crlf) FALSE,
MEMBER(mail_read_mmaped) FALSE,
MEMBER(maildir_copy_with_hardlinks) FALSE,
const char *client_workarounds;
unsigned int mailbox_check_interval;
int mail_full_filesystem_access;
+ int mail_max_flag_length;
int mail_save_crlf;
int mail_read_mmaped;
int maildir_copy_with_hardlinks;