cmd-notify.c revision 5bf4e28d6d2c25c54ee201943ac5398a74481360
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn/* Copyright (c) 2013-2015 Dovecot authors, see the included COPYING file */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "imap-common.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "str.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "mailbox-list-iter.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "imap-quote.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "imap-commands.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "imap-fetch.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "imap-list.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "imap-status.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#include "imap-notify.h"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#define IMAP_NOTIFY_MAX_NAMES_PER_NS 100
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynstatic const char *imap_notify_event_names[] = {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn "MessageNew", "MessageExpunge", "FlagChange", "AnnotationChange",
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn "MailboxName", "SubscriptionChange", "MailboxMetadataChange",
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn "ServerMetadataChange"
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn};
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynstatic int
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyncmd_notify_parse_event(const struct imap_arg *arg,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_event *event_r)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn{
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const char *str;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn unsigned int i;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (!imap_arg_get_atom(arg, &str))
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn for (i = 0; i < N_ELEMENTS(imap_notify_event_names); i++) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (strcasecmp(str, imap_notify_event_names[i]) == 0) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn *event_r = (enum imap_notify_event)(1 << i);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return 0;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn}
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallynstatic int
4759162d078d86628956cae4846c6efccf548e67Serge Hallyncmd_notify_parse_fetch(struct imap_notify_context *ctx,
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn const struct imap_arg *list)
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn{
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn return imap_fetch_att_list_parse(ctx->client, ctx->pool, list,
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn &ctx->fetch_ctx, &ctx->error);
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn}
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynstatic int
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyncmd_notify_set_selected(struct imap_notify_context *ctx,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const struct imap_arg *events)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn{
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn#define EV_NEW_OR_EXPUNGE \
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn (IMAP_NOTIFY_EVENT_MESSAGE_NEW | IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const struct imap_arg *list, *fetch_att_list;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const char *str;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_event event;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (imap_arg_get_atom(events, &str) &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn strcasecmp(str, "NONE") == 0) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* no events for selected mailbox. this is also the default
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn when NOTIFY command doesn't specify it explicitly */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return 0;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (!imap_arg_get_list(events, &list))
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn for (; list->type != IMAP_ARG_EOL; list++) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (cmd_notify_parse_event(list, &event) < 0)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn ctx->selected_events |= event;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn ctx->global_used_events |= event;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (event == IMAP_NOTIFY_EVENT_MESSAGE_NEW &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn imap_arg_get_list(&list[1], &fetch_att_list)) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* MessageNew: list of fetch-att */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (cmd_notify_parse_fetch(ctx, fetch_att_list) < 0)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn list++;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* if MessageNew or MessageExpunge is specified, both of them must */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if ((ctx->selected_events & EV_NEW_OR_EXPUNGE) != 0 &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn (ctx->selected_events & EV_NEW_OR_EXPUNGE) != EV_NEW_OR_EXPUNGE) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn ctx->error = "MessageNew and MessageExpunge must be together";
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* if FlagChange or AnnotationChange is specified,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn MessageNew and MessageExpunge must also be specified */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if ((ctx->selected_events &
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn (IMAP_NOTIFY_EVENT_FLAG_CHANGE |
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE)) != 0 &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn (ctx->selected_events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) == 0) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn ctx->error = "FlagChange requires MessageNew and MessageExpunge";
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return 0;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn}
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallynstatic struct imap_notify_namespace *
4759162d078d86628956cae4846c6efccf548e67Serge Hallynimap_notify_namespace_get(struct imap_notify_context *ctx,
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn struct mail_namespace *ns)
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn{
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn struct imap_notify_namespace *notify_ns;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn array_foreach_modifiable(&ctx->namespaces, notify_ns) {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn if (notify_ns->ns == ns)
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn return notify_ns;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn }
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn notify_ns = array_append_space(&ctx->namespaces);
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn notify_ns->ctx = ctx;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn notify_ns->ns = ns;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn p_array_init(&notify_ns->mailboxes, ctx->pool, 4);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return notify_ns;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn}
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynstatic struct imap_notify_mailboxes *
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynimap_notify_mailboxes_get(struct imap_notify_namespace *notify_ns,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_type type,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_event events)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn{
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn struct imap_notify_mailboxes *notify_boxes;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn array_foreach_modifiable(&notify_ns->mailboxes, notify_boxes) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (notify_boxes->type == type &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn notify_boxes->events == events)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return notify_boxes;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn notify_boxes = array_append_space(&notify_ns->mailboxes);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn notify_boxes->type = type;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn notify_boxes->events = events;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn p_array_init(&notify_boxes->names, notify_ns->ctx->pool, 4);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return notify_boxes;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn}
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynstatic void
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyncmd_notify_add_mailbox(struct imap_notify_context *ctx,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn struct mail_namespace *ns, const char *name,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_type type,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_event events)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn{
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn struct imap_notify_namespace *notify_ns;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn struct imap_notify_mailboxes *notify_boxes;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const char *const *names;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn unsigned int i, count, cur_len, name_len = strlen(name);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn char ns_sep = mail_namespace_get_sep(ns);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn strncmp(name, "INBOX", 5) != 0 &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn strncasecmp(name, "INBOX", 5) == 0 &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn (name[5] == '\0' || name[5] == ns_sep)) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* we'll do only case-sensitive comparisons later,
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn so sanitize INBOX to be uppercase */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn name = t_strconcat("INBOX", name + 5, NULL);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn notify_ns = imap_notify_namespace_get(ctx, ns);
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn notify_boxes = imap_notify_mailboxes_get(notify_ns, type, events);
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn names = array_get(&notify_boxes->names, &count);
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn for (i = 0; i < count; ) {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn if (strcmp(names[i], name) == 0) {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn /* exact duplicate, already added */
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn return;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn }
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn if (type != IMAP_NOTIFY_TYPE_SUBTREE)
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn i++;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn else {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn /* see if one is a subtree of the other */
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn cur_len = strlen(names[i]);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (strncmp(names[i], name, cur_len) == 0 &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn names[i][cur_len] == ns_sep) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* already matched in this subtree */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (strncmp(names[i], name, name_len) == 0 &&
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn names[i][name_len] == ns_sep) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* we're adding a parent, remove the child */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn array_delete(&notify_boxes->names, i, 1);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn names = array_get(&notify_boxes->names, &count);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn } else {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn i++;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn name = p_strdup(ctx->pool, name);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn array_append(&notify_boxes->names, &name, 1);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn ctx->global_max_mailbox_names =
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn I_MAX(ctx->global_max_mailbox_names,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn array_count(&notify_boxes->names));
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn}
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynstatic void cmd_notify_add_personal(struct imap_notify_context *ctx,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_event events)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn{
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn struct mail_namespace *ns;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn for (ns = ctx->client->user->namespaces; ns != NULL; ns = ns->next) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn cmd_notify_add_mailbox(ctx, ns, "",
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn IMAP_NOTIFY_TYPE_SUBTREE, events);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn}
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynstatic void cmd_notify_add_subscribed(struct imap_notify_context *ctx,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_event events)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn{
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn struct mail_namespace *ns;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn for (ns = ctx->client->user->namespaces; ns != NULL; ns = ns->next) {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn cmd_notify_add_mailbox(ctx, ns, "",
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn IMAP_NOTIFY_TYPE_SUBSCRIBED, events);
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn }
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn}
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallynstatic void
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyncmd_notify_add_mailbox_namespaces(struct imap_notify_context *ctx,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const char *name,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_type type,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_event events)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn{
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn struct mail_namespace *ns;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn ns = mail_namespace_find(ctx->client->user->namespaces, name);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn cmd_notify_add_mailbox(ctx, ns, name, type, events);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn}
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallynstatic int
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyncmd_notify_add_mailboxes(struct imap_notify_context *ctx,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const struct imap_arg *arg,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_type type,
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn enum imap_notify_event events)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn{
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const struct imap_arg *list;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn const char *name;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (imap_arg_get_astring(arg, &name)) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn cmd_notify_add_mailbox_namespaces(ctx, name, type, events);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return 0;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (!imap_arg_get_list(arg, &list))
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn for (; list->type != IMAP_ARG_EOL; list++) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (!imap_arg_get_astring(list, &name))
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn cmd_notify_add_mailbox_namespaces(ctx, name, type, events);
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn return 0;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn}
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallynstatic int
4759162d078d86628956cae4846c6efccf548e67Serge Hallyncmd_notify_set(struct imap_notify_context *ctx, const struct imap_arg *args)
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn{
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn const struct imap_arg *event_group, *mailboxes, *list;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn const char *str, *filter_mailboxes;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn enum imap_notify_event event, event_mask;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn if (imap_arg_get_atom(args, &str) &&
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn strcasecmp(str, "STATUS") == 0) {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn /* send STATUS replies for all matched mailboxes before
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn NOTIFY's OK reply */
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn ctx->send_immediate_status = TRUE;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn args++;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn for (; args->type != IMAP_ARG_EOL; args++) {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn if (!imap_arg_get_list(args, &event_group))
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* filter-mailboxes */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (!imap_arg_get_atom(event_group, &filter_mailboxes))
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn event_group++;
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn if (strcasecmp(filter_mailboxes, "selected") == 0 ||
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn strcasecmp(filter_mailboxes, "selected-delayed") == 0) {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn /* setting events for selected mailbox.
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn handle specially. */
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn if (ctx->selected_set) {
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn ctx->error = "Duplicate selected filter";
4759162d078d86628956cae4846c6efccf548e67Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn ctx->selected_set = TRUE;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (strcasecmp(filter_mailboxes, "selected") == 0)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn ctx->selected_immediate_expunges = TRUE;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (cmd_notify_set_selected(ctx, event_group) < 0)
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn return -1;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn continue;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (strcasecmp(filter_mailboxes, "subtree") == 0 ||
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn strcasecmp(filter_mailboxes, "mailboxes") == 0) {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn mailboxes = event_group++;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn } else {
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn mailboxes = NULL;
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn }
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn /* parse events */
d1458ac8d13880f83fa2d1e08623b97c50d311d7Serge Hallyn if (imap_arg_get_atom(event_group, &str) &&
strcasecmp(str, "NONE") == 0) {
/* NONE is the default, ignore this */
continue;
}
if (!imap_arg_get_list(event_group, &list) ||
list[0].type == IMAP_ARG_EOL)
return -1;
event_mask = 0;
for (; list->type != IMAP_ARG_EOL; list++) {
if (cmd_notify_parse_event(list, &event) < 0)
return -1;
event_mask |= event;
ctx->global_used_events |= event;
}
/* we can't currently know inboxes, so treat it the
same as personal */
if (strcasecmp(filter_mailboxes, "inboxes") == 0 ||
strcasecmp(filter_mailboxes, "personal") == 0)
cmd_notify_add_personal(ctx, event_mask);
else if (strcasecmp(filter_mailboxes, "subscribed") == 0)
cmd_notify_add_subscribed(ctx, event_mask);
else if (strcasecmp(filter_mailboxes, "subtree") == 0) {
if (cmd_notify_add_mailboxes(ctx, mailboxes,
IMAP_NOTIFY_TYPE_SUBTREE,
event_mask) < 0)
return -1;
} else if (strcasecmp(filter_mailboxes, "mailboxes") == 0) {
if (cmd_notify_add_mailboxes(ctx, mailboxes,
IMAP_NOTIFY_TYPE_MAILBOX,
event_mask) < 0)
return -1;
} else {
return -1;
}
}
return 0;
}
static void
imap_notify_box_list_noperm(struct client *client, struct mailbox *box)
{
string_t *str = t_str_new(128);
char ns_sep = mail_namespace_get_sep(mailbox_get_namespace(box));
enum mailbox_info_flags mailbox_flags;
if (mailbox_list_mailbox(mailbox_get_namespace(box)->list,
mailbox_get_name(box), &mailbox_flags) < 0)
mailbox_flags = 0;
str_append(str, "* LIST (");
if (imap_mailbox_flags2str(str, mailbox_flags))
str_append_c(str, ' ');
str_append(str, "\\NoAccess) \"");
if (ns_sep == '\\')
str_append_c(str, '\\');
str_append_c(str, ns_sep);
str_append(str, "\" ");
imap_append_astring(str, mailbox_get_vname(box));
client_send_line(client, str_c(str));
}
static void
imap_notify_box_send_status(struct client_command_context *cmd,
struct imap_notify_context *ctx,
const struct mailbox_info *info)
{
struct mailbox *box;
struct imap_status_items items;
struct imap_status_result result;
if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) != 0)
return;
/* don't send STATUS to selected mailbox */
if (cmd->client->mailbox != NULL &&
mailbox_equals(cmd->client->mailbox, info->ns, info->vname))
return;
memset(&items, 0, sizeof(items));
memset(&result, 0, sizeof(result));
items.status = STATUS_UIDVALIDITY | STATUS_UIDNEXT |
STATUS_MESSAGES | STATUS_UNSEEN;
if ((ctx->global_used_events & (IMAP_NOTIFY_EVENT_FLAG_CHANGE |
IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE)) != 0)
items.status |= STATUS_HIGHESTMODSEQ;
box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_READONLY);
if (ctx->client->enabled_features != 0)
(void)mailbox_enable(box, ctx->client->enabled_features);
if (imap_status_get(cmd, info->ns, info->vname, &items, &result) < 0) {
if (result.error == MAIL_ERROR_PERM)
imap_notify_box_list_noperm(ctx->client, box);
else if (result.error != MAIL_ERROR_NOTFOUND) {
client_send_line(ctx->client,
t_strconcat("* ", result.errstr, NULL));
}
} else {
imap_status_send(ctx->client, info->vname, &items, &result);
}
mailbox_free(&box);
}
static bool imap_notify_ns_want_status(struct imap_notify_namespace *notify_ns)
{
const struct imap_notify_mailboxes *notify_boxes;
array_foreach(&notify_ns->mailboxes, notify_boxes) {
if ((notify_boxes->events &
(IMAP_NOTIFY_EVENT_MESSAGE_NEW |
IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE |
IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE |
IMAP_NOTIFY_EVENT_FLAG_CHANGE)) != 0)
return TRUE;
}
return FALSE;
}
static void
imap_notify_ns_send_status(struct client_command_context *cmd,
struct imap_notify_context *ctx,
struct imap_notify_namespace *notify_ns)
{
struct mailbox_list_iterate_context *iter;
const struct imap_notify_mailboxes *notify_boxes;
const struct mailbox_info *info;
if (!imap_notify_ns_want_status(notify_ns))
return;
/* set _RETURN_SUBSCRIBED flag just in case IMAP_NOTIFY_TYPE_SUBSCRIBED
is used, which requires refreshing subscriptions */
iter = mailbox_list_iter_init(notify_ns->ns->list, "*",
MAILBOX_LIST_ITER_RETURN_SUBSCRIBED |
MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
while ((info = mailbox_list_iter_next(iter)) != NULL) {
array_foreach(&notify_ns->mailboxes, notify_boxes) {
if (imap_notify_match_mailbox(notify_ns, notify_boxes,
info->vname)) {
imap_notify_box_send_status(cmd, ctx, info);
break;
}
}
}
if (mailbox_list_iter_deinit(&iter) < 0) {
client_send_line(notify_ns->ctx->client,
"* NO Mailbox listing failed");
}
}
static void cmd_notify_send_status(struct client_command_context *cmd,
struct imap_notify_context *ctx)
{
struct imap_notify_namespace *notify_ns;
array_foreach_modifiable(&ctx->namespaces, notify_ns)
imap_notify_ns_send_status(cmd, ctx, notify_ns);
}
bool cmd_notify(struct client_command_context *cmd)
{
struct imap_notify_context *ctx;
const struct imap_arg *args;
const char *str;
int ret = 0;
pool_t pool;
if (!client_read_args(cmd, 0, 0, &args))
return FALSE;
pool = pool_alloconly_create("imap notify context", 1024);
ctx = p_new(pool, struct imap_notify_context, 1);
ctx->pool = pool;
ctx->client = cmd->client;
p_array_init(&ctx->namespaces, pool, 4);
if (!imap_arg_get_atom(&args[0], &str))
ret = -1;
else if (strcasecmp(str, "NONE") == 0)
;
else if (strcasecmp(str, "SET") == 0)
ret = cmd_notify_set(ctx, args+1);
else
ret = -1;
if (ret < 0) {
client_send_command_error(cmd, ctx->error != NULL ? ctx->error :
"Invalid arguments.");
pool_unref(&pool);
return TRUE;
}
if ((ctx->global_used_events & UNSUPPORTED_EVENTS) != 0) {
string_t *str = t_str_new(128);
unsigned int i;
str_append(str, "NO [BADEVENT");
for (i = 0; i < N_ELEMENTS(imap_notify_event_names); i++) {
if ((ctx->global_used_events & (1 << i)) != 0 &&
((1 << i) & UNSUPPORTED_EVENTS) != 0) {
str_append_c(str, ' ');
str_append(str, imap_notify_event_names[i]);
}
}
str_append(str, "] Unsupported NOTIFY events.");
client_send_tagline(cmd, str_c(str));
pool_unref(&pool);
return TRUE;
}
if (array_count(&ctx->namespaces) == 0) {
/* selected mailbox only */
} else if (ctx->global_max_mailbox_names > IMAP_NOTIFY_MAX_NAMES_PER_NS) {
client_send_tagline(cmd,
"NO [NOTIFICATIONOVERFLOW] Too many mailbox names");
pool_unref(&pool);
return TRUE;
} else if (imap_notify_begin(ctx) < 0) {
client_send_tagline(cmd,
"NO [NOTIFICATIONOVERFLOW] NOTIFY not supported for these mailboxes.");
pool_unref(&pool);
return TRUE;
}
if (cmd->client->notify_ctx != NULL)
imap_notify_deinit(&cmd->client->notify_ctx);
if (ctx->send_immediate_status)
cmd_notify_send_status(cmd, ctx);
cmd->client->notify_immediate_expunges =
ctx->selected_immediate_expunges;
cmd->client->notify_count_changes =
(ctx->selected_events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0;
cmd->client->notify_flag_changes =
(ctx->selected_events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0;
cmd->client->notify_ctx = ctx;
return cmd_sync(cmd, 0, IMAP_SYNC_FLAG_SAFE, "OK NOTIFY completed.");
}