bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch/* From RFC 5321:
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Reverse-path = Path / "<>"
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Forward-path = Path
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Path = "<" [ A-d-l ":" ] Mailbox ">"
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch A-d-l = At-domain *( "," At-domain )
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch ; Note that this form, the so-called "source
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch ; route", MUST BE accepted, SHOULD NOT be
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch ; generated, and SHOULD be ignored.
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch At-domain = "@" Domain
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Domain = sub-domain *("." sub-domain)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch sub-domain = Let-dig [Ldh-str]
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Let-dig = ALPHA / DIGIT
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch address-literal = "[" ( IPv4-address-literal /
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch IPv6-address-literal /
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch General-address-literal ) "]"
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch ; See Section 4.1.3
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Mailbox = Local-part "@" ( Domain / address-literal )
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Local-part = Dot-string / Quoted-string
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch ; MAY be case-sensitive
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Dot-string = Atom *("." Atom)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch Atom = 1*atext
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch * SMTP address parsing
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_parser_parse_dot_string(struct smtp_parser *parser,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch const char **value_r)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* Dot-string = Atom *("." Atom)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* NOTE: this deviates from Dot-String syntax to allow some Japanese
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch mail addresses with dots at non-standard places to be accepted. */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch (!smtp_char_is_atext(*parser->cur) && *parser->cur != '.'))
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch (smtp_char_is_atext(*parser->cur) || *parser->cur == '.'))
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch *value_r = t_strndup(pbegin, parser->cur - pbegin);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_parse_localpart(struct smtp_parser *parser,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parser_parse_quoted_string(parser, localpart_r)) != 0)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch return smtp_parser_parse_dot_string(parser, localpart_r);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_parse_mailbox(struct smtp_address_parser *aparser,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch struct smtp_parser *parser = &aparser->parser;
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* Mailbox = Local-part "@" ( Domain / address-literal )
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch value = (aparser->parse ? &aparser->address.localpart : NULL);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parse_localpart(parser, value)) <= 0)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((parser->cur >= parser->end || *parser->cur != '@') &&
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch (flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART) == 0) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch parser->error = "Invalid character in localpart";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur >= parser->end || *parser->cur != '@')
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch value = (aparser->parse ? &aparser->address.domain : NULL);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parser_parse_domain(parser, value)) == 0 &&
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch (ret=smtp_parser_parse_address_literal(parser,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_parse_source_route(struct smtp_parser *parser)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* Source-route = [ A-d-l ":" ]
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch A-d-l = At-domain *( "," At-domain )
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch At-domain = "@" Domain
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* "@" Domain */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur >= parser->end || *parser->cur != '@')
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (smtp_parser_parse_domain(parser, NULL) <= 0) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch "Missing domain after '@' in source route";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* *( "," At-domain ) */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur >= parser->end || *parser->cur != ',')
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* "@" Domain */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur >= parser->end || *parser->cur != '@') {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch parser->error = "Missing '@' after ',' in source route";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur >= parser->end || *parser->cur != ':') {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch parser->error = "Missing ':' at end of source route";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_parse_path(struct smtp_address_parser *aparser,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch struct smtp_parser *parser = &aparser->parser;
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* Path = "<" [ A-d-l ":" ] Mailbox ">"
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur < parser->end && *parser->cur == '<') {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch } else if ((flags & SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL) == 0) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* [ A-d-l ":" ] */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((sret=smtp_parse_source_route(parser)) < 0)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* Mailbox */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parse_mailbox(aparser, flags)) < 0)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur < parser->end && *parser->cur == '>') {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch "Path only consists of source route";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch parser->error = "Invalid character in localpart";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur >= parser->end || *parser->cur != '>') {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch } else if (parser->cur < parser->end && *parser->cur == '>') {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch parser->error = "Unmatched '>' at end of path";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_parse_username(struct smtp_address_parser *aparser)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch struct smtp_parser *parser = &aparser->parser;
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* Best-effort extraction of SMTP address from a user name.
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch value = (aparser->parse ? &aparser->address.localpart : NULL);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* if the local part is a quoted string, parse it as any other
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch SMTP address */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parse_localpart(parser, value)) <= 0)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* use the right-most '@' as separator */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* check whether the resulting localpart could be encoded as
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch quoted string */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch "Invalid character in user name";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch p_strdup_until(parser->pool, parser->cur, dp);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur < parser->end && *parser->cur != '@') {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch parser->error = "Invalid character in user name";
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (parser->cur >= parser->end || *parser->cur != '@')
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch value = (aparser->parse ? &aparser->address.domain : NULL);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parser_parse_domain(parser, value)) == 0 &&
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch (ret=smtp_parser_parse_address_literal(parser,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch const char *mailbox, enum smtp_address_parse_flags flags,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch struct smtp_address **address_r, const char **error_r)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) == 0) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch *address_r = p_new(pool, struct smtp_address, 1);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch smtp_parser_init(&aparser.parser, pool_datastack_create(), mailbox);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parse_mailbox(&aparser, flags)) <= 0) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch "Invalid character in localpart");
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (aparser.parser.cur != aparser.parser.end) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch *address_r = smtp_address_clone(pool, &aparser.address);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschint smtp_address_parse_path_full(pool_t pool, const char *path,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch struct smtp_address **address_r, const char **error_r,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch const char **endp_r)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((flags & SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY) == 0 ||
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch (flags & SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL) == 0) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch *address_r = p_new(pool, struct smtp_address, 1);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch smtp_parser_init(&aparser.parser, pool_datastack_create(), path);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parse_path(&aparser, flags)) <= 0) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch "Missing '<' at beginning of path");
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch else if (aparser.parser.cur != aparser.parser.end) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch *address_r = smtp_address_clone(pool, &aparser.address);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschint smtp_address_parse_path(pool_t pool, const char *path,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch struct smtp_address **address_r, const char **error_r)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch return smtp_address_parse_path_full(pool, path, flags,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschint smtp_address_parse_username(pool_t pool, const char *username,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch struct smtp_address **address_r, const char **error_r)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((username == NULL || *username == '\0')) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch smtp_parser_init(&aparser.parser, pool_datastack_create(), username);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if ((ret=smtp_parse_username(&aparser)) <= 0) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch "Invalid character in user name");
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (aparser.parser.cur != aparser.parser.end) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch *address_r = smtp_address_clone(pool, &aparser.address);
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Boschvoid smtp_address_detail_parse(pool_t pool, const char *delimiters,
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch struct smtp_address *address, const char **username_r,
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch /* first character that matches the recipient_delimiter */
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch p = (localpart[idx] != '\0' ? &localpart[idx] : NULL);
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch /* user+detail */
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch /* username is just glued to the domain... no SMTP escaping */
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch /* username contains '@'; apply escaping */
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch smtp_address_init(&uaddr, user, address->domain);
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch *username_r = p_strdup(pool, smtp_address_encode(&uaddr));
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Boschvoid smtp_address_detail_parse_temp(const char *delimiters,
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch struct smtp_address *address, const char **username_r,
61ae3dd363d32db75d2c3effaab4ba093ce0f245Stephan Bosch smtp_address_detail_parse(pool_datastack_create(), delimiters,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch * SMTP address construction
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch const struct smtp_address *address) ATTR_NULL(2)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* encode localpart */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch p = (const unsigned char *)address->localpart;
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch while (p < pend) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch if (!quoted && p < pend && (*p != '.' || p == pblock)) {
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_address_encode(const struct smtp_address *address)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_address_encode_path(const struct smtp_address *address)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch * SMTP address manipulation
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschvoid smtp_address_init(struct smtp_address *address,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch address->domain = (localpart == NULL ? NULL : domain);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschvoid smtp_address_init_from_msg(struct smtp_address *address,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch address->domain = (msg_addr->mailbox == NULL ? NULL : msg_addr->domain);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_address_clone(pool_t pool, const struct smtp_address *src)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch /* @UNSAFE */
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch domain = PTR_OFFSET(data, sizeof(*new) + lpsize);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_address_clone_temp(const struct smtp_address *src)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_address_create_temp(const char *localpart, const char *domain)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_address_create_from_msg_temp(const struct message_address *msg_addr)
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_address_add_detail(pool_t pool, const struct smtp_address *address,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch new_addr = p_new(pool, struct smtp_address, 1);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Bosch new_addr->domain = p_strdup(pool, address->domain);
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschsmtp_address_add_detail_temp(const struct smtp_address *address,
03f08e4abef2afc782069bdfef305f84e98f0922Stephan Boschint smtp_address_cmp(const struct smtp_address *address1,
aba05d93fe13aebb76d3ab8ca235a40f75b4afc4Stephan Bosch if ((ret=null_strcasecmp(address1->domain, address2->domain)) != 0)