<commit_before>/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */

#include "imap-common.h"
#include "array.h"
#include "buffer.h"
#include "istream.h"
#include "ostream.h"
#include "str.h"
#include "message-send.h"
#include "message-size.h"
#include "imap-date.h"
#include "mail-search-build.h"
#include "imap-commands.h"
#include "imap-quote.h"
#include "imap-fetch.h"
#include "imap-util.h"

#include <stdlib.h>
#include <ctype.h>

#define BODY_NIL_REPLY \
	"\"text\" \"plain\" NIL NIL NIL \"7bit\" 0 0"
#define ENVELOPE_NIL_REPLY \
	"(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)"

static ARRAY_DEFINE(fetch_handlers, struct imap_fetch_handler);

static int imap_fetch_handler_cmp(const struct imap_fetch_handler *h1,
				  const struct imap_fetch_handler *h2)
{
	return strcmp(h1->name, h2->name);
}

void imap_fetch_handlers_register(const struct imap_fetch_handler *handlers,
				  size_t count)
{
	array_append(&fetch_handlers, handlers, count);
	array_sort(&fetch_handlers, imap_fetch_handler_cmp);
}

static int
imap_fetch_handler_bsearch(const char *name, const struct imap_fetch_handler *h)
{
	return strcmp(name, h->name);
}

bool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name,
			     const struct imap_arg **args)
{
	const struct imap_fetch_handler *handler;
	const char *lookup_name, *p;

	for (p = name; i_isalnum(*p) || *p == '-'; p++) ;
	lookup_name = t_strdup_until(name, p);

	handler = array_bsearch(&fetch_handlers, lookup_name,
				imap_fetch_handler_bsearch);
	if (handler == NULL) {
		client_send_command_error(ctx->cmd,
			t_strconcat("Unknown parameter ", name, NULL));
		return FALSE;
	}

	return handler->init(ctx, name, args);
}

struct imap_fetch_context *
imap_fetch_init(struct client_command_context *cmd, struct mailbox *box)
{
	struct client *client = cmd->client;
	struct imap_fetch_context *ctx;

	ctx = p_new(cmd->pool, struct imap_fetch_context, 1);
	ctx->client = client;
	ctx->cmd = cmd;
	ctx->box = box;

	ctx->cur_str = str_new(default_pool, 8192);
	p_array_init(&ctx->all_headers, cmd->pool, 64);
	p_array_init(&ctx->handlers, cmd->pool, 16);
	p_array_init(&ctx->tmp_keywords, cmd->pool,
		     client->keywords.announce_count + 8);
	ctx->line_finished = TRUE;
	return ctx;
}

bool imap_fetch_add_changed_since(struct imap_fetch_context *ctx,
				  uint64_t modseq)
{
	struct mail_search_arg *search_arg;

	search_arg = p_new(ctx->search_args->pool, struct mail_search_arg, 1);
	search_arg->type = SEARCH_MODSEQ;
	search_arg->value.modseq =
		p_new(ctx->cmd->pool, struct mail_search_modseq, 1);
	search_arg->value.modseq->modseq = modseq + 1;

	search_arg->next = ctx->search_args->args->next;
	ctx->search_args->args->next = search_arg;

	return imap_fetch_init_handler(ctx, "MODSEQ", NULL);
}

#undef imap_fetch_add_handler
void imap_fetch_add_handler(struct imap_fetch_context *ctx,
			    bool buffered, bool want_deinit,
			    const char *name, const char *nil_reply,
			    imap_fetch_handler_t *handler, void *context)
{
	/* partially because of broken clients, but also partially because
	   it potentially can make client implementations faster, we have a
	   buffered parameter which basically means that the handler promises
	   to write the output in ctx->cur_str. The cur_str is then sent to
	   client before calling any non-buffered handlers.

	   We try to keep the handler registration order the same as the
	   client requested them. This is especially useful to get UID
	   returned first, which some clients rely on..
	*/
	const struct imap_fetch_context_handler *ctx_handler;
	struct imap_fetch_context_handler h;

	if (context == NULL) {
		/* don't allow duplicate handlers */
		array_foreach(&ctx->handlers, ctx_handler) {
			if (ctx_handler->handler == handler &&
			    ctx_handler->context == NULL)
				return;
		}
	}

	memset(&h, 0, sizeof(h));
	h.handler = handler;
	h.context = context;
	h.buffered = buffered;
	h.want_deinit = want_deinit;
	h.name = p_strdup(ctx->cmd->pool, name);
	h.nil_reply = p_strdup(ctx->cmd->pool, nil_reply);

	if (!buffered)
		array_append(&ctx->handlers, &h, 1);
	else {
		array_insert(&ctx->handlers, ctx->buffered_handlers_count,
			     &h, 1);
                ctx->buffered_handlers_count++;
	}
}

static void
expunges_drop_known(struct imap_fetch_context *ctx, struct mail *mail,
		    ARRAY_TYPE(seq_range) *expunged_uids)
{
	const uint32_t *seqs, *uids;
	unsigned int i, count;

	seqs = array_get(ctx->qresync_sample_seqset, &count);
	uids = array_idx(ctx->qresync_sample_uidset, 0);
	i_assert(array_count(ctx->qresync_sample_uidset) == count);
	i_assert(count > 0);

	/* FIXME: we could do removals from the middle as well */
	for (i = 0; i < count; i++) {
		mail_set_seq(mail, seqs[i]);
		if (uids[i] != mail->uid)
			break;
	}
	if (i > 0)
		seq_range_array_remove_range(expunged_uids, 1, uids[i-1]);
}<commit_msg>imap: Don't crash with QRESYNC SELECT specifying sequences larger than mailbox's message count.<commit_after>/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */

#include "imap-common.h"
#include "array.h"
#include "buffer.h"
#include "istream.h"
#include "ostream.h"
#include "str.h"
#include "message-send.h"
#include "message-size.h"
#include "imap-date.h"
#include "mail-search-build.h"
#include "imap-commands.h"
#include "imap-quote.h"
#include "imap-fetch.h"
#include "imap-util.h"

#include <stdlib.h>
#include <ctype.h>

#define BODY_NIL_REPLY \
	"\"text\" \"plain\" NIL NIL NIL \"7bit\" 0 0"
#define ENVELOPE_NIL_REPLY \
	"(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)"

static ARRAY_DEFINE(fetch_handlers, struct imap_fetch_handler);

static int imap_fetch_handler_cmp(const struct imap_fetch_handler *h1,
				  const struct imap_fetch_handler *h2)
{
	return strcmp(h1->name, h2->name);
}

void imap_fetch_handlers_register(const struct imap_fetch_handler *handlers,
				  size_t count)
{
	array_append(&fetch_handlers, handlers, count);
	array_sort(&fetch_handlers, imap_fetch_handler_cmp);
}

static int
imap_fetch_handler_bsearch(const char *name, const struct imap_fetch_handler *h)
{
	return strcmp(name, h->name);
}

bool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name,
			     const struct imap_arg **args)
{
	const struct imap_fetch_handler *handler;
	const char *lookup_name, *p;

	for (p = name; i_isalnum(*p) || *p == '-'; p++) ;
	lookup_name = t_strdup_until(name, p);

	handler = array_bsearch(&fetch_handlers, lookup_name,
				imap_fetch_handler_bsearch);
	if (handler == NULL) {
		client_send_command_error(ctx->cmd,
			t_strconcat("Unknown parameter ", name, NULL));
		return FALSE;
	}

	return handler->init(ctx, name, args);
}

struct imap_fetch_context *
imap_fetch_init(struct client_command_context *cmd, struct mailbox *box)
{
	struct client *client = cmd->client;
	struct imap_fetch_context *ctx;

	ctx = p_new(cmd->pool, struct imap_fetch_context, 1);
	ctx->client = client;
	ctx->cmd = cmd;
	ctx->box = box;

	ctx->cur_str = str_new(default_pool, 8192);
	p_array_init(&ctx->all_headers, cmd->pool, 64);
	p_array_init(&ctx->handlers, cmd->pool, 16);
	p_array_init(&ctx->tmp_keywords, cmd->pool,
		     client->keywords.announce_count + 8);
	ctx->line_finished = TRUE;
	return ctx;
}

bool imap_fetch_add_changed_since(struct imap_fetch_context *ctx,
				  uint64_t modseq)
{
	struct mail_search_arg *search_arg;

	search_arg = p_new(ctx->search_args->pool, struct mail_search_arg, 1);
	search_arg->type = SEARCH_MODSEQ;
	search_arg->value.modseq =
		p_new(ctx->cmd->pool, struct mail_search_modseq, 1);
	search_arg->value.modseq->modseq = modseq + 1;

	search_arg->next = ctx->search_args->args->next;
	ctx->search_args->args->next = search_arg;

	return imap_fetch_init_handler(ctx, "MODSEQ", NULL);
}

#undef imap_fetch_add_handler
void imap_fetch_add_handler(struct imap_fetch_context *ctx,
			    bool buffered, bool want_deinit,
			    const char *name, const char *nil_reply,
			    imap_fetch_handler_t *handler, void *context)
{
	/* partially because of broken clients, but also partially because
	   it potentially can make client implementations faster, we have a
	   buffered parameter which basically means that the handler promises
	   to write the output in ctx->cur_str. The cur_str is then sent to
	   client before calling any non-buffered handlers.

	   We try to keep the handler registration order the same as the
	   client requested them. This is especially useful to get UID
	   returned first, which some clients rely on..
	*/
	const struct imap_fetch_context_handler *ctx_handler;
	struct imap_fetch_context_handler h;

	if (context == NULL) {
		/* don't allow duplicate handlers */
		array_foreach(&ctx->handlers, ctx_handler) {
			if (ctx_handler->handler == handler &&
			    ctx_handler->context == NULL)
				return;
		}
	}

	memset(&h, 0, sizeof(h));
	h.handler = handler;
	h.context = context;
	h.buffered = buffered;
	h.want_deinit = want_deinit;
	h.name = p_strdup(ctx->cmd->pool, name);
	h.nil_reply = p_strdup(ctx->cmd->pool, nil_reply);

	if (!buffered)
		array_append(&ctx->handlers, &h, 1);
	else {
		array_insert(&ctx->handlers, ctx->buffered_handlers_count,
			     &h, 1);
                ctx->buffered_handlers_count++;
	}
}

static void
expunges_drop_known(struct imap_fetch_context *ctx, struct mail *mail,
		    ARRAY_TYPE(seq_range) *expunged_uids)
{
	struct mailbox_status status;
	const uint32_t *seqs, *uids;
	unsigned int i, count;

	seqs = array_get(ctx->qresync_sample_seqset, &count);
	uids = array_idx(ctx->qresync_sample_uidset, 0);
	i_assert(array_count(ctx->qresync_sample_uidset) == count);
	i_assert(count > 0);

	mailbox_get_status(ctx->box, STATUS_MESSAGES, &status);

	/* FIXME: we could do removals from the middle as well */
	for (i = 0; i < count && seqs[i] <= status.messages; i++) {
		mail_set_seq(mail, seqs[i]);
		if (uids[i] != mail->uid)
			break;
	}
	if (i > 0)
		seq_range_array_remove_range(expunged_uids, 1, uids[i-1]);
}
{'fraction_non_alphanumeric': 0.12275238573581115, 'fraction_numerical': 0.008237066800602712, 'mean_word_length': 4.698912421293646, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 112, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2092483', 'n_tokens_mistral': 3857, 'n_tokens_neox': 3590, 'n_words': 876}