imap-notify.c revision 4693a58116ae794e43fa59f301b2f2f703c6f929
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "imap-common.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "str.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "ostream.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "imap-quote.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "mailbox-list-iter.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "mailbox-list-notify.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "mail-search.h"
b7324e421e2132cbbf753e6fdbe675bbaecdf929Timo Sirainen#include "mail-search-build.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "imap-commands.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "imap-fetch.h"
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen#include "imap-list.h"
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen#include "imap-status.h"
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen#include "imap-notify.h"
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen#define IMAP_NOTIFY_WATCH_ADD_DELAY_MSECS 1000
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainenstatic bool notify_hook_registered;
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainenstatic int imap_notify_list(struct imap_notify_namespace *notify_ns,
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen const struct mailbox_list_notify_rec *rec,
c9099b08b3cae8a849098ca776b4363c6d5f5f36Timo Sirainen enum mailbox_info_flags flags)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen{
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen string_t *str = t_str_new(128);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen char ns_sep = mail_namespace_get_sep(notify_ns->ns);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen str_append(str, "* LIST (");
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen imap_mailbox_flags2str(str, flags);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen str_append(str, ") \"");
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (ns_sep == '\\')
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen str_append_c(str, '\\');
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen str_append_c(str, ns_sep);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen str_append(str, "\" ");
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen imap_append_astring(str, rec->vname);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (rec->old_vname != NULL) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen str_append(str, " (\"OLDNAME\" (");
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen imap_append_astring(str, rec->old_vname);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen str_append(str, "))");
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen }
92f5ea24e989266539e97c6fe59ede0565aec6fdTimo Sirainen return client_send_line_next(notify_ns->ctx->client, str_c(str));
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen}
fb79b36eb34532dbe67caf99eefe3660b8c841e0Timo Sirainen
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainenstatic int imap_notify_status(struct imap_notify_namespace *notify_ns,
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen const struct mailbox_list_notify_rec *rec)
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen{
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen struct client *client = notify_ns->ctx->client;
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen struct mailbox *box;
92f5ea24e989266539e97c6fe59ede0565aec6fdTimo Sirainen struct imap_status_items items;
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen struct imap_status_result result;
47daf6e810a4c2dd52640092092900dbcb12f265Timo Sirainen enum mail_error error;
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen int ret = 1;
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen i_zero(&items);
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0)
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen items.status |= STATUS_HIGHESTMODSEQ;
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen box = mailbox_alloc(notify_ns->ns->list, rec->vname, 0);
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen mailbox_set_reason(box, "NOTIFY STATUS");
cc23ad7b8ab96d93d5ab5139c431fcdd8d9e1d72Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0) {
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen items.status |= STATUS_UIDVALIDITY | STATUS_UIDNEXT |
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen STATUS_MESSAGES | STATUS_UNSEEN;
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen }
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_APPENDS |
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen MAILBOX_LIST_NOTIFY_EXPUNGES)) != 0)
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen items.status |= STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN;
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_SEEN_CHANGES) != 0)
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen items.status |= STATUS_UNSEEN;
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) != 0) {
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen /* if HIGHESTMODSEQ isn't being sent, don't send anything */
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen }
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen if (items.status == 0) {
1c0020171b04d14adc4966ed963361abc9a86787Timo Sirainen /* don't send anything */
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen } else if (mailbox_get_status(box, items.status, &result.status) < 0) {
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen /* hide permission errors from client. we don't want to leak
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen information about existence of mailboxes where user doesn't
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen have access to */
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen (void)mailbox_get_last_error(box, &error);
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen if (error != MAIL_ERROR_PERM)
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen ret = -1;
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen } else {
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen ret = imap_status_send(client, rec->vname, &items, &result);
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen }
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen mailbox_free(&box);
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen return ret;
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen}
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainenstatic int
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainenimap_notify_next(struct imap_notify_namespace *notify_ns,
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen const struct mailbox_list_notify_rec *rec)
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen{
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen enum mailbox_info_flags mailbox_flags;
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen int ret;
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_CREATE) != 0) {
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
df8046c9a4f6bc2a478ad1e74504d50f3110c906Timo Sirainen &mailbox_flags) < 0)
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen mailbox_flags = 0;
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) <= 0)
7000810786f2959f02cd6d2f4151a9eb61ff5db8Timo Sirainen return ret;
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen }
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_DELETE) != 0) {
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, MAILBOX_NONEXISTENT)) < 0)
abb5d20d3155db02a1afec4066d52707ba9d4e52Timo Sirainen return ret;
2aee623fcad4b931c27435ceaa495c3d3edd69b6Aki Tuomi }
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_RENAME) != 0) {
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen &mailbox_flags) < 0)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen mailbox_flags = 0;
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen return ret;
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen }
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_SUBSCRIBE) != 0) {
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen &mailbox_flags) < 0)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen mailbox_flags = 0;
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec,
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen mailbox_flags | MAILBOX_SUBSCRIBED)) < 0)
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen return ret;
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen }
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UNSUBSCRIBE) != 0) {
d6a7cb184cc882a90aa3d9312082e0029f354ff6Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen &mailbox_flags) < 0)
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen mailbox_flags = 0;
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0)
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen return ret;
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen }
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_UIDVALIDITY |
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen MAILBOX_LIST_NOTIFY_APPENDS |
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen MAILBOX_LIST_NOTIFY_EXPUNGES |
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen MAILBOX_LIST_NOTIFY_SEEN_CHANGES |
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES)) != 0) {
f5e8a76a128d4e92f0641135183c164fd5c5ce5eTimo Sirainen if ((ret = imap_notify_status(notify_ns, rec)) < 0)
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen return ret;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen }
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen return 1;
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen}
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainenstatic bool
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainenimap_notify_match_event(struct imap_notify_namespace *notify_ns,
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen const struct imap_notify_mailboxes *notify_boxes,
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen const struct mailbox_list_notify_rec *rec)
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen{
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen enum imap_notify_event wanted_events = notify_boxes->events;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen struct mailbox *box;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen /* check for mailbox list events first */
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) {
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_CREATE |
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen MAILBOX_LIST_NOTIFY_DELETE |
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen MAILBOX_LIST_NOTIFY_RENAME)) != 0)
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen return TRUE;
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen }
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) {
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_SUBSCRIBE |
92f9871ac981201fe0a47f6c909f790cce14b240Timo Sirainen MAILBOX_LIST_NOTIFY_UNSUBSCRIBE)) != 0)
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen return TRUE;
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen }
cc935aff970ed6c24d136cc560c7e705a49d536cTimo Sirainen
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen /* if this is an event for the selected mailbox, ignore it */
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen box = notify_ns->ctx->client->mailbox;
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen if (box != NULL && mailbox_equals(box, notify_ns->ns, rec->vname))
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen return FALSE;
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen if ((wanted_events & (IMAP_NOTIFY_EVENT_MESSAGE_NEW |
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE |
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen IMAP_NOTIFY_EVENT_FLAG_CHANGE)) != 0) {
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0)
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen return TRUE;
279c6b6d0b0a159c8533102e7e914db21dadcb03Timo Sirainen }
387f9e3b4120273ad0213206a0e9cc2dc0e62ccaTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) {
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_APPENDS) != 0)
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen return TRUE;
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen }
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) {
071543cc13df9600d2e97aa35f28907be5a79477Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_EXPUNGES) != 0)
27e859cee42654bff801ba96677cfc4e4e0108c7Timo Sirainen return TRUE;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen }
387f9e3b4120273ad0213206a0e9cc2dc0e62ccaTimo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) {
632018810af689442569cbb0139c55868923ccfeTimo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_SEEN_CHANGES |
632018810af689442569cbb0139c55868923ccfeTimo Sirainen MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES)) != 0)
632018810af689442569cbb0139c55868923ccfeTimo Sirainen return TRUE;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen }
632018810af689442569cbb0139c55868923ccfeTimo Sirainen return FALSE;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen}
632018810af689442569cbb0139c55868923ccfeTimo Sirainen
632018810af689442569cbb0139c55868923ccfeTimo Sirainenbool imap_notify_match_mailbox(struct imap_notify_namespace *notify_ns,
632018810af689442569cbb0139c55868923ccfeTimo Sirainen const struct imap_notify_mailboxes *notify_boxes,
387f9e3b4120273ad0213206a0e9cc2dc0e62ccaTimo Sirainen const char *vname)
632018810af689442569cbb0139c55868923ccfeTimo Sirainen{
632018810af689442569cbb0139c55868923ccfeTimo Sirainen struct mailbox *box;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen const char *const *namep;
632018810af689442569cbb0139c55868923ccfeTimo Sirainen size_t name_len;
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen char ns_sep;
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen bool ret;
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen switch (notify_boxes->type) {
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen case IMAP_NOTIFY_TYPE_SUBSCRIBED:
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen box = mailbox_alloc(notify_ns->ns->list, vname, 0);
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen mailbox_set_reason(box, "NOTIFY is subscribed");
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen ret = mailbox_is_subscribed(box);
8d3af185ae454653fad60e41c5f36edb1d45c868Timo Sirainen mailbox_free(&box);
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen return ret;
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen case IMAP_NOTIFY_TYPE_SUBTREE:
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen ns_sep = mail_namespace_get_sep(notify_ns->ns);
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen array_foreach(&notify_boxes->names, namep) {
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen name_len = strlen(*namep);
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen if (name_len == 0) {
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen /* everything under root. NOTIFY spec itself
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen doesn't define this, but we use it for
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen implementing "personal" */
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen return TRUE;
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen }
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen if (strncmp(*namep, vname, name_len) == 0 &&
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen (vname[name_len] == '\0' ||
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen vname[name_len] == ns_sep))
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen return TRUE;
dea8bfa31729dbdde1b12718f1ef98fac4e99db9Timo Sirainen }
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen break;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen case IMAP_NOTIFY_TYPE_MAILBOX:
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen array_foreach(&notify_boxes->names, namep) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (strcmp(*namep, vname) == 0)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return TRUE;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen }
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen break;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen }
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return FALSE;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen}
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic bool
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenimap_notify_match(struct imap_notify_namespace *notify_ns,
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen const struct mailbox_list_notify_rec *rec)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen{
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen const struct imap_notify_mailboxes *notify_boxes;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen array_foreach(&notify_ns->mailboxes, notify_boxes) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (imap_notify_match_event(notify_ns, notify_boxes, rec) &&
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen imap_notify_match_mailbox(notify_ns, notify_boxes, rec->vname))
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return TRUE;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen }
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return FALSE;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen}
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainenstatic int imap_client_notify_ns(struct imap_notify_namespace *notify_ns)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen{
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen const struct mailbox_list_notify_rec *rec;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen int ret, ret2 = 1;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (notify_ns->notify == NULL)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return 0; /* notifications not supported in this namespace */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen while ((ret = mailbox_list_notify_next(notify_ns->notify, &rec)) > 0) {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (imap_notify_match(notify_ns, rec)) T_BEGIN {
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen ret2 = imap_notify_next(notify_ns, rec);
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen } T_END;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen if (ret2 <= 0)
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen break;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen }
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen if (ret < 0) {
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen /* failed to get some notifications */
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen return -1;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen }
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen return ret2;
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen}
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainenstatic int
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainenimap_client_notify_selected(struct client *client)
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen{
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen int ret;
baf3e87e186453fda13bd21f7cbcb2efc8492e8bTimo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (!fetch_ctx->state.fetching)
6de0cbb4a498ce0519b0f28e221164ce8d39736aTimo Sirainen return 1;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if ((ret = imap_fetch_more_no_lock_update(fetch_ctx)) <= 0)
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen return ret;
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen /* finished the FETCH */
f8740ac53310cd28ba4ec6dc9e9ce6e9a3688f39Timo Sirainen if (imap_fetch_end(fetch_ctx) < 0)
return -1;
return 1;
}
static int imap_client_notify_more(struct client *client)
{
struct imap_notify_namespace *notify_ns;
int ret = 1;
/* send notifications for selected mailbox first. note that it may
leave the client's output stream in the middle of a FETCH reply. */
if (client->notify_ctx->fetch_ctx != NULL) {
if ((ret = imap_client_notify_selected(client)) < 0) {
client->notify_ctx->fetch_ctx->state.failed = FALSE;
ret = -1;
}
}
/* send notifications for non-selected mailboxes */
array_foreach_modifiable(&client->notify_ctx->namespaces, notify_ns) {
if (ret == 0)
break;
if (imap_client_notify_ns(notify_ns) < 0)
ret = -1;
}
if (ret < 0) {
client_send_line(notify_ns->ctx->client,
"* NO NOTIFY error, some events may have got lost");
}
return ret;
}
int imap_client_notify_newmails(struct client *client)
{
struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx;
struct mailbox_status status;
struct mail_search_args *search_args;
struct mail_search_arg *arg;
i_assert(client->mailbox != NULL);
if (fetch_ctx == NULL) {
/* FETCH notifications not enabled in this session */
return 1;
}
if (client->notify_ctx->notifying)
return imap_client_notify_more(client);
client->notify_ctx->notifying = TRUE;
i_assert(!fetch_ctx->state.fetching);
mailbox_get_open_status(client->mailbox, STATUS_UIDNEXT, &status);
search_args = mail_search_build_init();
arg = mail_search_build_add(search_args, SEARCH_UIDSET);
p_array_init(&arg->value.seqset, search_args->pool, 1);
seq_range_array_add_range(&arg->value.seqset,
client->notify_uidnext, status.uidnext-1);
client->notify_uidnext = status.uidnext;
imap_fetch_begin(fetch_ctx, client->mailbox, search_args);
mail_search_args_unref(&search_args);
return imap_client_notify_more(client);
}
void imap_client_notify_finished(struct client *client)
{
if (client->notify_ctx != NULL)
client->notify_ctx->notifying = FALSE;
}
static void notify_callback(void *context)
{
struct imap_notify_namespace *notify_ns = context;
o_stream_cork(notify_ns->ctx->client->output);
imap_client_notify_ns(notify_ns);
o_stream_uncork(notify_ns->ctx->client->output);
}
static enum mailbox_list_notify_event
imap_events_to_notify(enum imap_notify_event events)
{
enum mailbox_list_notify_event ret = 0;
if ((events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) {
ret |= MAILBOX_LIST_NOTIFY_APPENDS |
MAILBOX_LIST_NOTIFY_UIDVALIDITY;
}
if ((events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) {
ret |= MAILBOX_LIST_NOTIFY_EXPUNGES |
MAILBOX_LIST_NOTIFY_UIDVALIDITY;
}
if ((events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) {
ret |= MAILBOX_LIST_NOTIFY_SEEN_CHANGES |
MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES |
MAILBOX_LIST_NOTIFY_UIDVALIDITY;
}
if ((events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) {
ret |= MAILBOX_LIST_NOTIFY_CREATE |
MAILBOX_LIST_NOTIFY_DELETE |
MAILBOX_LIST_NOTIFY_RENAME;
}
if ((events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) {
ret |= MAILBOX_LIST_NOTIFY_SUBSCRIBE |
MAILBOX_LIST_NOTIFY_UNSUBSCRIBE;
}
return ret;
}
static void imap_notify_callback(struct mailbox *box, struct client *client)
{
struct client_command_context *cmd;
enum mailbox_sync_flags sync_flags = 0;
i_assert(client->command_queue_size == 0);
i_assert(box == client->mailbox);
/* create a fake command to handle this */
cmd = client_command_alloc(client);
cmd->tag = "*";
cmd->name = "NOTIFY-CALLBACK";
if (!client->notify_ctx->selected_immediate_expunges)
sync_flags |= MAILBOX_SYNC_FLAG_NO_EXPUNGES;
if (cmd_sync(cmd, sync_flags, 0, NULL))
i_unreached();
(void)cmd_sync_delayed(client);
}
static void imap_notify_watch_selected_mailbox(struct client *client)
{
if (client->command_queue_size > 0) {
/* don't add it until all commands are finished */
return;
}
if (client->mailbox == NULL) {
/* mailbox not selected */
return;
}
if (client->notify_ctx == NULL || !client->notify_ctx->selected_set) {
/* client doesn't want selected mailbox notifications */
return;
}
mailbox_notify_changes(client->mailbox, imap_notify_callback, client);
client->notify_ctx->watching_mailbox = TRUE;
}
static void imap_notify_watch_timeout(struct client *client)
{
timeout_remove(&client->notify_ctx->to_watch);
imap_notify_watch_selected_mailbox(client);
}
void imap_client_notify_command_freed(struct client *client)
{
struct imap_notify_context *ctx = client->notify_ctx;
if (ctx == NULL)
return;
/* add mailbox watched back after a small delay */
if (ctx->to_watch != NULL)
timeout_reset(ctx->to_watch);
else {
ctx->to_watch = timeout_add(IMAP_NOTIFY_WATCH_ADD_DELAY_MSECS,
imap_notify_watch_timeout,
client);
}
}
static void imap_notify_cmd_hook_pre(struct client_command_context *cmd)
{
struct imap_notify_context *ctx = cmd->client->notify_ctx;
if (ctx == NULL)
return;
/* remove mailbox watcher before starting any commands */
if (ctx->watching_mailbox) {
mailbox_notify_changes_stop(cmd->client->mailbox);
ctx->watching_mailbox = FALSE;
}
if (ctx->to_watch != NULL)
timeout_remove(&ctx->to_watch);
}
int imap_notify_begin(struct imap_notify_context *ctx)
{
struct imap_notify_namespace *notify_ns;
const struct imap_notify_mailboxes *notify_boxes;
enum mailbox_list_notify_event notify_events;
int ret = -1;
if (!notify_hook_registered) {
notify_hook_registered = TRUE;
command_hook_register(imap_notify_cmd_hook_pre, NULL);
}
array_foreach_modifiable(&ctx->namespaces, notify_ns) {
notify_events = 0;
array_foreach(&notify_ns->mailboxes, notify_boxes) {
notify_events |=
imap_events_to_notify(notify_boxes->events);
}
if (mailbox_list_notify_init(notify_ns->ns->list, notify_events,
&notify_ns->notify) < 0) {
/* notifications not supported */
} else {
ret = 0;
mailbox_list_notify_wait(notify_ns->notify,
notify_callback, notify_ns);
}
}
/* enable NOTIFY as long as even one namespace supports it,
ignore the rest */
return ret;
}
void imap_notify_deinit(struct imap_notify_context **_ctx)
{
struct imap_notify_context *ctx = *_ctx;
struct imap_notify_namespace *notify_ns;
*_ctx = NULL;
array_foreach_modifiable(&ctx->namespaces, notify_ns) {
if (notify_ns->notify != NULL)
mailbox_list_notify_deinit(&notify_ns->notify);
}
if (ctx->to_watch != NULL)
timeout_remove(&ctx->to_watch);
if (ctx->fetch_ctx != NULL)
imap_fetch_free(&ctx->fetch_ctx);
pool_unref(&ctx->pool);
}
void imap_notify_flush(struct imap_notify_context *ctx)
{
struct imap_notify_namespace *notify_ns;
array_foreach_modifiable(&ctx->namespaces, notify_ns) {
if (notify_ns->notify != NULL)
mailbox_list_notify_flush(notify_ns->notify);
}
}