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

#include "lib.h"
#include "array.h"
#include "istream.h"
#include "ostream.h"
#include "base64.h"
#include "randgen.h"
#include "str.h"
#include "message-size.h"
#include "imap-utf7.h"
#include "imap-util.h"
#include "mail-storage.h"
#include "mail-search.h"
#include "doveadm-mail.h"
#include "doveadm-mail-list-iter.h"
#include "doveadm-mail-iter.h"

#include <stdio.h>

struct fetch_cmd_context {
	struct doveadm_mail_cmd_context ctx;

	struct ostream *output;
	struct mail *mail;

	ARRAY_DEFINE(fields, const struct fetch_field);
	ARRAY_TYPE(const_string) header_fields;
	enum mail_fetch_field wanted_fields;

	const struct fetch_field *cur_field;
	string_t *hdr;
	const char *prefix;

	bool print_field_prefix;
};

struct fetch_field {
	const char *name;
	enum mail_fetch_field wanted_fields;
	int (*print)(struct fetch_cmd_context *ctx);
};

static int fetch_mailbox(struct fetch_cmd_context *ctx)
{
	const char *value;
	unsigned int len;

	if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME, &value) < 0)
		return -1;

	len = str_len(ctx->hdr);
	if (imap_utf7_to_utf8(value, ctx->hdr) < 0) {
		/* not a valid mUTF-7 name, fallback to showing it as-is */
		str_truncate(ctx->hdr, len);
		str_append(ctx->hdr, value);
	}
	return 0;
}

static int fetch_mailbox_guid(struct fetch_cmd_context *ctx)
{
	uint8_t guid[MAIL_GUID_128_SIZE];

	if (mailbox_get_guid(ctx->mail->box, guid) < 0)
		return -1;
	str_append(ctx->hdr, mail_guid_128_to_string(guid));
	return 0;
}

static int fetch_seq(struct fetch_cmd_context *ctx)
{
	str_printfa(ctx->hdr, "%u", ctx->mail->seq);
	return 0;
}

static int fetch_uid(struct fetch_cmd_context *ctx)
{
	str_printfa(ctx->hdr, "%u", ctx->mail->seq);
	return 0;
}

static int fetch_guid(struct fetch_cmd_context *ctx)
{
	const char *value;

	if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &value) < 0)
		return -1;
	str_append(ctx->hdr, value);
	return 0;
}

static int fetch_flags(struct fetch_cmd_context *ctx)
{
	imap_write_flags(ctx->hdr, mail_get_flags(ctx->mail),
			 mail_get_keywords(ctx->mail));
	return 0;
}

static void flush_hdr(struct fetch_cmd_context *ctx)
{
	o_stream_send(ctx->output, str_data(ctx->hdr), str_len(ctx->hdr));
	str_truncate(ctx->hdr, 0);
}

static int fetch_hdr(struct fetch_cmd_context *ctx)
{
	struct istream *input;
	struct message_size hdr_size;
	int ret = 0;

	if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
		return -1;

	if (ctx->print_field_prefix)
		str_append_c(ctx->hdr, '\n');
	flush_hdr(ctx);
	input = i_stream_create_limit(input, hdr_size.physical_size);
	while (!i_stream_is_eof(input)) {
		if (o_stream_send_istream(ctx->output, input) <= 0)
			i_fatal("write(stdout) failed: %m");
	}
	if (input->stream_errno != 0) {
		i_error("read() failed: %m");
		ret = -1;
	}
	i_stream_unref(&input);
	o_stream_flush(ctx->output);
	return ret;
}

static int fetch_hdr_field(struct fetch_cmd_context *ctx)
{
	const char *const *value;
	bool add_lf = FALSE;

	if (mail_get_headers(ctx->mail, ctx->cur_field->name, &value) < 0)
		return -1;

	for (; *value != NULL; value++) {
		if (add_lf)
			str_append_c(ctx->hdr, '\n');
		if (ctx->print_field_prefix)
			str_printfa(ctx->hdr, "hdr.%s: ", ctx->cur_field->name);
		str_append(ctx->hdr, *value);
		add_lf = TRUE;
	}
	return 0;
}

static int fetch_body(struct fetch_cmd_context *ctx)
{
	struct istream *input;
	struct message_size hdr_size;
	int ret = 0;

	if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
		return -1;

	if (ctx->print_field_prefix)
		str_append_c(ctx->hdr, '\n');
	flush_hdr(ctx);
	i_stream_skip(input, hdr_size.physical_size);
	while (!i_stream_is_eof(input)) {
		if (o_stream_send_istream(ctx->output, input) <= 0)
			i_fatal("write(stdout) failed: %m");
	}
	if (input->stream_errno != 0) {
		i_error("read() failed: %m");
		ret = -1;
	}
	o_stream_flush(ctx->output);
	return ret;
}

static int fetch_text(struct fetch_cmd_context *ctx)
{
	struct istream *input;
	int ret = 0;

	if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
		return -1;

	if (ctx->print_field_prefix)
		str_append_c(ctx->hdr, '\n');
	flush_hdr(ctx);
	while (!i_stream_is_eof(input)) {
		if (o_stream_send_istream(ctx->output, input) <= 0)
			i_fatal("write(stdout) failed: %m");
	}
	if (input->stream_errno != 0) {
		i_error("read() failed: %m");
		ret = -1;
	}
	o_stream_flush(ctx->output);
	return ret;
}

static int fetch_size_physical(struct fetch_cmd_context *ctx)
{
	uoff_t size;

	if (mail_get_physical_size(ctx->mail, &size) < 0)
		return -1;
	str_printfa(ctx->hdr, "%"PRIuUOFF_T, size);
	return 0;
}

static int fetch_size_virtual(struct fetch_cmd_context *ctx)
{
	uoff_t size;

	if (mail_get_virtual_size(ctx->mail, &size) < 0)
		return -1;
	str_printfa(ctx->hdr, "%"PRIuUOFF_T, size);
	return 0;
}

static int fetch_date_received(struct fetch_cmd_context *ctx)
{
	time_t t;

	if (mail_get_received_date(ctx->mail, &t) < 0)
		return -1;
	str_printfa(ctx->hdr, "%s", unixdate2str(t));
	return 0;
}

static int fetch_date_sent(struct fetch_cmd_context *ctx)
{
	time_t t;
	int tz;
	char chr;

	if (mail_get_date(ctx->mail, &t, &tz) < 0)
		return -1;

	chr = tz < 0 ? '-' : '+';
	if (tz < 0) tz = -tz;
	str_printfa(ctx->hdr, "%s (%c%02u%02u)", unixdate2str(t),
		    chr, tz/60, tz%60);
	return 0;
}

static int fetch_date_saved(struct fetch_cmd_context *ctx)
{
	time_t t;

	if (mail_get_save_date(ctx->mail, &t) < 0)
		return -1;
	str_printfa(ctx->hdr, "%s", unixdate2str(t));
	return 0;
}

static int fetch_imap_envelope(struct fetch_cmd_context *ctx)
{
	const char *value;

	if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_ENVELOPE, &value) < 0)
		return -1;
	str_append(ctx->hdr, value);
	return 0;
}

static int fetch_imap_body(struct fetch_cmd_context *ctx)
{
	const char *value;

	if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODY, &value) < 0)
		return -1;
	str_append(ctx->hdr, value);
	return 0;
}

static int fetch_imap_bodystructure(struct fetch_cmd_context *ctx)
{
	const char *value;

	if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, &value) < 0)
		return -1;
	str_append(ctx->hdr, value);
	return 0;
}

static const struct fetch_field fetch_fields[] = {
	{ "mailbox",       0,                        fetch_mailbox },
	{ "mailbox-guid",  0,                        fetch_mailbox_guid },
	{ "seq",           0,                        fetch_seq },
	{ "uid",           0,                        fetch_uid },
	{ "guid",          0,                        fetch_guid },
	{ "flags",         MAIL_FETCH_FLAGS,         fetch_flags },
	{ "hdr",           MAIL_FETCH_STREAM_HEADER, fetch_hdr },
	{ "body",          MAIL_FETCH_STREAM_BODY,   fetch_body },
	{ "text",          MAIL_FETCH_STREAM_HEADER |
	                   MAIL_FETCH_STREAM_BODY,   fetch_text },
	{ "size.physical", MAIL_FETCH_PHYSICAL_SIZE, fetch_size_physical },
	{ "size.virtual",  MAIL_FETCH_VIRTUAL_SIZE,  fetch_size_virtual },
	{ "date.received", MAIL_FETCH_RECEIVED_DATE, fetch_date_received },
	{ "date.sent",     MAIL_FETCH_DATE,          fetch_date_sent },
	{ "date.saved",    MAIL_FETCH_SAVE_DATE,     fetch_date_saved },
	{ "imap.envelope", MAIL_FETCH_IMAP_ENVELOPE, fetch_imap_envelope },
	{ "imap.body",     MAIL_FETCH_IMAP_BODY,     fetch_imap_body },<commit_msg>doveadm fetch: Added "user" field.<commit_after>/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "istream.h"
#include "ostream.h"
#include "base64.h"
#include "randgen.h"
#include "str.h"
#include "message-size.h"
#include "imap-utf7.h"
#include "imap-util.h"
#include "mail-user.h"
#include "mail-storage.h"
#include "mail-search.h"
#include "doveadm-mail.h"
#include "doveadm-mail-list-iter.h"
#include "doveadm-mail-iter.h"

#include <stdio.h>

struct fetch_cmd_context {
	struct doveadm_mail_cmd_context ctx;

	struct ostream *output;
	struct mail *mail;

	ARRAY_DEFINE(fields, const struct fetch_field);
	ARRAY_TYPE(const_string) header_fields;
	enum mail_fetch_field wanted_fields;

	const struct fetch_field *cur_field;
	string_t *hdr;
	const char *prefix;

	bool print_field_prefix;
};

struct fetch_field {
	const char *name;
	enum mail_fetch_field wanted_fields;
	int (*print)(struct fetch_cmd_context *ctx);
};

static int fetch_user(struct fetch_cmd_context *ctx)
{
	str_append(ctx->hdr, ctx->ctx.cur_mail_user->username);
	return 0;
}

static int fetch_mailbox(struct fetch_cmd_context *ctx)
{
	const char *value;
	unsigned int len;

	if (mail_get_special(ctx->mail, MAIL_FETCH_MAILBOX_NAME, &value) < 0)
		return -1;

	len = str_len(ctx->hdr);
	if (imap_utf7_to_utf8(value, ctx->hdr) < 0) {
		/* not a valid mUTF-7 name, fallback to showing it as-is */
		str_truncate(ctx->hdr, len);
		str_append(ctx->hdr, value);
	}
	return 0;
}

static int fetch_mailbox_guid(struct fetch_cmd_context *ctx)
{
	uint8_t guid[MAIL_GUID_128_SIZE];

	if (mailbox_get_guid(ctx->mail->box, guid) < 0)
		return -1;
	str_append(ctx->hdr, mail_guid_128_to_string(guid));
	return 0;
}

static int fetch_seq(struct fetch_cmd_context *ctx)
{
	str_printfa(ctx->hdr, "%u", ctx->mail->seq);
	return 0;
}

static int fetch_uid(struct fetch_cmd_context *ctx)
{
	str_printfa(ctx->hdr, "%u", ctx->mail->seq);
	return 0;
}

static int fetch_guid(struct fetch_cmd_context *ctx)
{
	const char *value;

	if (mail_get_special(ctx->mail, MAIL_FETCH_GUID, &value) < 0)
		return -1;
	str_append(ctx->hdr, value);
	return 0;
}

static int fetch_flags(struct fetch_cmd_context *ctx)
{
	imap_write_flags(ctx->hdr, mail_get_flags(ctx->mail),
			 mail_get_keywords(ctx->mail));
	return 0;
}

static void flush_hdr(struct fetch_cmd_context *ctx)
{
	o_stream_send(ctx->output, str_data(ctx->hdr), str_len(ctx->hdr));
	str_truncate(ctx->hdr, 0);
}

static int fetch_hdr(struct fetch_cmd_context *ctx)
{
	struct istream *input;
	struct message_size hdr_size;
	int ret = 0;

	if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
		return -1;

	if (ctx->print_field_prefix)
		str_append_c(ctx->hdr, '\n');
	flush_hdr(ctx);
	input = i_stream_create_limit(input, hdr_size.physical_size);
	while (!i_stream_is_eof(input)) {
		if (o_stream_send_istream(ctx->output, input) <= 0)
			i_fatal("write(stdout) failed: %m");
	}
	if (input->stream_errno != 0) {
		i_error("read() failed: %m");
		ret = -1;
	}
	i_stream_unref(&input);
	o_stream_flush(ctx->output);
	return ret;
}

static int fetch_hdr_field(struct fetch_cmd_context *ctx)
{
	const char *const *value;
	bool add_lf = FALSE;

	if (mail_get_headers(ctx->mail, ctx->cur_field->name, &value) < 0)
		return -1;

	for (; *value != NULL; value++) {
		if (add_lf)
			str_append_c(ctx->hdr, '\n');
		if (ctx->print_field_prefix)
			str_printfa(ctx->hdr, "hdr.%s: ", ctx->cur_field->name);
		str_append(ctx->hdr, *value);
		add_lf = TRUE;
	}
	return 0;
}

static int fetch_body(struct fetch_cmd_context *ctx)
{
	struct istream *input;
	struct message_size hdr_size;
	int ret = 0;

	if (mail_get_stream(ctx->mail, &hdr_size, NULL, &input) < 0)
		return -1;

	if (ctx->print_field_prefix)
		str_append_c(ctx->hdr, '\n');
	flush_hdr(ctx);
	i_stream_skip(input, hdr_size.physical_size);
	while (!i_stream_is_eof(input)) {
		if (o_stream_send_istream(ctx->output, input) <= 0)
			i_fatal("write(stdout) failed: %m");
	}
	if (input->stream_errno != 0) {
		i_error("read() failed: %m");
		ret = -1;
	}
	o_stream_flush(ctx->output);
	return ret;
}

static int fetch_text(struct fetch_cmd_context *ctx)
{
	struct istream *input;
	int ret = 0;

	if (mail_get_stream(ctx->mail, NULL, NULL, &input) < 0)
		return -1;

	if (ctx->print_field_prefix)
		str_append_c(ctx->hdr, '\n');
	flush_hdr(ctx);
	while (!i_stream_is_eof(input)) {
		if (o_stream_send_istream(ctx->output, input) <= 0)
			i_fatal("write(stdout) failed: %m");
	}
	if (input->stream_errno != 0) {
		i_error("read() failed: %m");
		ret = -1;
	}
	o_stream_flush(ctx->output);
	return ret;
}

static int fetch_size_physical(struct fetch_cmd_context *ctx)
{
	uoff_t size;

	if (mail_get_physical_size(ctx->mail, &size) < 0)
		return -1;
	str_printfa(ctx->hdr, "%"PRIuUOFF_T, size);
	return 0;
}

static int fetch_size_virtual(struct fetch_cmd_context *ctx)
{
	uoff_t size;

	if (mail_get_virtual_size(ctx->mail, &size) < 0)
		return -1;
	str_printfa(ctx->hdr, "%"PRIuUOFF_T, size);
	return 0;
}

static int fetch_date_received(struct fetch_cmd_context *ctx)
{
	time_t t;

	if (mail_get_received_date(ctx->mail, &t) < 0)
		return -1;
	str_printfa(ctx->hdr, "%s", unixdate2str(t));
	return 0;
}

static int fetch_date_sent(struct fetch_cmd_context *ctx)
{
	time_t t;
	int tz;
	char chr;

	if (mail_get_date(ctx->mail, &t, &tz) < 0)
		return -1;

	chr = tz < 0 ? '-' : '+';
	if (tz < 0) tz = -tz;
	str_printfa(ctx->hdr, "%s (%c%02u%02u)", unixdate2str(t),
		    chr, tz/60, tz%60);
	return 0;
}

static int fetch_date_saved(struct fetch_cmd_context *ctx)
{
	time_t t;

	if (mail_get_save_date(ctx->mail, &t) < 0)
		return -1;
	str_printfa(ctx->hdr, "%s", unixdate2str(t));
	return 0;
}

static int fetch_imap_envelope(struct fetch_cmd_context *ctx)
{
	const char *value;

	if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_ENVELOPE, &value) < 0)
		return -1;
	str_append(ctx->hdr, value);
	return 0;
}

static int fetch_imap_body(struct fetch_cmd_context *ctx)
{
	const char *value;

	if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODY, &value) < 0)
		return -1;
	str_append(ctx->hdr, value);
	return 0;
}

static int fetch_imap_bodystructure(struct fetch_cmd_context *ctx)
{
	const char *value;

	if (mail_get_special(ctx->mail, MAIL_FETCH_IMAP_BODYSTRUCTURE, &value) < 0)
		return -1;
	str_append(ctx->hdr, value);
	return 0;
}

static const struct fetch_field fetch_fields[] = {
	{ "user",          0,                        fetch_user },
	{ "mailbox",       0,                        fetch_mailbox },
	{ "mailbox-guid",  0,                        fetch_mailbox_guid },
	{ "seq",           0,                        fetch_seq },
	{ "uid",           0,                        fetch_uid },
	{ "guid",          0,                        fetch_guid },
	{ "flags",         MAIL_FETCH_FLAGS,         fetch_flags },
	{ "hdr",           MAIL_FETCH_STREAM_HEADER, fetch_hdr },
	{ "body",          MAIL_FETCH_STREAM_BODY,   fetch_body },
	{ "text",          MAIL_FETCH_STREAM_HEADER |
	                   MAIL_FETCH_STREAM_BODY,   fetch_text },
	{ "size.physical", MAIL_FETCH_PHYSICAL_SIZE, fetch_size_physical },
	{ "size.virtual",  MAIL_FETCH_VIRTUAL_SIZE,  fetch_size_virtual },
	{ "date.received", MAIL_FETCH_RECEIVED_DATE, fetch_date_received },
	{ "date.sent",     MAIL_FETCH_DATE,          fetch_date_sent },
	{ "date.saved",    MAIL_FETCH_SAVE_DATE,     fetch_date_saved },
	{ "imap.envelope", MAIL_FETCH_IMAP_ENVELOPE, fetch_imap_envelope },
	{ "imap.body",     MAIL_FETCH_IMAP_BODY,     fetch_imap_body },
{'fraction_non_alphanumeric': 0.14546557067780622, 'fraction_numerical': 0.01280150923056192, 'mean_word_length': 4.186233403214535, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 140, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3201068', 'n_tokens_mistral': 6318, 'n_tokens_neox': 5929, 'n_words': 1222}