diff options
Diffstat (limited to '')
-rw-r--r-- | src/lib-smtp/smtp-address.c | 958 |
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); +} |