summaryrefslogtreecommitdiffstats
path: root/src/lib-smtp/smtp-address.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-smtp/smtp-address.c
parentInitial commit. (diff)
downloaddovecot-upstream.tar.xz
dovecot-upstream.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/lib-smtp/smtp-address.c958
1 files changed, 958 insertions, 0 deletions
diff --git a/src/lib-smtp/smtp-address.c b/src/lib-smtp/smtp-address.c
new file mode 100644
index 0000000..eac25e1
--- /dev/null
+++ b/src/lib-smtp/smtp-address.c
@@ -0,0 +1,958 @@
+/* Copyright (c) 2013-2018 Dovecot authors, see the included 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;
+ 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);
+}