imap-notify.c revision 2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74c
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
5254d77805cd35b9356d072ba325c356c43b0d51Timo Sirainen#include "imap-common.h"
5254d77805cd35b9356d072ba325c356c43b0d51Timo Sirainen#include "str.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "ostream.h"
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen#include "imap-quote.h"
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen#include "mailbox-list-iter.h"
bdd36cfdba3ff66d25570a9ff568d69e1eb543cfTimo Sirainen#include "mailbox-list-notify.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-search.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "mail-search-build.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "imap-commands.h"
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen#include "imap-fetch.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "imap-list.h"
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#include "imap-status.h"
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen#include "imap-notify.h"
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen#define IMAP_NOTIFY_WATCH_ADD_DELAY_MSECS 1000
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainen
252db51b6c0a605163326b3ea5d09e9936ca3b29Timo Sirainenstatic bool notify_hook_registered;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int imap_notify_list(struct imap_notify_namespace *notify_ns,
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen const struct mailbox_list_notify_rec *rec,
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen enum mailbox_info_flags flags)
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen{
8cb72c59d5ea4e9e5f638d7ec840bb853f5a188eTimo Sirainen string_t *str = t_str_new(128);
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen char ns_sep = mail_namespace_get_sep(notify_ns->ns);
2c25e1360d4b5cc55eda969a3a7204d950de5a8fTimo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen str_append(str, "* LIST (");
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen imap_mailbox_flags2str(str, flags);
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen str_append(str, ") \"");
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen if (ns_sep == '\\')
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen str_append_c(str, '\\');
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen str_append_c(str, ns_sep);
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen str_append(str, "\" ");
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen imap_append_astring(str, rec->vname);
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen if (rec->old_vname != NULL) {
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen str_append(str, " (\"OLDNAME\" (");
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen imap_append_astring(str, rec->old_vname);
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen str_append(str, "))");
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen }
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen return client_send_line_next(notify_ns->ctx->client, str_c(str));
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen}
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainenstatic int imap_notify_status(struct imap_notify_namespace *notify_ns,
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen const struct mailbox_list_notify_rec *rec)
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct client *client = notify_ns->ctx->client;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct mailbox *box;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct imap_status_items items;
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen struct imap_status_result result;
14175321ddb88619015866978c05a27786ca4814Timo Sirainen enum mail_error error;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen int ret = 1;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen memset(&items, 0, sizeof(items));
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen if ((client->enabled_features & MAILBOX_FEATURE_CONDSTORE) != 0)
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen items.status |= STATUS_HIGHESTMODSEQ;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen box = mailbox_alloc(notify_ns->ns->list, rec->vname, 0);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen items.status |= STATUS_UIDVALIDITY | STATUS_UIDNEXT |
1060afdc2fcdf647dbb3bc11647401f1b44a3a8aTimo Sirainen STATUS_MESSAGES | STATUS_UNSEEN;
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen }
8edc373587d75f8040e3c4416e50638aa2a32188Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_APPENDS |
8edc373587d75f8040e3c4416e50638aa2a32188Timo Sirainen MAILBOX_LIST_NOTIFY_EXPUNGES)) != 0)
8edc373587d75f8040e3c4416e50638aa2a32188Timo Sirainen items.status |= STATUS_UIDNEXT | STATUS_MESSAGES | STATUS_UNSEEN;
8edc373587d75f8040e3c4416e50638aa2a32188Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_SEEN_CHANGES) != 0)
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen items.status |= STATUS_UNSEEN;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) != 0) {
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen /* if HIGHESTMODSEQ isn't being sent, don't send anything */
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen }
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen if (items.status == 0) {
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen /* don't send anything */
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen } else if (mailbox_get_status(box, items.status, &result.status) < 0) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* hide permission errors from client. we don't want to leak
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen information about existence of mailboxes where user doesn't
9e095dd6a77097356aca8216356d4d71ef1bea45Timo Sirainen have access to */
8edc373587d75f8040e3c4416e50638aa2a32188Timo Sirainen (void)mailbox_get_last_error(box, &error);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (error != MAIL_ERROR_PERM)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ret = -1;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen } else {
c9bf63e9094761767a63ac6b189bcf60bcffdc44Timo Sirainen ret = imap_status_send(client, rec->vname, &items, &result);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen }
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen mailbox_free(&box);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return ret;
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen}
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
9f131c8b6d88ffc65d94eae63e0b3c11d7c24cb9Timo Sirainenstatic int
9f131c8b6d88ffc65d94eae63e0b3c11d7c24cb9Timo Sirainenimap_notify_next(struct imap_notify_namespace *notify_ns,
9f131c8b6d88ffc65d94eae63e0b3c11d7c24cb9Timo Sirainen const struct mailbox_list_notify_rec *rec)
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen{
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen enum mailbox_info_flags mailbox_flags;
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen int ret;
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_CREATE) != 0) {
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen &mailbox_flags) < 0)
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen mailbox_flags = 0;
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) <= 0)
5024c4799b324ea15270152b775c67ccfc72d5bcTimo Sirainen return ret;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_DELETE) != 0) {
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, MAILBOX_NONEXISTENT)) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return ret;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_RENAME) != 0) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen &mailbox_flags) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mailbox_flags = 0;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return ret;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen }
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_SUBSCRIBE) != 0) {
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen &mailbox_flags) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen mailbox_flags = 0;
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mailbox_flags | MAILBOX_SUBSCRIBED)) < 0)
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen return ret;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UNSUBSCRIBE) != 0) {
1060afdc2fcdf647dbb3bc11647401f1b44a3a8aTimo Sirainen if (mailbox_list_mailbox(notify_ns->ns->list, rec->storage_name,
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen &mailbox_flags) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen mailbox_flags = 0;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen if ((ret = imap_notify_list(notify_ns, rec, mailbox_flags)) < 0)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return ret;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen }
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_UIDVALIDITY |
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen MAILBOX_LIST_NOTIFY_APPENDS |
c0435c854a0e7246373b9752d163095cc4fbe985Timo Sirainen MAILBOX_LIST_NOTIFY_EXPUNGES |
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen MAILBOX_LIST_NOTIFY_SEEN_CHANGES |
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES)) != 0) {
4ae354df6e08998137b527f495bfaaf3daf9eddcTimo Sirainen if ((ret = imap_notify_status(notify_ns, rec)) < 0)
4ae354df6e08998137b527f495bfaaf3daf9eddcTimo Sirainen return ret;
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen return 1;
143cb2e0744e647f8fc637bbdea1106c1587a4bfTimo Sirainen}
143cb2e0744e647f8fc637bbdea1106c1587a4bfTimo Sirainen
143cb2e0744e647f8fc637bbdea1106c1587a4bfTimo Sirainenstatic bool
8edc373587d75f8040e3c4416e50638aa2a32188Timo Sirainenimap_notify_match_event(struct imap_notify_namespace *notify_ns,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const struct imap_notify_mailboxes *notify_boxes,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const struct mailbox_list_notify_rec *rec)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen{
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen enum imap_notify_event wanted_events = notify_boxes->events;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen struct mailbox *box;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen /* check for mailbox list events first */
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MAILBOX_NAME) != 0) {
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_CREATE |
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen MAILBOX_LIST_NOTIFY_DELETE |
30c795b3ee48d753b75b2db8bfd4b88792017122Timo Sirainen MAILBOX_LIST_NOTIFY_RENAME)) != 0)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return TRUE;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_SUBSCRIPTION_CHANGE) != 0) {
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_SUBSCRIBE |
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen MAILBOX_LIST_NOTIFY_UNSUBSCRIBE)) != 0)
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen return TRUE;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen }
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen /* if this is an event for the selected mailbox, ignore it */
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen box = notify_ns->ctx->client->mailbox;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen if (box != NULL && mailbox_equals(box, notify_ns->ns, rec->vname))
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen return FALSE;
e17d72cec1c75554483d692edd687b411526f312Timo Sirainen
e17d72cec1c75554483d692edd687b411526f312Timo Sirainen if ((wanted_events & (IMAP_NOTIFY_EVENT_MESSAGE_NEW |
e17d72cec1c75554483d692edd687b411526f312Timo Sirainen IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE |
e17d72cec1c75554483d692edd687b411526f312Timo Sirainen IMAP_NOTIFY_EVENT_FLAG_CHANGE)) != 0) {
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_UIDVALIDITY) != 0)
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen return TRUE;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen }
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0) {
e17d72cec1c75554483d692edd687b411526f312Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_APPENDS) != 0)
e17d72cec1c75554483d692edd687b411526f312Timo Sirainen return TRUE;
e17d72cec1c75554483d692edd687b411526f312Timo Sirainen }
e17d72cec1c75554483d692edd687b411526f312Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) != 0) {
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if ((rec->events & MAILBOX_LIST_NOTIFY_EXPUNGES) != 0)
cd1eef2109b4476842b7757f1d69b104196d5941Timo Sirainen return TRUE;
07e4875d250e7a7157cd99132aafc773cf3cdf83Timo Sirainen }
01f4ee4a0243f3fe9af763e1a540cd5cff0d63f5Timo Sirainen if ((wanted_events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0) {
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen if ((rec->events & (MAILBOX_LIST_NOTIFY_SEEN_CHANGES |
dd62b77c932d1b518f2a3e4bf80e36542becc256Timo Sirainen MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES)) != 0)
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen return TRUE;
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen }
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen return FALSE;
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
9a755930537f13e9746c4fc8c1bc42a83e52275eTimo Sirainenbool imap_notify_match_mailbox(struct imap_notify_namespace *notify_ns,
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const struct imap_notify_mailboxes *notify_boxes,
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen const char *vname)
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen{
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen struct mailbox *box;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen const char *const *namep;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen size_t name_len;
7ef5ca6fb59a318c821a852ae48a2edbb671d7ddTimo Sirainen char ns_sep;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen bool ret;
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen switch (notify_boxes->type) {
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen case IMAP_NOTIFY_TYPE_SUBSCRIBED:
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen box = mailbox_alloc(notify_ns->ns->list, vname, 0);
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen ret = mailbox_is_subscribed(box);
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen mailbox_free(&box);
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen return ret;
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen case IMAP_NOTIFY_TYPE_SUBTREE:
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen ns_sep = mail_namespace_get_sep(notify_ns->ns);
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen array_foreach(&notify_boxes->names, namep) {
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen name_len = strlen(*namep);
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen if (name_len == 0) {
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen /* everything under root. NOTIFY spec itself
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen doesn't define this, but we use it for
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen implementing "personal" */
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen return TRUE;
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen }
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen if (strncmp(*namep, vname, name_len) == 0 &&
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen (vname[name_len] == '\0' ||
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen vname[name_len] == ns_sep))
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen return TRUE;
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen }
331b4805d76c0b3a5a38a560276f3cf110f55ba0Timo Sirainen break;
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen case IMAP_NOTIFY_TYPE_MAILBOX:
c06f4017027263cf3a08becc551f5126409e2a83Timo Sirainen array_foreach(&notify_boxes->names, namep) {
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen if (strcmp(*namep, vname) == 0)
411d6baa37f31d90730e90c4a28c43e1974bbe58Timo Sirainen return TRUE;
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen }
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen break;
9a755930537f13e9746c4fc8c1bc42a83e52275eTimo Sirainen }
9a755930537f13e9746c4fc8c1bc42a83e52275eTimo Sirainen return FALSE;
9a755930537f13e9746c4fc8c1bc42a83e52275eTimo Sirainen}
ecc81625167ed96c04c02aa190a1ea5baa65b474Timo Sirainen
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainenstatic bool
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärviimap_notify_match(struct imap_notify_namespace *notify_ns,
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen const struct mailbox_list_notify_rec *rec)
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen{
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen const struct imap_notify_mailboxes *notify_boxes;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen array_foreach(&notify_ns->mailboxes, notify_boxes) {
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen if (imap_notify_match_event(notify_ns, notify_boxes, rec) &&
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen imap_notify_match_mailbox(notify_ns, notify_boxes, rec->vname))
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi return TRUE;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen }
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen return FALSE;
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen}
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainenstatic int imap_client_notify_ns(struct imap_notify_namespace *notify_ns)
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi{
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi const struct mailbox_list_notify_rec *rec;
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi int ret, ret2 = 1;
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi if (notify_ns->notify == NULL)
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi return 0; /* notifications not supported in this namespace */
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen while ((ret = mailbox_list_notify_next(notify_ns->notify, &rec)) > 0) {
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen if (imap_notify_match(notify_ns, rec)) T_BEGIN {
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen ret2 = imap_notify_next(notify_ns, rec);
43834f87bf431198f986e86052a4f6e558fdb07dTimo Sirainen } T_END;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen if (ret2 <= 0)
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen break;
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen }
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen if (ret < 0) {
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen /* failed to get some notifications */
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen return -1;
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen }
bf132be3fe1c9e8de84f10d0b05c0b46ca542ac4Timo Sirainen return ret2;
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen}
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainen
09801f106cd531a28b4e03ec665e44c421264560Timo Sirainenstatic int
imap_client_notify_selected(struct client *client)
{
struct imap_fetch_context *fetch_ctx = client->notify_ctx->fetch_ctx;
int ret;
if (!fetch_ctx->state.fetching)
return 1;
if ((ret = imap_fetch_more_no_lock_update(fetch_ctx)) <= 0)
return ret;
/* finished the FETCH */
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)
{
imap_notify_watch_selected_mailbox(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);
}
static void imap_notify_cmd_hook_post(struct client_command_context *cmd)
{
struct imap_notify_context *ctx = cmd->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,
cmd->client);
}
}
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,
imap_notify_cmd_hook_post);
}
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);
}