static void
search_update_flag_changes(struct dsync_mailbox_exporter *exporter,
			   struct mail *mail, struct dsync_mail_change *change)
{
	const char *const *keywords;
	unsigned int i;
	char type;

	i_assert((change->add_flags & change->remove_flags) == 0);

	change->modseq = mail_get_modseq(mail);
	change->pvt_modseq = mail_get_pvt_modseq(mail);
	change->final_flags = mail_get_flags(mail);

	keywords = mail_get_keywords(mail);
	if (!array_is_created(&change->keyword_changes) &&
	    keywords[0] != NULL) {
		p_array_init(&change->keyword_changes, exporter->pool,
			     str_array_length(keywords));
	}
	for (i = 0; keywords[i] != NULL; i++) {
		/* add the final keyword if it's not already there
		   as +keyword */
		if (!final_keyword_check(change, keywords[i], &type)) {
			const char *keyword_change =
				p_strdup_printf(exporter->pool, "%c%s",
						type, keywords[i]);
			array_append(&change->keyword_changes,
				     &keyword_change, 1);
		}
	}
}
Beispiel #2
0
void smtp_server_command_fail(struct smtp_server_command *cmd,
			      unsigned int status, const char *enh_code,
			      const char *fmt, ...)
{
	unsigned int i;
	va_list args;

	i_assert(status / 100 > 2);

	va_start(args, fmt);
	if (cmd->replies_expected == 1) {
		smtp_server_reply_indexv(&cmd->context, 0,
					 status, enh_code, fmt, args);
	} else for (i = 0; i < cmd->replies_expected; i++) {
		bool sent = FALSE;

		if (array_is_created(&cmd->replies)) {
			const struct smtp_server_reply *reply =
				array_idx(&cmd->replies, i);
			sent = reply->sent;
		}
	
		/* send the same reply for all */
		if (!sent) {
			va_list args_copy;
			VA_COPY(args_copy, args);
			smtp_server_reply_indexv(&cmd->context, i,
				status, enh_code, fmt, args_copy);
			va_end(args_copy);
		}
	}
	va_end(args);
}
void auth_fields_reset(struct auth_fields *fields)
{
	if (array_is_created(&fields->fields)) {
		auth_fields_snapshot_preserve(fields);
		array_clear(&fields->fields);
	}
}
static void
ext_reset_update_atomic(struct mail_index_transaction *t,
			uint32_t ext_id, uint32_t expected_reset_id)
{
	const struct mail_index_ext *map_ext;
	struct mail_transaction_ext_reset *reset;
	uint32_t idx, reset_id;

	if (!mail_index_map_get_ext_idx(t->view->index->map, ext_id, &idx)) {
		/* new extension */
		reset_id = 1;
	} else {
		map_ext = array_idx(&t->view->index->map->extensions, idx);
		reset_id = map_ext->reset_id + 1;
	}
	if (reset_id != expected_reset_id) {
		/* ignore this extension update */
		mail_index_ext_set_reset_id(t, ext_id, 0);
		return;
	}

	if (reset_id == 0)
		reset_id++;

	array_idx_set(&t->ext_reset_ids, ext_id, &reset_id);

	/* reseting existing data is optional */
	if (array_is_created(&t->ext_resets)) {
		reset = array_idx_modifiable(&t->ext_resets, ext_id);
		if (reset->new_reset_id == (uint32_t)-1)
			reset->new_reset_id = reset_id;
	}
}
uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t)
{
	const struct mail_index_header *head_hdr, *hdr;
	unsigned int offset;
	uint32_t next_uid;

	head_hdr = &t->view->index->map->hdr;
	hdr = &t->view->map->hdr;
	next_uid = t->reset || head_hdr->uid_validity != hdr->uid_validity ?
		1 : hdr->next_uid;
	if (array_is_created(&t->appends) && t->highest_append_uid != 0) {
		/* get next_uid from appends if they have UIDs. it's possible
		   that some appends have too low UIDs, they'll be caught
		   later. */
		if (next_uid <= t->highest_append_uid)
			next_uid = t->highest_append_uid + 1;
	}

	/* see if it's been updated in pre/post header changes */
	offset = offsetof(struct mail_index_header, next_uid);
	if (t->post_hdr_mask[offset] != 0) {
		hdr = (const void *)t->post_hdr_change;
		if (hdr->next_uid > next_uid)
			next_uid = hdr->next_uid;
	}
	if (t->pre_hdr_mask[offset] != 0) {
		hdr = (const void *)t->pre_hdr_change;
		if (hdr->next_uid > next_uid)
			next_uid = hdr->next_uid;
	}
	return next_uid;
}
Beispiel #6
0
bool master_service_set_has_config_override(struct master_service *service,
					    const char *key)
{
	const char *const *override, *key_root;
	bool ret;

	if (!array_is_created(&service->config_overrides))
		return FALSE;

	key_root = settings_parse_unalias(service->set_parser, key);
	if (key_root == NULL)
		key_root = key;

	array_foreach(&service->config_overrides, override) {
		T_BEGIN {
			const char *okey, *okey_root;

			okey = t_strcut(*override, '=');
			okey_root = settings_parse_unalias(service->set_parser,
							   okey);
			if (okey_root == NULL)
				okey_root = okey;
			ret = strcmp(okey_root, key_root) == 0;
		} T_END;

		if (ret)
			return TRUE;
	}
	return FALSE;
}
Beispiel #7
0
static void
stats_metric_settings_to_query(const struct stats_metric_settings *set,
			       struct event_filter_query *query_r)
{
	i_zero(query_r);

	/* generate fields for event filter */
	if (array_is_created(&set->filter)) {
		struct event_filter_field *filter_fields;
		const char *const *filters;
		unsigned int i, count;

		filters = array_get(&set->filter, &count);
		i_assert(count % 2 == 0);
		count /= 2;

		filter_fields = t_new(struct event_filter_field, count + 1);
		for (i = 0; i < count; i++) {
			filter_fields[i].key = filters[i*2];
			filter_fields[i].value = filters[i*2+1];
		}
		query_r->fields = filter_fields;
	}

	/* add query to the event filter */
	query_r->categories = t_strsplit_spaces(set->categories, " ");
	query_r->name = set->event_name;
	query_r->source_filename = t_strcut(set->source_location, ':');
	query_r->source_linenum = set->parsed_source_linenum;
}
Beispiel #8
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));
	}
}
Beispiel #9
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;
}
Beispiel #10
0
void smtp_server_command_set_reply_count(struct smtp_server_command *cmd,
	unsigned int count)
{
	i_assert(count > 0);
	i_assert(!array_is_created(&cmd->replies));
	cmd->replies_expected = count;
}
Beispiel #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);
}
void mailbox_search_result_free(struct mail_search_result **_result)
{
	struct mail_search_result *result = *_result;
	struct mail_search_result *const *results;
	unsigned int i, count;

	*_result = NULL;

	results = array_get(&result->box->search_results, &count);
	for (i = 0; i < count; i++) {
		if (results[i] == result) {
			array_delete(&result->box->search_results, i, 1);
			break;
		}
	}
	i_assert(i != count);

	if (result->search_args != NULL)
		mail_search_args_unref(&result->search_args);

	array_free(&result->uids);
	array_free(&result->never_uids);
	if (array_is_created(&result->removed_uids)) {
		array_free(&result->removed_uids);
		array_free(&result->added_uids);
	}
	i_free(result);
}
Beispiel #13
0
void index_storage_mailbox_close(struct mailbox *box)
{
	struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);

	index_mailbox_check_remove_all(box);
	if (box->input != NULL)
		i_stream_unref(&box->input);

	if (box->view_pvt != NULL)
		mail_index_view_close(&box->view_pvt);
	if (box->index_pvt != NULL)
		mail_index_close(box->index_pvt);
	mail_index_view_close(&box->view);
	mail_index_close(box->index);
	box->cache = NULL;

	ibox->keyword_names = NULL;
	i_free_and_null(ibox->cache_fields);

	if (array_is_created(&ibox->recent_flags))
		array_free(&ibox->recent_flags);
	ibox->recent_flags_prev_uid = 0;
	ibox->recent_flags_count = 0;

	ibox->sync_last_check = 0;
}
Beispiel #14
0
static void
mail_log_action_add_group(struct mail_log_transaction_context *lt,
			  struct mail *mail, enum mail_log_event event,
			  const char *data)
{
	struct mail_log_group_changes *group;
	uoff_t size;

	group = mail_log_action_get_group(lt, event, data);

	if ((mail_log_set.fields & MAIL_LOG_FIELD_UID) != 0) {
		if (!array_is_created(&group->uids))
			p_array_init(&group->uids, lt->pool, 32);
                if (event != MAIL_LOG_EVENT_SAVE_FINISH)
		    seq_range_array_add(&group->uids, 0, mail->uid);
	}

	if ((mail_log_set.fields & MAIL_LOG_FIELD_PSIZE) != 0 &&
	    (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) {
		if (mail_get_physical_size(mail, &size) == 0)
			group->psize_total += size;
	}

	if ((mail_log_set.fields & MAIL_LOG_FIELD_VSIZE) != 0 &&
	    (event & (MAIL_LOG_EVENT_EXPUNGE | MAIL_LOG_EVENT_COPY)) != 0) {
		if (mail_get_virtual_size(mail, &size) == 0)
			group->vsize_total += size;
	}
}
int index_mailbox_sync_deinit(struct mailbox_sync_context *_ctx,
			      struct mailbox_sync_status *status_r)
{
	struct index_mailbox_sync_context *ctx =
		(struct index_mailbox_sync_context *)_ctx;
	struct mailbox_sync_rec sync_rec;
	bool delayed_expunges = FALSE;
	int ret = ctx->failed ? -1 : 0;

	/* finish handling expunges, so we don't break when updating
	   recent flags */
	while (index_mailbox_sync_next_expunge(ctx, &sync_rec) > 0) ;

	/* convert sequences to uids before syncing view */
	index_sync_search_results_uidify(ctx);

	if (ctx->sync_ctx != NULL) {
		if (mail_index_view_sync_commit(&ctx->sync_ctx,
						&delayed_expunges) < 0) {
			mailbox_set_index_error(_ctx->box);
			ret = -1;
		}
	}
	index_mailbox_expunge_unseen_recent(ctx);

	if ((_ctx->box->flags & MAILBOX_FLAG_DROP_RECENT) == 0 &&
	    _ctx->box->opened) {
		/* mailbox syncing didn't necessarily update our recent state */
		index_sync_update_recent_count(_ctx->box);
	}

	if (status_r != NULL)
		status_r->sync_delayed_expunges = delayed_expunges;

	/* update search results after private index is updated */
	index_sync_search_results_update(ctx);

	if (array_is_created(&ctx->flag_updates))
		array_free(&ctx->flag_updates);
	if (array_is_created(&ctx->hidden_updates))
		array_free(&ctx->hidden_updates);
	if (array_is_created(&ctx->all_flag_update_uids))
		array_free(&ctx->all_flag_update_uids);

	i_free(ctx);
	return ret;
}
static const struct mail_index_record *
tview_apply_flag_updates(struct mail_index_view_transaction *tview,
			 struct mail_index_map *map,
			 const struct mail_index_record *rec, uint32_t seq)
{
	struct mail_index_transaction *t = tview->t;
	const struct mail_index_flag_update *updates;
	struct mail_index_record *trec;
	unsigned int idx, count;

	/* see if there are any flag updates */
	if (seq < t->min_flagupdate_seq || seq > t->max_flagupdate_seq ||
	    !array_is_created(&t->updates))
		return rec;

	updates = array_get(&t->updates, &count);
	idx = mail_index_transaction_get_flag_update_pos(t, 0, count, seq);
	if (seq < updates[idx].uid1 || seq > updates[idx].uid2)
		return rec;

	/* yes, we have flag updates. since we can't modify rec directly and
	   we want to be able to handle multiple mail_index_lookup() calls
	   without the second one overriding the first one's data, we'll
	   create a records array and return data from there.

	   it's also possible that the record size increases, so we potentially
	   have to create multiple arrays. they all get eventually freed when
	   the view gets freed. */
	if (map->hdr.record_size > tview->record_size) {
		if (!array_is_created(&tview->all_recs))
			i_array_init(&tview->all_recs, 4);
		tview->recs_count = t->first_new_seq;
		tview->record_size = I_MAX(map->hdr.record_size,
					   tview->view.map->hdr.record_size);
		tview->recs = i_malloc(tview->record_size *
				       tview->recs_count);
		array_append(&tview->all_recs, &tview->recs, 1);
	}
	i_assert(tview->recs_count == t->first_new_seq);
	i_assert(seq > 0 && seq <= tview->recs_count);

	trec = PTR_OFFSET(tview->recs, (seq-1) * tview->record_size);
	memcpy(trec, rec, map->hdr.record_size);
	trec->flags |= updates[idx].add_flags;
	trec->flags &= ~updates[idx].remove_flags;
	return trec;
}
Beispiel #17
0
static void
index_mailbox_expunge_unseen_recent(struct index_mailbox_sync_context *ctx)
{
	struct mailbox *box = ctx->ctx.box;
	struct mail_index_view *view = ctx->ctx.box->view;
	const struct mail_index_header *hdr;
	uint32_t seq, start_uid, uid;

	if (!array_is_created(&box->recent_flags))
		return;

	/* expunges array contained expunges for the messages that were already
	   visible in this view, but append+expunge would be invisible.
	   recent_flags may however contain the append UID, so we'll have to
	   remove it separately */
	hdr = mail_index_get_header(view);
	if (ctx->messages_count == 0)
		uid = 0;
	else if (ctx->messages_count <= hdr->messages_count)
		mail_index_lookup_uid(view, ctx->messages_count, &uid);
	else {
		i_assert(mail_index_view_is_inconsistent(view));
		return;
	}

	for (seq = ctx->messages_count + 1; seq <= hdr->messages_count; seq++) {
		start_uid = uid;
		mail_index_lookup_uid(view, seq, &uid);
		if (start_uid + 1 > uid - 1)
			continue;

		box->recent_flags_count -=
			seq_range_array_remove_range(&box->recent_flags,
						     start_uid + 1, uid - 1);
	}

	if (uid + 1 < hdr->next_uid) {
		box->recent_flags_count -=
			seq_range_array_remove_range(&box->recent_flags,
						     uid + 1,
						     hdr->next_uid - 1);
	}
#ifdef DEBUG
	if (!mail_index_view_is_inconsistent(view)) {
		const struct seq_range *range;
		unsigned int i, count;

		range = array_get(&box->recent_flags, &count);
		for (i = 0; i < count; i++) {
			for (uid = range[i].seq1; uid <= range[i].seq2; uid++) {
				if (uid >= hdr->next_uid)
					break;
				(void)mail_index_lookup_seq(view, uid, &seq);
				i_assert(seq != 0);
			}
		}
	}
#endif
}
Beispiel #18
0
static void request_add_context(struct indexer_request *request, void *context)
{
	if (context == NULL)
		return;

	if (!array_is_created(&request->contexts))
		i_array_init(&request->contexts, 2);
	array_append(&request->contexts, &context, 1);
}
void sieve_file_script_sequence_destroy(struct sieve_script_sequence *seq)
{
	struct sieve_file_script_sequence *fseq =
		(struct sieve_file_script_sequence *)seq;

	if ( array_is_created(&fseq->script_files) )
		array_free(&fseq->script_files);
	pool_unref(&fseq->pool);
}
Beispiel #20
0
static void db_passwd_file_set_userdb(struct db_passwd_file *db)
{
	db->userdb = TRUE;
	/* warn about missing userdb fields only when there aren't any other
	   userdbs. */
	db->userdb_warn_missing =
		array_is_created(&global_auth_settings->userdbs) &&
		array_count(&global_auth_settings->userdbs) == 1;
}
static int acl_backend_vfile_object_refresh_cache(struct acl_object *_aclobj)
{
	struct acl_object_vfile *aclobj = (struct acl_object_vfile *)_aclobj;
	struct acl_backend_vfile *backend =
		(struct acl_backend_vfile *)_aclobj->backend;
	struct acl_backend_vfile_validity *old_validity;
	struct acl_backend_vfile_validity validity;
	time_t mtime;
	int ret;

	old_validity = acl_cache_get_validity(_aclobj->backend->cache,
					      _aclobj->name);
	ret = _aclobj->backend->global_file != NULL ?
		acl_global_file_refresh(_aclobj->backend->global_file) :
		acl_backend_vfile_refresh(_aclobj, aclobj->global_path,
					  old_validity == NULL ? NULL :
					  &old_validity->global_validity);
	if (ret == 0) {
		ret = acl_backend_vfile_refresh(_aclobj, aclobj->local_path,
						old_validity == NULL ? NULL :
						&old_validity->local_validity);
	}
	if (ret <= 0)
		return ret;

	/* either global or local ACLs changed, need to re-read both */
	if (!array_is_created(&_aclobj->rights)) {
		_aclobj->rights_pool =
			pool_alloconly_create("acl rights", 256);
		i_array_init(&_aclobj->rights, 16);
	} else {
		array_clear(&_aclobj->rights);
		p_clear(_aclobj->rights_pool);
	}

	memset(&validity, 0, sizeof(validity));
	if (_aclobj->backend->global_file != NULL)
		acl_object_add_global_acls(_aclobj);
	else {
		if (acl_backend_vfile_read_with_retry(_aclobj, TRUE, aclobj->global_path,
						      &validity.global_validity) < 0)
			return -1;
	}
	if (acl_backend_vfile_read_with_retry(_aclobj, FALSE, aclobj->local_path,
					      &validity.local_validity) < 0)
		return -1;

	acl_rights_sort(_aclobj);
	/* update cache only after we've successfully read everything */
	acl_object_rebuild_cache(_aclobj);
	acl_cache_set_validity(_aclobj->backend->cache,
			       _aclobj->name, &validity);

	if (acl_backend_vfile_object_get_mtime(_aclobj, &mtime) == 0)
		acl_backend_vfile_acllist_verify(backend, _aclobj->name, mtime);
	return 0;
}
static void auth_fields_snapshot_preserve(struct auth_fields *fields)
{
	if (!fields->snapshotted || array_is_created(&fields->snapshot_fields))
		return;

	p_array_init(&fields->snapshot_fields, fields->pool,
		     array_count(&fields->fields));
	array_append_array(&fields->snapshot_fields, &fields->fields);
}
void index_mailbox_reset_uidvalidity(struct mailbox *box)
{
	struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);

	/* can't trust the currently cached recent flags anymore */
	if (array_is_created(&ibox->recent_flags))
		array_clear(&ibox->recent_flags);
	ibox->recent_flags_count = 0;
	ibox->recent_flags_prev_uid = 0;
}
void mailbox_search_result_remove(struct mail_search_result *result,
				  uint32_t uid)
{
	if (seq_range_array_remove(&result->uids, uid)) {
		if (array_is_created(&result->removed_uids)) {
			seq_range_array_add(&result->removed_uids, uid);
			seq_range_array_remove(&result->added_uids, uid);
		}
	}
}
Beispiel #25
0
void lib_atexit_run(void)
{
	lib_atexit_callback_t *const *cbp;

	if (array_is_created(&atexit_callbacks)) {
		array_foreach(&atexit_callbacks, cbp)
			(**cbp)();
		array_free(&atexit_callbacks);
	}
}
Beispiel #26
0
static void view_close(struct mail_index_view *view)
{
	i_assert(view->refcount == 0);
	i_assert(view->index->views != NULL);

	DLLIST_REMOVE(&view->index->views, view);

	mail_transaction_log_view_close(&view->log_view);

	if (array_is_created(&view->syncs_hidden))
		array_free(&view->syncs_hidden);
	mail_index_unmap(&view->map);
	if (array_is_created(&view->map_refs)) {
		mail_index_view_unref_maps(view);
		array_free(&view->map_refs);
	}
	array_free(&view->module_contexts);
	i_free(view);
}
Beispiel #27
0
void
oauth2_parse_json(struct oauth2_request *req)
{
	bool success;
	enum json_type type;
	const char *token, *error;
	int ret;

	req->field_name = NULL;

	while((ret = json_parse_next(req->parser, &type, &token)) > 0) {
		if (req->field_name == NULL) {
			if (type != JSON_TYPE_OBJECT_KEY) break;
			/* cannot use t_strdup because we might
			   have to read more */
			req->field_name = p_strdup(req->pool, token);
		} else if (type < JSON_TYPE_STRING) {
			/* this should be last allocation */
			p_free(req->pool, req->field_name);
			json_parse_skip_next(req->parser);
		} else {
			if (!array_is_created(&req->fields))
				p_array_init(&req->fields, req->pool, 4);
			struct oauth2_field *field =
				array_append_space(&req->fields);
			field->name = req->field_name;
			req->field_name = NULL;
			field->value = p_strdup(req->pool, token);
		}
	}

	/* read more */
	if (ret == 0) return;

	io_remove(&req->io);

	if (ret > 0) {
		(void)json_parser_deinit(&req->parser, &error);
		error = "Invalid response data";
		success = FALSE;
	} else if (i_stream_read_eof(req->is) &&
		   req->is->v_offset == 0 && req->is->stream_errno == 0) {
		/* discard error, empty response is OK. */
		(void)json_parser_deinit(&req->parser, &error);
		error = NULL;
		success = TRUE;
	} else {
		ret = json_parser_deinit(&req->parser, &error);
		success = (ret == 0);
	}

	i_stream_unref(&req->is);

	req->json_parsed_cb(req, success, error);
}
static void index_transaction_free(struct mailbox_transaction_context *t)
{
	if (t->view_pvt != NULL)
		mail_index_view_close(&t->view_pvt);
	mail_cache_view_close(&t->cache_view);
	mail_index_view_close(&t->view);
	if (array_is_created(&t->pvt_saves))
		array_free(&t->pvt_saves);
	array_free(&t->module_contexts);
	i_free(t);
}
Beispiel #29
0
void io_stream_add_destroy_callback(struct iostream_private *stream,
				    void (*callback)(void *), void *context)
{
	struct iostream_destroy_callback *dc;

	if (!array_is_created(&stream->destroy_callbacks))
		i_array_init(&stream->destroy_callbacks, 2);
	dc = array_append_space(&stream->destroy_callbacks);
	dc->callback = callback;
	dc->context = context;
}
void client_search_updates_free(struct client *client)
{
	struct imap_search_update *update;

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

	array_foreach_modifiable(&client->search_updates, update)
		imap_search_update_free(update);
	array_clear(&client->search_updates);
}