/*
 * 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: ibdb.h,v 1.70 2007/06/18 04:42:30 ca Exp $
 */

#ifndef SM_IDB_H
#define SM_IDB_H 1

#include "sm/generic.h"
#include "sm/str.h"
#include "sm/io.h"
#include "sm/mta.h"
#include "sm/cstr.h"
#include "sm/cdb.h"
#include "sm/queue.h"
#include "sm/pthread.h"
#include "sm/fs.h"
#include "sm/hdrmod.h"

/* Abstraction layer for ibdb (Incoming DB, disk backup) */

/*
**  Questions:
**	- Do we want to use sm_io_*() or do we access the FS directly?
**	- Size of io buffer: same as size of file? That might be too big.
**	- How to avoid I/O? Write a list of RCBs and write their buffers
**	  directly (using vwrite())? That's ugly because it's too much
**	  buffering: first in RCB, then in sm_io buffer, then OS...
*/

#if 0

ibdb_open(IN name, IN mode, IN size, OUT status, OUT ibdb-handle):
open an incoming envelope database.

ibdb_close(IN ibdb-handle, OUT status):
close an incoming envelope database.

ibdb_ta_add(IN ibdb-handle, IN sender-env-info, IN ta-id, OUT status):
write envelope data

ibdb_ta_rm(IN ibdb-handle, IN ta-id, OUT status):
remove an envelope from the EDB

ibdb_ta_discard(IN ibdb-handle, IN ta-id, OUT status):
discard envelope

ibdb_rcpt_add(IN ibdb-handle, IN ta-id, IN rcpt-env-info, OUT status):
add a new recipient (RCPT command).

ibdb_rcpt_rm(IN ibdb-handle, IN ta-id, IN rcpt-id, IN rpct-status,
OUT status):
remove a recipient (mail has been taken care of).

ibdb_commit(IN ibdb-handle, OUT status):
commit envelope information to stable storage.

recovery functions:

ibdb_readprep(IN ibdb-handle, OUT cursor, OUT status):
prepare to read through EDB.

ibdb_getnext(IN cursor, OUT status, OUT record):
Get next entry from EDB.

ibdb_readclose(IN ibdb-handle, IN cursor, OUT status):
stop reading through EDB.


Data:

Sender (transaction):

transaction-id	& transaction identifier
start-time	& start time of transaction
sender-spec	& address incl. ESMTP extensions
cdb-id		& CDB identifier (obtained from cdb?)
n-rcpts		& reference count for cdb-id

ESMTP sender extensions (substructure of the structure above)

size		& size of mail content (SIZE=)
bodytype	& type of body
envid		& envelope id
ret		& DSN return information (FULL, HDRS)
auth		& AUTH parameter
by		& Deliverby specification

Per recipient (transaction):

transaction-id	& transaction identifier
rcpt-spec	& address incl.	ESMTP extensions
		& and maybe a unique id (per session/transaction?)

ESMTP Recipient extensions (substructure of the structure above):

notify	& DSN parameters (SUCCESS, FAILURE, WARNING)
orcpt	& original recipient

#endif /* 0 */

/* IBDB record types */
/* #define RT_CONT		0x80000000	* continues in next record */

/*
**  should these types contain a type specifier?
**  For example:
**	0x04	uint32_t
**	0x02	str
**	0x01	uchar *
**  That might help to write generic(?) en/decode functions.
**  However, how can we (un)pack into(from) an unknown struct?
**  Could we do some hacking with offsetof()? I doubt it.
*/

#define RT_IBDB_TA		0x0100	/* transaction record (sender) */
#define RT_IBDB_TAID		0x0101	/* transaction ID */
#define RT_IBDB_CDBID		0x0102	/* CDB ID */
#define RT_IBDB_NRCPTS	0x0103	/* nrcpts */
#define RT_IBDB_MAIL		0x0104	/* sender address */
#define RT_IBDB_TA_ST		0x0110	/* transaction status */
#define IBDB_TA_NEW		0x0000	/* new transaction */
#define IBDB_TA_DONE		0x0010	/* transaction done */
				/* more detailled status: */
#define IBDB_TA_DELIV		0x0011	/*	delivered */
#define IBDB_TA_TEMP		0x0012	/*	deferred (in defedb) */
#define IBDB_TA_PERM		0x0013	/*	deferred (in defedb) */
#define IBDB_TA_CANCEL		0x0020	/*	cancelled */

#define idbd_ta_done(status)	(((status) & 0x00f0) == IBDB_TA_DONE)
#define idbd_ta_cancelled(status)	(((status) & 0x00f0) == IBDB_TA_CANCEL)

#define RT_IBDB_RCPT		0x0200	/* recipient record */
#define RT_IBDB_RCPT_ST		0x0210	/* recipient status */
#define RT_IBDB_RCPT_IDX	0x0212	/* recipient index */
#define RT_IBDB_RCPT_PA		0x0213	/* recipient printable address */

#define IBDB_RCPT_NEW		0x0000	/* new transaction */
#define IBDB_RCPT_DONE		0x0010	/* transaction done */
				/* more detailled status: */
#define IBDB_RCPT_DELIV		0x0011	/*	delivered */
#define IBDB_RCPT_TEMP		0x0012	/*	deferred (in defedb) */
#define IBDB_RCPT_PERM		0x0013	/*	deferred (in defedb) */

#define idbd_rcpt_done(status)	(((status) & 0x00f0) == IBDB_RCPT_DONE)

#define RT_IBDB_HDRMOD		0x0300	/* header modification record */
#define RT_IBDB_HM_TAID		0x0301	/* transaction ID */
#define RT_IBDB_HM_TYPE		0x0303	/* type */
#define RT_IBDB_HM_POS		0x0305	/* position */
#define RT_IBDB_HM_HDR		0x0306	/* header */

#define RT_IBDB_VERS_R		0x0400	/* version record */
#define RT_IBDB_VERS_N		0x0403	/* version number */


/* current version (16 bits major/8 bits minor/8 bits patchlevel) */
#define IBDB_VERSION	0x00010100
#define IBDB_VRS_MAJ(v)	((v) & 0x7FFF0000)
#define IBDB_VRS_MIN(v)	((v) & 0x0000FF00)
#define IBDB_VRS_PL(v)	((v) & 0x000000FF)
#define IBDB_VRS_COMPAT(new, old) (((new) == (old)) ||		\
		(IBDB_VRS_MAJ(new) == IBDB_VRS_MAJ(old) &&	\
		 IBDB_VRS_MIN(new) >= IBDB_VRS_MIN(old)))

#define IBDB_REC_SIZE	512	/* size of one record */
#define IBDB_MINBUF_SIZE 512	/* minimum size of file buffer */
#define IBDB_BUF_SIZE 8192	/* size of file buffer if none if provided */

/* for testing; currently not used anywhere */
#define IBDB_MAXSIZE	(10 * 1024)

/* flags for IBDB status updates */
#define IBDB_FL_NONE	0x00U
#define IBDB_FL_NOROLL	0x01U	/* no roll-over */
#define IBDB_FL_CLEAN	0x02U	/* call cleanup */

#define IBDB_IS_FLAG(flags, fl)	(((flags) & (fl)) != 0)
#define ibdb_is_noroll(fl)	(((fl) & IBDB_FL_NOROLL) != 0)

/* flags for IBDB open() */
#define IBDB_OFL_WRITE	0x01U	/* open write ("normal") IBDB */
#define IBDB_OFL_RCVR	0x02U	/* open recovery IBDB */
#define IBDB_OFL_CLEAN	0x04U	/* open cleanup IBDB */

#define IBDB_IS_OPEN_FLAG(flags, fl)	(((flags) & (fl)) != 0)

typedef struct ibdb_ctx_S	ibdb_ctx_T, *ibdb_ctx_P;
typedef struct ibdbr_ctx_S	ibdbr_ctx_T, *ibdbr_ctx_P;

/* Define sender and recipient records */

/*
**  XXX: Really use sessta_id_P (that is, just a pointer) here?
**  It may be pointed elsewhere (to another string).
**  It's ok for writing, but reading?
**
**  How about omitting the struct completely and just call the functions
**  with these parameters?  The data is not used for anything else
**  but storing data in a file. For reading back this is actually not
**  the best since it mostly contains pointers instead of storage space.
*/

struct ibdb_ta_S
{
	sessta_id_P	ibt_ta_id;
	sm_str_P	ibt_mail_pa;	/* printable version of MAIL address */
	cdb_id_P	ibt_cdb_id;
	uint		ibt_nrcpts;	/* number of valid recipients */

	/* The next entries are only used by recovery program */
	uint		ibt_rcpts_left; /* rcpts still to deliver */
	uint		ibt_rcpts_temp;	/* rcpts temp failed */
	uint		ibt_rcpts_perm;	/* rcpts perm failed */

	sm_hdrmodhd_P	ibt_hdrmodhd;

	/*
	**  May need other data, e.g.,
	**  time: can be retrieved from timestamp on file (ctime?)
	*/
};

struct ibdb_rcpt_S
{
	sessta_id_P	ibr_ta_id;
	sm_str_P	ibr_pa;	/* printable version of RCPT address */
	rcpt_idx_T	ibr_idx;	/* RCPT index */
};

typedef struct ibdb_ta_S	ibdb_ta_T, *ibdb_ta_P;
typedef struct ibdb_rcpt_S	ibdb_rcpt_T, *ibdb_rcpt_P;

struct ibdb_hdrmod_S
{
	sessta_id_P	 ibh_ta_id;
	sm_hm_type_T	 ibh_type;	/* can this be mapped to an int? */
	uint		 ibh_pos;
	sm_cstr_P	 ibh_hdr;
};
typedef struct ibdb_hdrmod_S	ibdb_hdrmod_T, *ibdb_hdrmod_P;

/*
**  List of requests for group updates after getting delivery status
**  which has been written to DEFEDB.
*/

/* Use enum? */
#define IBDB_REQ_TA	0x01
#define IBDB_REQ_RCPT	0x02
#define IBDB_REQ_HDRMOD	0x04

typedef struct ibdb_req_S	ibdb_req_T, *ibdb_req_P;

/* this is a "merge" (union) of all IBDB structures */
struct ibdb_req_S
{
	uint			 ibdb_req_type;
	int			 ibdb_req_status;
	sessta_id_T		 ibdb_req_ss_ta_id; /* SMTPS transaction id */
	sm_str_P		 ibdb_req_addr_pa; /* MAIL/RCPT address */
	cdb_id_P		 ibdb_req_cdb_id;
	uint			 ibdb_req_nrcpts;
	rcpt_idx_T		 ibdb_req_rcpt_idx;	/* RCPT index */

	/* note: this could be a union with cdb_id_P as it's the same type */
	sm_cstr_P		 ibdb_req_hdr;
	SIMPLEQ_ENTRY(ibdb_req_S)	 ibdb_req_link;
};
#define ibh_req_hdr	ibdb_req_hdr
#define ibh_req_pos	ibdb_req_nrcpts
#define ibh_req_type	ibdb_req_status

typedef SIMPLEQ_HEAD(, ibdb_req_S)	ibdb_req_hd_T, *ibdb_req_hd_P;

#define IBDBREQL_INIT(ibdb_req_hd)	SIMPLEQ_INIT(ibdb_req_hd)
#define IBDBREQL_FIRST(ibdb_req_hd)	SIMPLEQ_FIRST(ibdb_req_hd)
#define IBDBREQL_END(ibdb_req_hd)	SIMPLEQ_END(ibdb_req_hd)
#define IBDBREQL_EMPTY(ibdb_req_hd)	SIMPLEQ_EMPTY(ibdb_req_hd)
#define IBDBREQL_NEXT(ibdb_req)		SIMPLEQ_NEXT(ibdb_req, ibdb_req_link)
#define IBDBREQL_PRE(ibdb_req_hd, ibdb_req) SIMPLEQ_INSERT_HEAD(ibdb_req_hd, ibdb_req, ibdb_req_link)
#define IBDBREQL_APP(ibdb_req_hd, ibdb_req) SIMPLEQ_INSERT_TAIL(ibdb_req_hd, ibdb_req, ibdb_req_link)
#define IBDBREQL_REMOVE(ibdb_req_hd) SIMPLEQ_REMOVE_HEAD(ibdb_req_hd, ibdb_req_link)

#define SM_IS_IBDBREQL(ibdb_req_hd)	SM_ASSERT((ibdb_req_hd) != NULL)

/* Function prototypes */
sm_ret_T ibdb_open(const char *_base_dir, const char *_name, int _mode, uint32_t _seq, size_t _size, uint _flags, fs_ctx_P _fs_ctx, ibdb_ctx_P *_ibdb_handle);
sm_ret_T ibdb_ta_status(ibdb_ctx_P _ibdbc, ibdb_ta_P _ta, int _status, uint _flags, rcpt_idx_T _rcpt_idx_high, thr_lock_T _locktype);
sm_ret_T ibdb_rcpt_status(ibdb_ctx_P _ibdbc, ibdb_rcpt_P _rcpt, int _status, uint _flags, thr_lock_T _locktype);
sm_ret_T ibdb_commit(ibdb_ctx_P _ibdbc);
sm_ret_T ibdb_close(ibdb_ctx_P _ibdbc);

sm_ret_T ibdb_rcpt_app(ibdb_ctx_P _ibdb_ctx, ibdb_rcpt_P _ibdb_rcpt, ibdb_req_hd_P _ibdb_req_hd, int _status);
sm_ret_T ibdb_ta_app(ibdb_ctx_P _ibdb_ctx, ibdb_ta_P _ibdb_ta, ibdb_req_hd_P _ibdb_req_hd, int _status);
sm_ret_T ibdb_req_cancel(ibdb_ctx_P _ibdb_ctx, ibdb_req_hd_P _ibdb_req_hd);
sm_ret_T ibdb_wr_status(ibdb_ctx_P _ibdb_ctx, ibdb_req_hd_P _ibdb_req_hd);

sm_ret_T ibdb_hdrmod_wr(ibdb_ctx_P _ibdb_ctx, ibdb_hdrmod_P _ibdb_hdrmod, uint _flags, thr_lock_T _locktype);
sm_ret_T ibdb_hdrmod_app(ibdb_ctx_P _ibdb_ctx, ibdb_hdrmod_P _ibdb_hdrmod, ibdb_req_hd_P _ibdb_req_hd);
sm_ret_T ibdb_hdrmodl_wr(ibdb_ctx_P _ibdb_ctx, sm_hdrmodhd_P _sm_hdrmodhd, sessta_id_T _ta_id, uint _flags, thr_lock_T _locktype);

sm_ret_T ibdbr_get(ibdbr_ctx_P _ibdbr_ctx, ibdb_rcpt_P _rcpt, ibdb_ta_P _ta
		, ibdb_hdrmod_P ibdb_hdrmod, int *_status);
sm_ret_T ibdbr_open(const char *_base_dir, const char *_name, int _mode, uint32_t _seq, size_t _size, uint _flags, ibdbr_ctx_P *_ibdbr_handle);
sm_ret_T ibdbr_close(ibdbr_ctx_P _ibdbr_ctx);

sm_ret_T ibdbr_rcpt_free(ibdb_rcpt_P _ibdb_rcpt);
sm_ret_T ibdbr_rcpt_new(ibdb_rcpt_P *_pibdb_rcpt);
sm_ret_T ibdbr_ta_free(ibdb_ta_P _ibdb_ta);
sm_ret_T ibdbr_ta_new(ibdb_ta_P *_pibdb_ta);
sm_ret_T ibdbr_hdrmod_free(ibdb_hdrmod_P _ibdb_hdrmod);
sm_ret_T ibdbr_hdrmod_new(ibdb_hdrmod_P *_pibdb_hdrmod);

sm_ret_T ibdbr_unlink(ibdbr_ctx_P _ibdbr_ctx, uint32_t _first, uint32_t _last);

sm_ret_T ibdbf_get_seq(const char *_base_dir, char *_name, uint _flags, uint32_t *_first, uint32_t *_last);

sm_ret_T ibdb_clean(ibdb_ctx_P _ibdb_ctx, thr_lock_T _locktype);
sm_ret_T ibdbc_show_seq(ibdb_ctx_P _ibdb_ctx, thr_lock_T _locktype, sm_file_T *_fp);

sm_ret_T ibdb_fs_getfree(ibdb_ctx_P _ibdb_ctx, ulong *_pkbfree);

sm_ret_T ibdb_stats(ibdb_ctx_P _ibdb_ctx, sm_file_T *_fp);

/* IBDB recovery */
#define SM_IBDB_NO_RCVR		1	/* no recovery necessary: no IBDB */
#define SM_IBDB_RCVR_EXISTS	2

/* more parameters? */
sm_ret_T ibdbrcvr_open(void);
sm_ret_T ibdbrcvr_close(void);

#endif /* SM_IDB_H */


syntax highlighted by Code2HTML, v. 0.9.1