1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "imap-common.h"
4 #include "str.h"
5 #include "mailbox-list-iter.h"
6 #include "imap-quote.h"
7 #include "imap-commands.h"
8 #include "imap-fetch.h"
9 #include "imap-list.h"
10 #include "imap-status.h"
11 #include "imap-notify.h"
12 
13 #define IMAP_NOTIFY_MAX_NAMES_PER_NS 100
14 
15 static const char *imap_notify_event_names[] = {
16 	"MessageNew", "MessageExpunge", "FlagChange", "AnnotationChange",
17 	"MailboxName", "SubscriptionChange", "MailboxMetadataChange",
18 	"ServerMetadataChange"
19 };
20 
21 static int
cmd_notify_parse_event(const struct imap_arg * arg,enum imap_notify_event * event_r)22 cmd_notify_parse_event(const struct imap_arg *arg,
23 		       enum imap_notify_event *event_r)
24 {
25 	const char *str;
26 	unsigned int i;
27 
28 	if (!imap_arg_get_atom(arg, &str))
29 		return -1;
30 
31 	for (i = 0; i < N_ELEMENTS(imap_notify_event_names); i++) {
32 		if (strcasecmp(str, imap_notify_event_names[i]) == 0) {
33 			*event_r = (enum imap_notify_event)(1 << i);
34 			return 0;
35 		}
36 	}
37 	return -1;
38 }
39 
40 static int
cmd_notify_parse_fetch(struct imap_notify_context * ctx,const struct imap_arg * list)41 cmd_notify_parse_fetch(struct imap_notify_context *ctx,
42 		       const struct imap_arg *list)
43 {
44 	if (list->type == IMAP_ARG_EOL)
45 		return -1; /* at least one attribute must be set */
46 	return imap_fetch_att_list_parse(ctx->client, ctx->pool, list,
47 					 &ctx->fetch_ctx, &ctx->error);
48 }
49 
50 static int
cmd_notify_set_selected(struct imap_notify_context * ctx,const struct imap_arg * events)51 cmd_notify_set_selected(struct imap_notify_context *ctx,
52 			const struct imap_arg *events)
53 {
54 #define EV_NEW_OR_EXPUNGE \
55 	(IMAP_NOTIFY_EVENT_MESSAGE_NEW | IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE)
56 	const struct imap_arg *list, *fetch_att_list;
57 	const char *str;
58 	enum imap_notify_event event;
59 
60 	if (imap_arg_get_atom(events, &str) &&
61 	    strcasecmp(str, "NONE") == 0) {
62 		/* no events for selected mailbox. this is also the default
63 		   when NOTIFY command doesn't specify it explicitly */
64 		if (events[1].type != IMAP_ARG_EOL)
65 			return -1; /* no extra parameters */
66 		return 0;
67 	}
68 
69 	if (!imap_arg_get_list(events, &list))
70 		return -1;
71 	if (events[1].type != IMAP_ARG_EOL)
72 		return -1; /* no extra parameters */
73 	if (list->type == IMAP_ARG_EOL)
74 		return -1; /* at least one event */
75 
76 	for (; list->type != IMAP_ARG_EOL; list++) {
77 		if (cmd_notify_parse_event(list, &event) < 0)
78 			return -1;
79 		ctx->selected_events |= event;
80 		ctx->global_used_events |= event;
81 
82 		if (event == IMAP_NOTIFY_EVENT_MESSAGE_NEW &&
83 		    imap_arg_get_list(&list[1], &fetch_att_list)) {
84 			/* MessageNew: list of fetch-att */
85 			if (cmd_notify_parse_fetch(ctx, fetch_att_list) < 0)
86 				return -1;
87 			list++;
88 		}
89 	}
90 
91 	/* if MessageNew or MessageExpunge is specified, both of them must */
92 	if ((ctx->selected_events & EV_NEW_OR_EXPUNGE) != 0 &&
93 	    (ctx->selected_events & EV_NEW_OR_EXPUNGE) != EV_NEW_OR_EXPUNGE) {
94 		ctx->error = "MessageNew and MessageExpunge must be together";
95 		return -1;
96 	}
97 
98 	/* if FlagChange or AnnotationChange is specified,
99 	   MessageNew and MessageExpunge must also be specified */
100 	if ((ctx->selected_events &
101 	     (IMAP_NOTIFY_EVENT_FLAG_CHANGE |
102 	      IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE)) != 0 &&
103 	    (ctx->selected_events & IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE) == 0) {
104 		ctx->error = "FlagChange requires MessageNew and MessageExpunge";
105 		return -1;
106 	}
107 	return 0;
108 }
109 
110 static struct imap_notify_namespace *
imap_notify_namespace_get(struct imap_notify_context * ctx,struct mail_namespace * ns)111 imap_notify_namespace_get(struct imap_notify_context *ctx,
112 			  struct mail_namespace *ns)
113 {
114 	struct imap_notify_namespace *notify_ns;
115 
116 	array_foreach_modifiable(&ctx->namespaces, notify_ns) {
117 		if (notify_ns->ns == ns)
118 			return notify_ns;
119 	}
120 	notify_ns = array_append_space(&ctx->namespaces);
121 	notify_ns->ctx = ctx;
122 	notify_ns->ns = ns;
123 	p_array_init(&notify_ns->mailboxes, ctx->pool, 4);
124 	return notify_ns;
125 }
126 
127 static struct imap_notify_mailboxes *
imap_notify_mailboxes_get(struct imap_notify_namespace * notify_ns,enum imap_notify_type type,enum imap_notify_event events)128 imap_notify_mailboxes_get(struct imap_notify_namespace *notify_ns,
129 			  enum imap_notify_type type,
130 			  enum imap_notify_event events)
131 {
132 	struct imap_notify_mailboxes *notify_boxes;
133 
134 	array_foreach_modifiable(&notify_ns->mailboxes, notify_boxes) {
135 		if (notify_boxes->type == type &&
136 		    notify_boxes->events == events)
137 			return notify_boxes;
138 	}
139 	notify_boxes = array_append_space(&notify_ns->mailboxes);
140 	notify_boxes->type = type;
141 	notify_boxes->events = events;
142 	p_array_init(&notify_boxes->names, notify_ns->ctx->pool, 4);
143 	return notify_boxes;
144 }
145 
146 static void
cmd_notify_add_mailbox(struct imap_notify_context * ctx,struct mail_namespace * ns,const char * name,enum imap_notify_type type,enum imap_notify_event events)147 cmd_notify_add_mailbox(struct imap_notify_context *ctx,
148 		       struct mail_namespace *ns, const char *name,
149 		       enum imap_notify_type type,
150 		       enum imap_notify_event events)
151 {
152 	struct imap_notify_namespace *notify_ns;
153 	struct imap_notify_mailboxes *notify_boxes;
154 	const char *const *names;
155 	unsigned int i, count;
156 	size_t cur_len, name_len = strlen(name);
157 	char ns_sep = mail_namespace_get_sep(ns);
158 
159 	if (mail_namespace_is_removable(ns)) {
160 		/* exclude removable namespaces */
161 		return;
162 	}
163 
164 	if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
165 	    !str_begins(name, "INBOX") &&
166 	    strncasecmp(name, "INBOX", 5) == 0 &&
167 	    (name[5] == '\0' || name[5] == ns_sep)) {
168 		/* we'll do only case-sensitive comparisons later,
169 		   so sanitize INBOX to be uppercase */
170 		name = t_strconcat("INBOX", name + 5, NULL);
171 	}
172 
173 	notify_ns = imap_notify_namespace_get(ctx, ns);
174 	notify_boxes = imap_notify_mailboxes_get(notify_ns, type, events);
175 
176 	names = array_get(&notify_boxes->names, &count);
177 	for (i = 0; i < count; ) {
178 		if (strcmp(names[i], name) == 0) {
179 			/* exact duplicate, already added */
180 			return;
181 		}
182 		if (type != IMAP_NOTIFY_TYPE_SUBTREE)
183 			i++;
184 		else {
185 			/* see if one is a subtree of the other */
186 			cur_len = strlen(names[i]);
187 			if (str_begins(name, names[i]) &&
188 			    names[i][cur_len] == ns_sep) {
189 				/* already matched in this subtree */
190 				return;
191 			}
192 			if (strncmp(names[i], name, name_len) == 0 &&
193 			    names[i][name_len] == ns_sep) {
194 				/* we're adding a parent, remove the child */
195 				array_delete(&notify_boxes->names, i, 1);
196 				names = array_get(&notify_boxes->names, &count);
197 			} else {
198 				i++;
199 			}
200 		}
201 	}
202 	name = p_strdup(ctx->pool, name);
203 	array_push_back(&notify_boxes->names, &name);
204 
205 	ctx->global_max_mailbox_names =
206 		I_MAX(ctx->global_max_mailbox_names,
207 		      array_count(&notify_boxes->names));
208 }
209 
cmd_notify_add_personal(struct imap_notify_context * ctx,enum imap_notify_event events)210 static void cmd_notify_add_personal(struct imap_notify_context *ctx,
211 				    enum imap_notify_event events)
212 {
213 	struct mail_namespace *ns;
214 
215 	for (ns = ctx->client->user->namespaces; ns != NULL; ns = ns->next) {
216 		if (ns->type == MAIL_NAMESPACE_TYPE_PRIVATE) {
217 			cmd_notify_add_mailbox(ctx, ns, "",
218 				IMAP_NOTIFY_TYPE_SUBTREE, events);
219 		}
220 	}
221 }
222 
223 static int
imap_notify_refresh_subscriptions(struct client_command_context * cmd,struct imap_notify_context * ctx)224 imap_notify_refresh_subscriptions(struct client_command_context *cmd,
225 				  struct imap_notify_context *ctx)
226 {
227 	struct mailbox_list_iterate_context *iter;
228 	struct mail_namespace *ns;
229 
230 	if (!ctx->have_subscriptions)
231 		return 0;
232 
233 	/* make sure subscriptions are refreshed at least once */
234 	for (ns = ctx->client->user->namespaces; ns != NULL; ns = ns->next) {
235 		iter = mailbox_list_iter_init(ns->list, "*", MAILBOX_LIST_ITER_SELECT_SUBSCRIBED);
236 		(void)mailbox_list_iter_next(iter);
237 		if (mailbox_list_iter_deinit(&iter) < 0) {
238 			client_send_list_error(cmd, ns->list);
239 			return -1;
240 		}
241 	}
242 	return 0;
243 }
244 
cmd_notify_add_subscribed(struct imap_notify_context * ctx,enum imap_notify_event events)245 static void cmd_notify_add_subscribed(struct imap_notify_context *ctx,
246 				      enum imap_notify_event events)
247 {
248 	struct mail_namespace *ns;
249 
250 	ctx->have_subscriptions = TRUE;
251 	for (ns = ctx->client->user->namespaces; ns != NULL; ns = ns->next) {
252 		cmd_notify_add_mailbox(ctx, ns, "",
253 				       IMAP_NOTIFY_TYPE_SUBSCRIBED, events);
254 	}
255 }
256 
257 static void
cmd_notify_add_mailbox_namespaces(struct imap_notify_context * ctx,const char * name,enum imap_notify_type type,enum imap_notify_event events)258 cmd_notify_add_mailbox_namespaces(struct imap_notify_context *ctx,
259 				  const char *name,
260 				  enum imap_notify_type type,
261 				  enum imap_notify_event events)
262 {
263 	struct mail_namespace *ns;
264 
265 	ns = mail_namespace_find(ctx->client->user->namespaces, name);
266 	cmd_notify_add_mailbox(ctx, ns, name, type, events);
267 }
268 
269 static int
cmd_notify_add_mailboxes(struct imap_notify_context * ctx,const struct imap_arg * arg,enum imap_notify_type type,enum imap_notify_event events)270 cmd_notify_add_mailboxes(struct imap_notify_context *ctx,
271 			 const struct imap_arg *arg,
272 			 enum imap_notify_type type,
273 			 enum imap_notify_event events)
274 {
275 	const struct imap_arg *list;
276 	const char *name;
277 
278 	if (imap_arg_get_astring(arg, &name)) {
279 		cmd_notify_add_mailbox_namespaces(ctx, name, type, events);
280 		return 0;
281 	}
282 	if (!imap_arg_get_list(arg, &list))
283 		return -1;
284 
285 	for (; list->type != IMAP_ARG_EOL; list++) {
286 		if (!imap_arg_get_astring(list, &name))
287 			return -1;
288 
289 		cmd_notify_add_mailbox_namespaces(ctx, name, type, events);
290 	}
291 	return 0;
292 }
293 
294 static int
cmd_notify_set(struct imap_notify_context * ctx,const struct imap_arg * args)295 cmd_notify_set(struct imap_notify_context *ctx, const struct imap_arg *args)
296 {
297 	const struct imap_arg *event_group, *mailboxes, *list;
298 	const char *str, *filter_mailboxes;
299 	enum imap_notify_event event, event_mask;
300 
301 	if (imap_arg_get_atom(args, &str) &&
302 	    strcasecmp(str, "STATUS") == 0) {
303 		/* send STATUS replies for all matched mailboxes before
304 		   NOTIFY's OK reply */
305 		ctx->send_immediate_status = TRUE;
306 		args++;
307 	}
308 	for (; args->type != IMAP_ARG_EOL; args++) {
309 		if (!imap_arg_get_list(args, &event_group))
310 			return -1;
311 
312 		/* filter-mailboxes */
313 		if (!imap_arg_get_atom(event_group, &filter_mailboxes))
314 			return -1;
315 		event_group++;
316 
317 		if (strcasecmp(filter_mailboxes, "selected") == 0 ||
318 		    strcasecmp(filter_mailboxes, "selected-delayed") == 0) {
319 			/* setting events for selected mailbox.
320 			   handle specially. */
321 			if (ctx->selected_set) {
322 				ctx->error = "Duplicate selected filter";
323 				return -1;
324 			}
325 			ctx->selected_set = TRUE;
326 			if (strcasecmp(filter_mailboxes, "selected") == 0)
327 				ctx->selected_immediate_expunges = TRUE;
328 			if (cmd_notify_set_selected(ctx, event_group) < 0)
329 				return -1;
330 			continue;
331 		}
332 
333 		if (strcasecmp(filter_mailboxes, "subtree") == 0 ||
334 		    strcasecmp(filter_mailboxes, "mailboxes") == 0) {
335 			if (event_group->type == IMAP_ARG_EOL)
336 				return -1;
337 			mailboxes = event_group++;
338 			/* check that the mailboxes parameter is valid */
339 			if (IMAP_ARG_IS_ASTRING(mailboxes))
340 				;
341 			else if (!imap_arg_get_list(mailboxes, &list))
342 				return -1;
343 			else if (list->type == IMAP_ARG_EOL) {
344 				/* should have at least one mailbox */
345 				return -1;
346 			}
347 		} else {
348 			mailboxes = NULL;
349 		}
350 
351 		/* parse events */
352 		if (imap_arg_get_atom(event_group, &str) &&
353 		    strcasecmp(str, "NONE") == 0) {
354 			/* NONE is the default, ignore this */
355 			continue;
356 		}
357 		if (!imap_arg_get_list(event_group, &list) ||
358 		    list[0].type == IMAP_ARG_EOL)
359 			return -1;
360 
361 		event_mask = 0;
362 		for (; list->type != IMAP_ARG_EOL; list++) {
363 			if (cmd_notify_parse_event(list, &event) < 0)
364 				return -1;
365 			event_mask |= event;
366 			ctx->global_used_events |= event;
367 		}
368 
369 		/* we can't currently know inboxes, so treat it the
370 		   same as personal */
371 		if (strcasecmp(filter_mailboxes, "inboxes") == 0 ||
372 		    strcasecmp(filter_mailboxes, "personal") == 0)
373 			cmd_notify_add_personal(ctx, event_mask);
374 		else if (strcasecmp(filter_mailboxes, "subscribed") == 0)
375 			cmd_notify_add_subscribed(ctx, event_mask);
376 		else if (strcasecmp(filter_mailboxes, "subtree") == 0) {
377 			if (cmd_notify_add_mailboxes(ctx, mailboxes,
378 						     IMAP_NOTIFY_TYPE_SUBTREE,
379 						     event_mask) < 0)
380 				return -1;
381 		} else if (strcasecmp(filter_mailboxes, "mailboxes") == 0) {
382 			if (cmd_notify_add_mailboxes(ctx, mailboxes,
383 						     IMAP_NOTIFY_TYPE_MAILBOX,
384 						     event_mask) < 0)
385 				return -1;
386 		} else {
387 			return -1;
388 		}
389 	}
390 	return 0;
391 }
392 
393 static void
imap_notify_box_list_noperm(struct client * client,struct mailbox * box)394 imap_notify_box_list_noperm(struct client *client, struct mailbox *box)
395 {
396 	string_t *str = t_str_new(128);
397 	char ns_sep = mail_namespace_get_sep(mailbox_get_namespace(box));
398 	enum mailbox_info_flags mailbox_flags;
399 
400 	if (mailbox_list_mailbox(mailbox_get_namespace(box)->list,
401 				 mailbox_get_name(box), &mailbox_flags) < 0)
402 		mailbox_flags = 0;
403 
404 	str_append(str, "* LIST (");
405 	if (imap_mailbox_flags2str(str, mailbox_flags))
406 		str_append_c(str, ' ');
407 	str_append(str, "\\NoAccess) \"");
408 	if (ns_sep == '\\')
409 		str_append_c(str, '\\');
410 	str_append_c(str, ns_sep);
411 	str_append(str, "\" ");
412 
413 	imap_append_astring(str, mailbox_get_vname(box));
414 	client_send_line(client, str_c(str));
415 }
416 
417 static void
imap_notify_box_send_status(struct client_command_context * cmd,struct imap_notify_context * ctx,const struct mailbox_info * info)418 imap_notify_box_send_status(struct client_command_context *cmd,
419 			    struct imap_notify_context *ctx,
420 			    const struct mailbox_info *info)
421 {
422 	struct mailbox *box;
423 	struct imap_status_items items;
424 	struct imap_status_result result;
425 
426 	if ((info->flags & (MAILBOX_NONEXISTENT | MAILBOX_NOSELECT)) != 0)
427 		return;
428 
429 	/* don't send STATUS to selected mailbox */
430 	if (cmd->client->mailbox != NULL &&
431 	    mailbox_equals(cmd->client->mailbox, info->ns, info->vname))
432 		return;
433 
434 	i_zero(&items);
435 	i_zero(&result);
436 
437 	items.flags = IMAP_STATUS_ITEM_UIDVALIDITY | IMAP_STATUS_ITEM_UIDNEXT |
438 		IMAP_STATUS_ITEM_MESSAGES | IMAP_STATUS_ITEM_UNSEEN;
439 	if ((ctx->global_used_events & (IMAP_NOTIFY_EVENT_FLAG_CHANGE |
440 					IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE)) != 0)
441 		items.flags |= IMAP_STATUS_ITEM_HIGHESTMODSEQ;
442 
443 	box = mailbox_alloc(info->ns->list, info->vname, MAILBOX_FLAG_READONLY);
444 	mailbox_set_reason(box, "NOTIFY send STATUS");
445 	(void)mailbox_enable(box, client_enabled_mailbox_features(ctx->client));
446 
447 	if (imap_status_get(cmd, info->ns, info->vname, &items, &result) < 0) {
448 		if (result.error == MAIL_ERROR_PERM)
449 			imap_notify_box_list_noperm(ctx->client, box);
450 		else if (result.error != MAIL_ERROR_NOTFOUND) {
451 			client_send_line(ctx->client,
452 				t_strconcat("* ", result.errstr, NULL));
453 		}
454 	} else {
455 		imap_status_send(ctx->client, info->vname, &items, &result);
456 	}
457 	mailbox_free(&box);
458 }
459 
imap_notify_ns_want_status(struct imap_notify_namespace * notify_ns)460 static bool imap_notify_ns_want_status(struct imap_notify_namespace *notify_ns)
461 {
462 	const struct imap_notify_mailboxes *notify_boxes;
463 
464 	array_foreach(&notify_ns->mailboxes, notify_boxes) {
465 		if ((notify_boxes->events &
466 		     (IMAP_NOTIFY_EVENT_MESSAGE_NEW |
467 		      IMAP_NOTIFY_EVENT_MESSAGE_EXPUNGE |
468 		      IMAP_NOTIFY_EVENT_ANNOTATION_CHANGE |
469 		      IMAP_NOTIFY_EVENT_FLAG_CHANGE)) != 0)
470 			return TRUE;
471 	}
472 	return FALSE;
473 }
474 
475 static void
imap_notify_ns_send_status(struct client_command_context * cmd,struct imap_notify_context * ctx,struct imap_notify_namespace * notify_ns)476 imap_notify_ns_send_status(struct client_command_context *cmd,
477 			   struct imap_notify_context *ctx,
478 			   struct imap_notify_namespace *notify_ns)
479 {
480 	struct mailbox_list_iterate_context *iter;
481 	const struct imap_notify_mailboxes *notify_boxes;
482 	const struct mailbox_info *info;
483 
484 	if (!imap_notify_ns_want_status(notify_ns))
485 		return;
486 
487 	/* set _RETURN_SUBSCRIBED flag just in case IMAP_NOTIFY_TYPE_SUBSCRIBED
488 	   is used, which requires refreshing subscriptions */
489 	iter = mailbox_list_iter_init(notify_ns->ns->list, "*",
490 				      MAILBOX_LIST_ITER_RETURN_SUBSCRIBED |
491 				      MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
492 	while ((info = mailbox_list_iter_next(iter)) != NULL) {
493 		array_foreach(&notify_ns->mailboxes, notify_boxes) {
494 			if (imap_notify_match_mailbox(notify_ns, notify_boxes,
495 						      info->vname)) {
496 				imap_notify_box_send_status(cmd, ctx, info);
497 				break;
498 			}
499 		}
500 	}
501 	if (mailbox_list_iter_deinit(&iter) < 0) {
502 		client_send_line(notify_ns->ctx->client,
503 				 "* NO Mailbox listing failed");
504 	}
505 }
506 
cmd_notify_send_status(struct client_command_context * cmd,struct imap_notify_context * ctx)507 static void cmd_notify_send_status(struct client_command_context *cmd,
508 				   struct imap_notify_context *ctx)
509 {
510 	struct imap_notify_namespace *notify_ns;
511 
512 	array_foreach_modifiable(&ctx->namespaces, notify_ns)
513 		imap_notify_ns_send_status(cmd, ctx, notify_ns);
514 }
515 
cmd_notify(struct client_command_context * cmd)516 bool cmd_notify(struct client_command_context *cmd)
517 {
518 	struct imap_notify_context *ctx;
519 	const struct imap_arg *args;
520 	const char *str;
521 	int ret = 0;
522 	pool_t pool;
523 
524 	if (!client_read_args(cmd, 0, 0, &args))
525 		return FALSE;
526 
527 	pool = pool_alloconly_create("imap notify context", 1024);
528 	ctx = p_new(pool, struct imap_notify_context, 1);
529 	ctx->pool = pool;
530 	ctx->client = cmd->client;
531 	p_array_init(&ctx->namespaces, pool, 4);
532 
533 	if (!imap_arg_get_atom(&args[0], &str))
534 		ret = -1;
535 	else if (strcasecmp(str, "NONE") == 0)
536 		;
537 	else if (strcasecmp(str, "SET") == 0)
538 		ret = cmd_notify_set(ctx, args+1);
539 	else
540 		ret = -1;
541 
542 	if (ret < 0) {
543 		client_send_command_error(cmd, ctx->error != NULL ? ctx->error :
544 					  "Invalid arguments.");
545 		pool_unref(&pool);
546 		return TRUE;
547 	}
548 
549 	if ((ctx->global_used_events & UNSUPPORTED_EVENTS) != 0) {
550 		string_t *client_error = t_str_new(128);
551 		unsigned int i;
552 
553 		str_append(client_error, "NO [BADEVENT");
554 		for (i = 0; i < N_ELEMENTS(imap_notify_event_names); i++) {
555 			if ((ctx->global_used_events & (1 << i)) != 0 &&
556 			    ((1 << i) & UNSUPPORTED_EVENTS) != 0) {
557 				str_append_c(client_error, ' ');
558 				str_append(client_error, imap_notify_event_names[i]);
559 			}
560 		}
561 		str_append(client_error, "] Unsupported NOTIFY events.");
562 		client_send_tagline(cmd, str_c(client_error));
563 		pool_unref(&pool);
564 		return TRUE;
565 	}
566 
567 	if (array_count(&ctx->namespaces) == 0) {
568 		/* selected mailbox only */
569 	} else if (ctx->global_max_mailbox_names > IMAP_NOTIFY_MAX_NAMES_PER_NS) {
570 		client_send_tagline(cmd,
571 			"NO [NOTIFICATIONOVERFLOW] Too many mailbox names");
572 		pool_unref(&pool);
573 		return TRUE;
574 	} else if (imap_notify_refresh_subscriptions(cmd, ctx) < 0) {
575 		/* tagline already sent */
576 		pool_unref(&pool);
577 		return TRUE;
578 	} else if (imap_notify_begin(ctx) < 0) {
579 		client_send_tagline(cmd,
580 			"NO [NOTIFICATIONOVERFLOW] NOTIFY not supported for these mailboxes.");
581 		pool_unref(&pool);
582 		return TRUE;
583 	}
584 	if (cmd->client->notify_ctx != NULL)
585 		imap_notify_deinit(&cmd->client->notify_ctx);
586 
587 	if (ctx->send_immediate_status)
588 		cmd_notify_send_status(cmd, ctx);
589 	cmd->client->notify_immediate_expunges =
590 		ctx->selected_immediate_expunges;
591 	cmd->client->notify_count_changes =
592 		(ctx->selected_events & IMAP_NOTIFY_EVENT_MESSAGE_NEW) != 0;
593 	cmd->client->notify_flag_changes =
594 		(ctx->selected_events & IMAP_NOTIFY_EVENT_FLAG_CHANGE) != 0;
595 
596 	cmd->client->notify_ctx = ctx;
597 	return cmd_sync(cmd, 0, IMAP_SYNC_FLAG_SAFE, "OK NOTIFY completed.");
598 }
599