bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic int imap_notify_list(struct imap_notify_namespace *notify_ns,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen char ns_sep = mail_namespace_get_sep(notify_ns->ns);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return client_send_line_next(notify_ns->ctx->client, str_c(str));
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic int imap_notify_status(struct imap_notify_namespace *notify_ns,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct client *client = notify_ns->ctx->client;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen box = mailbox_alloc(notify_ns->ns->list, rec->vname, 0);
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen items.status |= STATUS_UIDVALIDITY | STATUS_UIDNEXT |
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_APPENDS |
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen items.status |= STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN;
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_SEEN_CHANGES) != 0)
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* if HIGHESTMODSEQ isn't being sent, don't send anything */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* don't send anything */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen } else if (mailbox_get_status(box, items.status, &result.status) < 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* hide permission errors from client. we don't want to leak
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen information about existence of mailboxes where user doesn't
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen have access to */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ret = imap_status_send(client, rec->vname, &items, &result);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenimap_notify_next(struct imap_notify_namespace *notify_ns,
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_CREATE) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) <= 0)
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_DELETE) != 0) {
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((ret = imap_notify_list(notify_ns, rec, MAILBOX_NONEXISTENT)) < 0)
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_RENAME) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0)
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_SUBSCRIBE) != 0) {
24acd68c82dc137b88bb3ba3258b9d1f7ebcb44dTimo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UNSUBSCRIBE) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0)
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_UIDVALIDITY |
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((ret = imap_notify_status(notify_ns, rec)) < 0)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenimap_notify_match_event(struct imap_notify_namespace *notify_ns,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const struct imap_notify_mailboxes *notify_boxes,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen enum imap_notify_event wanted_events = notify_boxes->events;
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen /* check for mailbox list events first */
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) {
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_CREATE |
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) {
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_SUBSCRIBE |
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen /* if this is an event for the selected mailbox, ignore it */
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if (box != NULL && mailbox_equals(box, notify_ns->ns, rec->vname))
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((wanted_events & (IMAP_NOTIFY_EVENT_MESSAGE_NEW |
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0)
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) {
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_APPENDS) != 0)
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) {
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_EXPUNGES) != 0)
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) {
e27db67b4549773e5015643e776bda9e7025719fTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_SEEN_CHANGES |
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenbool imap_notify_match_mailbox(struct imap_notify_namespace *notify_ns,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const struct imap_notify_mailboxes *notify_boxes,
98bd9a8b6d894c3648d0e2074a0d441e48425cfcTimo Sirainen box = mailbox_alloc(notify_ns->ns->list, vname, 0);
61cf001f1944d92eb25f113ba4c08985d6e30d53Timo Sirainen mailbox_set_reason(box, "NOTIFY is subscribed");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ns_sep = mail_namespace_get_sep(notify_ns->ns);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* everything under root. NOTIFY spec itself
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen doesn't define this, but we use it for
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen implementing "personal" */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenimap_notify_match(struct imap_notify_namespace *notify_ns,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const struct imap_notify_mailboxes *notify_boxes;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen array_foreach(¬ify_ns->mailboxes, notify_boxes) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (imap_notify_match_event(notify_ns, notify_boxes, rec) &&
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen imap_notify_match_mailbox(notify_ns, notify_boxes, rec->vname))
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic int imap_client_notify_ns(struct imap_notify_namespace *notify_ns)
1c1e591e42f6c3cdfeeed26164f5c85d85d01877Timo Sirainen return 0; /* notifications not supported in this namespace */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen while ((ret = mailbox_list_notify_next(notify_ns->notify, &rec)) > 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (imap_notify_match(notify_ns, rec)) T_BEGIN {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* failed to get some notifications */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenimap_client_notify_selected(struct client *client)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if ((ret = imap_fetch_more_no_lock_update(fetch_ctx)) <= 0)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* finished the FETCH */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic int imap_client_notify_more(struct client *client)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* send notifications for selected mailbox first. note that it may
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen leave the client's output stream in the middle of a FETCH reply. */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if ((ret = imap_client_notify_selected(client)) < 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen client->notify_ctx->fetch_ctx->state.failed = FALSE;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* send notifications for non-selected mailboxes */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen array_foreach_modifiable(&client->notify_ctx->namespaces, notify_ns) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen "* NO NOTIFY error, some events may have got lost");
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenint imap_client_notify_newmails(struct client *client)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* FETCH notifications not enabled in this session */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen mailbox_get_open_status(client->mailbox, STATUS_UIDNEXT, &status);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen arg = mail_search_build_add(search_args, SEARCH_UIDSET);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen p_array_init(&arg->value.seqset, search_args->pool, 1);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen imap_fetch_begin(fetch_ctx, client->mailbox, search_args);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenvoid imap_client_notify_finished(struct client *client)
ffe330e26377fe1f3661586b7edf6520e11f52daAki Tuomistatic void notify_callback(struct imap_notify_namespace *notify_ns)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen o_stream_cork(notify_ns->ctx->client->output);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen o_stream_uncork(notify_ns->ctx->client->output);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenimap_events_to_notify(enum imap_notify_event events)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) {
24acd68c82dc137b88bb3ba3258b9d1f7ebcb44dTimo Sirainen if ((events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic void imap_notify_callback(struct mailbox *box, struct client *client)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* create a fake command to handle this */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (!client->notify_ctx->selected_immediate_expunges)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic void imap_notify_watch_selected_mailbox(struct client *client)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* mailbox not selected */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (client->notify_ctx == NULL || !client->notify_ctx->selected_set) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* client doesn't want selected mailbox notifications */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen mailbox_notify_changes(client->mailbox, imap_notify_callback, client);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenstatic void imap_notify_watch_timeout(struct client *client)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen timeout_remove(&client->notify_ctx->to_watch);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenvoid imap_client_notify_command_freed(struct client *client)
4693a58116ae794e43fa59f301b2f2f703c6f929Timo Sirainen struct imap_notify_context *ctx = client->notify_ctx;
906a651134bead2be605f2be6c47bd91a4cb94beTimo Sirainen /* don't add it until all commands are finished */
906a651134bead2be605f2be6c47bd91a4cb94beTimo Sirainen /* add mailbox watch back after a small delay. if another command
906a651134bead2be605f2be6c47bd91a4cb94beTimo Sirainen is started this timeout is aborted. */
906a651134bead2be605f2be6c47bd91a4cb94beTimo Sirainen ctx->to_watch = timeout_add(IMAP_NOTIFY_WATCH_ADD_DELAY_MSECS,
0081ed0d90d302027ed4a53cb617c0179c246868Timo Sirainenvoid imap_client_notify_command_allocated(struct client *client)
0081ed0d90d302027ed4a53cb617c0179c246868Timo Sirainen struct imap_notify_context *ctx = client->notify_ctx;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* remove mailbox watcher before starting any commands */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenint imap_notify_begin(struct imap_notify_context *ctx)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen const struct imap_notify_mailboxes *notify_boxes;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen array_foreach_modifiable(&ctx->namespaces, notify_ns) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen array_foreach(¬ify_ns->mailboxes, notify_boxes) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen if (mailbox_list_notify_init(notify_ns->ns->list, notify_events,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* notifications not supported */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* enable NOTIFY as long as even one namespace supports it,
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen ignore the rest */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenvoid imap_notify_deinit(struct imap_notify_context **_ctx)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen array_foreach_modifiable(&ctx->namespaces, notify_ns) {
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen mailbox_list_notify_deinit(¬ify_ns->notify);
369847496787678ee18608754a97f0c2df80a36fTimo Sirainenvoid imap_notify_flush(struct imap_notify_context *ctx)