imap-notify.c revision 1c02804cdc5f1ad830fec081100e951bc67204b4
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainenstatic int imap_notify_list(struct imap_notify_namespace *notify_ns,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen char ns_sep = mail_namespace_get_sep(notify_ns->ns);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainen return client_send_line_next(notify_ns->ctx->client, str_c(str));
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainenstatic int imap_notify_status(struct imap_notify_namespace *notify_ns,
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen struct client *client = notify_ns->ctx->client;
1388b590dbd85245b591346f860bc1319953318aTimo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0)
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen box = mailbox_alloc(notify_ns->ns->list, rec->vname, 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0) {
5d03d9f439e41c90215a3c938ffebe4c2a8ae257Timo Sirainen items.status |= STATUS_UIDVALIDITY | STATUS_UIDNEXT |
3482fee0e3733456512ba110780824e6daa7ff9fTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_APPENDS |
3482fee0e3733456512ba110780824e6daa7ff9fTimo Sirainen items.status |= STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_SEEN_CHANGES) != 0)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) != 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* if HIGHESTMODSEQ isn't being sent, don't send anything */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* don't send anything */
d1bf4ae66b8bf3b9e28df1823d6d4adda2b923b6Timo Sirainen } else if (mailbox_get_status(box, items.status, &result.status) < 0) {
d1bf4ae66b8bf3b9e28df1823d6d4adda2b923b6Timo Sirainen /* hide permission errors from client. we don't want to leak
1388b590dbd85245b591346f860bc1319953318aTimo Sirainen information about existence of mailboxes where user doesn't
d1fff80640050631b06bfab904a34b2ad24601e8Timo Sirainen have access to */
1388b590dbd85245b591346f860bc1319953318aTimo Sirainen ret = imap_status_send(client, rec->vname, &items, &result);
c977ee6ce06cbc0b4668fde1ec34f2f5e1773684Timo Sirainenimap_notify_next(struct imap_notify_namespace *notify_ns,
38f227941bcf673e0e523c1ac7267bca9cbcd2c4Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_CREATE) != 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
265cb53cf8d5cb35edd4c4ff086ca6165605b708Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) <= 0)
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_DELETE) != 0) {
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, MAILBOX_NONEXISTENT)) < 0)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_RENAME) != 0) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_SUBSCRIBE) != 0) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UNSUBSCRIBE) != 0) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_UIDVALIDITY |
096e109f9f332bc758ca5e22ec64337379c5f231Timo Sirainen if ((ret = imap_notify_status(notify_ns, rec)) < 0)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainenimap_notify_match_event(struct imap_notify_namespace *notify_ns,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen const struct imap_notify_mailboxes *notify_boxes,
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen enum imap_notify_event wanted_events = notify_boxes->events;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen /* check for mailbox list events first */
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_CREATE |
7af5f78e9fee296e42430d94ef252ff0333d8024Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) {
7af5f78e9fee296e42430d94ef252ff0333d8024Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_SUBSCRIBE |
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen /* if this is an event for the selected mailbox, ignore it */
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if (box != NULL && mailbox_equals(box, notify_ns->ns, rec->vname))
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((wanted_events & (IMAP_NOTIFY_EVENT_MESSAGE_NEW |
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) {
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_APPENDS) != 0)
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) {
1f5597beba229acd914e30a6da3c0e62d83b6e8fTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_EXPUNGES) != 0)
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) {
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_SEEN_CHANGES |
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainenbool imap_notify_match_mailbox(struct imap_notify_namespace *notify_ns,
47569a4b2b4d3cc55e786177798c922c3c44233dTimo Sirainen const struct imap_notify_mailboxes *notify_boxes,
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen const char *const *namep;
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen box = mailbox_alloc(notify_ns->ns->list, vname, 0);
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen mailbox_set_reason(box, "NOTIFY is subscribed");
c07d7eb3ca9754367697c98f5e66a3982a45d142Timo Sirainen ns_sep = mail_namespace_get_sep(notify_ns->ns);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* everything under root. NOTIFY spec itself
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen doesn't define this, but we use it for
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen implementing "personal" */
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainenimap_notify_match(struct imap_notify_namespace *notify_ns,
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen const struct imap_notify_mailboxes *notify_boxes;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_foreach(¬ify_ns->mailboxes, notify_boxes) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (imap_notify_match_event(notify_ns, notify_boxes, rec) &&
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen imap_notify_match_mailbox(notify_ns, notify_boxes, rec->vname))
f3e17726502b6cf1912f30aae7e283b5d31ea69cTimo Sirainenstatic int imap_client_notify_ns(struct imap_notify_namespace *notify_ns)
f3e17726502b6cf1912f30aae7e283b5d31ea69cTimo Sirainen return 0; /* notifications not supported in this namespace */
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen while ((ret = mailbox_list_notify_next(notify_ns->notify, &rec)) > 0) {
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen if (imap_notify_match(notify_ns, rec)) T_BEGIN {
002179a890bf4f1942cad6463787719eaa9fd6c0Timo Sirainen /* failed to get some notifications */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenimap_client_notify_selected(struct client *client)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx;
e438c85a6b0f77889e25913bbbba808d6078282dStephan Bosch if ((ret = imap_fetch_more_no_lock_update(fetch_ctx)) <= 0)
4c261fb48e6e36570a0841aa51ca483024d6a0a6Timo Sirainen /* finished the FETCH */
baf97389e203e105b97a8183b2893740691f8c63Timo Sirainenstatic int imap_client_notify_more(struct client *client)
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch /* send notifications for selected mailbox first. note that it may
c93aca832ee532010ead91b85fa9f614132e1be2Stephan Bosch leave the client's output stream in the middle of a FETCH reply. */
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen if ((ret = imap_client_notify_selected(client)) < 0) {
542e28b384a6b26695f3e8de38fd5727d06f3333Timo Sirainen client->notify_ctx->fetch_ctx->state.failed = FALSE;
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen /* send notifications for non-selected mailboxes */
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen array_foreach_modifiable(&client->notify_ctx->namespaces, notify_ns) {
18c209a06941ef583b08b173dadfbe4571995bf9Timo Sirainen "* NO NOTIFY error, some events may have got lost");
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenint imap_client_notify_newmails(struct client *client)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* FETCH notifications not enabled in this session */
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen mailbox_get_open_status(client->mailbox, STATUS_UIDNEXT, &status);
11120acd01d43973cd504952d691a2ae1c546ee2Timo Sirainen arg = mail_search_build_add(search_args, SEARCH_UIDSET);
fc464e5b2b2ab4d415a5d5b90ce4475d34620a75Timo Sirainen p_array_init(&arg->value.seqset, search_args->pool, 1);
4da8c6cdefabd31262318c32da3c13de1d9ea953Timo Sirainen imap_fetch_begin(fetch_ctx, client->mailbox, search_args);
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainenvoid imap_client_notify_finished(struct client *client)
3b22894b8805b186c73d8b754001e8d7e944be85Timo Sirainenstatic void notify_callback(struct imap_notify_namespace *notify_ns)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen o_stream_cork(notify_ns->ctx->client->output);
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen o_stream_uncork(notify_ns->ctx->client->output);
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainenimap_events_to_notify(enum imap_notify_event events)
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if ((events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) {
70afae43cc78ea6ecca83f6c587072c442a15ec1Timo Sirainenstatic void imap_notify_callback(struct mailbox *box, struct client *client)
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen /* create a fake command to handle this */
acfda38b75d0f0e899ef692fef01593bd56ed85eTimo Sirainen if (!client->notify_ctx->selected_immediate_expunges)
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainenstatic void imap_notify_watch_selected_mailbox(struct client *client)
acfda38b75d0f0e899ef692fef01593bd56ed85eTimo Sirainen /* mailbox not selected */
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainen if (client->notify_ctx == NULL || !client->notify_ctx->selected_set) {
acfda38b75d0f0e899ef692fef01593bd56ed85eTimo Sirainen /* client doesn't want selected mailbox notifications */
acfda38b75d0f0e899ef692fef01593bd56ed85eTimo Sirainen mailbox_notify_changes(client->mailbox, imap_notify_callback, client);
acfda38b75d0f0e899ef692fef01593bd56ed85eTimo Sirainenstatic void imap_notify_watch_timeout(struct client *client)
acfda38b75d0f0e899ef692fef01593bd56ed85eTimo Sirainen timeout_remove(&client->notify_ctx->to_watch);
acfda38b75d0f0e899ef692fef01593bd56ed85eTimo Sirainenvoid imap_client_notify_command_freed(struct client *client)
acfda38b75d0f0e899ef692fef01593bd56ed85eTimo Sirainen struct imap_notify_context *ctx = client->notify_ctx;
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen /* don't add it until all commands are finished */
19cadcc25c26af7afea1355d78e20ad64eaad263Timo Sirainen /* add mailbox watch back after a small delay. if another command
19cadcc25c26af7afea1355d78e20ad64eaad263Timo Sirainen is started this timeout is aborted. */
19cadcc25c26af7afea1355d78e20ad64eaad263Timo Sirainen ctx->to_watch = timeout_add(IMAP_NOTIFY_WATCH_ADD_DELAY_MSECS,
2f90189c6ee66a17f7bf838a8eb8a69868630fb8Timo Sirainenvoid imap_client_notify_command_allocated(struct client *client)
19cadcc25c26af7afea1355d78e20ad64eaad263Timo Sirainen struct imap_notify_context *ctx = client->notify_ctx;
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen /* remove mailbox watcher before starting any commands */
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainenint imap_notify_begin(struct imap_notify_context *ctx)
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen const struct imap_notify_mailboxes *notify_boxes;
0f5dc4da3982053036be65190e44bf28a67b1ca2Timo Sirainen array_foreach_modifiable(&ctx->namespaces, notify_ns) {
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen array_foreach(¬ify_ns->mailboxes, notify_boxes) {
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen if (mailbox_list_notify_init(notify_ns->ns->list, notify_events,
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainen /* notifications not supported */
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch /* enable NOTIFY as long as even one namespace supports it,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ignore the rest */
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainenvoid imap_notify_deinit(struct imap_notify_context **_ctx)
216cd45a5f47c9bd46fe67c1b3bd6b1a42f6e39cTimo Sirainen array_foreach_modifiable(&ctx->namespaces, notify_ns) {
c06d6ea0766d0520af1a93e6000c0e73f350e0a2Timo Sirainen mailbox_list_notify_deinit(¬ify_ns->notify);
13b063ba3ea51256fd97d7fa883f14cb08842b0dTimo Sirainenvoid imap_notify_flush(struct imap_notify_context *ctx)