/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "str.h"
#include "strfuncs.h"
#include "smtp-reply.h"

void smtp_reply_init(struct smtp_reply *reply, unsigned int status,
	const char *text)
{
	const char **text_lines = t_new(const char *, 2);

	text_lines[0] = text;
	text_lines[1] = NULL;

	i_zero(reply);
	reply->status = status;
	reply->text_lines = text_lines;
}

void smtp_reply_printf(struct smtp_reply *reply, unsigned int status,
	const char *format, ...)
{
	va_list args;

	va_start(args, format);
	smtp_reply_init(reply, status, t_strdup_vprintf(format, args));
	va_end(args);
}

const char *
smtp_reply_get_enh_code(const struct smtp_reply *reply)
{
	if (reply->enhanced_code.x < 2)
		return NULL;
	if (reply->enhanced_code.x >= 6)
		return NULL;

	return t_strdup_printf("%u.%u.%u",
		reply->enhanced_code.x, reply->enhanced_code.y, reply->enhanced_code.z);
}

const char *const *
smtp_reply_get_text_lines_omit_prefix(const struct smtp_reply *reply)
{
	unsigned int lines_count, i;
	const char **lines;
	const char *p;

	if ((p=strchr(reply->text_lines[0], ' ')) == NULL)
		return reply->text_lines;

	lines_count = str_array_length(reply->text_lines);
	lines = t_new(const char *, lines_count + 1);

	lines[0] = p + 1;
	for (i = 1; i < lines_count; i++)
		lines[i] = reply->text_lines[i];

	return lines;
}

void
smtp_reply_write(string_t *out, const struct smtp_reply *reply)
{
	const char *prefix, *enh_code;
	const char *const *lines;

	i_assert(reply->status < 560);
	i_assert(reply->enhanced_code.x < 6);

	prefix = t_strdup_printf("%03u", reply->status);
	enh_code = smtp_reply_get_enh_code(reply);

	if (reply->text_lines == NULL || *reply->text_lines == NULL) {
		str_append(out, prefix);
		if (enh_code != NULL) {
			str_append_c(out, ' ');
			str_append(out, enh_code);
		}
		str_append(out, " \r\n");
		return;
	}

	lines = reply->text_lines;
	while (*lines != NULL) {
		str_append(out, prefix);
		if (*(lines+1) == NULL)
			str_append_c(out, ' ');
		else
			str_append_c(out, '-');
		if (enh_code != NULL) {
			str_append(out, enh_code);
			str_append_c(out, ' ');
		}
		str_append(out, *lines);
		str_append(out, "\r\n");
		lines++;
	}
}

static void
smtp_reply_write_message_one_line(string_t *out, const struct smtp_reply *reply)
{
	const char *const *lines;

	lines = reply->text_lines;
	while (*lines != NULL) {
		if (str_len(out) > 0)
			str_append_c(out, ' ');
		str_append(out, *lines);
		lines++;
	}
}

void smtp_reply_write_one_line(string_t *out, const struct smtp_reply *reply)
{
	const char *enh_code = smtp_reply_get_enh_code(reply);

	i_assert(reply->status < 560);
	i_assert(reply->enhanced_code.x < 6);

	str_printfa(out, "%03u", reply->status);
	if (enh_code != NULL) {
		str_append_c(out, ' ');
		str_append(out, enh_code);
	}

	smtp_reply_write_message_one_line(out, reply);
}

const char *smtp_reply_log(const struct smtp_reply *reply)
{
	string_t *msg = t_str_new(256);

	if (smtp_reply_is_remote(reply)) {
		const char *enh_code = smtp_reply_get_enh_code(reply);

		str_printfa(msg, "%03u", reply->status);
		if (enh_code != NULL) {
			str_append_c(msg, ' ');
			str_append(msg, enh_code);
		}
	}

	smtp_reply_write_message_one_line(msg, reply);
	return str_c(msg);
}

const char *smtp_reply_get_message(const struct smtp_reply *reply)
{
	string_t *msg = t_str_new(256);

	smtp_reply_write_message_one_line(msg, reply);
	return str_c(msg);
}

void smtp_reply_copy(pool_t pool, struct smtp_reply *dst,
	const struct smtp_reply *src)
{
	*dst = *src;
	dst->text_lines = p_strarray_dup(pool, src->text_lines);
}

struct smtp_reply *smtp_reply_clone(pool_t pool,
	const struct smtp_reply *src)
{
	struct smtp_reply *dst;

	dst = p_new(pool, struct smtp_reply, 1);
	smtp_reply_copy(pool, dst, src);

	return dst;
}

void smtp_reply_add_to_event(const struct smtp_reply *reply,
			     struct event_passthrough *e)
{
	const char *enh_code = smtp_reply_get_enh_code(reply);

	e->add_int("status_code", reply->status);
	e->add_str("enhanced_code", enh_code);
	if (!smtp_reply_is_success(reply))
		e->add_str("error", smtp_reply_get_message(reply));
}
