/* Copyright (c) Dovecot authors, see top-level COPYING file */

#include "lib.h"
#include "str.h"
#include "message-address.h"
#include "smtp-parser.h"
#include "smtp-address.h"

/* From RFC 5321:

   Reverse-path     = Path / "<>"
   Forward-path     = Path

   Path             = "<" [ A-d-l ":" ] Mailbox ">"
   A-d-l            = At-domain *( "," At-domain )
                    ; Note that this form, the so-called "source
                    ; route", MUST BE accepted, SHOULD NOT be
                    ; generated, and SHOULD be ignored.
   At-domain        = "@" Domain

   Domain           = sub-domain *("." sub-domain)

   sub-domain       = Let-dig [Ldh-str]
   Let-dig          = ALPHA / DIGIT
   Ldh-str          = *( ALPHA / DIGIT / "-" ) Let-dig

   address-literal  = "[" ( IPv4-address-literal /
                      IPv6-address-literal /
                      General-address-literal ) "]"
                    ; See Section 4.1.3

   Mailbox          = Local-part "@" ( Domain / address-literal )

   Local-part       = Dot-string / Quoted-string
                    ; MAY be case-sensitive
   Dot-string       = Atom *("."  Atom)
   Atom             = 1*atext
 */

/*
 * SMTP address parsing
 */

struct smtp_address_parser {
	struct smtp_parser parser;

	struct smtp_address address;
	const unsigned char *address_end;

	bool parse:1;
	bool path:1;
	bool parsed_any:1;
	bool totally_broken:1;
};

static int
smtp_parser_parse_dot_string(struct smtp_parser *parser, const char **value_r)
{
	const unsigned char *pbegin = parser->cur;

	/* Dot-string = Atom *("." Atom)
	 */

	/* NOTE: this deviates from Dot-String syntax to allow some Japanese
	   mail addresses with dots at non-standard places to be accepted. */

	if (parser->cur >= parser->end ||
	    (!smtp_char_is_atext(*parser->cur) && *parser->cur != '.'))
		return 0;
	parser->cur++;

	while (parser->cur < parser->end &&
	       (smtp_char_is_atext(*parser->cur) || *parser->cur == '.'))
		parser->cur++;

	if (value_r != NULL)
		*value_r = t_strndup(pbegin, parser->cur - pbegin);
	return 1;
}

static int
smtp_parse_localpart(struct smtp_parser *parser, const char **localpart_r)
{
	int ret;

	if ((ret = smtp_parser_parse_quoted_string(parser, localpart_r)) != 0)
		return ret;

	return smtp_parser_parse_dot_string(parser, localpart_r);
}

static int
smtp_address_parser_find_end(struct smtp_address_parser *aparser,
			     enum smtp_address_parse_flags flags)
{
	struct smtp_parser *parser = &aparser->parser;
	const char *begin = (const char *)parser->begin, *end;
	const char **address_p = NULL;

	if (aparser->address_end != NULL)
		return 0;

	if (aparser->parse &&
	    HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW))
		address_p = &aparser->address.raw;
	if (smtp_address_parse_any(begin, address_p, &end) < 0) {
		parser->error = "Invalid character";
		aparser->totally_broken = TRUE;
		return -1;
	}
	aparser->parsed_any = TRUE;
	aparser->address_end = (const unsigned char *)end;
	if (aparser->path) {
		i_assert(aparser->address_end > parser->begin);
		aparser->address_end--;
	}
	return 0;
}

static int
smtp_parse_mailbox(struct smtp_address_parser *aparser,
		   enum smtp_address_parse_flags flags)
{
	struct smtp_parser *parser = &aparser->parser;
	const char **value = NULL;
	const unsigned char *p, *dp;
	int ret;

	/* Mailbox = Local-part "@" ( Domain / address-literal )
	 */

	value = (aparser->parse ? &aparser->address.localpart : NULL);
	if ((flags & SMTP_ADDRESS_PARSE_FLAG_STRICT) != 0 ||
	    (flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART) == 0 ||
	    aparser->path || *parser->cur == '\"') {
		if ((ret = smtp_parse_localpart(parser, value)) <= 0)
			return ret;
	} else {
		/* find the end of the address */
		if (smtp_address_parser_find_end(aparser, flags) < 0)
			return -1;
		/* use the right-most '@' as separator */
		dp = aparser->address_end - 1;
		/* an empty address leaves address_end at cur, so clamp to
		   avoid dp < cur underflowing into p_strdup_until() below */
		if (dp < parser->cur)
			dp = parser->cur;
		while (dp > parser->cur && *dp != '@')
			dp--;
		if (dp == parser->cur)
			dp = aparser->address_end;
		/* check whether the resulting localpart could be encoded as
		   quoted string */
		for (p = parser->cur; p < dp; p++) {
			if (!smtp_char_is_qtext(*p) &&
			    !smtp_char_is_qpair(*p)) {
				parser->error =
					"Invalid character in localpart";
				return -1;
			}
		}
		if (aparser->parse) {
			aparser->address.localpart =
				p_strdup_until(parser->pool, parser->cur, dp);
		}
		parser->cur = dp;
	}

	if ((parser->cur >= parser->end || *parser->cur != '@') &&
	    (flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART) == 0) {
		if (parser->cur >= parser->end ||
		    (aparser->path && *parser->cur == '>'))
			parser->error = "Missing domain";
		else
			parser->error = "Invalid character in localpart";
		return -1;
	}

	if (parser->cur >= parser->end || *parser->cur != '@')
		return 1;
	parser->cur++;

	value = (aparser->parse ? &aparser->address.domain : NULL);
	if ((ret = smtp_parser_parse_domain(parser, value)) == 0 &&
	    (ret = smtp_parser_parse_address_literal(
		parser, value, NULL)) == 0) {
		if (parser->cur >= parser->end ||
		    (aparser->path && *parser->cur == '>')) {
			parser->error = "Missing domain after '@'";
			return -1;
		} else {
			parser->error = "Invalid domain";
			return -1;
		}
	}
	return ret;
}

static int smtp_parse_source_route(struct smtp_parser *parser)
{
	/* Source-route = [ A-d-l ":" ]
	   A-d-l        = At-domain *( "," At-domain )
	   At-domain    = "@" Domain
	 */

	/* "@" Domain */
	if (parser->cur >= parser->end || *parser->cur != '@')
		return 0;
	parser->cur++;

	for (;;) {
		/* Domain */
		if (smtp_parser_parse_domain(parser, NULL) <= 0) {
			parser->error =
				"Missing domain after '@' in source route";
			return -1;
		}

		/* *( "," At-domain ) */
		if (parser->cur >= parser->end || *parser->cur != ',')
			break;
		parser->cur++;

		/* "@" Domain */
		if (parser->cur >= parser->end || *parser->cur != '@') {
			parser->error = "Missing '@' after ',' in source route";
			return -1;
		}
		parser->cur++;
	}

	/* ":" */
	if (parser->cur >= parser->end || *parser->cur != ':') {
		parser->error = "Missing ':' at end of source route";
		return -1;
	}
	parser->cur++;
	return 1;
}

static int
smtp_parse_path(struct smtp_address_parser *aparser,
		enum smtp_address_parse_flags flags)
{
	struct smtp_parser *parser = &aparser->parser;
	int ret, sret = 0;

	/* Path = "<" [ A-d-l ":" ] Mailbox ">"
	 */

	/* "<" */
	if (parser->cur < parser->end && *parser->cur == '<') {
		aparser->path = TRUE;
		parser->cur++;
	} else if ((flags & SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL) == 0) {
		return 0;
	}

	/* [ A-d-l ":" ] */
	if (aparser->path && (sret = smtp_parse_source_route(parser)) < 0)
		return -1;

	/* Mailbox */
	if ((ret = smtp_parse_mailbox(aparser, flags)) < 0)
		return -1;
	if (ret == 0) {
		if (parser->cur < parser->end && *parser->cur == '>') {
			if (sret > 0) {
				parser->error =
					"Path only consists of source route";
				return -1;
			}
			if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY)
				== 0) {
				parser->error = "Null path not allowed";
				return -1;
			}
		} else {
			parser->error = "Invalid character in localpart";
			return -1;
		}
	}

	/* ">" */
	if (aparser->path) {
		if (parser->cur >= parser->end || *parser->cur != '>') {
			parser->error = "Missing '>' at end of path";
			return -1;
		}
		parser->cur++;
	} else if (parser->cur < parser->end && *parser->cur == '>') {
		parser->error = "Unmatched '>' at end of path";
		return -1;
	}
	return 1;
}

int smtp_address_parse_mailbox(pool_t pool, const char *mailbox,
			       enum smtp_address_parse_flags flags,
			       struct smtp_address **address_r,
			       const char **error_r)
{
	struct smtp_address_parser aparser;
	int ret;

	if (address_r != NULL)
		*address_r = NULL;
	if (error_r != NULL)
		*error_r = NULL;

	if (error_r != NULL)
		*error_r = NULL;

	if ((mailbox == NULL || *mailbox == '\0')) {
		if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) == 0) {
			if (error_r != NULL)
				*error_r = "Mailbox is empty string";
			return -1;
		}

		if (address_r != NULL)
			*address_r = p_new(pool, struct smtp_address, 1);
		return 0;
	}

	i_zero(&aparser);
	smtp_parser_init(&aparser.parser, pool_datastack_create(), mailbox);
	aparser.address_end = aparser.parser.end;
	aparser.parse = (address_r != NULL);

	if ((ret = smtp_parse_mailbox(&aparser, flags)) <= 0) {
		if (error_r != NULL) {
			*error_r = (ret < 0 ? aparser.parser.error :
				"Invalid character in localpart");
		}
		return -1;
	}
	if (aparser.parser.cur != aparser.parser.end) {
		if (error_r != NULL)
			*error_r = "Invalid character in mailbox";
		return -1;
	}

	if (address_r != NULL)
		*address_r = smtp_address_clone(pool, &aparser.address);
	return 0;
}

static int
smtp_address_parse_path_broken(struct smtp_address_parser *aparser,
			       enum smtp_address_parse_flags flags,
			       const char **endp_r) ATTR_NULL(3)
{
	struct smtp_parser *parser = &aparser->parser;
	const char *begin = (const char *)parser->begin, *end;
	const char *raw = aparser->address.raw;
	const char **address_p = NULL;

	i_zero(&aparser->address);
	aparser->address.raw = raw;

	if (aparser->totally_broken ||
	    HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN))
		return -1;
	if (*begin != '<' &&
	    HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL)) {
		/* brackets missing; totally broken */
		return -1;
	}
	i_assert(aparser->parse);
	if (aparser->parsed_any) {
		if (endp_r != NULL)
			*endp_r = (const char *)aparser->address_end;
		return 0;
	}

	if (HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW))
		address_p = &aparser->address.raw;
	if (smtp_address_parse_any(begin, address_p, &end) < 0) {
		/* totally broken */
		return -1;
	}
	if (endp_r != NULL)
		*endp_r = end;
	return 0;
}

int smtp_address_parse_path_full(pool_t pool, const char *path,
				 enum smtp_address_parse_flags flags,
				 struct smtp_address **address_r,
				 const char **error_r, const char **endp_r)
{
	struct smtp_address_parser aparser;
	int ret;

	if (address_r != NULL)
		*address_r = NULL;
	if (error_r != NULL)
		*error_r = NULL;
	if (endp_r != NULL)
		*endp_r = NULL;

	if (path == NULL || *path == '\0') {
		if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) == 0 ||
		    (flags & SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL) == 0) {
			if (error_r != NULL)
				*error_r = "Path is empty string";
			return -1;
		}
		if (address_r != NULL)
			*address_r = p_new(pool, struct smtp_address, 1);
		if (endp_r != NULL)
			*endp_r = path;
		return 0;
	}

	i_zero(&aparser);
	smtp_parser_init(&aparser.parser, pool_datastack_create(), path);
	aparser.address_end = (endp_r != NULL ? NULL : aparser.parser.end);
	aparser.parse = (address_r != NULL);

	if ((ret = smtp_parse_path(&aparser, flags)) <= 0) {
		if (error_r != NULL) {
			*error_r = (ret < 0 ? aparser.parser.error :
				"Missing '<' at beginning of path");
		}
		ret = -1;
	} else if (endp_r != NULL) {
		if (aparser.parser.cur == aparser.parser.end ||
		    *aparser.parser.cur == ' ' ||
		    HAS_NO_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN)) {
			*endp_r = (const char *)aparser.parser.cur;
			ret = 0;
		} else {
			if (error_r != NULL)
				*error_r = "Invalid character in path";
			ret = -1;
		}
	} else if (aparser.parser.cur == aparser.parser.end) {
		ret = 0;
	} else {
		if (error_r != NULL)
			*error_r = "Invalid character in path";
		ret = -1;
	}

	if (ret < 0) {
		/* normal parsing failed */
		if (smtp_address_parse_path_broken(&aparser, flags,
						   endp_r) < 0) {
			/* failed to parse it as a broken address as well */
			return -1;
		}
		/* broken address */
	} else if (HAS_ALL_BITS(flags, SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW) &&
		   aparser.address.localpart != NULL) {
		if (aparser.path &&
		    ((const unsigned char *)(path + 1) < aparser.parser.cur)) {
			aparser.address.raw = t_strdup_until(
				path + 1, aparser.parser.cur - 1);
		} else {
			aparser.address.raw = t_strdup_until(
				path, aparser.parser.cur);
		}
	}

	if (address_r != NULL)
		*address_r = smtp_address_clone(pool, &aparser.address);
	return ret;
}

int smtp_address_parse_path(pool_t pool, const char *path,
			    enum smtp_address_parse_flags flags,
			    struct smtp_address **address_r,
			    const char **error_r)
{
	return smtp_address_parse_path_full(pool, path, flags,
					    address_r, error_r, NULL);
}

int smtp_address_parse_username(pool_t pool, const char *username,
				struct smtp_address **address_r,
				const char **error_r)
{
	enum smtp_address_parse_flags flags =
		SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
		SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART;

	struct smtp_address_parser aparser;
	int ret;

	if (address_r != NULL)
		*address_r = NULL;
	if (error_r != NULL)
		*error_r = NULL;

	if ((username == NULL || *username == '\0')) {
		if (error_r != NULL)
			*error_r = "Username is empty string";
		return -1;
	}

	i_zero(&aparser);
	smtp_parser_init(&aparser.parser, pool_datastack_create(), username);
	aparser.address_end = aparser.parser.end;
	aparser.parse = (address_r != NULL);

	if ((ret = smtp_parse_mailbox(&aparser, flags)) <= 0) {
		if (error_r != NULL) {
			*error_r = (ret < 0 ? aparser.parser.error :
				"Invalid character in user name");
		}
		return -1;
	}
	if (aparser.parser.cur != aparser.parser.end) {
		if (error_r != NULL)
			*error_r = "Invalid character in user name";
		return -1;
	}

	if (address_r != NULL)
		*address_r = smtp_address_clone(pool, &aparser.address);
	return 0;
}

void smtp_address_detail_parse(pool_t pool, const char *delimiters,
			       struct smtp_address *address,
			       const char **username_r, char *delim_r,
			       const char **detail_r)
{
	const char *localpart;
	const char *user, *p;
	size_t idx;

	i_assert(!smtp_address_isnull(address));

	localpart = address->localpart;
	user = localpart;
	*detail_r = "";
	*delim_r = '\0';

	/* first character that matches the recipient_delimiter */
	idx = strcspn(localpart, delimiters);
	p = (localpart[idx] != '\0' ? &localpart[idx] : NULL);

	if (p != NULL) {
		*delim_r = *p;
		/* user+detail */
		user = p_strdup_until(pool, localpart, p);
		*detail_r = p+1;
	}

	if (address->domain == NULL || *address->domain == '\0')
		*username_r = user;
	else if (strchr(user, '@') == NULL) {
		/* username is just glued to the domain... no SMTP escaping */
		*username_r = p_strconcat(pool,	user, "@", address->domain,
					  NULL);
	} else {
		struct smtp_address uaddr;

		/* username contains '@'; apply escaping */
		smtp_address_init(&uaddr, user, address->domain);
		if (pool->datastack_pool)
			*username_r = smtp_address_encode(&uaddr);
		else {
			*username_r =
				p_strdup(pool, smtp_address_encode(&uaddr));
		}
	}
}

void smtp_address_detail_parse_temp(const char *delimiters,
				    struct smtp_address *address,
				    const char **username_r, char *delim_r,
				    const char **detail_r)
{
	smtp_address_detail_parse(pool_datastack_create(), delimiters,
				  address, username_r, delim_r, detail_r);
}

int smtp_address_parse_any(const char *in, const char **address_r,
			   const char **endp_r)
{
	const unsigned char *p, *pend, *poffset;
	bool path = FALSE;
	bool quoted = FALSE;

	if (endp_r != NULL)
		*endp_r = in;

	poffset = p = (const unsigned char *)in;
	pend = p + strlen(in);
	if (*p  == '<') {
		path = TRUE;
		p++;
		poffset = p;
	}
	if (*p == '"') {
		quoted = TRUE;
		p++;
	}

	while (p < pend) {
		if (quoted && *p == '\\') {
			p++;
			if (p == pend || *p < 0x20)
				return -1;
			p++;
			if (p == pend)
				break;
		}
		switch (*p) {
		case '"':
			quoted = FALSE;
			break;
		case ' ':
			if (!quoted) {
				if (path)
					return -1;
				if (address_r != NULL)
					*address_r = t_strdup_until(poffset, p);
				if (endp_r != NULL)
					*endp_r = (const char *)p;
				return 0;
			}
			break;
		case '>':
			if (!quoted) {
				if (address_r != NULL)
					*address_r = t_strdup_until(poffset, p);
				if (endp_r != NULL)
					*endp_r = (const char *)(p + 1);
				return 0;
			}
			break;
		default:
			if (*p < 0x20)
				return -1;
			break;
		}
		p++;
	}
	if (quoted || path)
		return -1;
	if (address_r != NULL)
		*address_r = t_strdup_until(poffset, p);
	if (endp_r != NULL)
		*endp_r = (const char *)p;
	return 0;
}

/*
 * SMTP address construction
 */

void smtp_address_write(string_t *out, const struct smtp_address *address)
			ATTR_NULL(2)
{
	bool quoted = FALSE;
	const unsigned char *p, *pend, *pblock;
	size_t begin;

	if (smtp_address_isnull(address))
		return;
	begin = str_len(out);

	/* encode localpart */
	p = (const unsigned char *)address->localpart;
	pend = p + strlen(address->localpart);
	pblock = p;
	while (p < pend) {
		while (p < pend && smtp_char_is_atext(*p))
			p++;

		if (!quoted && p < pend && (*p != '.' || p == pblock)) {
			quoted = TRUE;
			str_insert(out, begin, "\"");
		}

		str_append_data(out, pblock, p - pblock);
		if (p >= pend)
			break;

		if (!quoted) {
			str_append_c(out, '.');
		} else {
			i_assert(smtp_char_is_qpair(*p));
			if (!smtp_char_is_qtext(*p))
				str_append_c(out, '\\');
			str_append_c(out, *p);
		}

		p++;
		pblock = p;
	}

	if (p == pblock && !quoted) {
		quoted = TRUE;
		str_insert(out, begin, "\"");
	}

	if (quoted)
		str_append_c(out, '\"');

	if (address->domain == NULL || *address->domain == '\0')
		return;

	str_append_c(out, '@');
	str_append(out, address->domain);
}

void smtp_address_write_path(string_t *out, const struct smtp_address *address)
{
	str_append_c(out, '<');
	smtp_address_write(out, address);
	str_append_c(out, '>');
}

const char *smtp_address_encode(const struct smtp_address *address)
{
	string_t *str = t_str_new(256);
	smtp_address_write(str, address);
	return str_c(str);
}

const char *smtp_address_encode_path(const struct smtp_address *address)
{
	string_t *str = t_str_new(256);
	smtp_address_write_path(str, address);
	return str_c(str);
}

const char *smtp_address_encode_raw(const struct smtp_address *address)
{
	if (address != NULL && address->raw != NULL && *address->raw != '\0')
		return address->raw;

	return smtp_address_encode(address);
}

const char *smtp_address_encode_raw_path(const struct smtp_address *address)
{
	if (address != NULL && address->raw != NULL && *address->raw != '\0')
		return t_strconcat("<", address->raw, ">", NULL);

	return smtp_address_encode_path(address);
}

/*
 * SMTP address manipulation
 */

void smtp_address_init(struct smtp_address *address,
		       const char *localpart, const char *domain)
{
	i_zero(address);
	if (localpart == NULL || *localpart == '\0')
		return;

	address->localpart = localpart;
	if (domain != NULL && *domain != '\0')
		address->domain = domain;
}

int smtp_address_init_from_msg(struct smtp_address *address,
			       const struct message_address *msg_addr)
{
	const unsigned char *p;

	i_zero(address);
	if (msg_addr->mailbox == NULL || *msg_addr->mailbox == '\0')
		return 0;

	/* The message_address_parse() function allows UTF-8 codepoints in
	   the localpart. For SMTP addresses that is not an option, so we
	   need to check this upon conversion. */
	for (p = (const unsigned char *)msg_addr->mailbox; *p != '\0'; p++) {
		if (!smtp_char_is_qpair(*p))
			return -1;
	}

	address->localpart = msg_addr->mailbox;
	if (msg_addr->domain != NULL && *msg_addr->domain != '\0')
		address->domain = msg_addr->domain;
	return 0;
}

struct smtp_address *
smtp_address_clone(pool_t pool, const struct smtp_address *src)
{
	struct smtp_address *new;
	size_t size, lpsize = 0, dsize = 0, rsize = 0;
	char *data, *localpart = NULL, *domain = NULL, *raw = NULL;

	if (src == NULL)
		return NULL;

	/* @UNSAFE */

	size = sizeof(struct smtp_address);
	if (!smtp_address_isnull(src)) {
		lpsize = strlen(src->localpart) + 1;
		size = MALLOC_ADD(size, lpsize);
	}
	if (src->domain != NULL && *src->domain != '\0') {
		dsize = strlen(src->domain) + 1;
		size = MALLOC_ADD(size, dsize);
	}
	if (src->raw != NULL && *src->raw != '\0') {
		rsize = strlen(src->raw) + 1;
		size = MALLOC_ADD(size, rsize);
	}

	data = p_malloc(pool, size);
	new = (struct smtp_address *)data;
	if (lpsize > 0) {
		localpart = PTR_OFFSET(data, sizeof(*new));
		memcpy(localpart, src->localpart, lpsize);
	}
	if (dsize > 0) {
		domain = PTR_OFFSET(data, sizeof(*new) + lpsize);
		memcpy(domain, src->domain, dsize);
	}
	if (rsize > 0) {
		raw = PTR_OFFSET(data, sizeof(*new) + lpsize + dsize);
		memcpy(raw, src->raw, rsize);
	}
	new->localpart = localpart;
	new->domain = domain;
	new->raw = raw;

	return new;
}

struct smtp_address *
smtp_address_create(pool_t pool, const char *localpart, const char *domain)
{
	struct smtp_address addr;

	smtp_address_init(&addr, localpart, domain);
	return smtp_address_clone(pool, &addr);
}


int smtp_address_create_from_msg(pool_t pool,
				 const struct message_address *msg_addr,
				 struct smtp_address **address_r)
{
	struct smtp_address addr;

	if (smtp_address_init_from_msg(&addr, msg_addr) < 0) {
		*address_r = NULL;
		return -1;
	}
	*address_r = smtp_address_clone(pool, &addr);
	return 0;
}

struct smtp_address *smtp_address_clone_temp(const struct smtp_address *src)
{
	struct smtp_address *new;

	if (src == NULL)
		return NULL;

	new = t_new(struct smtp_address, 1);
	new->localpart = t_strdup_empty(src->localpart);
	new->domain = t_strdup_empty(src->domain);
	new->raw = t_strdup_empty(src->raw);
	return new;
}

struct smtp_address *
smtp_address_create_temp(const char *localpart, const char *domain)
{
	struct smtp_address addr;

	smtp_address_init(&addr, localpart, domain);
	return smtp_address_clone_temp(&addr);
}

int  smtp_address_create_from_msg_temp(const struct message_address *msg_addr,
				       struct smtp_address **address_r)
{
	struct smtp_address addr;

	if (smtp_address_init_from_msg(&addr, msg_addr) < 0) {
		*address_r = NULL;
		return -1;
	}
	*address_r = smtp_address_clone_temp(&addr);
	return 0;
}

struct smtp_address *
smtp_address_add_detail(pool_t pool, const struct smtp_address *address,
			const char *detail, char delim_c)
{
	struct smtp_address *new_addr;
	const char delim[] = {delim_c, '\0'};

	i_assert(!smtp_address_isnull(address));

	new_addr = p_new(pool, struct smtp_address, 1);
	new_addr->localpart = p_strconcat(pool,	address->localpart, delim,
					  detail, NULL);
	new_addr->domain = p_strdup_empty(pool, address->domain);

	return new_addr;
}

struct smtp_address *
smtp_address_add_detail_temp(const struct smtp_address *address,
			     const char *detail, char delim_c)
{
	struct smtp_address *new_addr;
	const char delim[] = {delim_c, '\0'};

	i_assert(!smtp_address_isnull(address));

	new_addr = t_new(struct smtp_address, 1);
	new_addr->localpart = t_strconcat(address->localpart, delim, detail,
					  NULL);
	new_addr->domain = t_strdup_empty(address->domain);

	return new_addr;
}

int smtp_address_cmp(const struct smtp_address *address1,
		     const struct smtp_address *address2)
{
	bool null1, null2;
	int ret;

	null1 = smtp_address_isnull(address1);
	null2 = smtp_address_isnull(address2);
	if (null1)
		return (null2 ? 0 : -1);
	else if (null2)
		return 1;
	if ((ret = null_strcasecmp(address1->domain, address2->domain)) != 0)
		return ret;
	return null_strcmp(address1->localpart, address2->localpart);
}

int smtp_address_cmp_icase(const struct smtp_address *address1,
			  const struct smtp_address *address2)
{
	bool null1, null2;
	int ret;

	null1 = smtp_address_isnull(address1);
	null2 = smtp_address_isnull(address2);
	if (null1)
		return (null2 ? 0 : -1);
	else if (null2)
		return 1;
	if ((ret = null_strcasecmp(address1->domain, address2->domain)) != 0)
		return ret;
	return null_strcasecmp(address1->localpart, address2->localpart);
}
