view src/imap/cmd-search.c @ 6429:65c69a53a7be HEAD

Replaced my Copyright notices. The year range always ends with 2007 now. My name was replaced with "Dovecot authors". In many cases I didn't really even own the copyright, so this is more correct.
author Timo Sirainen <tss@iki.fi>
date Sun, 16 Sep 2007 14:34:22 +0300
parents 6342c8cc76c7
children 1a3604c8ee05
line wrap: on
line source

/* Copyright (c) 2002-2007 Dovecot authors, see the included COPYING file */

#include "common.h"
#include "ostream.h"
#include "str.h"
#include "commands.h"
#include "imap-search.h"

#define OUTBUF_SIZE 65536

struct imap_search_context {
        struct mailbox_transaction_context *trans;
        struct mail_search_context *search_ctx;
	struct mail *mail;

	struct timeout *to;
	string_t *output_buf;

	unsigned int output_sent:1;
};

static struct imap_search_context *
imap_search_init(struct client_command_context *cmd, const char *charset,
		 struct mail_search_arg *sargs)
{
	struct imap_search_context *ctx;

	ctx = p_new(cmd->pool, struct imap_search_context, 1);
	ctx->trans = mailbox_transaction_begin(cmd->client->mailbox, 0);
	ctx->search_ctx = mailbox_search_init(ctx->trans, charset, sargs, NULL);
	ctx->mail = mail_alloc(ctx->trans, 0, NULL);

	ctx->output_buf = str_new(default_pool, OUTBUF_SIZE);
	str_append(ctx->output_buf, "* SEARCH");
	return ctx;
}

static int imap_search_deinit(struct client_command_context *cmd,
			      struct imap_search_context *ctx)
{
	int ret;

	mail_free(&ctx->mail);
	ret = mailbox_search_deinit(&ctx->search_ctx);

	if (mailbox_transaction_commit(&ctx->trans, 0) < 0)
		ret = -1;

	if (ctx->output_sent || (ret == 0 && !cmd->cancel)) {
		str_append(ctx->output_buf, "\r\n");
		o_stream_send(cmd->client->output,
			      str_data(ctx->output_buf),
			      str_len(ctx->output_buf));
	}
	if (ctx->to != NULL)
		timeout_remove(&ctx->to);
	str_free(&ctx->output_buf);

	cmd->context = NULL;
	return ret;
}

static bool cmd_search_more(struct client_command_context *cmd)
{
	struct imap_search_context *ctx = cmd->context;
	bool tryagain;
	int ret;

	if (cmd->cancel) {
		(void)imap_search_deinit(cmd, ctx);
		return TRUE;
	}

	while ((ret = mailbox_search_next_nonblock(ctx->search_ctx, ctx->mail,
						   &tryagain)) > 0) {
		if (str_len(ctx->output_buf) >= OUTBUF_SIZE - MAX_INT_STRLEN) {
			/* flush. this also causes us to lock the output. */
			cmd->client->output_lock = cmd;
			o_stream_send(cmd->client->output,
				      str_data(ctx->output_buf),
				      str_len(ctx->output_buf));
			str_truncate(ctx->output_buf, 0);
			ctx->output_sent = TRUE;
		}

		str_printfa(ctx->output_buf, " %u",
			    cmd->uid ? ctx->mail->uid : ctx->mail->seq);
	}
	if (tryagain)
		return FALSE;

	if (imap_search_deinit(cmd, ctx) < 0)
		ret = -1;

	if (ret < 0) {
		client_send_storage_error(cmd,
			mailbox_get_storage(cmd->client->mailbox));
		return TRUE;
	} else {
		return cmd_sync(cmd, MAILBOX_SYNC_FLAG_FAST |
				(cmd->uid ? 0 : MAILBOX_SYNC_FLAG_NO_EXPUNGES),
				0, "OK Search completed.");
	}
}

static void cmd_search_more_callback(struct client_command_context *cmd)
{
	struct client *client = cmd->client;
	bool finished;

	o_stream_cork(client->output);
	finished = cmd_search_more(cmd);
	o_stream_uncork(client->output);

	if (finished) {
		client_command_free(cmd);
		client_continue_pending_input(client);
	} else {
		if (cmd->output_pending)
			o_stream_set_flush_pending(client->output, TRUE);
	}
}

bool cmd_search(struct client_command_context *cmd)
{
	struct imap_search_context *ctx;
	struct mail_search_arg *sargs;
	const struct imap_arg *args;
	int args_count;
	const char *error, *charset;

	args_count = imap_parser_read_args(cmd->parser, 0, 0, &args);
	if (args_count < 1) {
		if (args_count == -2)
			return FALSE;

		client_send_command_error(cmd, args_count < 0 ? NULL :
					  "Missing SEARCH arguments.");
		return TRUE;
	}
	cmd->client->input_lock = NULL;

	if (!client_verify_open_mailbox(cmd))
		return TRUE;

	if (args->type == IMAP_ARG_ATOM &&
	    strcasecmp(IMAP_ARG_STR_NONULL(args), "CHARSET") == 0) {
		/* CHARSET specified */
		args++;
		if (args->type != IMAP_ARG_ATOM &&
		    args->type != IMAP_ARG_STRING) {
			client_send_command_error(cmd,
						  "Invalid charset argument.");
			return TRUE;
		}

		charset = IMAP_ARG_STR(args);
		args++;
	} else {
		charset = "UTF-8";
	}

	sargs = imap_search_args_build(cmd->pool, cmd->client->mailbox,
				       args, &error);
	if (sargs == NULL) {
		/* error in search arguments */
		client_send_tagline(cmd, t_strconcat("BAD ", error, NULL));
		return TRUE;
	}

	ctx = imap_search_init(cmd, charset, sargs);
	cmd->func = cmd_search_more;
	cmd->context = ctx;

	if (cmd_search_more(cmd))
		return TRUE;

	/* we could have moved onto syncing by now */
	if (cmd->func == cmd_search_more)
		ctx->to = timeout_add(0, cmd_search_more_callback, cmd);
	return FALSE;
}