Example #1
0
static bool pop3_uidl_assign_by_size(struct mailbox *box)
{
	struct pop3_migration_mailbox *mbox = POP3_MIGRATION_CONTEXT(box);
	struct pop3_migration_mail_storage *mstorage =
		POP3_MIGRATION_CONTEXT(box->storage);
	struct pop3_uidl_map *pop3_map;
	struct imap_msg_map *imap_map;
	unsigned int i, pop3_count, imap_count, count;

	pop3_map = array_get_modifiable(&mstorage->pop3_uidl_map, &pop3_count);
	imap_map = array_get_modifiable(&mbox->imap_msg_map, &imap_count);
	count = I_MIN(pop3_count, imap_count);

	/* see if we can match the messages using sizes */
	for (i = 0; i < count; i++) {
		if (pop3_map[i].size != imap_map[i].psize)
			break;
		if (i+1 < count && pop3_map[i].size == pop3_map[i+1].size) {
			/* two messages with same size, don't trust them */
			break;
		}

		pop3_map[i].imap_uid = imap_map[i].uid;
		imap_map[i].pop3_uidl = pop3_map[i].pop3_uidl;
		imap_map[i].pop3_seq = pop3_map[i].pop3_seq;
	}
	mbox->first_unfound_idx = i;
	return i == count;
}
Example #2
0
static void
virtual_mailbox_get_list_patterns(struct virtual_parse_context *ctx)
{
    struct virtual_mailbox *mbox = ctx->mbox;
    ARRAY_TYPE(mailbox_virtual_patterns) *dest;
    struct mailbox_virtual_pattern pattern;
    struct virtual_backend_box *const *bboxes;
    unsigned int i, count;

    memset(&pattern, 0, sizeof(pattern));
    bboxes = array_get_modifiable(&mbox->backend_boxes, &count);
    p_array_init(&mbox->list_include_patterns, ctx->pool, count);
    p_array_init(&mbox->list_exclude_patterns, ctx->pool, count);
    for (i = 0; i < count; i++) {
        if (bboxes[i]->metadata_entry == NULL)
            continue;
        pattern.ns = bboxes[i]->ns;
        pattern.pattern = bboxes[i]->name;
        if (bboxes[i]->negative_match)
            dest = &mbox->list_include_patterns;
        else {
            dest = &mbox->list_exclude_patterns;
            pattern.pattern++;
        }
        array_append(dest, &pattern, 1);
    }
}
Example #3
0
void io_loop_handler_run_internal(struct ioloop *ioloop)
{
	struct ioloop_handler_context *ctx = ioloop->handler_context;
	struct epoll_event *events;
	const struct epoll_event *event;
	struct io_list *list;
	struct io_file *io;
	struct timeval tv;
	unsigned int events_count;
	int msecs, ret, i, j;
	bool call;

        /* get the time left for next timeout task */
	msecs = io_loop_get_wait_time(ioloop, &tv);

	events = array_get_modifiable(&ctx->events, &events_count);
	if (ioloop->io_files != NULL && events_count > ctx->deleted_count) {
		ret = epoll_wait(ctx->epfd, events, events_count, msecs);
		if (ret < 0 && errno != EINTR)
			i_fatal("epoll_wait(): %m");
	} else {
		/* no I/Os, but we should have some timeouts.
		   just wait for them. */
		i_assert(msecs >= 0);
		usleep(msecs*1000);
		ret = 0;
	}

	/* execute timeout handlers */
        io_loop_handle_timeouts(ioloop);

	if (!ioloop->running)
		return;

	for (i = 0; i < ret; i++) {
		/* io_loop_handle_add() may cause events array reallocation,
		   so we have use array_idx() */
		event = array_idx(&ctx->events, i);
		list = event->data.ptr;

		for (j = 0; j < IOLOOP_IOLIST_IOS_PER_FD; j++) {
			io = list->ios[j];
			if (io == NULL)
				continue;

			call = FALSE;
			if ((event->events & (EPOLLHUP | EPOLLERR)) != 0)
				call = TRUE;
			else if ((io->io.condition & IO_READ) != 0)
				call = (event->events & EPOLLIN) != 0;
			else if ((io->io.condition & IO_WRITE) != 0)
				call = (event->events & EPOLLOUT) != 0;
			else if ((io->io.condition & IO_ERROR) != 0)
				call = (event->events & IO_EPOLL_ERROR) != 0;

			if (call)
				io_loop_call_io(&io->io);
		}
	}
}
static unsigned int
view_sync_expunges2seqs(struct mail_index_view_sync_ctx *ctx)
{
	struct mail_index_view *view = ctx->view;
	struct seq_range *src, *src_end, *dest;
	unsigned int count, expunge_count = 0;
	uint32_t prev_seq = 0;

	/* convert UIDs to sequences */
	src = dest = array_get_modifiable(&ctx->expunges, &count);
	src_end = src + count;
	for (; src != src_end; src++) {
		if (!mail_index_lookup_seq_range(view, src->seq1, src->seq2,
						 &dest->seq1, &dest->seq2))
			count--;
		else {
			i_assert(dest->seq1 > prev_seq);
			prev_seq = dest->seq2;

			expunge_count += dest->seq2 - dest->seq1 + 1;
			dest++;
		}
	}
	array_delete(&ctx->expunges, count,
		     array_count(&ctx->expunges) - count);
	return expunge_count;
}
int virtual_transaction_commit(struct mailbox_transaction_context *t,
			       struct mail_transaction_commit_changes *changes_r)
{
	struct virtual_transaction_context *vt =
		(struct virtual_transaction_context *)t;
	struct mailbox_transaction_context **bt;
	unsigned int i, count;
	int ret = 0;

	if (t->save_ctx != NULL) {
		virtual_save_free(t->save_ctx);
		t->save_ctx = NULL;
	}

	bt = array_get_modifiable(&vt->backend_transactions, &count);
	for (i = 0; i < count; i++) {
		if (mailbox_transaction_commit(&bt[i]) < 0)
			ret = -1;
	}
	array_free(&vt->backend_transactions);

	if (index_transaction_commit(t, changes_r) < 0)
		ret = -1;
	return ret;
}
Example #6
0
void lmtp_client_fail(struct lmtp_client *client, const char *line)
{
	struct lmtp_rcpt *recipients;
	unsigned int i, count;

	client->global_fail_string = p_strdup(client->pool, line);

	lmtp_client_ref(client);
	recipients = array_get_modifiable(&client->recipients, &count);
	for (i = client->rcpt_next_receive_idx; i < count; i++) {
		recipients[i].rcpt_to_callback(FALSE, line,
					       recipients[i].context);
		recipients[i].failed = TRUE;
	}
	client->rcpt_next_receive_idx = count;

	for (i = client->rcpt_next_data_idx; i < count; i++) {
		if (!recipients[i].failed) {
			recipients[i].data_callback(FALSE, line,
						    recipients[i].context);
		}
	}
	client->rcpt_next_data_idx = count;

	lmtp_client_close(client);
	lmtp_client_unref(&client);
}
Example #7
0
static void
mail_user_expand_plugins_envs(struct mail_user *user)
{
	const char **envs, *home;
	string_t *str;
	unsigned int i, count;

	if (!array_is_created(&user->set->plugin_envs))
		return;

	str = t_str_new(256);
	envs = array_get_modifiable(&user->set->plugin_envs, &count);
	i_assert((count % 2) == 0);
	for (i = 0; i < count; i += 2) {
		if (user->_home == NULL &&
		    var_has_key(envs[i+1], 'h', "home") &&
		    mail_user_get_home(user, &home) <= 0) {
			user->error = p_strdup_printf(user->pool,
				"userdb didn't return a home directory, "
				"but plugin setting %s used it (%%h): %s",
				envs[i], envs[i+1]);
			return;
		}
		str_truncate(str, 0);
		var_expand(str, envs[i+1], mail_user_var_expand_table(user));
		envs[i+1] = p_strdup(user->pool, str_c(str));
	}
}
Example #8
0
void program_client_set_extra_fd
(struct program_client *pclient, int fd,
	program_client_fd_callback_t *callback, void *context)
{
	struct program_client_extra_fd *efds;
	struct program_client_extra_fd *efd = NULL;
	unsigned int i, count;
	i_assert(fd > 1);
	
	if ( !array_is_created(&pclient->extra_fds) )
		p_array_init(&pclient->extra_fds, pclient->pool, 2);

	efds = array_get_modifiable(&pclient->extra_fds, &count);
	for ( i = 0; i < count; i++ ) {
		if ( efds[i].child_fd == fd ) {
			efd = &efds[i];
			break;
		}
	}

	if ( efd == NULL ) {
		efd = array_append_space(&pclient->extra_fds);
		efd->pclient = pclient;
		efd->child_fd = fd;
		efd->parent_fd = -1;
	}
	efd->callback = callback;
	efd->context = context;
}
Example #9
0
static int
lmtp_client_data_next(struct lmtp_client *client, const char *line)
{
	struct lmtp_rcpt *rcpt;
	unsigned int i, count;

	rcpt = array_get_modifiable(&client->recipients, &count);
	for (i = client->rcpt_next_data_idx; i < count; i++) {
		if (rcpt[i].failed) {
			/* already called rcpt_to_callback with failure */
			continue;
		}

		client->rcpt_next_data_idx = i + 1;
		rcpt[i].failed = line[0] != '2';
		rcpt[i].data_callback(!rcpt[i].failed, line, rcpt[i].context);
		if (client->protocol == LMTP_CLIENT_PROTOCOL_LMTP)
			break;
	}
	if (client->rcpt_next_data_idx < count)
		return 0;

	o_stream_send_str(client->output, "QUIT\r\n");
	lmtp_client_close(client);
	return -1;
}
Example #10
0
static void
lmtp_client_fail_full(struct lmtp_client *client, const char *line, bool remote)
{
	enum lmtp_client_result result;
	struct lmtp_rcpt *recipients;
	unsigned int i, count;

	client->global_fail_string = p_strdup(client->pool, line);
	client->global_remote_failure = remote;
	result = remote ? LMTP_CLIENT_RESULT_REMOTE_ERROR :
		LMTP_CLIENT_RESULT_INTERNAL_ERROR;

	lmtp_client_ref(client);
	recipients = array_get_modifiable(&client->recipients, &count);
	for (i = client->rcpt_next_receive_idx; i < count; i++) {
		recipients[i].rcpt_to_callback(result, line,
					       recipients[i].context);
		recipients[i].failed = TRUE;
	}
	client->rcpt_next_receive_idx = count;

	for (i = client->rcpt_next_data_idx; i < count; i++) {
		if (!recipients[i].failed) {
			recipients[i].data_callback(result, line,
						    recipients[i].context);
		}
	}
	client->rcpt_next_data_idx = count;

	lmtp_client_close(client);
	lmtp_client_unref(&client);
}
Example #11
0
void acl_rights_sort(struct acl_object *aclobj)
{
	struct acl_rights *rights;
	unsigned int i, dest, count;

	if (!array_is_created(&aclobj->rights))
		return;

	array_sort(&aclobj->rights, acl_rights_cmp);

	/* merge identical identifiers */
	rights = array_get_modifiable(&aclobj->rights, &count);
	for (dest = 0, i = 1; i < count; i++) {
		if (acl_rights_cmp(&rights[i], &rights[dest]) == 0) {
			/* add i's rights to dest and delete i */
			acl_right_names_merge(aclobj->rights_pool,
					      &rights[dest].rights,
					      rights[i].rights, FALSE);
			acl_right_names_merge(aclobj->rights_pool,
					      &rights[dest].neg_rights,
					      rights[i].neg_rights, FALSE);
		} else {
			if (++dest != i)
				rights[dest] = rights[i];
		}
	}
	if (++dest != count)
		array_delete(&aclobj->rights, dest, count - dest);
}
Example #12
0
void io_loop_handler_run_internal(struct ioloop *ioloop)
{
	struct ioloop_handler_context *ctx = ioloop->handler_context;
	struct kevent *events;
	const struct kevent *event;
	struct timeval tv;
	struct timespec ts;
	struct io_file *io;
	unsigned int events_count;
	int ret, i, msecs;

	/* get the time left for next timeout task */
	msecs = io_loop_get_wait_time(ioloop, &tv);
	ts.tv_sec = tv.tv_sec;
	ts.tv_nsec = tv.tv_usec * 1000;

	/* wait for events */
	events = array_get_modifiable(&ctx->events, &events_count);

	if (events_count > 0) {
		ret = kevent (ctx->kq, NULL, 0, events, events_count, &ts);
		if (ret < 0 && errno != EINTR)
			i_panic("kevent() failed: %m");
	} else {
		if (msecs < 0)
			i_panic("BUG: No IOs or timeouts set. Not waiting for infinity.");
		usleep(msecs * 1000);
		ret = 0;
	}

	/* reference all IOs */
	for (i = 0; i < ret; i++) {
		io = (void *)events[i].udata;
		i_assert(io->refcount > 0);
		io->refcount++;
	}

	/* execute timeout handlers */
	io_loop_handle_timeouts(ioloop);

	for (i = 0; i < ret; i++) {
		/* io_loop_handle_add() may cause events array reallocation,
		   so we have use array_idx() */
		event = array_idx(&ctx->events, i);
		io = (void *)event->udata;

		/* callback is NULL if io_remove() was already called */
		if (io->io.callback != NULL)
			io_loop_call_io(&io->io);

		i_assert(io->refcount > 0);
		if (--io->refcount == 0)
			i_free(io);
	}
}
static struct ext_editheader_header *ext_editheader_config_header_find
(struct ext_editheader_config *ext_config, const char *hname)
{
	struct ext_editheader_header *headers;
	unsigned int count, i;

	headers = array_get_modifiable(&ext_config->headers, &count);
	for ( i = 0; i < count; i++ ) {
		if ( strcasecmp(hname, headers[i].name) == 0 )
			return &headers[i];
	}

	return NULL;
}
Example #14
0
void mail_index_view_unref_maps(struct mail_index_view *view)
{
	struct mail_index_map **maps;
	unsigned int i, count;

	if (!array_is_created(&view->map_refs))
		return;

	maps = array_get_modifiable(&view->map_refs, &count);
	for (i = 0; i < count; i++)
		mail_index_unmap(&maps[i]);

	array_clear(&view->map_refs);
}
static void sieve_extension_registry_deinit(struct sieve_instance *svinst)
{
	struct sieve_extension_registry *ext_reg = svinst->ext_reg;
	struct sieve_extension * const *exts;
    unsigned int i, ext_count;

	if ( !hash_table_is_created(ext_reg->extension_index) ) return;

    exts = array_get_modifiable(&ext_reg->extensions, &ext_count);
	for ( i = 0; i < ext_count; i++ ) {
		_sieve_extension_unload(exts[i]);
	}

	hash_table_destroy(&ext_reg->extension_index);
}
Example #16
0
void io_loop_handler_deinit(struct ioloop *ioloop)
{
	struct ioloop_handler_context *ctx = ioloop->handler_context;
	struct io_list **list;
	unsigned int i, count;

	list = array_get_modifiable(&ctx->fd_index, &count);
	for (i = 0; i < count; i++)
		i_free(list[i]);

	if (close(ctx->epfd) < 0)
		i_error("close(epoll) failed: %m");
	array_free(&ioloop->handler_context->fd_index);
	array_free(&ioloop->handler_context->events);
	i_free(ioloop->handler_context);
}
Example #17
0
static void virtual_mail_free(struct mail *mail)
{
	struct virtual_mail *vmail = (struct virtual_mail *)mail;
	struct mail **mails;
	unsigned int i, count;

	mails = array_get_modifiable(&vmail->backend_mails, &count);
	for (i = 0; i < count; i++)
		mail_free(&mails[i]);
	array_free(&vmail->backend_mails);

	if (vmail->wanted_headers != NULL)
		mailbox_header_lookup_unref(&vmail->wanted_headers);

	pool_unref(&vmail->imail.data_pool);
	pool_unref(&vmail->imail.mail.pool);
}
Example #18
0
void program_client_init_streams(struct program_client *pclient)
{
	/* Create streams for normal program I/O */
	if ( pclient->fd_out >= 0 ) {
		pclient->program_output =
			o_stream_create_fd(pclient->fd_out, MAX_OUTPUT_BUFFER_SIZE, FALSE);
	}
	if ( pclient->fd_in >= 0 ) {
		struct istream *input;
		
		input = i_stream_create_fd(pclient->fd_in, (size_t)-1, FALSE);

		if (pclient->output_seekable) {
			struct istream *input2 = input, *input_list[2];
	
			input_list[0] = input2; input_list[1] = NULL;
			input = i_stream_create_seekable(input_list, MAX_OUTPUT_MEMORY_BUFFER,
						 program_client_seekable_fd_callback, pclient);
			i_stream_unref(&input2);
			pclient->seekable_output = input;
			i_stream_ref(pclient->seekable_output);
		}

		pclient->program_input = input;
		pclient->io = io_add
			(pclient->fd_in, IO_READ, program_client_program_input, pclient);
	}

	/* Create streams for additional output through side-channel fds */
	if ( array_is_created(&pclient->extra_fds) ) {
		struct program_client_extra_fd *efds = NULL;
		unsigned int count, i;
		
		efds = array_get_modifiable(&pclient->extra_fds, &count);
		for ( i = 0; i < count; i++ ) {
			i_assert( efds[i].parent_fd >= 0 );
			efds[i].input = i_stream_create_fd
				(efds[i].parent_fd, (size_t)-1, FALSE);
			efds[i].io = io_add
				(efds[i].parent_fd, IO_READ, program_client_extra_fd_input, &efds[i]);
		}
	}
}
Example #19
0
static void program_client_disconnect_extra_fds
(struct program_client *pclient)
{
	struct program_client_extra_fd *efds;
	unsigned int i, count;
	
	if (!array_is_created(&pclient->extra_fds))
		return;

	efds = array_get_modifiable(&pclient->extra_fds, &count);
	for ( i = 0; i < count; i++ ) {
		if ( efds[i].input != NULL )
			i_stream_unref(&efds[i].input);
		if ( efds[i].io != NULL )
			io_remove(&efds[i].io);
		if ( efds[i].parent_fd != -1 && close(efds[i].parent_fd) < 0 )
			i_error("close(fd=%d) failed: %m", efds[i].parent_fd);
	}
}
void virtual_transaction_rollback(struct mailbox_transaction_context *t)
{
	struct virtual_transaction_context *vt =
		(struct virtual_transaction_context *)t;
	struct mailbox_transaction_context **bt;
	unsigned int i, count;

	if (t->save_ctx != NULL) {
		virtual_save_free(t->save_ctx);
		t->save_ctx = NULL;
	}

	bt = array_get_modifiable(&vt->backend_transactions, &count);
	for (i = 0; i < count; i++)
		mailbox_transaction_rollback(&bt[i]);
	array_free(&vt->backend_transactions);

	index_transaction_rollback(t);
}
Example #21
0
static void priorityq_remove_idx(struct priorityq *pq, unsigned int idx)
{
	struct priorityq_item **items;
	unsigned int count;

	items = array_get_modifiable(&pq->items, &count);
	i_assert(idx < count);

	/* move last item over the removed one and fix the heap */
	count--;
	heap_items_swap(items, idx, count);
	array_delete(&pq->items, count, 1);

	if (count > 0 && idx != count) {
		if (idx > 0)
			idx = heap_item_bubble_up(pq, idx);
		heap_item_bubble_down(pq, idx);
	}
}
Example #22
0
struct imap_search_update *
client_search_update_lookup(struct client *client, const char *tag,
			    unsigned int *idx_r)
{
	struct imap_search_update *updates;
	unsigned int i, count;

	if (!array_is_created(&client->search_updates))
		return NULL;

	updates = array_get_modifiable(&client->search_updates, &count);
	for (i = 0; i < count; i++) {
		if (strcmp(updates[i].tag, tag) == 0) {
			*idx_r = i;
			return &updates[i];
		}
	}
	return NULL;
}
Example #23
0
void index_sync_changes_delete_to(struct index_sync_changes_context *ctx,
				  uint32_t last_uid)
{
	struct mail_index_sync_rec *syncs;
	unsigned int src, dest, count;

	syncs = array_get_modifiable(&ctx->syncs, &count);

	for (src = dest = 0; src < count; src++) {
		i_assert(last_uid >= syncs[src].uid1);
		if (last_uid <= syncs[src].uid2) {
			/* keep it */
			if (src != dest)
				syncs[dest] = syncs[src];
			dest++;
		}
	}

	array_delete(&ctx->syncs, dest, count - dest);
}
Example #24
0
static unsigned int
heap_item_bubble_up(struct priorityq *pq, unsigned int idx)
{
	struct priorityq_item **items;
	unsigned int parent_idx, count;

	items = array_get_modifiable(&pq->items, &count);
	while (idx > 0) {
		parent_idx = PARENT_IDX(idx);

		i_assert(idx < count);
		if (pq->cmp_callback(items[idx], items[parent_idx]) >= 0)
			break;

		/* wrong order - swap */
		heap_items_swap(items, idx, parent_idx);
		idx = parent_idx;
	}
	return idx;
}
Example #25
0
static void heap_item_bubble_down(struct priorityq *pq, unsigned int idx)
{
	struct priorityq_item **items;
	unsigned int left_idx, right_idx, min_child_idx, count;

	items = array_get_modifiable(&pq->items, &count);
	while ((left_idx = LEFT_CHILD_IDX(idx)) < count) {
		right_idx = RIGHT_CHILD_IDX(idx);
		if (right_idx >= count ||
		    pq->cmp_callback(items[left_idx], items[right_idx]) < 0)
			min_child_idx = left_idx;
		else
			min_child_idx = right_idx;

		if (pq->cmp_callback(items[min_child_idx], items[idx]) >= 0)
			break;

		/* wrong order - swap */
		heap_items_swap(items, idx, min_child_idx);
		idx = min_child_idx;
	}
}
Example #26
0
static void tview_close(struct mail_index_view *view)
{
	struct mail_index_view_transaction *tview =
		(struct mail_index_view_transaction *)view;
	struct mail_index_transaction *t = tview->t;
	void **recs;
	unsigned int i, count;

	if (tview->lookup_map != NULL)
		mail_index_unmap(&tview->lookup_map);
	buffer_free(&tview->lookup_return_data);

	if (array_is_created(&tview->all_recs)) {
		recs = array_get_modifiable(&tview->all_recs, &count);
		for (i = 0; i < count; i++)
			i_free(recs[i]);
		array_free(&tview->all_recs);
	}

	tview->super->close(view);
	mail_index_transaction_unref(&t);
}
Example #27
0
static struct mail_log_group_changes *
mail_log_action_get_group(struct mail_log_transaction_context *lt,
			  enum mail_log_event event, const char *data)
{
	struct mail_log_group_changes *group;
	unsigned int i, count;

	if (!array_is_created(&lt->group_changes))
		p_array_init(&lt->group_changes, lt->pool, 8);

	group = array_get_modifiable(&lt->group_changes, &count);
	for (i = 0; i < count; i++) {
		if (group[i].event == event &&
		    null_strcmp(data, group[i].data) == 0)
			return &group[i];
	}

	group = array_append_space(&lt->group_changes);
	group->event = event;
	group->data = p_strdup(lt->pool, data);
	return group;
}
Example #28
0
static bool program_client_input_pending(struct program_client *pclient)
{
	struct program_client_extra_fd *efds = NULL;
	unsigned int count, i;

	if ( pclient->program_input != NULL &&
		!pclient->program_input->closed &&
		!i_stream_is_eof(pclient->program_input) ) {
		return TRUE;
	}

	if ( array_is_created(&pclient->extra_fds) ) {
		efds = array_get_modifiable(&pclient->extra_fds, &count);
		for ( i = 0; i < count; i++ ) {
			if ( efds[i].input != NULL &&
				!efds[i].input->closed &&
				!i_stream_is_eof(efds[i].input) ) {
				return TRUE;
			}
		}
	}

	return FALSE;
}
		} T_END;
                data->save_bodystructure_header = FALSE;
		return;
	}

	if (!hdr->continued) {
		T_BEGIN {
			const char *cache_field_name =
				t_strconcat("hdr.", hdr->name, NULL);
			data->parse_line.field_idx =
				mail_cache_register_lookup(_mail->box->cache,
							   cache_field_name);
		} T_END;
	}
	field_idx = data->parse_line.field_idx;
	match = array_get_modifiable(&mail->header_match, &count);
	if (field_idx >= count ||
	    !HEADER_MATCH_USABLE(mail, match[field_idx])) {
		/* we don't want this header. */
		return;
	}

	if (!hdr->continued) {
		/* beginning of a line. add the header name. */
		data->parse_line.start_pos = str_len(mail->header_data);
		data->parse_line.line_num = data->parse_line_num;
		str_append(mail->header_data, hdr->name);
		str_append_n(mail->header_data, hdr->middle, hdr->middle_len);

		/* remember that we saw this header so we don't add it to
		   cache as nonexistent. */
Example #30
0
        }
        array_append(dest, &pattern, 1);
    }
}

static void
separate_wildcard_mailboxes(struct virtual_mailbox *mbox,
                            ARRAY_TYPE(virtual_backend_box) *wildcard_boxes,
                            ARRAY_TYPE(virtual_backend_box) *neg_boxes,
                            ARRAY_TYPE(virtual_backend_box) *metadata_boxes)
{
    struct virtual_backend_box *const *bboxes;
    ARRAY_TYPE(virtual_backend_box) *dest;
    unsigned int i, count;

    bboxes = array_get_modifiable(&mbox->backend_boxes, &count);
    t_array_init(wildcard_boxes, I_MIN(16, count));
    t_array_init(neg_boxes, 4);
    t_array_init(metadata_boxes, 4);
    for (i = 0; i < count;) {
        if (bboxes[i]->metadata_entry != NULL)
            dest = metadata_boxes;
        else if (bboxes[i]->negative_match)
            dest = neg_boxes;
        else if (bboxes[i]->glob != NULL)
            dest = wildcard_boxes;
        else {
            dest = NULL;
            i++;
        }