|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T t
Length: 41322 (0xa16a) Types: TextFile Names: »tables.c«
└─⟦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«
/* 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; }