/*++
/* NAME
/*	milter_macros
/* SUMMARY
/*	manipulate MILTER_MACROS structures
/* SYNOPSIS
/*	#include <milter.h>
/*
/*	MILTER_MACROS *milter_macros_create(conn_macros, helo_macros,
/*					mail_macros, rcpt_macros,
/*					data_macros, eoh_macros,
/*					eod_macros, unk_macros)
/*	const char *conn_macros;
/*	const char *helo_macros;
/*	const char *mail_macros;
/*	const char *rcpt_macrps;
/*	const char *data_macros;
/*	const char *eoh_macros;
/*	const char *eod_macros;
/*	const char *unk_macros;
/*
/*	MILTER_MACROS *milter_macros_alloc(init_mode)
/*	int	init_mode;
/*
/*	void	milter_macros_free(mp)
/*	MILTER_MACROS *mp;
/*
/*	int     milter_macros_print(print_fn, stream, flags, ptr)
/*	ATTR_PRINT_COMMON_FN print_fn;
/*	VSTREAM *stream;
/*	int	flags;
/*	void	*ptr;
/*
/*	int	milter_macros_scan(scan_fn, fp, flags, ptr)
/*	ATTR_SCAN_COMMON_FN scan_fn;
/*	VSTREAM	*fp;
/*	int	flags;
/*	void	*ptr;
/* DESCRIPTION
/*	Sendmail mail filter (Milter) applications receive sets of
/*	macro name=value pairs with each SMTP or content event.
/*	In Postfix, these macro names are stored in MILTER_MACROS
/*	structures, as one list for each event type. By default,
/*	the same structure is shared by all Milter applications;
/*	it is initialized with information from main.cf. With
/*	Sendmail 8.14 a Milter can override one or more lists of
/*	macro names. Postfix implements this by giving the Milter
/*	its own MILTER_MACROS structure and by storing the per-Milter
/*	information there.
/*
/*	This module maintains per-event macro name lists as
/*	mystrdup()'ed values. The user is explicitly allowed to
/*	update these values directly, as long as the result is
/*	compatible with mystrdup().
/*
/*	milter_macros_create() creates a MILTER_MACROS structure
/*	and initializes it with copies of its string arguments.
/*	Null pointers are not valid as input.
/*
/*	milter_macros_alloc() creates am empty MILTER_MACROS structure
/*	that is initialized according to its init_mode argument.
/* .IP MILTER_MACROS_ALLOC_ZERO
/*	Initialize all structure members as null pointers. This
/*	mode must be used with milter_macros_scan(), because that
/*	function blindly overwrites all structure members.  No other
/*	function except milter_macros_free() allows structure members
/*	with null pointer values.
/* .IP MILTER_MACROS_ALLOC_EMPTY
/*	Initialize all structure members with mystrdup(""). This
/*	is not as expensive as it appears to be.
/* .PP
/*	milter_macros_free() destroys a MILTER_MACROS structure and
/*	frees any strings referenced by it.
/*
/*	milter_macros_print() writes the contents of a MILTER_MACROS
/*	structure to the named stream using the specified attribute
/*	print routine.  milter_macros_print() is meant to be passed
/*	as a call-back to attr_print*(), thusly:
/*
/*	SEND_ATTR_FUNC(milter_macros_print, (const void *) macros),
/*
/*	milter_macros_scan() reads a MILTER_MACROS structure from
/*	the named stream using the specified attribute scan routine.
/*	No attempt is made to free the memory of existing structure
/*	members.  milter_macros_scan() is meant to be passed as a
/*	call-back to attr_scan*(), thusly:
/*
/*	RECV_ATTR_FUNC(milter_macros_scan, (void *) macros),
/* DIAGNOSTICS
/*	Fatal: out of memory.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this
/*	software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>

/* Utility library. */

#include <msg.h>
#include <attr.h>
#include <mymalloc.h>
#include <vstring.h>

/* Global library. */

#include <mail_proto.h>
#include <milter.h>

 /*
  * Ad-hoc protocol to send/receive milter macro name lists.
  */
#define MAIL_ATTR_MILT_MAC_CONN	"conn_macros"
#define MAIL_ATTR_MILT_MAC_HELO	"helo_macros"
#define MAIL_ATTR_MILT_MAC_MAIL	"mail_macros"
#define MAIL_ATTR_MILT_MAC_RCPT	"rcpt_macros"
#define MAIL_ATTR_MILT_MAC_DATA	"data_macros"
#define MAIL_ATTR_MILT_MAC_EOH	"eoh_macros"
#define MAIL_ATTR_MILT_MAC_EOD	"eod_macros"
#define MAIL_ATTR_MILT_MAC_UNK	"unk_macros"

/* milter_macros_print - write macros structure to stream */

int     milter_macros_print(ATTR_PRINT_COMMON_FN print_fn, VSTREAM *fp,
			            int flags, const void *ptr)
{
    MILTER_MACROS *mp = (MILTER_MACROS *) ptr;
    int     ret;

    /*
     * The attribute order does not matter, except that it must be the same
     * as in the milter_macros_scan() function.
     */
    ret = print_fn(fp, flags | ATTR_FLAG_MORE,
		   SEND_ATTR_STR(MAIL_ATTR_MILT_MAC_CONN, mp->conn_macros),
		   SEND_ATTR_STR(MAIL_ATTR_MILT_MAC_HELO, mp->helo_macros),
		   SEND_ATTR_STR(MAIL_ATTR_MILT_MAC_MAIL, mp->mail_macros),
		   SEND_ATTR_STR(MAIL_ATTR_MILT_MAC_RCPT, mp->rcpt_macros),
		   SEND_ATTR_STR(MAIL_ATTR_MILT_MAC_DATA, mp->data_macros),
		   SEND_ATTR_STR(MAIL_ATTR_MILT_MAC_EOH, mp->eoh_macros),
		   SEND_ATTR_STR(MAIL_ATTR_MILT_MAC_EOD, mp->eod_macros),
		   SEND_ATTR_STR(MAIL_ATTR_MILT_MAC_UNK, mp->unk_macros),
		   ATTR_TYPE_END);
    return (ret);
}

/* milter_macros_scan - receive macros structure from stream */

int     milter_macros_scan(ATTR_SCAN_COMMON_FN scan_fn, VSTREAM *fp,
			           int flags, void *ptr)
{
    MILTER_MACROS *mp = (MILTER_MACROS *) ptr;
    int     ret;

    /*
     * We could simplify this by moving memory allocation into attr_scan*().
     */
    VSTRING *conn_macros = vstring_alloc(10);
    VSTRING *helo_macros = vstring_alloc(10);
    VSTRING *mail_macros = vstring_alloc(10);
    VSTRING *rcpt_macros = vstring_alloc(10);
    VSTRING *data_macros = vstring_alloc(10);
    VSTRING *eoh_macros = vstring_alloc(10);
    VSTRING *eod_macros = vstring_alloc(10);
    VSTRING *unk_macros = vstring_alloc(10);

    /*
     * The attribute order does not matter, except that it must be the same
     * as in the milter_macros_print() function.
     */
    ret = scan_fn(fp, flags | ATTR_FLAG_MORE,
		  RECV_ATTR_STR(MAIL_ATTR_MILT_MAC_CONN, conn_macros),
		  RECV_ATTR_STR(MAIL_ATTR_MILT_MAC_HELO, helo_macros),
		  RECV_ATTR_STR(MAIL_ATTR_MILT_MAC_MAIL, mail_macros),
		  RECV_ATTR_STR(MAIL_ATTR_MILT_MAC_RCPT, rcpt_macros),
		  RECV_ATTR_STR(MAIL_ATTR_MILT_MAC_DATA, data_macros),
		  RECV_ATTR_STR(MAIL_ATTR_MILT_MAC_EOH, eoh_macros),
		  RECV_ATTR_STR(MAIL_ATTR_MILT_MAC_EOD, eod_macros),
		  RECV_ATTR_STR(MAIL_ATTR_MILT_MAC_UNK, unk_macros),
		  ATTR_TYPE_END);

    /*
     * Don't optimize for error.
     */
    mp->conn_macros = vstring_export(conn_macros);
    mp->helo_macros = vstring_export(helo_macros);
    mp->mail_macros = vstring_export(mail_macros);
    mp->rcpt_macros = vstring_export(rcpt_macros);
    mp->data_macros = vstring_export(data_macros);
    mp->eoh_macros = vstring_export(eoh_macros);
    mp->eod_macros = vstring_export(eod_macros);
    mp->unk_macros = vstring_export(unk_macros);

    return (ret == 8 ? 1 : -1);
}

/* milter_macros_create - create and initialize macros structure */

MILTER_MACROS *milter_macros_create(const char *conn_macros,
				            const char *helo_macros,
				            const char *mail_macros,
				            const char *rcpt_macros,
				            const char *data_macros,
				            const char *eoh_macros,
				            const char *eod_macros,
				            const char *unk_macros)
{
    MILTER_MACROS *mp;

    mp = (MILTER_MACROS *) mymalloc(sizeof(*mp));
    mp->conn_macros = mystrdup(conn_macros);
    mp->helo_macros = mystrdup(helo_macros);
    mp->mail_macros = mystrdup(mail_macros);
    mp->rcpt_macros = mystrdup(rcpt_macros);
    mp->data_macros = mystrdup(data_macros);
    mp->eoh_macros = mystrdup(eoh_macros);
    mp->eod_macros = mystrdup(eod_macros);
    mp->unk_macros = mystrdup(unk_macros);

    return (mp);
}

/* milter_macros_alloc - allocate macros structure with simple initialization */

MILTER_MACROS *milter_macros_alloc(int mode)
{
    MILTER_MACROS *mp;

    /*
     * This macro was originally in milter.h, but no-one else needed it.
     */
#define milter_macros_init(mp, expr) do { \
	MILTER_MACROS *__mp = (mp); \
	char *__expr = (expr); \
	__mp->conn_macros = __expr; \
	__mp->helo_macros = __expr; \
	__mp->mail_macros = __expr; \
	__mp->rcpt_macros = __expr; \
	__mp->data_macros = __expr; \
	__mp->eoh_macros = __expr; \
	__mp->eod_macros = __expr; \
	__mp->unk_macros = __expr; \
    } while (0)

    mp = (MILTER_MACROS *) mymalloc(sizeof(*mp));
    switch (mode) {
    case MILTER_MACROS_ALLOC_ZERO:
	milter_macros_init(mp, 0);
	break;
    case MILTER_MACROS_ALLOC_EMPTY:
	milter_macros_init(mp, mystrdup(""));
	break;
    default:
	msg_panic("milter_macros_alloc: unknown mode %d", mode);
    }
    return (mp);
}

/* milter_macros_free - destroy memory for MILTER_MACROS structure */

void    milter_macros_free(MILTER_MACROS *mp)
{

    /*
     * This macro was originally in milter.h, but no-one else needed it.
     */
#define milter_macros_wipe(mp) do { \
	MILTER_MACROS *__mp = mp; \
	if (__mp->conn_macros) \
	    myfree(__mp->conn_macros); \
	if (__mp->helo_macros) \
	    myfree(__mp->helo_macros); \
	if (__mp->mail_macros) \
	    myfree(__mp->mail_macros); \
	if (__mp->rcpt_macros) \
	    myfree(__mp->rcpt_macros); \
	if (__mp->data_macros) \
	    myfree(__mp->data_macros); \
	if (__mp->eoh_macros) \
	    myfree(__mp->eoh_macros); \
	if (__mp->eod_macros) \
	    myfree(__mp->eod_macros); \
	if (__mp->unk_macros) \
	    myfree(__mp->unk_macros); \
    } while (0)

    milter_macros_wipe(mp);
    myfree((void *) mp);
}
