DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T t

⟦1de19b420⟧ TextFile

    Length: 41322 (0xa16a)
    Types: TextFile
    Names: »tables.c«

Derivation

└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦dc59850a2⟧ »EurOpenD22/pp5.0/pp-5.tar.Z« 
        └─⟦e5a54fb17⟧ 
            └─⟦this⟧ »pp-5.0/Src/qmgr/tables.c« 

TextFile

/* tables.c: table handling routines */

# ifndef lint
static char Rcsid[] = "@(#)$Header: /cs/research/pp/hubris/pp-beta/Src/qmgr/RCS/tables.c,v 5.0 90/09/20 16:21:49 pp Exp Locker: pp $";
# endif

/*
 * $Header: /cs/research/pp/hubris/pp-beta/Src/qmgr/RCS/tables.c,v 5.0 90/09/20 16:21:49 pp Exp Locker: pp $
 *
 * $Log:	tables.c,v $
 * Revision 5.0  90/09/20  16:21:49  pp
 * rcsforce : 5.0 public release
 * 
 */



#include "util.h"
#include "types.h"
#include "qmgr.h"

/* Variables */

MsgStruct	*msg_hash[HASHSIZE];
Chanlist	**chan_list;
int		nchanlist = 0;

/* Procedures & functions */

extern	char	*strdup ();
int		hash ();
void		addtochan ();
void		create_chan ();
Mtalist		*findmtalist ();
MsgStruct	*find_msg ();
Mlist		*findmtamsg ();
Chanlist	*delete_chan = NULLCHANLIST;
Chanlist	*loader_chan = NULLCHANLIST;
Chanlist	*trash_chan = NULLCHANLIST;
Chanlist	*timeout_chan = NULLCHANLIST;
Chanlist	*findchanlist ();
static void	mcontrol ();
static int	chaninsert ();
static int	insertinmta ();
static int 	addmtaid ();
static int	filtermatch ();
static int	compmpduid ();
static int	nullstrcmp ();
static int	bind_result ();
static int 	bind_error ();
static void	setchannels ();

void		insertinchan ();
void		insertindelchan ();
void		insertindrchan ();
void		zapmtamsg ();

/* External functions */
extern struct type_Qmgr_PrioritisedChannel *lpchan ();
extern struct type_Qmgr_ChannelInfo *lchani ();
extern struct type_Qmgr_PrioritisedMta *lpmta ();
extern struct type_Qmgr_MtaInfo	*lmta ();
extern struct type_Qmgr_ProcStatus *lstatus ();
extern struct type_Qmgr_MsgStruct *lmessage ();
extern struct type_Qmgr_MsgStruct *l_cm_message ();
extern struct type_UNIV_UTCTime *ltime ();

extern MsgStruct *newmsgstruct ();
extern Filter *newfilter ();

#define STR2QB(s)	str2qb ((s), strlen((s)), 1)

static int check_credentails (cb, name, passwd)
Connblk *cb;
char	*name, *passwd;
{
	extern char *crypt ();
	char 	result[BUFSIZ];
	static Table	*auth= NULLTBL;
	char	*av[30], ac;
	char	*tbl_passwd = NULLCP;
	char	*tbl_rights = NULLCP;
	char	*tbl_name;
	extern char *qmgr_auth_tbl;

	cb -> cb_authenticated = 0;
	if (auth == NULLTBL) {
		if ((auth = tb_nm2struct(qmgr_auth_tbl)) == NULLTBL) {
			PP_NOTICE (("Accepted Limited Access for %s",
				    name));
			return int_Qmgr_result_acceptedLimitedAccess;
		}
	}
	if (name == NULLCP)
		tbl_name = "anon";
	else	tbl_name = name;

	if (tb_k2val (auth, tbl_name, result) == NOTOK) {
		if (name == NULLCP) {
			PP_NOTICE (("Accepted Limited Access for %s",
				    tbl_name));
			return int_Qmgr_result_acceptedLimitedAccess;
		}
		else {
			PP_NOTICE (("Refused connection for %s", tbl_name));
			return NOTOK;
		}
	}

	ac = sstr2arg (result, 30, av, ", \t");
	for (ac --; ac >= 0; ac --) {
		char	*cp;

		if ((cp = index (av[ac], '=')) == NULLCP)
			tbl_passwd = av[ac];
		else {
			*cp ++ = '\0';
			if (lexequ (av[ac], "passwd") == 0)
				tbl_passwd = cp;
			else if (lexequ (av[ac], "rights") == 0)
				tbl_rights = cp;
		}
	}

	if (tbl_passwd) {
		if (passwd == NULLCP) {
			PP_LOG (LLOG_EXCEPTIONS,
				("Password required, none given for %s",
				 tbl_name));
			return NOTOK;
		}
		if (strcmp (tbl_passwd, crypt (passwd, tbl_passwd)) != 0) {
			PP_LOG (LLOG_EXCEPTIONS,
				("Password mismatch for %s", tbl_name));
			return NOTOK;
		}
	}
	/* now authenticated, if required */

	if (tbl_rights == NULLCP) {
		PP_NOTICE (("Limited Access granted for %s (no default rights)",
			    tbl_name));
		cb -> cb_authenticated = 0;
		return int_Qmgr_result_acceptedLimitedAccess;
	}
	if (lexequ (tbl_rights, "none") == 0) {
		PP_NOTICE (("%s explicitly refused", tbl_name));
		return NOTOK;
	}
	if (lexequ (tbl_rights, "limited") == 0) {
		PP_NOTICE (("Limited access granted for %s", tbl_name));
		return int_Qmgr_result_acceptedLimitedAccess;
	}
	if (lexequ (tbl_rights, "full") == 0) {
		PP_NOTICE (("Full Access granted for %s", tbl_name));
		cb -> cb_authenticated = 1;
		return int_Qmgr_result_acceptedFullAccess;
	}
	PP_LOG (LLOG_EXCEPTIONS, ("Unknown rights '%s'", tbl_rights));
	return NOTOK;
}

int start_routine (sd, acs, pep, npe)
int	sd;
struct AcSAPstart *acs;
PE	*pep;
int	*npe;
{
	struct type_Qmgr_BindArgument *ba;
	Connblk *cb;

	PP_TRACE (("start_routine (%d)", sd));
	*pep = NULLPE;
	cb = newcblk (cb_responder);
	cb -> cb_fd = sd;

	*npe = 0;
	if (acs -> acs_ninfo == 0) {
		cb -> cb_authenticated = 0;
		return bind_result (pep, npe, cb,
				    int_Qmgr_result_acceptedLimitedAccess);
	}
	
	if (decode_Qmgr_BindArgument (acs -> acs_info[0], 1,
				       NULLVP, NULLIP, &ba) == NOTOK) {
		PP_LOG (LLOG_EXCEPTIONS, ("failed to parse connect data [%s]",
					  PY_pepy));
		cb -> cb_authenticated = 0;
		return bind_result (pep, npe, cb,
				     int_Qmgr_result_acceptedLimitedAccess);
	}
	if (ba -> offset == type_Qmgr_BindArgument_noAuthentication) {
		cb -> cb_authenticated = 0;
		free_Qmgr_BindArgument (ba);
		if (check_credentails (cb, NULLCP, NULLCP) == NOTOK)
			return bind_error (pep, npe, cb,
					   int_Qmgr_reason_badCredentials);
		return bind_result (pep, npe, cb,
				    int_Qmgr_result_acceptedLimitedAccess);
	}

	if (ba -> offset == type_Qmgr_BindArgument_weakAuthentication) {
		char	*username;
		char	*pass;
		int	result;

		username = qb2str (ba -> un.weakAuthentication -> username);
		if (ba -> un.weakAuthentication -> passwd)
			pass = qb2str (ba -> un.weakAuthentication -> passwd);
		else pass = NULLCP;

		result = check_credentails (cb, username, pass);

		free (username);
		free (pass);
		free_Qmgr_BindArgument (ba);
		if (result == NOTOK)
			return bind_error (pep, npe, cb,
					   int_Qmgr_reason_badCredentials);
		else
			return bind_result (pep, npe, cb, result);
	}
	free_Qmgr_BindArgument (ba);
	return bind_error (pep, npe, cb, int_Qmgr_reason_congested);
}


static int bind_result (pep, npe, cb, type)
PE	*pep;
int	*npe;
Connblk *cb;
int	type;
{
	struct type_Qmgr_BindResult *br;
	extern char *qmgr_hostname, *ppversion;

	PP_TRACE (("bind_result"));

	br = (struct type_Qmgr_BindResult *) smalloc (sizeof *br);
	br -> result = type;
	br -> information = STR2QB (qmgr_hostname);
	br -> version = STR2QB (ppversion);

	if (encode_Qmgr_BindResult (pep, 1, NULLCP, 0, br) == NOTOK) {
		PP_LOG (LLOG_EXCEPTIONS, ("failed to encode BindResult [%s]",
			PY_pepy));
		free_Qmgr_BindResult (br);
		return bind_error (pep, npe, cb,
				   int_Qmgr_reason_congested);
	}
	(*pep) -> pe_context = 3;
	free_Qmgr_BindResult (br);
	*npe = 1;
	return ACS_ACCEPT;
}

static int bind_error (pep, npe, cb, type)
PE	*pep;
int	*npe;
Connblk *cb;
int	type;
{
	struct type_Qmgr_BindError *be;

	PP_TRACE (("bind_error"));

	be = (struct type_Qmgr_BindError *) smalloc (sizeof *be);
	be -> reason = type;
	be -> information = NULL;

	cb -> cb_fd = NOTOK;
	freecblk (cb);
	if (encode_Qmgr_BindError (pep, 1, NULLCP, 0, be) == NOTOK) {
		PP_LOG (LLOG_EXCEPTIONS,
			("Failed to build BindError [%s]", PY_pepy));
		*pep = NULLPE;
		*npe = 0;
		free_Qmgr_BindError (be);
		return ACS_PERMANENT;
	}
	(*pep) -> pe_context = 3;
	*npe = 1;
	free_Qmgr_BindError (be);
	return ACS_PERMANENT;
}

int add_msg (sd, msg, rox, roi)
int sd;
struct type_Qmgr_MsgStruct *msg;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	MsgStruct *ms, *oldms;
	int	result;

	PP_TRACE (("add_msg (%d, msg, rox, roi)", sd));

	if (submission_disabled == 1)	/* not submitting right now */
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);

	if ((ms = newmsgstruct (msg)) == NULLMS)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);

	if (ms -> inchan && ms -> inchan -> ch_chan_type == CH_IN) {
		Chanlist *clp;

		if ((clp = findchanlist (ms -> inchan)) &&
		    clp -> lastsuccess < ms -> age)
			clp -> lastsuccess = ms -> age;
	}

	if (oldms = find_msg (ms -> queid)) {
		result = updatemessage (oldms, ms);
		freems (ms);
	}
	else
		result = insertmessage (ms);

	if (result == OK)
		return RyDsResult (sd, rox -> rox_id, (caddr_t) NULL,
				   ROS_NOPRIO, roi);
	else
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);
}

int updatemessage (old, new)
MsgStruct *old, *new;
{
	PP_TRACE (("updatemessage ()"));
	/* to be written !! */
	PP_NOTICE (("update on message %s - ignored", new -> queid));

	return OK;
}

int insertmessage (ms)
MsgStruct *ms;
{
	MsgStruct **msp;
	

	PP_TRACE (("insertmessage ()"));

	for (msp = &msg_hash[hash(ms -> queid, HASHSIZE)]; *msp;
	     msp = &(*msp) -> ms_forw)
		continue;

	(*msp) = ms;

	if (chaninsert (ms) == NOTOK) { /* back out change */
		freems (ms);
		(*msp) = NULLMS;
		return NOTOK;
	}
	return OK;
}

/* ARGSUSED */
int	read_msg (sd, rma, rox, roi)
int	sd;
struct type_Qmgr_ReadMessageArgument *rma;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	MsgStruct **msp, *ms;
	struct type_Qmgr_MsgStructList **mlp;
	struct type_Qmgr_MsgList *base;
	Filter	*filter;

	PP_TRACE (("read_msg (%d)", sd));

	if ((base = (struct type_Qmgr_MsgList *) calloc (1, sizeof *base))
	    == NULL)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);
	mlp = &base -> msgs;

	filter = newfilter (rma -> filters);

	for (msp = msg_hash; msp < &msg_hash[HASHSIZE]; msp++) {
		for (ms = *msp; ms; ms = ms -> ms_forw) {
			if (filtermatch (filter, ms)) {
				*mlp = (struct type_Qmgr_MsgStructList *)
					calloc (1, sizeof (**mlp));
				
				(*mlp) -> MsgStruct = lmessage (ms);
				mlp = &(*mlp) -> next;
			}
		}
	}
	freefilter (filter);

	if (RyDsResult (sd, rox -> rox_id, (caddr_t) base, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");
	free_Qmgr_MsgList (base);

	return OK;
}

int	channel_list (sd, arg, rox, roi)
int	sd;
struct type_UNIV_UTCTime *arg;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	struct type_Qmgr_ChannelReadResult *crr;
	struct type_Qmgr_PrioritisedChannelList **list;
	Chanlist	**clp;
	extern double load_avg[];
	extern int maxchansrunning, nchansrunning;

	PP_TRACE (("channel_list (%d)", sd));

	crr = (struct type_Qmgr_ChannelReadResult *) smalloc (sizeof *crr);
	list = &crr -> channels;
	crr -> channels = NULL;
	crr -> load1 = 100 * load_avg[0];
	crr -> load2 = 100 * load_avg[1];
	crr -> currchans = nchansrunning;
	crr -> maxchans = maxchansrunning;

	for (clp = chan_list; clp < &chan_list[nchanlist]; clp ++) {
		*list = (struct type_Qmgr_PrioritisedChannelList *)
			calloc (1, sizeof (**list));

		if (*list == NULL ||
		    ((*list) -> PrioritisedChannel = lpchan (*clp)) == NULL) {
			free_Qmgr_ChannelReadResult (crr);
			return error (sd, error_Qmgr_congested, (caddr_t) NULL,
				      rox, roi);
		}

		list = &((*list) -> next);
	}

	if (RyDsResult (sd, rox -> rox_id, (caddr_t) crr, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");

	free_Qmgr_ChannelReadResult (crr);
	return OK;
}

int chan_control (sd, in, rox, roi)
int	sd;
struct type_Qmgr_ChannelControl *in;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	Chanlist *clp;
	struct type_Qmgr_PrioritisedChannelList *pcl;
	struct type_Qmgr_PrioritisedChannel *pc;
	Connblk *cb;
	char	*p;

	PP_TRACE (("chan_control (%d)", sd));

	if ((cb = findcblk (sd)) == NULLCB || cb -> cb_type != cb_responder)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);
	if (cb -> cb_authenticated == 0)
		return error (sd, error_Qmgr_authenticationFailure,
			      (caddr_t) NULL, rox, roi);

	p = qb2str (in -> channel);
	clp = findchanlist (ch_nm2struct (p));
	free (p);
	if (clp == NULLCHANLIST)
		return error (sd, error_Qmgr_noSuchChannel, (caddr_t) NULL,
			      rox, roi);

	switch (in -> control -> offset) {
	    case type_Qmgr_Control_stop:
		clp -> chan_enabled = 0;
		break;

	    case type_Qmgr_Control_start:
		clp -> chan_enabled = 1;
		break;

	    case type_Qmgr_Control_cacheClear:
		cache_clear (&clp -> cache);
		break;

	    case type_Qmgr_Control_cacheAdd:
		cache_set (&clp -> cache, in -> control -> un.cacheAdd);
		break;

	    default:
		return error (sd, error_Qmgr_illegalOperation, (caddr_t) NULL,
			      rox, roi);
	}
	clp -> chan_update = 1;

	pcl = (struct type_Qmgr_PrioritisedChannelList *) malloc (sizeof *pcl);

	if ((pc = lpchan (clp)) == NULL)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);

	pcl -> PrioritisedChannel = pc;
	pcl -> next = NULL;
	if (RyDsResult (sd, rox -> rox_id, (caddr_t) pcl, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");
	free_Qmgr_PrioritisedChannelList (pcl);
	return OK;
}

int	chan_begin (sd, arg, rox, roi)
int	sd;
struct type_Qmgr_FilterList *arg;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	struct type_Qmgr_FilterList *out;

	PP_TRACE (("chan_begin (%d)", sd));

	out = (struct type_Qmgr_FilterList *) calloc (1, sizeof(*out));
	if (out == NULL || (out -> Filter = (struct type_Qmgr_Filter *)
			    calloc (1, sizeof (struct type_Qmgr_Filter)))
	    == NULL)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);

	if (RyDsResult (sd, rox -> rox_id, (caddr_t) out, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");
	free_Qmgr_FilterList (out);
	return OK;
}

int	mta_control (sd, arg, rox, roi)
int	sd;
struct type_Qmgr_MtaControl *arg;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	Chanlist	*clp = NULLCHANLIST;
	Mtalist	       *mlp;
	struct type_Qmgr_MtaInfo *mta;
	CHAN	*chan;
	Connblk *cb;
	char	*p;

	PP_TRACE (("mta_control (%d)", sd));

	if ((cb = findcblk (sd)) == NULLCB || cb -> cb_type != cb_responder)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);
	if (cb -> cb_authenticated == 0)
		return error (sd, error_Qmgr_authenticationFailure,
			      (caddr_t) NULL, rox, roi);

	p = qb2str (arg -> channel);
	if ((chan = ch_nm2struct (p)) != NULLCHAN)
		clp = findchanlist (chan);
	free (p);

	if (clp == NULLCHANLIST)
		return error (sd, error_Qmgr_noSuchChannel, (caddr_t) NULL,
			      rox, roi);
	p = qb2str (arg -> mta);
	mlp = findmtalist (clp, p);
	free(p);

	if (mlp == NULLMTALIST)
		return error (sd, error_Qmgr_mtaNotInQueue, (caddr_t) NULL,
			      rox, roi);

	switch (arg -> control -> offset) {
	    case type_Qmgr_Control_stop:
		mlp -> mta_enabled = 0;
		break;
	    case type_Qmgr_Control_start:
		mlp -> mta_enabled = 1;
		break;
	    case type_Qmgr_Control_cacheClear:
		cache_clear (&mlp -> cache);
		break;
	    case type_Qmgr_Control_cacheAdd:
		cache_set (&mlp -> cache, arg -> control -> un.cacheAdd);
		break;
	    default:
		return error (sd, error_Qmgr_illegalOperation, (caddr_t) NULL,
			      rox, roi);
	}

	mlp -> mta_changed = 1;
	clp -> chan_update = 1;

	if ((mta = lmta (mlp, clp)) == NULL)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);
	if (RyDsResult (sd, rox -> rox_id, (caddr_t) mta, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");
	free_Qmgr_MtaInfo (mta);
	return OK;
}

int	mta_read (sd, arg, rox, roi)
int	sd;
struct type_Qmgr_MtaRead *arg;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	Chanlist *clp;
	struct type_Qmgr_PrioritisedMtaList **qmlpp,  *qmlp;
	char	*p;
	Mtalist *mlp;

	PP_TRACE (("mta_read (%d)", sd));

	qmlp = 0;
	qmlpp = &qmlp;

	p = qb2str (arg -> channel);
	clp = findchanlist (ch_nm2struct (p));
	free (p);
	if (clp == NULLCHANLIST)
		return error (sd, error_Qmgr_noSuchChannel, (caddr_t) NULL,
			      rox, roi);

	for (mlp = clp -> mtas -> mta_forw; mlp != clp -> mtas;
	     mlp = mlp -> mta_forw) {
		*qmlpp = (struct type_Qmgr_PrioritisedMtaList *)
			calloc (1, sizeof(**qmlpp));

		if (*qmlpp == NULL ||
		    ((*qmlpp) -> PrioritisedMta = lpmta (mlp, clp)) == NULL) {
			free_Qmgr_PrioritisedMtaList (qmlp);
			return error (sd, error_Qmgr_congested, (caddr_t) NULL,
				      rox, roi);
		}

		qmlpp = &((*qmlpp) -> next);
	}

	if (RyDsResult (sd, rox -> rox_id, (caddr_t) qmlp, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");
	free_Qmgr_PrioritisedMtaList (qmlp);
	return OK;
}

int msg_control (sd, arg, rox, roi)
int	sd;
struct type_Qmgr_MsgControl *arg;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	char	*q;
	Reciplist *rl;
	struct type_Qmgr_UserList *ul;
	Connblk *cb;
	MsgStruct *ms;

	PP_TRACE (("msg_control (%d)", sd));
	if ((cb = findcblk (sd)) == NULLCB || cb -> cb_type != cb_responder)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);
	if (cb -> cb_authenticated == 0)
		return error (sd, error_Qmgr_authenticationFailure,
			      (caddr_t) NULL, rox, roi);

	q = qb2str (arg -> qid);
	if ((ms = find_msg (q)) == NULLMS)
		return error  (sd, error_Qmgr_noSuchChannel, (caddr_t)NULL,
			       rox, roi);
	for (rl = ms -> recips; rl; rl = rl -> rp_next) {
		for (ul = arg -> users; ul; ul = ul -> next) {
			if (ul -> RecipientId -> parm == rl -> id)
				mcontrol (ms, rl, arg -> control);
		}
	}
	if (RyDsResult (sd, rox -> rox_id, (caddr_t) NULL, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");
	return OK;
}

static void mcontrol (ms, rl, ctrl)
MsgStruct *ms;
Reciplist	*rl;
struct type_Qmgr_Control *ctrl;
{
	LIST_RCHAN	*cl;
	char	*p;
	Chanlist *clp = NULL;
	Mlist *ml;
	Mtalist *mlp;
	int	i;

	PP_TRACE (("mcontrol (%s, %d)", ms -> queid, rl -> id));

	switch (rl -> status) {
	    case st_normal:
		for (cl = rl -> chans, i = rl -> chans_done; i > 0;
		     i--, cl = cl -> li_next)
			continue;
		if (cl == NULL)
			return;
		clp = findchanlist (cl -> li_chan);
		p = chan2mta (cl -> li_chan, rl);
		break;

	    case st_dr:
		{
			Reciplist *rlp2;

			for (rlp2 = ms -> recips; rlp2; rlp2 = rlp2 -> rp_next)
				if (rlp2 -> id == 0)
					break;
			if (rlp2 == NULL)
				return;
			clp = findchanlist(rlp2 -> chans->li_chan);
			
			p = chan2mta (clp -> chan, rlp2);
			break;
		}

	    case st_delete:
		clp = delete_chan;
		p = delete_chan -> channame;
		break;

	    case st_timeout:
		clp = timeout_chan;
		p = timeout_chan -> channame;
		break;

	    default:
		return;

	}
	if (clp == NULLCHANLIST)
		return;

	if ((mlp = findmtalist (clp, p)) == NULLMTALIST)
		return;

	for (ml = mlp -> msgs -> ml_forw; ml != mlp -> msgs;
	     ml = ml -> ml_forw) {
		if (ml -> ms == ms)
			break;
	}
	if (ml == mlp -> msgs)
		return;

	switch (ctrl -> offset) {
	    case type_Qmgr_Control_stop:
		rl -> msg_enabled = 0;
		break;
	    case type_Qmgr_Control_start:
		rl -> msg_enabled = 1;
		break;
	    case type_Qmgr_Control_cacheClear:
		cache_clear (&rl -> cache);
		break;
	    case type_Qmgr_Control_cacheAdd:
		cache_set (&rl -> cache, ctrl -> un.cacheAdd);
		break;
	}
	mlp -> mta_changed = 1;
	clp -> chan_update = 1;
}

int qmgrcontrol (sd, op, rox, roi)
int	sd;
int	op;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	Connblk *cb;

	PP_TRACE (("qmgrcontrol (%d)", sd));

	if ((cb = findcblk (sd)) == NULLCB || cb -> cb_type != cb_responder)
		return error (sd, error_Qmgr_congested, (caddr_t) NULL,
			      rox, roi);
	if (cb -> cb_authenticated == 0)
		return error (sd, error_Qmgr_authenticationFailure,
			      (caddr_t) NULL, rox, roi);
	switch (op) {
	    case int_Qmgr_QMGROp_abort:
		exit (0);
		break;

	    case int_Qmgr_QMGROp_gracefulTerminate:
		setchannels (1);
		opmode = OP_SHUTDOWN;
		break;

	    case int_Qmgr_QMGROp_restart:
		setchannels (0);
		opmode = OP_RESTART;
		break;

	    case int_Qmgr_QMGROp_rereadQueue:
		if (loader_chan) {
			cache_clear (&loader_chan -> cache);
			loader_chan -> chan_update = 1;
		}
		break;

	    case int_Qmgr_QMGROp_disableSubmission:
		submission_disabled = 1;
		break;

	    case int_Qmgr_QMGROp_enableSubmission:
		submission_disabled = 0;
		if (loader_chan) {
			cache_clear (&loader_chan -> cache);
			loader_chan -> chan_update = 1;
		}
		break;

	    case int_Qmgr_QMGROp_disableAll:
	    case int_Qmgr_QMGROp_enableAll:
		setchannels (op == int_Qmgr_QMGROp_enableAll ? 1 : 0);
		break;

	    case int_Qmgr_QMGROp_increasemaxchans:
		maxchansrunning ++;
		break;

	    case int_Qmgr_QMGROp_decreasemaxchans:
		maxchansrunning --;
		maxchansrunning = max (1, maxchansrunning);
		break;

	    default:
		PP_LOG (LLOG_EXCEPTIONS,  ("Unknown operation %d", op));
		break;
	}
	if (RyDsResult (sd, rox -> rox_id, (caddr_t) NULL, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");
	return OK;
}

int msgread (sd, mr, rox, roi)
int	sd;
struct type_Qmgr_MsgRead *mr;
struct RoSAPinvoke *rox;
struct RoSAPindication *roi;
{
	struct type_Qmgr_MsgStructList **qmlpp;
	struct type_Qmgr_MsgList *base;
	Mtalist *mlp;
	Chanlist *clp;
	Mlist *ml;
	char	*p;

	PP_TRACE (("msgread (%d)", sd));

	p = qb2str (mr -> channel);
	clp = findchanlist (ch_nm2struct (p));
	free (p);
	if (clp == NULLCHANLIST)
		return error (sd, error_Qmgr_noSuchChannel, (caddr_t) NULL,
			      rox, roi);

	p = qb2str (mr -> mta);
	mlp = findmtalist (clp, p);
	free (p);

	if (mlp == NULLMTALIST)
		return error (sd, error_Qmgr_mtaNotInQueue, (caddr_t) NULL,
			      rox, roi);

	base = (struct type_Qmgr_MsgList *) calloc (1, sizeof *base);
	qmlpp = &base -> msgs;
	
	for (ml = mlp -> msgs -> ml_forw; ml != mlp -> msgs;
	     ml = ml -> ml_forw) {
		*qmlpp = (struct type_Qmgr_MsgStructList *)
			calloc (1, sizeof **qmlpp);
		(*qmlpp) -> MsgStruct = l_cm_message (ml);
		qmlpp = &(*qmlpp) -> next;
	}

	if (RyDsResult (sd, rox -> rox_id, (caddr_t)base, ROS_NOPRIO, roi)
	    == NOTOK)
		ros_adios (&roi -> roi_preject, "RESULT");
	free_Qmgr_MsgList (base);

	return OK;
}

/* private functions */

/* Channel functions */

static int	chaninsert (ms)
MsgStruct *ms;
{
	LIST_RCHAN	*clp;
	Reciplist *rlp;
	int	i;
	int	num = 0;

	PP_TRACE (("chaninsert ()"));

	for (rlp = ms -> recips; rlp; rlp = rlp -> rp_next) {

		if ( rlp -> id == 0)
			continue;
		num ++;
		rlp -> status = st_normal;
		for (clp = rlp -> chans,
		     i = rlp -> chans_done; clp && i > 0;
		     clp = clp -> li_next, i--)
			continue;
		if (i == 0 && clp == NULL) {
			insertindrchan (ms, rlp);
			PP_NOTICE (("Message %s to %s added - DR",
				    ms -> queid, rlp -> user));
			continue;
		}

		if (clp == NULL)
			continue;

		insertinchan (clp -> li_chan, ms, rlp,
			      chan2mta (clp -> li_chan, rlp));
		PP_NOTICE (("Message %s to %s added",
			    ms -> queid, rlp -> user));
	}
	if (num == 0) {
		ms -> recips -> chans_done ++;
		insertindelchan (ms);
		PP_NOTICE (("Message %s added - for deletion", ms -> queid));
	}
	return OK;
}

void insertinchan (chan, ms, ri, mta)
CHAN *chan;
MsgStruct *ms;
Reciplist *ri;
char	*mta;
{
	Mtalist	       *mlp;
	Chanlist	*clp;

	PP_TRACE (("insertinchan (%s, %s, id=%d, mta=%s)", chan -> ch_name,
		   ms -> queid, ri -> id, mta));

	clp = findchanlist (chan);
	if (clp == NULLCHANLIST) {
		advise (LLOG_EXCEPTIONS, NULLCP, "No channel for %s",
			chan -> ch_name);
		return;
	}
	if (ri -> realmta)
		free (ri -> realmta);
	ri -> realmta = strdup (mta);
	if ((mlp = findmtalist(clp, mta)) == NULLMTALIST) {
		mlp = (Mtalist *) calloc (1, sizeof *mlp);
		mlp -> msgs = (Mlist *) calloc (1, sizeof *mlp -> msgs);
		mlp -> msgs -> ml_back = mlp -> msgs -> ml_forw = mlp -> msgs;
		insque (mlp, clp -> mtas -> mta_back);
		mlp -> mtaname = strdup (mta);
		mlp -> mta_enabled = 1;
	}
	if (insertinmta (mlp, ms, ri, clp) == OK ) {
		if (ri -> status == st_dr)
			clp -> num_drs ++;
		else
			clp -> num_msgs ++;
		clp -> volume += ms -> size;
		clp -> chan_update = 1;
	}
}

void insertindelchan (ms)
MsgStruct *ms;
{
	Reciplist *ri;

	PP_TRACE (("insertindelchan ()"));

	if (delete_chan == NULLCHANLIST) {
		advise (LLOG_EXCEPTIONS, NULLCP, "No delete channel!");
		return;
	}

	ri = ms -> recips;
	ri -> status = st_delete;
	insertinchan (delete_chan -> chan, ms, ri, delete_chan -> channame);
}

void insertindrchan (ms, rlp)
MsgStruct *ms;
Reciplist *rlp;
{
	Reciplist *r;

	PP_TRACE (("insertindrchan ()"));

	rlp -> status = st_dr;
	for (r = ms -> recips; r; r = r -> rp_next)
		if (r -> id == 0)
			break;
	if (r == NULLRL)
		return;
	insertinchan (r -> chans -> li_chan, ms,
		      rlp, chan2mta (r -> chans -> li_chan, r));
}

Chanlist *findchanlist (chan)
CHAN	*chan;
{
	Chanlist	**clpp;

	if (chan == NULLCHAN)
		return NULLCHANLIST;

	for (clpp = chan_list; clpp < &chan_list[nchanlist]; clpp++)
		if ((*clpp) -> chan == chan)
			return *clpp;

	return NULLCHANLIST;
}

static void	setchannels (state)
int	state;
{
	Chanlist **cpp;

	for (cpp = chan_list; cpp < &chan_list[nchanlist]; cpp++) {
		(*cpp) -> chan_enabled = state;
		(*cpp) -> chan_update = 1;
	}
}


void	create_chan (chan)
CHAN	*chan;
{
	Chanlist *clp;
	extern int chan_state;

	PP_TRACE (("create_chan (%s)", chan -> ch_name));

	if (nchanlist == 0)
		chan_list = (Chanlist **) malloc (sizeof (Chanlist *));
	else
		chan_list = (Chanlist **) realloc ((char *)chan_list,
						   (unsigned)(nchanlist + 1)
						   * sizeof (Chanlist *));
	chan_list[nchanlist] = clp = (Chanlist *) calloc (1, sizeof (Chanlist));
	clp -> channame = chan -> ch_name;
	clp -> chan = chan;
	clp -> mtas = (struct Mtalist *) calloc (1, sizeof (Mtalist));
	clp -> mtas -> mta_back = clp -> mtas -> mta_forw = clp -> mtas;
	clp -> chan_enabled = chan_state;
	switch (chan -> ch_chan_type) {
	    case CH_DELETE:
		delete_chan = clp;
		break;

	    case CH_QMGR_LOAD:
		clp -> chan_special = 1;
		loader_chan = clp;
		break;

	    case CH_DEBRIS:
		clp -> chan_special = 1;
		trash_chan = clp;
		break;

	    case CH_TIMEOUT:
		timeout_chan = clp;
		break;
	}

	nchanlist ++;
}

static int chan_sort (clp1, clp2)
Chanlist **clp1, **clp2;
{
	switch ((*clp1) -> chan -> ch_chan_type) {
	    case CH_WARNING:
	    case CH_DELETE:
	    case CH_QMGR_LOAD:
	    case CH_DEBRIS:
	    case CH_TIMEOUT:
	    case CH_SHAPER:
		switch ((*clp2) -> chan -> ch_chan_type) {
		    case CH_BOTH:
		    case CH_OUT:
		    case CH_IN:
			return -1;

		    default:
			return 0;
		}
		break;

	    case CH_IN:
		switch ((*clp2) -> chan -> ch_chan_type) {
		    case CH_IN:
			return 0;

		    default:
			return 1;
		}
		break;

	    case CH_BOTH:
	    case CH_OUT:
		switch ((*clp2) -> chan -> ch_chan_type) {
		    case CH_IN:
			return -1;
		    case CH_OUT:
		    case CH_BOTH:
			return ((*clp1) -> averaget -
				(*clp2) -> averaget) * 100.0;
		    default:
			return 1;
		}
		break;
	}
	return 0;
}

void sort_chans ()
{
	int i;

	if (nchanlist)
		qsort ((char *)chan_list, nchanlist,
		       sizeof (Chanlist *), chan_sort);
	PP_TRACE (("Channels resorted"));
	for (i = 0; i < nchanlist; i++)
		PP_TRACE (("Channel %d - %s (%g)", i,
			    chan_list[i] -> channame,
			    chan_list[i] -> averaget));
}


void delfromchan (clp, mta, ml, rno)
Chanlist *clp;
char *mta;
Mlist	*ml;
int	rno;
{
	Mtalist *mlp;
	int	i;
	enum rstatus st = st_normal;

	PP_TRACE (("delfromchan (chan=%s, id=%d)",
		clp -> channame, rno));

	mlp = findmtalist (clp, mta);

	msg_unlock (ml -> ms);

	for (i = 0; i < ml -> rcount; i++)
		if (rno == ml -> recips[i] -> id)
			break;
	if (i < ml -> rcount) {
		st = ml -> recips[i] -> status;
		for (i++; i < ml -> rcount; i++)
			ml -> recips[i-1] = ml -> recips[i];
	}
	ml -> rcount --;
	if (ml -> rcount == 0)
		zapmtamsg (ml, mlp, clp, st);
}

/* Host functions */

static int csort_prio (ml1, ml2)
Mlist	*ml1, *ml2;
{
	if (ml1 -> ms -> priority == ml2 -> ms -> priority)
		return 0;
	if (ml1 -> ms -> priority == int_Qmgr_Priority_high)
		return 1;
	if (ml2 -> ms -> priority == int_Qmgr_Priority_high)
		return -1;
	if (ml1 -> ms -> priority == int_Qmgr_Priority_normal)
		return 1;
	if (ml1 -> ms -> priority == int_Qmgr_Priority_normal)
		return -1;
	return 0;
}

static int csort_time (ml1, ml2)
Mlist	*ml1, *ml2;
{
	if (ml1 -> ms -> age < ml2 -> ms -> age)
		return 1;
	if (ml2 -> ms -> age < ml1 -> ms -> age)
		return -1;
	return 0;
}

static int csort_size (ml1, ml2)
Mlist	*ml1, *ml2;
{
	if (ml1 -> ms -> size < ml2 -> ms -> size)
		return 1;
	if (ml2 -> ms -> size > ml1 -> ms -> size)
		return -1;
	return 0;
}

static mtaenqueue (ml, mlp, clp)
Mlist	*ml;
Mtalist  *mlp;
Chanlist *clp;
{
	int	i, msort;
	Mlist	*m;
	IFP	sfnx[CH_MAX_SORT - 1];

	if (clp -> chan -> ch_sort[1] == 0) {
		insque (ml, mlp -> msgs -> ml_back);
		return;
	}

	msort = 0;
	for (i = 1; i < CH_MAX_SORT && clp -> chan -> ch_sort[i]; i++) {
		switch (clp -> chan -> ch_sort[i]) {
		    case CH_SORT_USR:
		    case CH_SORT_MTA:
			PP_LOG (LLOG_EXCEPTIONS, ("Bad sort criteria for %s",
						  clp -> channame));
			insque (ml, mlp -> msgs -> ml_back);
			return;

		    case CH_SORT_PRIORITY:
			sfnx[msort++] = csort_prio;
			continue;
		    case CH_SORT_TIME:
			sfnx[msort++] = csort_time;
			continue;
		    case CH_SORT_SIZE:
			sfnx[msort++] = csort_size;
			continue;

		    default:
			break;
		}
		break;
	}
	for (m = mlp -> msgs -> ml_forw; m != mlp -> msgs; m = m -> ml_forw) {
		int	res;

		for (i = 0; i < msort; i++) {
			switch ((*sfnx[i])(m, ml)) {
			    case 0:
				continue;
			    case -1:
				insque (ml, m -> ml_back);
				return;

			    case 1:
				break;
			}
			break;
		}
	}
	insque (ml, mlp -> msgs -> ml_back);
}


static int insertinmta (mlp, ms, rlp, clp)
Mtalist	       *mlp;
MsgStruct *ms;
Reciplist *rlp;
Chanlist *clp;
{
	Mlist	*ml;
	int	result;

	PP_TRACE (("insertinmta (%s, id=%d)",
		   mlp -> mtaname, rlp -> id));

	if (rlp -> status != st_dr) {
		for (ml = mlp -> msgs -> ml_forw; ml != mlp -> msgs;
		     ml = ml -> ml_forw) {
			if (ml -> ms -> m_locked)
				continue;
			if (ml -> ms == ms) {
				(void) addmtaid (ml, rlp);
				return DONE;
			}
		}
	}
	ml = (Mlist *) calloc (1, sizeof *ml);
	ml -> ms = ms;

	ms -> count ++;
	PP_TRACE (("Message reference count = %d", ms -> count));

	mtaenqueue (ml, mlp, clp);

	if ((result = addmtaid (ml, rlp)) == OK) {
		if (rlp -> status == st_dr)
			mlp -> num_drs ++;
		else
			mlp -> num_msgs ++;
		mlp -> volume += ms -> size;
		mlp -> mta_changed = 1;
	}
	return result;
}

static int addmtaid (ml, rlp)
Mlist	*ml;
Reciplist *rlp;
{
	Reciplist **ri;

	PP_TRACE (("addmtaid (%d)", rlp -> id));

	for (ri = ml -> recips; ri && ri < &ml->recips[ml->rcount];
	     ri ++)
		if (*ri == rlp)
			return DONE;

	if (ml -> rcount == 0) {
		ml -> recips = (Reciplist **) malloc (sizeof (ri));
		ml -> recips[0] = rlp;
		ml -> rcount = 1;
		return OK;
	}

	ml -> rcount ++;
	ml -> recips = (Reciplist **) realloc ((char *)ml -> recips,
				 (unsigned) ml -> rcount * sizeof (ri));
	ml -> recips[ml -> rcount - 1] = rlp;

	return DONE;
}

Mtalist *findmtalist (clp, mta)
Chanlist *clp;
char	*mta;
{
	Mtalist	       *mlp;

	PP_TRACE (("findmtalist (%s)", mta));

	for (mlp = clp -> mtas -> mta_forw; mlp != clp -> mtas;
	     mlp = mlp -> mta_forw)
		if (lexequ (mta, mlp -> mtaname) == 0)
			return mlp;
	return NULLMTALIST;
}

int zapmta (mlp)
Mtalist	*mlp;
{
	PP_TRACE (("zapmta (%s)", mlp -> mtaname));

	if (mlp -> num_msgs != 0 || mlp -> num_drs != 0)
		return NOTOK;

	remque (mlp);

	free ((char *) mlp -> msgs);
	if (mlp -> info)
		free (mlp -> info);
	free ((char *) mlp);
	return DONE;
}

void zapmtamsg (ml, mlp, clp, st)
Mlist	*ml;
Mtalist	*mlp;
Chanlist	*clp;
enum rstatus st;
{
	PP_TRACE (("zapmtamsg (mta=%s, chan=%s)",
		mlp -> mtaname, clp -> channame));

	if (st == st_dr)
		clp -> num_drs --;
	else
		clp -> num_msgs --;
	clp -> volume -= ml -> ms -> size;
	clp -> chan_update = 1;
	if (st == st_dr)
		mlp -> num_drs --;
	else
		mlp -> num_msgs --;
	mlp -> volume -= ml -> ms -> size;
	mlp -> mta_changed = 1;

	remque (ml);
	ml -> ms -> count --;
	PP_TRACE (("Message reference count = %d",
		ml -> ms -> count));

	if (ml -> info)
		free (ml -> info);
	free ((char *)ml -> recips);
	free ((char *)ml);

	if (mlp -> num_msgs == 0 && mlp -> num_drs == 0)
		zapmta (mlp);
}

static Mlist *findnextml ();

/* Msg functions */

Mlist *nextmsg (clp, mtap, lock)
Chanlist *clp;
char	**mtap;
int	lock;
{
	Mtalist *mlp;
	char *mta = *mtap;
	Mlist	*ml;

	PP_TRACE (("nextmsg (clp, mlp)"));

	if (mta && (mlp = findmtalist (clp, mta))) {
		if (mlp -> mta_enabled &&
		    mlp -> cache.cachetime <= current_time &&
		    (ml = findnextml (clp, mlp, current_time, lock)) != NULL)
			return ml;
		mlp -> nactive --;
	}

	if (mta) {
		PP_TRACE (("nothing on current MTA list - trying others"));
		free (mta);
		*mtap = NULL;
	}

	for (mlp = clp -> mtas -> mta_forw; mlp != clp -> mtas;
	     mlp = mlp -> mta_forw) {
		if (! mtaready (mlp)) {
			PP_TRACE (("mta %s not ready", mlp -> mtaname));
			continue;
		}

		if (mlp -> cache.cachetime > current_time) {
			PP_TRACE (("Mta %s cached for %d seconds",
				   mlp -> mtaname,
				   mlp -> cache.cachetime - current_time));
			continue;
		}

		if ((ml = findnextml (clp, mlp, current_time, lock)) != NULL) {
			if (lock)
				mlp -> nactive ++;
			*mtap = strdup (mlp -> mtaname);
			PP_TRACE (("Found a ml"));
			return ml;
		}
		PP_TRACE (("Mta %s no messages ready", mlp -> mtaname));
	}
	return (Mlist *)0;
}

static Mlist *findnextml (clp, mlp, now, lock)
Chanlist *clp;
Mtalist	*mlp;
time_t	now;
int	lock;
{
	Mlist	*ml;

	PP_TRACE (("findnextml (mta=%s)", mlp -> mtaname));

	for (ml = mlp -> msgs -> ml_forw; ml != mlp -> msgs;
	     ml = ml -> ml_forw) {
		if (!msgready (ml)) {
			PP_TRACE (("Message %s not ready", ml -> ms -> queid));
			continue;
		}

		if (msgiscached (ml, now)) {
			PP_TRACE (("Message %s cached", ml -> ms -> queid));
			continue;
		}

		if (ml -> ms -> defferedtime &&
		    ml -> ms -> defferedtime > now) {
			PP_TRACE (("Message defered"));
			continue;
		}

		if (lock)
			ml -> ms -> m_locked = 1;
		return ml;
	}
	return (Mlist *)0;
}

void zapmsg (ms)
MsgStruct *ms;
{
	MsgStruct **mspp;

	PP_TRACE (("zapmsg (%s)", ms -> queid));

	mspp = &msg_hash[hash(ms -> queid, HASHSIZE)];

	for (; *mspp; mspp = &(*mspp) -> ms_forw)
		if (*mspp == ms) {
			*mspp = (*mspp) -> ms_forw;
			freems (ms);
			PP_TRACE (("Message zapped"));
			return;
		}
	advise (LLOG_EXCEPTIONS, NULLCP, "Can't locate message");
}


Mlist	*findmtamsg (mlp, qid)
Mtalist	*mlp;
char *qid;
{
	MsgStruct *ms;
	Mlist	*ml;

	ms = find_msg (qid);

	for (ml = mlp -> msgs -> ml_forw; ml != mlp -> msgs;
	     ml = ml -> ml_forw)
		if (ml -> ms == ms)
			return ml;
	return NULL;
}

MsgStruct *find_msg (qid)
char *qid;
{
	MsgStruct	*ms;

	PP_TRACE (("find_msg (%s)", qid));
	for (ms = msg_hash[hash (qid, HASHSIZE)]; ms; ms = ms -> ms_forw)
		if ( strcmp (ms -> queid, qid) == 0)
			return ms;
	return NULLMS;
}

void maybezapmsg (ms)
MsgStruct *ms;
{
	if (ms -> count == 0)
		zapmsg (ms);
}

void kill_msg (ms)
MsgStruct *ms;
{
	PP_TRACE (("kill_msg"));
}

static int filtermatch (f, ms)
Filter *f;
MsgStruct *ms;
{
	PP_TRACE (("filtermatch ()"));

	if (f == NULLFL)
		return 0;

	if (f -> cont) {
		if (lexequ (f -> cont, ms -> contenttype) != 0)
			return filtermatch (f -> next, ms);
	}

	if (f -> eit) {
		LIST_BPT *bp, *bp2;

		for (bp = ms -> eit; bp; bp = bp -> li_next) {
			for (bp2 = f -> eit; bp2; bp2 = bp2 -> li_next) {
				if (lexequ (bp -> li_name,
					    bp2 -> li_name) != 0)
					return filtermatch (f -> next, ms);
			}
		}
	}

	if (f -> orig) {
		if (lexequ (f -> orig, ms -> originator) != 0)
			return filtermatch (f -> next, ms);
	}
	
	if (f -> recip) {
		Reciplist *rl, *rl0;

		for (rl0 = ms -> recips; rl0; rl0 = rl0 -> rp_next)
			if (rl0 -> id == 0)
				break;

		for (rl = ms -> recips; rl; rl = rl -> rp_next) {
			switch (rl -> status) {
			    case st_delete:
				continue; /* we ignore recips that dead */

			    case st_timeout:
			    case st_normal:
				if (rl -> id == 0)
					continue;
				if (lexequ (rl -> user, f -> recip) == 0)
					break;
				continue;

			    case st_dr:
				if (rl0 &&
				    lexequ (rl0 -> user, f -> recip) == 0)
					break;
				continue;
			}
			break;
		}
		if (rl == NULLRL)
			return filtermatch (f -> next, ms);
	}
	if (f -> channel) {
		int cc;
		Reciplist *rl, *rl0;
		LIST_RCHAN *chans;

		for (rl0 = ms -> recips; rl0; rl0 = rl0 -> rp_next)
			if (rl0 -> id == 0)
				break;
		if (rl0 == NULLRL)
			return filtermatch (f -> next, ms);

		for (rl = ms -> recips; rl; rl = rl -> rp_next) {
			if (rl -> status == st_dr) {
				if (f -> channel == rl0 -> chans -> li_chan)
					break;
				else	continue;
			}
			if (rl -> status == st_delete) {
				if (f -> channel == delete_chan -> chan)
					break;
				else continue;
			}
			if (rl -> status == st_timeout) {
				if (f -> channel == timeout_chan -> chan)
					break;
				else	continue;
			}
			for (chans = rl -> chans, cc = rl -> chans_done;
			     chans; chans = chans -> li_next, cc--) {
				if (cc == 0)
					break;
			}
			if (chans && f -> channel == chans -> li_chan)
				break;
		}
		if (rl == NULLRL)
			return filtermatch (f -> next, ms);
	}

	if (f -> mta) {
		Reciplist *rl;

		for (rl = ms -> recips; rl; rl = rl -> rp_next) {
			switch (rl -> status) {
			    case st_dr:
			    case st_normal:
				if (lexequ (rl -> mta, f -> mta) == 0)
					break;
				if (rl -> realmta &&
				    lexequ (rl -> realmta, f -> mta) == 0)
					break;
				continue;

			    case st_delete:
				if (delete_chan &&
				    lexequ (f -> mta,
					    delete_chan -> channame) == 0)
				    break;
				continue;
			    case st_timeout:
				if (timeout_chan &&
				    lexequ (f -> mta,
					    timeout_chan -> channame) == 0)
					break;
				continue;
			    default:
				PP_TRACE (("Bad state %d", rl -> status));
				continue;
			}
			break;
		}
		if (rl == NULLRL)
			return filtermatch (f -> next, ms);
	}

	if (f -> queid) {
		if (lexequ (f -> queid, ms -> queid) != 0)
			return filtermatch (f -> next, ms);
	}

	if (f -> mpduid) {
		if (!compmpduid (f -> mpduid, ms -> mpduid))
			return filtermatch (f -> next, ms);
	}
	if (f -> uacontent) {
		if (lexequ (f -> uacontent, ms -> uacontent) != 0)
			return filtermatch (f -> next, ms);
	}
	if (f ->morerecent) {
		if (ms -> age <= f -> morerecent)
			return filtermatch (f -> next, ms);
	}
	if (f -> earlier) {
		if (ms -> age >= f -> earlier)
			return filtermatch (f -> next, ms);
	}
	if (f -> priority != -1) {
		if (ms -> priority != f -> priority)
			return filtermatch (f -> next, ms);
	}
	if (f -> maxsize) {
		if (ms -> size > f -> maxsize)
			return filtermatch (f -> next, ms);

	}
	return 1;
}

static int compmpduid (m1, m2)
MPDUid *m1, *m2;
{
	PP_TRACE (("compmpduid ()"));

	if (!nullstrcmp (m1 -> mpduid_string, m2 -> mpduid_string))
		return 0;

	if (!nullstrcmp (m1 -> mpduid_DomId.global_Country,
			 m2 -> mpduid_DomId.global_Country))
		return 0;
	if (!nullstrcmp (m1 -> mpduid_DomId.global_Admin,
			 m2 -> mpduid_DomId.global_Admin))
		return 0;
	if (!nullstrcmp (m1 -> mpduid_DomId.global_Private,
			 m2 -> mpduid_DomId.global_Private))
		return 0;
	return 1;
}

static int nullstrcmp (s1, s2)
char	*s1, *s2;
{
	if (s1 && s2)
		return lexequ (s1, s2) == 0;
	if (s1 == NULLCP && s2 == NULLCP)
		return 1;
	return 0;
}

int mtaready (mlp)
Mtalist *mlp;
{
	if (mlp -> nactive > 0) {
		PP_TRACE (("mta %s started already",
			   mlp -> mtaname));
		return FALSE;
	}
	if (mlp -> mta_enabled == 0) {
		PP_TRACE (("Mta %s not enabled",
			   mlp -> mtaname));
		return FALSE;
	}
	return TRUE;
}

msgready (ml)
Mlist	*ml;
{
	Reciplist **rl;

	if (ml -> ms -> m_locked) {
		PP_TRACE (("Message locked"));
		return FALSE;
	}
	for (rl = ml -> recips; rl < &ml -> recips[ml->rcount]; rl ++) {
		if ((*rl) -> msg_enabled == 0) {
			PP_TRACE (("Message disabled"));
			return FALSE;
		}
	}
		
	return TRUE;
}

msgiscached (ml, now)
Mlist *ml;
time_t now;
{
	Reciplist **rl;

	for (rl = ml -> recips; rl < &ml -> recips[ml->rcount]; rl ++) {
		if ((*rl) -> cache.cachetime < now)
			return FALSE;
	}
	return TRUE;
}

clear_msgcache (ml)
Mlist *ml;
{
	Reciplist **rl;

	for (rl = ml -> recips; rl < &ml -> recips[ml->rcount]; rl ++) {
		cache_clear (&(*rl) -> cache);
	}
	return TRUE;
}

msgcache_inc (ml, plus)
Mlist *ml;
time_t plus;
{
	Reciplist **rl;

	for (rl = ml -> recips; rl < &ml -> recips[ml->rcount]; rl ++) {
		cache_inc (&(*rl) -> cache, plus);
	}
	return TRUE;
}

time_t msgmincache (ml)
Mlist *ml;
{
	time_t minc = 0;
	int	flag = 0;
	Reciplist **rl;

	for (rl = ml -> recips; rl < &ml -> recips[ml->rcount]; rl ++) {
		if (!flag) {
			flag = 1;
			minc = (*rl) -> cache.cachetime;
		}
		else
			minc = minc < (*rl) -> cache.cachetime ? minc :
				(*rl) -> cache.cachetime;
	}
	return minc;
}

msg_unlock (ms)
MsgStruct *ms;
{
	Reciplist *rlp, *r, *rp;
	LIST_RCHAN *cl;
	Chanlist *clp;
	Mtalist *mlp;
	int	i;

	PP_TRACE (("msg_unlock"));
	ms -> m_locked = 0;

	for (rlp = r = ms -> recips; rlp; rlp = rlp -> rp_next) {

		if ( rlp -> id == 0)
			continue;

		for (cl = rlp -> chans,
		     i = rlp -> chans_done; cl && i > 0;
		     cl = cl -> li_next, i--)
			continue;
		if (i == 0 && cl == NULL) {
			cl = r -> chans;
			rp = r;
		}
		else if (cl == NULL)
			continue;
		else
			rp = rlp;
		clp = findchanlist (cl -> li_chan);
		clp -> chan_update = 1;
		PP_TRACE (("setting update bit on %s", clp -> channame));
		if ((mlp = findmtalist (clp, chan2mta (cl -> li_chan, rp)))
		    == NULLMTALIST)
			continue;
		PP_TRACE (("Setting update bit on MTA %s", mlp -> mtaname));
		mlp -> mta_changed = 1;
	}
}

char	*chan2mta (chan, rlp)
CHAN *chan;
Reciplist *rlp;
{
	if (chan -> ch_chan_type != CH_OUT &&
	    chan -> ch_chan_type != CH_BOTH)
		return chan -> ch_name;

	if (chan -> ch_sort[0] == CH_SORT_USR)
		return rlp -> user;
	else	if (chan -> ch_mta)
		return chan -> ch_mta;
	else
		return rlp -> mta;
}