/*
 * Copyright (c) 2002-2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 *
 * $Id: rfc2821.h,v 1.37 2007/11/14 06:03:08 ca Exp $
 */

#ifndef SM_RFC2821_H
#define SM_RFC2821_H 1

#include "sm/generic.h"

#include "sm/str.h"
#include "sm/rdstr.h"
#include "sm/queue.h"

/*

RFC 2821

4.1.2 Command Argument Syntax

      Path = "<" [ A-d-l ":" ] Mailbox ">"
      A-d-l = At-domain *( "," A-d-l )
      At-domain = "@" Domain
      Domain = (sub-domain 1*("." sub-domain)) / address-literal
      sub-domain = Let-dig [Ldh-str]
      address-literal = "[" IPv4-address-literal /
                            IPv6-address-literal /
                            General-address-literal "]"
      Mailbox = Local-part "@" Domain
      Local-part = Dot-string / Quoted-string
      Dot-string = Atom *("." Atom)
      Atom = 1*atext
      Quoted-string = DQUOTE *qcontent DQUOTE
      atext = ALPHA / DIGIT / ; Any character except controls,
              "!" / "#" /     ;  SP, and specials.
              "$" / "%" /     ;  Used for atoms
              "&" / "'" / "*" / "+" / "-" / "/" /
              "=" / "?" / "^" / "_" / "`" / "{" / "|" / "}" / "~"
2821 doesn't allow \c in Atom, 821 does
      Let-dig = ALPHA / DIGIT
      Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig

      IPv4-address-literal = Snum 3("." Snum)
      IPv6-address-literal = "IPv6:" IPv6-addr
      General-address-literal = Standardized-tag ":" 1*dcontent
      Standardized-tag = Ldh-str

      Snum = 1*3DIGIT  ; representing a decimal integer
            ; value in the range 0 through 255

      IPv6-addr = IPv6-full / IPv6-comp / IPv6v4-full / IPv6v4-comp
      IPv6-hex  = 1*4HEXDIG
      IPv6-full = IPv6-hex 7(":" IPv6-hex)
      IPv6-comp = [IPv6-hex *5(":" IPv6-hex)] "::" [IPv6-hex *5(":"
                 IPv6-hex)]
            ; The "::" represents at least 2 16-bit groups of zeros
            ; No more than 6 groups in addition to the "::" may be
            ; present
      IPv6v4-full = IPv6-hex 5(":" IPv6-hex) ":" IPv4-address-literal
      IPv6v4-comp = [IPv6-hex *3(":" IPv6-hex)] "::"
                   [IPv6-hex *3(":" IPv6-hex) ":"] IPv4-address-literal
            ; The "::" represents at least 2 16-bit groups of zeros
            ; No more than 4 groups in addition to the "::" and
            ; IPv4-address-literal may be present

*/

/* XXX define these as T2822 (or vice versa) for interchangeability? */
/* type of element, values below T2821_MIN stand for themselves */
#define T2821_MINTOK	256
#define T2821_ATOM	256
#define T2821_LITERAL	257	/* address literal */
#define T2821_QUOTED	258	/* quoted string */
#define T2821_COMMENT	259	/* comment */
#define T2821_ENDTOK	260	/* artificial end token */

#define T2821_COMMA	','
#define T2821_AT	'@'
#define T2821_DOT	'.'
#define T2821_LEFT	'<'
#define T2821_RIGHT	'>'
#define T2821_SEMI	';'
#define T2821_COLON	':'

/* RFC 2821 requirements/elements */
#define R2821_ANGLE	0x0001u
#define R2821_AT	0x0002u
#define R2821_HOST	0x0004u
#define R2821_FQDN	0x0008u
#define R2821_LOCAL	0x0010u
#define R2821_DOMAIN	0x0020u	/* valid domain syntax */
#define R2821_NO_TRLDOT	0x0040u	/* no trailing dot */
#define R2821_STRICT	0x007fu	/* all of the above... */
#define R2821_CORRECT	0x0077u	/* all of the above but FQDN (see grammar) */
#define R2821_EMPTY	0x0080u	/* <> is ok */
#define R2821__		0x0100u	/* _ is ok in domain */
#define R2821_ROUTE	0x1000u
#define R2821_FREE	0x8000u

#define HAS_R2821(flag, which)	(((flag) & (which)) != 0)
#define SET_R2821(flag, which)	(flag) |= (which)

#define T2821_FL_NOANGLE	0x0001u

/* error codes from parser */
/* XXX sort these? */

/* missing '<' */
#define R2821_ERR_LEFT		(R2821_BASE + 1)

/* missing '>' */
#define R2821_ERR_RIGHT		(R2821_BASE + 2)

/* route address (but not allowed) */
#define R2821_ERR_ROUTE		(R2821_BASE + 3)

/* domain part does not start with an atom */
#define R2821_ERR_HOST		(R2821_BASE + 4)

/* domain part is not a FQDN */
#define R2821_ERR_FQDN		(R2821_BASE + 5)

/* missing local part */
#define R2821_ERR_LOCAL		(R2821_BASE + 6)

/* missing '@' */
#define R2821_ERR_AT		(R2821_BASE + 7)

/* <>, but not allowed */
#define R2821_ERR_EMPTY		(R2821_BASE + 8)

/* completely empty address */
#define R2821_ERR_MBOX		(R2821_BASE + 9)

/* missing ':' (unused) */
#define R2821_ERR_COLON		(R2821_BASE + 10)

/* "dotstr" syntax is wrong: expected atom after '.' */
#define R2821_ERR_DOTSTR	(R2821_BASE + 11)

/* domain part is empty */
#define R2821_ERR_DOMAIN	(R2821_BASE + 12)

/* expecting ':' (unused) */
#define R2821_ERR_DOT		(R2821_BASE + 13)

/* domain has trailing '.' */
#define R2821_ERR_TRAILDOT	(R2821_BASE + 14)

/* '@' in route address missing */
#define R2821_ERR_ATDOMAIN	(R2821_BASE + 15)

/* ',' in route address missing */
#define R2821_ERR_COMMA		(R2821_BASE + 16)

/* domain part has wrong syntax */
#define R2821_ERR_DOMSYNTAX	(R2821_BASE + 17)

/* tokens after end */
#define R2821_ERR_MORE		(R2821_BASE + 18)

/* domain doesn't end in delimiter */
#define R2821_ERR_DEL_MISS	(R2821_BASE + 19)

#define R2821_ERR_TOOLONG	(R2821_BASE + 20)
#define R2821_ERR_TRAILBACKSL	(R2821_BASE + 21)

/* too many closing brackets */
#define R2821_ERR_CLS_BRACKET	(R2821_BASE + 22)

/* too many closing parentheses */
#define R2821_ERR_CLS_PARENTH	(R2821_BASE + 23)

/* parenthesis in address not allowed */
#define R2821_ERR_PARENTHESIS	(R2821_BASE + 24)

/* character must be quoted but isn't */
#define R2821_ERR_MUSTQUOTE	(R2821_BASE + 25)

typedef struct sm_t2821_S	sm_t2821_T, *sm_t2821_P;

/* one token in an address */
struct sm_t2821_S
{
	int				sm_t2821_type;
	sm_str_P			sm_t2821_val;
	CIRCLEQ_ENTRY(sm_t2821_S)	sm_t2821_l;
};

/* static in libmta/rfc2821.c
extern sm_t2821_T sm_t2821_end;
*/

typedef struct sm_a2821_S	sm_a2821_T, *sm_a2821_P;

/* an address (list head) */
struct sm_a2821_S
{
	CIRCLEQ_HEAD(, sm_t2821_S)	sm_a2821_hd;
	sm_rpool_P			sm_a2821_rpool;
};

sm_ret_T t2821_scan(sm_rdstr_P _str, sm_a2821_T *_ta, int _off);
sm_ret_T t2821_domain(sm_a2821_T *_addr, sm_t2821_P *_ptok, uint _flags);
sm_ret_T t2821_parse(sm_a2821_T *_addrin, uint _flags);
sm_ret_T t2821_str(sm_a2821_T *_addr, sm_str_P _str, uint _flags);
void	t2821_free(sm_rpool_P _rpool, sm_t2821_P _tok);
void	a2821_free(sm_a2821_T *_addr);
sm_ret_T t2821_cp_token(sm_rpool_P _rpool, sm_t2821_P _src_tok,sm_t2821_P *_pdst_tok);
sm_ret_T t2821_extract_domain(sm_rpool_P _rpool, sm_a2821_T *_addr, sm_a2821_T **_pdomain);
sm_ret_T t2821_extract_local(sm_rpool_P _rpool, sm_a2821_T *_addr, int delim_type, sm_a2821_T **_plocal);

#if 0
sm_ret_T t2821_locdet(sm_a2821_T *_addr, const uchar *_delim, bool _observequotes, sm_str_P _local, sm_str_P _detail);
#endif
sm_ret_T t2821_parts(sm_a2821_T *_addr, const uchar *_delim, bool _observequotes, sm_str_P _local, sm_str_P _detail, sm_str_P _domain, uchar *_pdelim);

/* operations on addresses */

/* this shouldn't be used, see below for A2821_INIT_RP() instead */
#define A2821_INIT(addr)	CIRCLEQ_INIT(&((addr)->sm_a2821_hd))
#define A2821_FIRST(addr)	CIRCLEQ_FIRST(&((addr)->sm_a2821_hd))
#define A2821_LAST(addr)	CIRCLEQ_LAST(&((addr)->sm_a2821_hd))
#define A2821_END(addr)		CIRCLEQ_END(&((addr)->sm_a2821_hd))
#define A2821_INSERT_TAIL(addr, tok) CIRCLEQ_INSERT_TAIL(&((addr)->sm_a2821_hd), tok, sm_t2821_l)
#define A2821_INSERT_HEAD(addr, tok) CIRCLEQ_INSERT_HEAD(&((addr)->sm_a2821_hd), tok, sm_t2821_l)
#define A2821_REMOVE(addr, tok)	CIRCLEQ_REMOVE(&((addr)->sm_a2821_hd), tok, sm_t2821_l)
#define A2821_REMOVE_FREE(addr, tok) do {						\
		CIRCLEQ_REMOVE(&((addr)->sm_a2821_hd), tok, sm_t2821_l); \
		t2821_free((addr)->sm_a2821_rpool, tok);	\
	} while (0)
#define T2821_NEXT(tok)		(SM_ASSERT((tok) != NULL && (tok)->sm_t2821_type != T2821_ENDTOK), CIRCLEQ_NEXT(tok, sm_t2821_l))

#define A2821_INIT_RP(addr, rpool) do {		\
		A2821_INIT(addr);			\
		(addr)->sm_a2821_rpool = (rpool);	\
	} while (0)
/*
**  If we stay with CIRCLEQ, then deletion can be simplified (really?):
**  while (CIRCLEQ_FIRST(&head) != CIRCLEQ_END(&head))
**       CIRCLEQ_REMOVE(&head, CIRCLEQ_FIRST(&head), entries);
*/

#endif /* SM_RFC2821_H */


syntax highlighted by Code2HTML, v. 0.9.1