/*
 * 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: mta.h,v 1.90 2007/06/02 04:11:39 ca Exp $
 */

#ifndef SM_MTA_H
#define SM_MTA_H 1
#include "sm/generic.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/limits.h"
#include "sm/smreplycodes.h"
#include "sm/smmts.h"

/*
**  Session/transaction id: 64bit "id" counter, 8bit process"id"
**  Do NOT change this: some functions extract the process "id" out of it.
**  C/S (1) + 64bit (16) + 8bit (2) + '\0' (1)
*/
#define SMTP_STID_SIZE	((size_t) 20)
#define SMTPS_STID_FORMAT "S%016qX%02X"
#define SMTPS_STID_PID_OFF 17		/* offset of process "id" in id */
#define SMTPS_STID_IDC_OFF 1		/* offset of id counter in id */
#define SMTPS_MAX_SMTPS_ID	255	/* max process"id" */

#define sm_idcmp(se_id1, se_id2) \
	strncmp((se_id1) + SMTPS_STID_IDC_OFF, (se_id2) + SMTPS_STID_IDC_OFF, \
				SMTPS_STID_PID_OFF - SMTPS_STID_IDC_OFF)

/*
**  'C' SMTPC idx (8bit), running counter (32bit), thread idx (32bit)
**  Do NOT change this: some functions extract the thread idx out of it.
*/
#define SMTPC_STID_FORMAT "C%02X%08X%08X"
#define SMTPC_STID_CID_OFF 1		/* offset of SMTPC idx in id */
#define SMTPC_STID_THR_OFF 11		/* offset of thread idx in id */

/* extract thread index from DA transaction id, ok iff return value is 1 */
#define SMTPC_GETTHRIDX(da_ta_id, thr_id)	\
	sm_io_sscanf((da_ta_id) + SMTPC_STID_THR_OFF, "%x", &thr_id)

/* extract SMTPC id from DA transaction id, ok iff return value is 1 */
#define SMTPC_GETCID(da_ta_id, sc_id)	\
	sm_io_sscanf((da_ta_id) + SMTPC_STID_CID_OFF, "%2x", &sc_id)

typedef char		 sessta_id_T[SMTP_STID_SIZE], *sessta_id_P;
typedef uint64_t	 id_count_T;

#define SESSTA_COPY(dst_id, src_id)	sm_memcpy((dst_id), (src_id), SMTP_STID_SIZE)
#define SESSTA_EQ(id1, id2)	sm_memeq((id1), (id2), SMTP_STID_SIZE)
#define SESSTA_CLR(id)		sm_memzero((id), SMTP_STID_SIZE)

sm_ret_T sm_id2idc(sessta_id_P _se_id, id_count_T *_pidc);
sm_ret_T sm_getsmtpsid(sessta_id_P _se_id);

/*
**  Recipient id: transaction id + recipient index (32bit)
**  27 =  "S" + 8bit + 64bit + 32bit (base 64) + '\0'
**  Question: is the hyphen necessary? Note: if it is removed then the code
**  to extract the recipient index must be changed.
*/
#define SMTP_RCPTID_LEN		27
#define SMTP_RCPTID_SIZE	((size_t) SMTP_RCPTID_LEN)
#define SMTP_RCPTID_FORMAT	"%19s-%06Y"
#define SMTP_RCPTIDX_MAX	UINT32_MAX	/* maximum recipient index */

typedef char	rcpt_id_T[SMTP_RCPTID_SIZE], *rcpt_id_P;

#define RCPT_IDX_FORMAT	"%Y"

#define RCPT_ID_COPY(dst_id, src_id)	sm_memcpy((dst_id), (src_id), SMTP_RCPTID_SIZE)
#define RCPT_ID_EQ(id1, id2)	sm_memeq((id1), (id2), SMTP_RCPTID_SIZE)

/* extract transaction id from recipient id */
#define RCPTID2SESSTAID(rcpt_id, ta_id)	do {		\
		SESSTA_COPY((ta_id), (rcpt_id));	\
		(ta_id)[SMTP_STID_SIZE - 1] = '\0';	\
	} while (0)

/* extract recipient index from recipient id, ok iff return value is 1 */
#define RCPTID2IDX(rcpt_id, rcpt_idx)	\
	sm_io_sscanf((rcpt_id) + SMTP_STID_SIZE, RCPT_IDX_FORMAT, &(rcpt_idx))

typedef int smtp_status_T; /* (extended) smtp status code, includes errors */

/* Largest size of transaction/session/recipient id */
#define SMTP_ID_SIZE	SMTP_RCPTID_SIZE
typedef char		smtp_id_T[SMTP_ID_SIZE], *smtp_id_P;

/* should this be 200 instead to match the SMTP reply codes? */
#define SMTP_OK	SM_SUCCESS

#define smtp_reply_type(r)	((int) ((r) / 100))

/* SMTP reply types */
#define SMTP_RTYPE_OK	2
#define SMTP_RTYPE_CONT	3
#define SMTP_RTYPE_TEMP	4
#define SMTP_RTYPE_PERM	5

#define IS_SMTP_REPLY(r) ((r) >= 200 && (r) <= 599)
#define smtp_is_reply_ok(r) (smtp_reply_type(r) == SMTP_RTYPE_OK)
#define smtp_is_reply_temp(r) (smtp_reply_type(r) == SMTP_RTYPE_TEMP)
#define smtp_is_reply_fail(r) (smtp_reply_type(r) == SMTP_RTYPE_PERM)
#define SMTP_IS_REPLY_ERROR(r) (smtp_is_reply_temp(r) || smtp_is_reply_fail(r))

/*
**  Check whether str has an SMTP reply code at offset.
**  Doesn't check whether str ends with ' ', '\0', or '-'.
*/

#define IS_SMTP_REPLY_STR(str, off)					\
	(sm_str_getlen(str) >= ((off) + 3)				\
	 && ISDIGIT(sm_str_rd_elem((str), (off)))			\
	 && ISDIGIT(sm_str_rd_elem((str), (off) + 1))			\
	 && ISDIGIT(sm_str_rd_elem((str), (off) + 2))			\
	 && (sm_str_rd_elem((str), (off)) == '2'			\
	     || sm_str_rd_elem((str), (off)) == '3'			\
	     || sm_str_rd_elem((str), (off)) == '4'			\
	     || sm_str_rd_elem((str), (off)) == '5')			\
	)

/* Use a real function to check for correct range? */
#define char2digit(c)	((c) - '0')

/* convert SMTP reply code to integer value; must be checked before! */
#define SMTP_REPLY_STR2VAL(str, off)	\
	((sm_str_rd_elem((str), (off)) == '2') ? SMTP_OK	\
	 : (char2digit(sm_str_rd_elem((str), (off))) * 100	\
	   + char2digit(sm_str_rd_elem((str), (off) + 1)) * 10	\
	   + char2digit(sm_str_rd_elem((str), (off) + 2))))

/*
**  Check whether a string contains a "matching" reply for a return code,
**  i.e., it must be a SMTP reply string and the reply types must match,
**  and they either must be identical or both must not be 421.
*/

#define SMTP_REPLY_MATCHES_RCODE(str, rcode, off, temp)			\
	(IS_SMTP_REPLY_STR((str), (off)) &&				\
	 smtp_reply_type((temp) = SMTP_REPLY_STR2VAL((str), (off))) ==	\
	 smtp_reply_type(rcode) &&					\
	 ((temp) == (rcode) || ((temp) != SMTP_R_SSD && (rcode) != SMTP_R_SSD))\
	)

/* some additional pseudo SMTP reply codes */
#define SMTPC_TEMP_ST       460 /* generic temporary error in DA */
#define SMTPC_IO_ST         461 /* generic I/O error in DA */
#define SMTPC_DA_ST         462 /* DA went away */
#define SMTPC_PROT          463 /* protocol error */
#define SMTPC_SE_OPEN_ST    465 /* session open failed */
#define SMTPC_SE_OP_TMO     466 /* session open timeout */
#define SMTPC_SE_OP_REFUSED 467 /* session open connection refused */
#define SMTPC_SE_OP_UNREACH 468 /* session open host/net unreachable */
#define SMTP_AR_TMO         480 /* TIMEOUT in AR */
#define SMTP_AR_TEMP        481 /* TEMP in AR */
#define SMTP_AR_ALIAS       482 /* alias expansion in AR failed */
#define SMTP_MAP_TEMP       483 /* temp map lookup failure */
#define SMTP_CDB_TEMP       485 /* tempfail in CDB */
#define SMTP_TMO_SCHED      490 /* timeout in sched */
#define SMTP_TMO_DA         491 /* timeout waiting for DA */
#define SMTP_TMO_DDSN       492 /* delayed DSN stored in DEFEDB first time */
#define SMTP_QUARANTINE     498 /* quarantine (not yet implemented) */

/*
**  SMTPC: no reply yet from server (useful for pipelining, but also as
**  initial status).
**  SMTPS: do not send reply in main loop
*/

#define SMTP_NO_REPLY	499

#define SMTP_AR_NOTF	580	/* NOTFOUND in AR */
#define SMTP_AR_PERM	581	/* PERM in AR */
#define SMTP_AR_LOOP	582	/* CNAME loop in AR */
#define SMTP_AR_AL_REC	583	/* alias nesting too deep in AR */
#define SMTP_AR_MXEMPTY	584	/* MX list empty in AR ("loops back") */
#define SMTPC_SE_TTMYSELF	585	/* talking to myself */

/*
**  Permanent error in CDB: entry not found,
**  HACK: treat it as OK, this is "required" by the way updating recipients
**  and transactions are handled: 2xy indicates "entry has been taken care of".
**  Note: this will cause problem if success DSNs are implemented, because
**  this wasn't a successful delivery.
*/

#define SMTP_CDB_PERM	285

/* TA/RCPT is "done" (has been taken care of) */
#define SMTP_DONE(status) ((status) == SM_SUCCESS || smtp_is_reply_ok(status))

/*
**  XXX intermediate solution: host identification
**	This should be a generic description of the connecting host,
**	similar to SOCKADDR in V8.
*/

#if 0
struct sm_host_S
{
	struct in_addr	sss_client;
};
#endif

/* XXX HACK */
typedef struct in_addr	sm_host_T, *sm_host_P;

/* maximum length of domain part of an address, RFC ? */
/* rename these to have an SM_ prefix?? */
#define MAXDOMAINLEN	256
#define MAXADDRLEN	(256+64)
#define SM_MAXHDRLEN	1000

/* XXX HACK use LMTP if these are encountered */
/* used in various places, e.g., smar/t-mt-0.sh */
#if 0
#define LMTP_IPV4_ADDR	(htonl(0x7FFFFFFF))
#else
#define LMTP_IPV4_ADDR	(htonl(0x7F0000FF))
#endif
#define LMTP_IPV4_S2	"[127.0.0.255]"
#define LMTP_MT		"lmtp:"

/* default log level */
#define SM_LOG_LEVEL	99999

/* some configuration defaults */
#define SM_MAX_RCPTS	65535
#define SM_MAX_TAS	65535

/* maximum length of section + id */
#define MAXSECTIONNAME	256

#define SM_TA_FL_VERP	0x0001

#define SM_GOT_CRLF	2	/* got CRLF, i.e., at begin of a line */

#define SM_EOT "\r\n.\r\n"
#define SM_DECL_EOT	const char eot[] = SM_EOT
#define SM_EOT_LEN	(sizeof(SM_EOT) - 1)

#define SM_EOH "\r\n\r\n"
#define SM_DECL_EOH	const char eoh[] = SM_EOH
#define SM_EOH_LEN	(sizeof(SM_EOH) - 1)

#define SM_EOL "\r\n"
#define SM_DECL_EOL	const char eol[] = SM_EOL
#define SM_EOL_LEN	(sizeof(SM_EOL) - 1)

#endif /* SM_MTA_H */


syntax highlighted by Code2HTML, v. 0.9.1