/* comsys.c - module implementing DarkZone-style channel system */
/* $Id: comsys.c,v 1.80 2003/02/24 18:05:23 rmg Exp $ */
#include "../../api.h"
extern BOOLEXP *FDECL(getboolexp1, (FILE *));
extern void FDECL(putboolexp, (FILE *, BOOLEXP *));
/* --------------------------------------------------------------------------
* Constants.
*/
#define NO_CHAN_MSG "That is not a valid channel name."
#define CHAN_FLAG_PUBLIC 0x00000010
#define CHAN_FLAG_LOUD 0x00000020
#define CHAN_FLAG_P_JOIN 0x00000040
#define CHAN_FLAG_P_TRANS 0x00000080
#define CHAN_FLAG_P_RECV 0x00000100
#define CHAN_FLAG_O_JOIN 0x00000200
#define CHAN_FLAG_O_TRANS 0x00000400
#define CHAN_FLAG_O_RECV 0x00000800
#define CHAN_FLAG_SPOOF 0x00001000
#define CBOOT_QUIET 1 /* No boot message, just has left */
#define CEMIT_NOHEADER 1 /* Channel emit without header */
#define CHANNEL_SET 1 /* Set channel flag */
#define CHANNEL_CHARGE 2 /* Set channel charge */
#define CHANNEL_DESC 4 /* Set channel desc */
#define CHANNEL_LOCK 8 /* Set channel lock */
#define CHANNEL_OWNER 16 /* Set channel owner */
#define CHANNEL_JOIN 32 /* Channel lock: join */
#define CHANNEL_TRANS 64 /* Channel lock: transmit */
#define CHANNEL_RECV 128 /* Channel lock: receive */
#define CHANNEL_HEADER 256 /* Set channel header */
#define CLIST_FULL 1 /* Full listing of channels */
#define CLIST_HEADER 2 /* Header listing of channels */
#define CWHO_ALL 1 /* Show disconnected players on channel */
#define MAX_CHAN_NAME_LEN 20
#define MAX_CHAN_ALIAS_LEN 10
#define MAX_CHAN_DESC_LEN 256
#define MAX_CHAN_HEAD_LEN 64
/* --------------------------------------------------------------------------
* Configuration and hash tables.
*/
struct mod_comsys_confstorage {
char *public_channel; /* Name of public channel */
char *guests_channel; /* Name of guests channel */
char *public_calias; /* Alias of public channel */
char *guests_calias; /* Alias of guests channel */
} mod_comsys_config;
CONF mod_comsys_conftable[] = {
{(char *)"guests_calias", cf_string, CA_STATIC, CA_PUBLIC, (int *)&mod_comsys_config.guests_calias, SBUF_SIZE},
{(char *)"guests_channel", cf_string, CA_STATIC, CA_PUBLIC, (int *)&mod_comsys_config.guests_channel, SBUF_SIZE},
{(char *)"public_calias", cf_string, CA_STATIC, CA_PUBLIC, (int *)&mod_comsys_config.public_calias, SBUF_SIZE},
{(char *)"public_channel", cf_string, CA_STATIC, CA_PUBLIC, (int *)&mod_comsys_config.public_channel, SBUF_SIZE},
{ NULL, NULL, 0, 0, NULL, 0}};
HASHTAB mod_comsys_comsys_htab;
HASHTAB mod_comsys_calias_htab;
NHSHTAB mod_comsys_comlist_htab;
MODHASHES mod_comsys_hashtable[] = {
{ "Channels", &mod_comsys_comsys_htab, 15, 8},
{ "Channel aliases", &mod_comsys_calias_htab, 500, 16},
{ NULL, NULL, 0, 0}};
MODNHASHES mod_comsys_nhashtable[] = {
{ "Channel lists", &mod_comsys_comlist_htab, 100, 16},
{ NULL, NULL, 0, 0}};
/* --------------------------------------------------------------------------
* Structure definitions.
*/
typedef struct com_player CHANWHO;
struct com_player {
dbref player;
int is_listening;
CHANWHO *next;
};
typedef struct com_channel CHANNEL;
struct com_channel {
char *name;
dbref owner;
unsigned int flags;
char *header; /* channel header prefixing messages */
int num_who; /* number of people on the channel */
CHANWHO *who; /* linked list of players on channel */
int num_connected; /* number of connected players on channel */
CHANWHO **connect_who; /* array of connected player who structs */
int charge; /* cost to use channel */
int charge_collected; /* amount paid thus far */
int num_sent; /* number of messages sent */
char *descrip; /* description */
BOOLEXP *join_lock; /* who can join */
BOOLEXP *trans_lock; /* who can transmit */
BOOLEXP *recv_lock; /* who can receive */
};
typedef struct com_alias COMALIAS;
struct com_alias {
dbref player;
char *alias;
char *title;
CHANNEL *channel;
};
typedef struct com_list COMLIST;
struct com_list {
COMALIAS *alias_ptr;
COMLIST *next;
};
/* --------------------------------------------------------------------------
* Macros.
*/
#define check_owned_channel(p,c) \
if (!Comm_All((p)) && ((p) != (c)->owner)) { \
notify((p), NOPERM_MESSAGE); \
return; \
}
#define find_channel(d,n,p) \
(p) = ((CHANNEL *) hashfind((n), &mod_comsys_comsys_htab)); \
if (!(p)) { \
notify((d), NO_CHAN_MSG); \
return; \
}
#define find_calias(d,a,p) \
(p)=((COMALIAS *) hashfind(tprintf("%d.%s",(d),(a)), &mod_comsys_calias_htab)); \
if (!(p)) { \
notify((d), "No such channel alias."); \
return; \
}
#define lookup_channel(s) ((CHANNEL *) hashfind((s), &mod_comsys_comsys_htab))
#define lookup_calias(d,s) \
((COMALIAS *) hashfind(tprintf("%d.%s",(d),(s)), &mod_comsys_calias_htab))
#define lookup_clist(d) \
((COMLIST *) nhashfind((int) (d), &mod_comsys_comlist_htab))
#define ok_joinchannel(d,c) \
ok_chanperms((d),(c),CHAN_FLAG_P_JOIN,CHAN_FLAG_O_JOIN,(c)->join_lock)
#define ok_recvchannel(d,c) \
ok_chanperms((d),(c),CHAN_FLAG_P_RECV,CHAN_FLAG_O_RECV,(c)->recv_lock)
#define ok_sendchannel(d,c) \
ok_chanperms((d),(c),CHAN_FLAG_P_TRANS,CHAN_FLAG_O_TRANS,(c)->trans_lock)
#define clear_chan_alias(n,a) \
XFREE((a)->alias, "clear_chan_alias.astring"); \
if ((a)->title) \
XFREE((a)->title, "clear_chan_alias.title"); \
XFREE((a), "clear_chan_alias.alias"); \
hashdelete((n), &mod_comsys_calias_htab)
/* --------------------------------------------------------------------------
* Basic channel utilities.
*/
INLINE static int is_onchannel(player, chp)
dbref player;
CHANNEL *chp;
{
CHANWHO *wp;
for (wp = chp->who; wp != NULL; wp = wp->next) {
if (wp->player == player)
return 1;
}
return 0;
}
INLINE static int is_listenchannel(player, chp)
dbref player;
CHANNEL *chp;
{
int i;
for (i = 0; i < chp->num_connected; i++) {
if (chp->connect_who[i]->player == player)
return (chp->connect_who[i]->is_listening);
}
return 0;
}
INLINE static int is_listening_disconn(player, chp)
dbref player;
CHANNEL *chp;
{
CHANWHO *wp;
for (wp = chp->who; wp != NULL; wp = wp->next) {
if (wp->player == player)
return (wp->is_listening);
}
return 0;
}
static int ok_channel_string(str, maxlen, ok_spaces, ok_ansi)
char *str;
int maxlen;
int ok_spaces, ok_ansi;
{
char *p;
if (!str || !*str)
return 0;
if ((int)strlen(str) > maxlen - 1)
return 0;
for (p = str; *p; p++) {
if ((!ok_spaces && isspace(*p)) ||
(!ok_ansi && (*p == ESC_CHAR))) {
return 0;
}
}
return 1;
}
INLINE static char *munge_comtitle(title)
char *title;
{
static char tbuf[MBUF_SIZE];
char *tp;
tp = tbuf;
if (strchr(title, ESC_CHAR)) {
safe_copy_str(title, tbuf, &tp, MBUF_SIZE - 5);
safe_mb_str(ANSI_NORMAL, tbuf, &tp);
} else {
safe_mb_str(title, tbuf, &tp);
}
return tbuf;
}
INLINE static int ok_chanperms(player, chp, pflag, oflag, c_lock)
dbref player;
CHANNEL *chp;
int pflag, oflag;
BOOLEXP *c_lock;
{
if (Comm_All(player))
return 1;
switch (Typeof(player)) {
case TYPE_PLAYER:
if (chp->flags & pflag)
return 1;
break;
case TYPE_THING:
if (chp->flags & oflag)
return 1;
break;
default: /* only players and things on channels */
return 0;
}
/* If we don't have a flag, and we don't have a lock, we default to
* permission denied.
*/
if (!c_lock)
return 0;
/* Channel locks are evaluated with respect to the channel owner. */
if (eval_boolexp(player, chp->owner, chp->owner, c_lock))
return 1;
return 0;
}
/* --------------------------------------------------------------------------
* More complex utilities.
*/
static void update_comwho(chp)
CHANNEL *chp;
{
/* We have to call this every time a channel is joined or left,
* explicitly, as well as when players connect and disconnect.
* This is a candidate for optimization; we should really just update
* the list in-place, but this will do.
*/
CHANWHO *wp;
int i, count;
/* We're only interested in whether or not a player is connected,
* not whether or not they're actually listening to the channel.
*/
count = 0;
for (wp = chp->who; wp != NULL; wp = wp->next) {
if (!isPlayer(wp->player) || Connected(wp->player))
count++;
}
if (chp->connect_who)
XFREE(chp->connect_who, "update_comwho");
chp->num_connected = count;
if (count > 0) {
chp->connect_who = (CHANWHO **) XCALLOC(count, sizeof(CHANWHO *), "update_comwho");
i = 0;
for (wp = chp->who; wp != NULL; wp = wp->next) {
if (!isPlayer(wp->player) || Connected(wp->player)) {
chp->connect_who[i] = wp;
i++;
}
}
}
}
static void com_message(chp, msg, cause)
CHANNEL *chp;
char *msg;
dbref cause;
{
int i;
CHANWHO *wp;
char *mp, msg_ns[LBUF_SIZE];
#ifdef PUEBLO_SUPPORT
char *mh, *mh_ns;
mh = mh_ns = NULL;
#endif
chp->num_sent++;
mp = NULL;
for (i = 0; i < chp->num_connected; i++) {
wp = chp->connect_who[i];
if (wp->is_listening && ok_recvchannel(wp->player, chp)) {
if (isPlayer(wp->player)) {
if (Nospoof(wp->player) && (wp->player != cause) &&
(wp->player != mudstate.curr_enactor) &&
(wp->player != mudstate.curr_player)) {
if (!mp) {
/* Construct Nospoof buffer. Can't use tprintf
* because we end up calling it later.
*/
mp = msg_ns;
safe_chr('[', msg_ns, &mp);
safe_name(cause, msg_ns, &mp);
safe_chr('(', msg_ns, &mp);
safe_chr('#', msg_ns, &mp);
safe_ltos(msg_ns, &mp, cause);
safe_chr(')', msg_ns, &mp);
if (cause != Owner(cause)) {
safe_chr('{', msg_ns, &mp);
safe_name(Owner(cause), msg_ns, &mp);
safe_chr('}', msg_ns, &mp);
}
if (cause != mudstate.curr_enactor) {
safe_known_str((char *) "<-(#", 4, msg_ns, &mp);
safe_ltos(msg_ns, &mp, cause);
safe_chr(')', msg_ns, &mp);
}
safe_known_str((char *) "] ", 2, msg_ns, &mp);
safe_str(msg, msg_ns, &mp);
}
#ifndef PUEBLO_SUPPORT
raw_notify(wp->player, msg_ns);
#else
if (Html(wp->player)) {
if (!mh_ns) {
mh_ns = alloc_lbuf("com_message.html.nospoof");
html_escape(msg_ns, mh_ns, 0);
}
raw_notify(wp->player, mh_ns);
} else {
raw_notify(wp->player, msg_ns);
}
#endif
} else {
#ifndef PUEBLO_SUPPORT
raw_notify(wp->player, msg);
#else
if (Html(wp->player)) {
if (!mh) {
mh = alloc_lbuf("com_message.html");
html_escape(msg, mh, 0);
}
raw_notify(wp->player, mh);
} else {
raw_notify(wp->player, msg);
}
#endif
}
} else {
notify_with_cause(wp->player, cause, msg);
}
}
}
#ifdef PUEBLO_SUPPORT
if (mh)
free_lbuf(mh);
if (mh_ns)
free_lbuf(mh_ns);
#endif
}
static void remove_from_channel(player, chp, is_quiet)
dbref player;
CHANNEL *chp;
int is_quiet;
{
/* We assume that the player's channel aliases have already been
* removed, and that other cleanup that is not directly related to
* the channel structure itself has been accomplished. (We also
* do no sanity-checking.)
*/
CHANWHO *wp, *prev;
/* Should never happen, but just in case... */
if ((chp->num_who == 0) || !chp->who)
return;
/* If we only had one person, we can just nuke stuff. */
if (chp->num_who == 1) {
chp->num_who = 0;
XFREE(chp->who, "remove_from_channel.who");
return;
}
for (wp = chp->who, prev = NULL; wp != NULL; wp = wp->next) {
if (wp->player == player) {
if (prev) {
prev->next = wp->next;
} else {
chp->who = wp->next;
}
XFREE(wp, "remove_from_channel.who");
break;
} else {
prev = wp;
}
}
chp->num_who--;
update_comwho(chp);
if (!is_quiet &&
(!isPlayer(player) || (Connected(player) && !Hidden(player)))) {
com_message(chp, tprintf("%s %s has left this channel.",
chp->header, Name(player)),
player);
}
}
INLINE static void zorch_alias_from_list(cap)
COMALIAS *cap;
{
COMLIST *clist, *cl_ptr, *prev;
clist = lookup_clist(cap->player);
if (!clist)
return;
prev = NULL;
for (cl_ptr = clist; cl_ptr != NULL; cl_ptr = cl_ptr->next) {
if (cl_ptr->alias_ptr == cap) {
if (prev)
prev->next = cl_ptr->next;
else {
clist = cl_ptr->next;
if (clist)
nhashrepl((int) cap->player, (int *) clist,
&mod_comsys_comlist_htab);
else
nhashdelete((int) cap->player, &mod_comsys_comlist_htab);
}
XFREE(cl_ptr, "zorch_alias.cl_ptr");
return;
}
prev = cl_ptr;
}
}
static void process_comsys(player, arg, cap)
dbref player;
char *arg;
COMALIAS *cap;
{
CHANWHO *wp;
char *buff, *name_buf, tbuf[LBUF_SIZE], *tp;
int i;
if (!arg || !*arg) {
notify(player, "No message.");
return;
}
if (!strcmp(arg, (char *) "on")) {
for (wp = cap->channel->who; wp != NULL; wp = wp->next) {
if (wp->player == player)
break;
}
if (!wp) {
STARTLOG(LOG_ALWAYS, "BUG", "COM")
log_printf("Object #%d with alias %s is on channel %s but not on its player list.", player, cap->alias, cap->channel->name);
ENDLOG
notify(player, "An unusual channel error has been detected.");
return;
}
if (wp->is_listening) {
notify(player, tprintf("You are already on channel %s.",
cap->channel->name));
return;
}
wp->is_listening = 1;
/* Only tell people that we've joined if we're an object, or
* we're a connected and non-hidden player.
*/
if (!isPlayer(player) || (Connected(player) && !Hidden(player))) {
com_message(cap->channel,
tprintf("%s %s has joined this channel.",
cap->channel->header, Name(player)),
player);
}
return;
} else if (!strcmp(arg, (char *) "off")) {
for (wp = cap->channel->who; wp != NULL; wp = wp->next) {
if (wp->player == player)
break;
}
if (!wp) {
STARTLOG(LOG_ALWAYS, "BUG", "COM")
log_printf("Object #%d with alias %s is on channel %s but not on its player list.", player, cap->alias, cap->channel->name);
ENDLOG
notify(player, "An unusual channel error has been detected.");
return;
}
if (wp->is_listening == 0) {
notify(player, tprintf("You are not on channel %s.",
cap->channel->name));
return;
}
wp->is_listening = 0;
notify(player, tprintf("You leave channel %s.", cap->channel->name));
/* Only tell people about it if we're an object, or we're a
* connected and non-hidden player.
*/
if (!isPlayer(player) || (Connected(player) && !Hidden(player))) {
com_message(cap->channel,
tprintf("%s %s has left this channel.",
cap->channel->header, Name(player)),
player);
}
return;
} else if (!strcmp(arg, (char *) "who")) {
/* Allow players who have an alias for a channel to see who is
* on it, even if they are not actively receiving.
*/
notify(player, "-- Players --");
for (i = 0; i < cap->channel->num_connected; i++) {
wp = cap->channel->connect_who[i];
if (isPlayer(wp->player)) {
if (wp->is_listening && Connected(wp->player) &&
(!Hidden(wp->player) || See_Hidden(player))) {
buff = unparse_object(player, wp->player, 0);
notify(player, buff);
free_lbuf(buff);
}
}
}
notify(player, "-- Objects -- ");
for (i = 0; i < cap->channel->num_connected; i++) {
wp = cap->channel->connect_who[i];
if (!isPlayer(wp->player)) {
if (wp->is_listening) {
buff = unparse_object(player, wp->player, 0);
notify(player, buff);
free_lbuf(buff);
}
}
}
notify(player, tprintf("-- %s --", cap->channel->name));
return;
} else {
if (Gagged(player)) {
notify(player, NOPERM_MESSAGE);
return;
}
if (!is_listenchannel(player, cap->channel)) {
notify(player, tprintf("You must be on %s to do that.",
cap->channel->name));
return;
}
if (!ok_sendchannel(player, cap->channel)) {
notify(player, "You cannot transmit on that channel.");
return;
}
if (!payfor(player, Guest(player) ? 0 : cap->channel->charge)) {
notify(player, tprintf("You don't have enough %s.",
mudconf.many_coins));
return;
}
cap->channel->charge_collected += cap->channel->charge;
giveto(cap->channel->owner, cap->channel->charge);
if (cap->title) {
if (cap->channel->flags & CHAN_FLAG_SPOOF) {
name_buf = cap->title;
} else {
tp = tbuf;
safe_str(cap->title, tbuf, &tp);
safe_chr(' ', tbuf, &tp);
safe_name(player, tbuf, &tp);
*tp = '\0';
name_buf = tbuf;
}
} else {
name_buf = NULL;
}
if (*arg == ':') {
com_message(cap->channel,
tprintf("%s %s %s",
cap->channel->header,
(name_buf) ? name_buf : Name(player),
arg + 1),
player);
} else if (*arg == ';') {
com_message(cap->channel,
tprintf("%s %s%s",
cap->channel->header,
(name_buf) ? name_buf : Name(player),
arg + 1),
player);
} else {
com_message(cap->channel,
tprintf("%s %s says, \"%s\"",
cap->channel->header,
(name_buf) ? name_buf : Name(player),
arg),
player);
}
return;
}
}
/* --------------------------------------------------------------------------
* Other externally-exposed utilities.
*/
void join_channel(player, chan_name, alias_str, title_str)
dbref player;
char *chan_name, *alias_str, *title_str;
{
CHANNEL *chp;
COMALIAS *cap;
CHANWHO *wp;
COMLIST *clist;
int has_joined;
if (!ok_channel_string(alias_str, MAX_CHAN_ALIAS_LEN, 0, 0)) {
notify(player, "That is not a valid channel alias.");
return;
}
if (lookup_calias(player, alias_str) != NULL) {
notify(player, "You are already using that channel alias.");
return;
}
find_channel(player, chan_name, chp);
has_joined = is_onchannel(player, chp);
if (!has_joined && !ok_joinchannel(player, chp)) {
notify(player, "You cannot join that channel.");
return;
}
/* Construct the alias. */
cap = (COMALIAS *) XMALLOC(sizeof(COMALIAS), "join_channel.alias");
cap->player = player;
cap->alias = XSTRDUP(alias_str, "join_channel.alias_str");
/* Note that even if the player is already on this channel,
* we do not inherit the channel title from other aliases.
*/
if (title_str && *title_str)
cap->title = XSTRDUP(munge_comtitle(title_str),
"join_channel.title_str");
else
cap->title = NULL;
cap->channel = chp;
hashadd(tprintf("%d.%s", player, alias_str), (int *) cap,
&mod_comsys_calias_htab, 0);
/* Add this to the list of all aliases for the player. */
clist = (COMLIST *) XMALLOC(sizeof(COMLIST), "join_channel.clist");
clist->alias_ptr = cap;
clist->next = lookup_clist(player);
if (clist->next == NULL)
nhashadd((int) player, (int *) clist, &mod_comsys_comlist_htab);
else
nhashrepl((int) player, (int *) clist, &mod_comsys_comlist_htab);
/* If we haven't joined the channel, go do that. */
if (!has_joined) {
wp = (CHANWHO *) XMALLOC(sizeof(CHANWHO), "join_channel.who");
wp->player = player;
wp->is_listening = 1;
if ((chp->num_who == 0) || (chp->who == NULL)) {
wp->next = NULL;
} else {
wp->next = chp->who;
}
chp->who = wp;
chp->num_who++;
update_comwho(chp);
if (!isPlayer(player) || (Connected(player) && !Hidden(player))) {
com_message(chp, tprintf("%s %s has joined this channel.",
chp->header, Name(player)),
player);
}
if (title_str) {
notify(player,
tprintf("Channel '%s' added with alias '%s' and title '%s'.",
chp->name, alias_str, cap->title));
} else {
notify(player,
tprintf("Channel '%s' added with alias '%s'.",
chp->name, alias_str));
}
} else {
if (title_str) {
notify(player,
tprintf("Alias '%s' with title '%s' added for channel '%s'.",
alias_str, cap->title, chp->name));
} else {
notify(player,
tprintf("Alias '%s' added for channel '%s'.",
alias_str, chp->name));
}
}
}
void channel_clr(player)
dbref player;
{
CHANNEL **ch_array;
COMLIST *clist, *cl_ptr, *next;
int i, found, pos;
char tbuf[SBUF_SIZE];
/* We do not check if the comsys is enabled, because we want to clean
* up our mess regardless.
*/
clist = lookup_clist(player);
if (!clist)
return;
/* Figure out all the channels we're on, then free up aliases. */
ch_array = (CHANNEL **) XCALLOC(mod_comsys_comsys_htab.entries,
sizeof(CHANNEL *), "channel_clr.array");
pos = 0;
for (cl_ptr = clist; cl_ptr != NULL; cl_ptr = next) {
/* This is unnecessarily redundant, but it's not as if
* a player is going to be on tons of channels.
*/
found = 0;
for (i = 0;
(i < mod_comsys_comsys_htab.entries) && (ch_array[i] != NULL);
i++) {
if (ch_array[i] == cl_ptr->alias_ptr->channel) {
found = 1;
break;
}
}
if (!found) {
ch_array[pos] = cl_ptr->alias_ptr->channel;
pos++;
}
sprintf(tbuf, "%d.%s", player, cl_ptr->alias_ptr->alias),
clear_chan_alias(tbuf, cl_ptr->alias_ptr);
next = cl_ptr->next;
XFREE(cl_ptr, "channel_clr.cl_ptr");
}
nhashdelete((int) player, &mod_comsys_comlist_htab);
/* Remove from channels. */
for (i = 0; i < pos; i++)
remove_from_channel(player, ch_array[i], 0);
XFREE(ch_array, "channel_clr.array");
}
void mod_comsys_announce_connect(player, reason, num)
dbref player;
const char *reason;
int num;
{
CHANNEL *chp;
/* It's slightly easier to just go through the channels and see
* which ones the player is on, for announcement purposes.
*/
for (chp = (CHANNEL *) hash_firstentry(&mod_comsys_comsys_htab);
chp != NULL;
chp = (CHANNEL *) hash_nextentry(&mod_comsys_comsys_htab)) {
if (is_onchannel(player, chp)) {
update_comwho(chp);
if ((chp->flags & CHAN_FLAG_LOUD) && !Hidden(player) &&
is_listenchannel(player, chp)) {
com_message(chp, tprintf("%s %s has connected.",
chp->header, Name(player)),
player);
}
}
}
}
void mod_comsys_announce_disconnect(player, reason, num)
dbref player;
const char *reason;
int num;
{
CHANNEL *chp;
for (chp = (CHANNEL *) hash_firstentry(&mod_comsys_comsys_htab);
chp != NULL;
chp = (CHANNEL *) hash_nextentry(&mod_comsys_comsys_htab)) {
if (is_onchannel(player, chp)) {
if ((chp->flags & CHAN_FLAG_LOUD) && !Hidden(player) &&
is_listenchannel(player, chp)) {
com_message(chp, tprintf("%s %s has disconnected.",
chp->header, Name(player)),
player);
}
update_comwho(chp);
}
}
}
void update_comwho_all()
{
CHANNEL *chp;
for (chp = (CHANNEL *) hash_firstentry(&mod_comsys_comsys_htab);
chp != NULL;
chp = (CHANNEL *) hash_nextentry(&mod_comsys_comsys_htab)) {
update_comwho(chp);
}
}
void comsys_chown(from_player, to_player)
dbref from_player, to_player;
{
CHANNEL *chp;
for (chp = (CHANNEL *) hash_firstentry(&mod_comsys_comsys_htab);
chp != NULL;
chp = (CHANNEL *) hash_nextentry(&mod_comsys_comsys_htab)) {
if (chp->owner == from_player)
chp->owner = to_player;
}
}
/* --------------------------------------------------------------------------
* Comsys commands: channel administration.
*/
void do_ccreate(player, cause, key, name)
dbref player, cause;
int key;
char *name;
{
CHANNEL *chp;
char buf[LBUF_SIZE];
if (!Comm_All(player)) {
notify(player, NOPERM_MESSAGE);
return;
}
if (!ok_channel_string(name, MAX_CHAN_NAME_LEN, 1, 0)) {
notify(player, NO_CHAN_MSG);
return;
}
if (lookup_channel(name) != NULL) {
notify(player, "That channel name is in use.");
return;
}
chp = (CHANNEL *) XMALLOC(sizeof(CHANNEL), "ccreate.channel");
if (!chp) {
notify(player, "Out of memory.");
return;
}
chp->name = XSTRDUP(name, "ccreate.name");
chp->owner = Owner(player);
chp->flags = CHAN_FLAG_P_JOIN | CHAN_FLAG_P_TRANS | CHAN_FLAG_P_RECV |
CHAN_FLAG_O_JOIN | CHAN_FLAG_O_TRANS | CHAN_FLAG_O_RECV;
chp->who = NULL;
chp->num_who = 0;
chp->connect_who = NULL;
chp->num_connected = 0;
chp->charge = 0;
chp->charge_collected = 0;
chp->num_sent = 0;
chp->descrip = NULL;
chp->join_lock = chp->trans_lock = chp->recv_lock = NULL;
sprintf(buf, "[%s]", chp->name);
chp->header = XSTRDUP(buf, "ccreate.header");
hashadd(name, (int *) chp, &mod_comsys_comsys_htab, 0);
notify(player, tprintf("Channel %s created.", name));
}
void do_cdestroy(player, cause, key, name)
dbref player, cause;
int key;
char *name;
{
CHANNEL *chp;
COMALIAS **alias_array;
COMALIAS *cap;
char **name_array;
HASHTAB *htab;
HASHENT *hptr;
int i, count;
find_channel(player, name, chp);
check_owned_channel(player, chp);
/* We have the wonderful joy of cleaning out all the aliases
* that are currently pointing to this channel. We begin by
* warning everyone that it's going away, and then we obliterate
* it. We have to delete the pointers one by one or we run into
* hashtable chaining issues.
*/
com_message(chp, tprintf("Channel %s has been destroyed by %s.",
chp->name, Name(player)),
player);
htab = &mod_comsys_calias_htab;
alias_array = (COMALIAS **) XCALLOC(htab->entries, sizeof(COMALIAS *),
"cdestroy.alias_array");
name_array = (char **) XCALLOC(htab->entries, sizeof(char *),
"cdestroy.name_array");
count = 0;
for (i = 0; i < htab->hashsize; i++) {
for (hptr = htab->entry[i]; hptr != NULL; hptr = hptr->next) {
cap = (COMALIAS *) hptr->data;
if (cap->channel == chp) {
name_array[count] = hptr->target.s;
alias_array[count] = cap;
count++;
}
}
}
/* Delete the aliases from the players' lists, then wipe them out. */
if (count > 0) {
for (i = 0; i < count; i++) {
zorch_alias_from_list(alias_array[i]);
clear_chan_alias(name_array[i], alias_array[i]);
}
}
XFREE(name_array, "cdestroy.name_array");
XFREE(alias_array, "cdestroy.alias_array");
/* Zap the channel itself. */
XFREE(chp->name, "cdestroy.cname");
if (chp->who)
XFREE(chp->who, "cdestroy.who");
if (chp->connect_who)
XFREE(chp->connect_who, "cdestroy.connect_who");
if (chp->descrip)
XFREE(chp->descrip, "cdestroy.descrip");
XFREE(chp->header, "cdestroy.header");
if (chp->join_lock)
free_boolexp(chp->join_lock);
if (chp->trans_lock)
free_boolexp(chp->trans_lock);
if (chp->recv_lock)
free_boolexp(chp->recv_lock);
XFREE(chp, "cdestroy.channel");
hashdelete(name, &mod_comsys_comsys_htab);
notify(player, tprintf("Channel %s destroyed.", name));
}
void do_channel(player, cause, key, chan_name, arg)
dbref player, cause;
int key;
char *chan_name, *arg;
{
CHANNEL *chp;
BOOLEXP *boolp;
dbref new_owner;
int c_charge, negate, flag;
find_channel(player, chan_name, chp);
check_owned_channel(player, chp);
if (!key || (key & CHANNEL_SET)) {
if (*arg == '!') {
negate = 1;
arg++;
} else {
negate = 0;
}
if (!strcasecmp(arg, (char *) "public")) {
flag = CHAN_FLAG_PUBLIC;
} else if (!strcasecmp(arg, (char *) "loud")) {
flag = CHAN_FLAG_LOUD;
} else if (!strcasecmp(arg, (char *) "spoof")) {
flag = CHAN_FLAG_SPOOF;
} else if (!strcasecmp(arg, (char *) "p_join")) {
flag = CHAN_FLAG_P_JOIN;
} else if (!strcasecmp(arg, (char *) "p_transmit")) {
flag = CHAN_FLAG_P_TRANS;
} else if (!strcasecmp(arg, (char *) "p_receive")) {
flag = CHAN_FLAG_P_RECV;
} else if (!strcasecmp(arg, (char *) "o_join")) {
flag = CHAN_FLAG_O_JOIN;
} else if (!strcasecmp(arg, (char *) "o_transmit")) {
flag = CHAN_FLAG_O_TRANS;
} else if (!strcasecmp(arg, (char *) "o_receive")) {
flag = CHAN_FLAG_O_RECV;
} else {
notify(player, "That is not a valid channel flag name.");
return;
}
if (negate)
chp->flags &= ~flag;
else
chp->flags |= flag;
notify(player, "Set.");
} else if (key & CHANNEL_LOCK) {
if (arg && *arg) {
boolp = parse_boolexp(player, arg, 0);
if (boolp == TRUE_BOOLEXP) {
notify(player, "I don't understand that key.");
free_boolexp(boolp);
return;
}
if (key & CHANNEL_JOIN) {
if (chp->join_lock)
free_boolexp(chp->join_lock);
chp->join_lock = boolp;
} else if (key & CHANNEL_RECV) {
if (chp->recv_lock)
free_boolexp(chp->recv_lock);
chp->recv_lock = boolp;
} else if (key & CHANNEL_TRANS) {
if (chp->trans_lock)
free_boolexp(chp->trans_lock);
chp->trans_lock = boolp;
} else {
notify(player, "You must specify a valid lock type.");
free_boolexp(boolp);
return;
}
notify(player, "Channel locked.");
} else {
if (key & CHANNEL_JOIN) {
if (chp->join_lock)
free_boolexp(chp->join_lock);
chp->join_lock = NULL;
} else if (key & CHANNEL_RECV) {
if (chp->recv_lock)
free_boolexp(chp->recv_lock);
chp->recv_lock = NULL;
} else if (key & CHANNEL_TRANS) {
if (chp->trans_lock)
free_boolexp(chp->trans_lock);
chp->trans_lock = NULL;
}
notify(player, "Channel unlocked.");
}
} else if (key & CHANNEL_OWNER) {
new_owner = lookup_player(player, arg, 1);
if (Good_obj(new_owner)) {
chp->owner = Owner(new_owner); /* no robots */
notify(player, "Owner set.");
} else {
notify(player, "No such player.");
}
} else if (key & CHANNEL_CHARGE) {
c_charge = atoi(arg);
if ((c_charge < 0) || (c_charge > 32767)) {
notify(player, "That is not a reasonable cost.");
return;
}
chp->charge = c_charge;
notify(player, "Set.");
} else if (key & CHANNEL_DESC) {
if (arg && *arg && !ok_channel_string(arg, MAX_CHAN_DESC_LEN, 1, 1)) {
notify(player, "That is not a reasonable channel description.");
return;
}
if (chp->descrip)
XFREE(chp->descrip, "do_channel.desc");
if (arg && *arg)
chp->descrip = XSTRDUP(arg, "do_channel.desc");
notify(player, "Set.");
} else if (key & CHANNEL_HEADER) {
if (arg && *arg && !ok_channel_string(arg, MAX_CHAN_HEAD_LEN, 1, 1)) {
notify(player, "That is not a reasonable channel header.");
return;
}
XFREE(chp->header, "do_channel.header");
if (!arg)
chp->header = XSTRDUP("", "do_channel.header");
else
chp->header = XSTRDUP(arg, "do_channel.header");
notify(player, "Set.");
} else {
notify(player, "Invalid channel command.");
}
}
void do_cboot(player, cause, key, name, objstr)
dbref player, cause;
int key;
char *name, *objstr;
{
CHANNEL *chp;
dbref thing;
COMLIST *chead, *clist, *cl_ptr, *next, *prev;
char *t;
char tbuf[SBUF_SIZE];
find_channel(player, name, chp);
check_owned_channel(player, chp);
thing = match_thing(player, objstr);
if (thing == NOTHING)
return;
if (!is_onchannel(thing, chp)) {
notify(player, "Your target is not on that channel.");
return;
}
/* Clear out all of the player's aliases for this channel. */
chead = clist = lookup_clist(thing);
if (clist) {
prev = NULL;
for (cl_ptr = clist; cl_ptr != NULL; cl_ptr = next) {
next = cl_ptr->next;
if (cl_ptr->alias_ptr->channel == chp) {
if (prev)
prev->next = cl_ptr->next;
else
clist = cl_ptr->next;
sprintf(tbuf, "%d.%s", thing, cl_ptr->alias_ptr->alias);
clear_chan_alias(tbuf, cl_ptr->alias_ptr);
XFREE(cl_ptr, "do_cboot.cl_ptr");
} else {
prev = cl_ptr;
}
}
if (!clist)
nhashdelete((int) thing, &mod_comsys_comlist_htab);
else if (chead != clist)
nhashrepl((int) thing, (int *) clist, &mod_comsys_comlist_htab);
}
notify(player, tprintf("You boot %s off channel %s.",
Name(thing), chp->name));
notify(thing, tprintf("%s boots you off channel %s.",
Name(player), chp->name));
if (key & CBOOT_QUIET) {
remove_from_channel(thing, chp, 0);
} else {
remove_from_channel(thing, chp, 1);
t = tbuf;
safe_sb_str(Name(player), tbuf, &t);
com_message(chp, tprintf("%s %s boots %s off the channel.",
chp->header, tbuf, Name(thing)),
player);
}
}
void do_cemit(player, cause, key, chan_name, str)
dbref player, cause;
int key;
char *chan_name, *str;
{
CHANNEL *chp;
find_channel(player, chan_name, chp);
check_owned_channel(player, chp);
if (key & CEMIT_NOHEADER)
com_message(chp, str, player);
else
com_message(chp, tprintf("%s %s", chp->header, str),
player);
}
void do_cwho(player, cause, key, chan_name)
dbref player, cause;
int key;
char *chan_name;
{
CHANNEL *chp;
CHANWHO *wp;
int i;
int p_count, o_count;
find_channel(player, chan_name, chp);
check_owned_channel(player, chp);
p_count = o_count = 0;
notify(player, " Name Player?");
if (key & CWHO_ALL) {
for (wp = chp->who; wp != NULL; wp = wp->next) {
notify(player, tprintf("%s %-25s %7s",
(wp->is_listening) ? "[on]" : " ",
Name(wp->player),
isPlayer(wp->player) ? "Yes" : "No"));
if (isPlayer(wp->player))
p_count++;
else
o_count++;
}
} else {
for (i = 0; i < chp->num_connected; i++) {
wp = chp->connect_who[i];
if (!Hidden(wp->player) || See_Hidden(player)) {
notify(player, tprintf("%s %-25s %7s",
(wp->is_listening) ? "[on]" : " ",
Name(wp->player),
isPlayer(wp->player) ? "Yes" : "No"));
if (isPlayer(wp->player))
p_count++;
else
o_count++;
}
}
}
notify(player, tprintf("Counted %d %s and %d %s on channel %s.",
p_count, (p_count == 1) ? "player" : "players",
o_count, (o_count == 1) ? "object" : "objects",
chp->name));
}
/* --------------------------------------------------------------------------
* Comsys commands: player-usable.
*/
void do_addcom(player, cause, key, alias_str, args, nargs)
dbref player, cause;
int key;
char *alias_str;
char *args[];
int nargs;
{
char *chan_name, *title_str;
if (nargs < 1) {
notify(player, "You need to specify a channel.");
return;
}
chan_name = args[0];
if (nargs < 2) {
title_str = NULL;
} else {
title_str = args[1];
}
join_channel(player, chan_name, alias_str, title_str);
}
void do_delcom(player, cause, key, alias_str)
dbref player, cause;
int key;
char *alias_str;
{
COMALIAS *cap;
CHANNEL *chp;
COMLIST *clist, *cl_ptr;
int has_mult;
find_calias(player, alias_str, cap);
chp = cap->channel; /* save this for later */
zorch_alias_from_list(cap);
clear_chan_alias(tprintf("%d.%s", player, alias_str), cap);
/* Check if we have any aliases left pointing to that channel. */
clist = lookup_clist(player);
has_mult = 0;
for (cl_ptr = clist; cl_ptr != NULL; cl_ptr = cl_ptr->next) {
if (cl_ptr->alias_ptr->channel == chp) {
has_mult = 1;
break;
}
}
if (has_mult) {
notify(player, tprintf("You remove the alias '%s' for channel %s.",
alias_str, chp->name));
} else {
notify(player, tprintf("You leave channel %s.", chp->name));
remove_from_channel(player, chp, 0);
}
}
void do_clearcom(player, cause, key)
dbref player, cause;
int key;
{
notify(player, "You remove yourself from all channels.");
channel_clr(player);
}
void do_comtitle(player, cause, key, alias_str, title)
dbref player, cause;
int key;
char *alias_str, *title;
{
COMALIAS *cap;
find_calias(player, alias_str, cap);
if (cap->title)
XFREE(cap->title, "do_comtitle.title");
if (!title || !*title) {
notify(player, tprintf("Title cleared on channel %s.",
cap->channel->name));
return;
}
cap->title = XSTRDUP(munge_comtitle(title), "do_comtitle.title");
notify(player, tprintf("Title set to '%s' on channel %s.",
cap->title, cap->channel->name));
}
void do_clist(player, cause, key, chan_name)
dbref player, cause;
int key;
char *chan_name;
{
CHANNEL *chp;
char *buff, tbuf[LBUF_SIZE], *tp;
int count = 0;
if (chan_name && *chan_name) {
find_channel(player, chan_name, chp);
check_owned_channel(player, chp);
notify(player, chp->name);
tp = tbuf;
safe_str("Flags:", tbuf, &tp);
if (chp->flags & CHAN_FLAG_PUBLIC)
safe_str(" Public", tbuf, &tp);
if (chp->flags & CHAN_FLAG_LOUD)
safe_str(" Loud", tbuf, &tp);
if (chp->flags & CHAN_FLAG_SPOOF)
safe_str(" Spoof", tbuf, &tp);
if (chp->flags & CHAN_FLAG_P_JOIN)
safe_str(" P_Join", tbuf, &tp);
if (chp->flags & CHAN_FLAG_P_RECV)
safe_str(" P_Receive", tbuf, &tp);
if (chp->flags & CHAN_FLAG_P_TRANS)
safe_str(" P_Transmit", tbuf, &tp);
if (chp->flags & CHAN_FLAG_O_JOIN)
safe_str(" O_Join", tbuf, &tp);
if (chp->flags & CHAN_FLAG_O_RECV)
safe_str(" O_Receive", tbuf, &tp);
if (chp->flags & CHAN_FLAG_O_TRANS)
safe_str(" O_Transmit", tbuf, &tp);
*tp = '\0';
notify(player, tbuf);
if (chp->join_lock)
buff = unparse_boolexp(player, chp->join_lock);
else
buff = (char *) "*UNLOCKED*";
notify(player, tprintf("Join Lock: %s", buff));
if (chp->trans_lock)
buff = unparse_boolexp(player, chp->trans_lock);
else
buff = (char *) "*UNLOCKED*";
notify(player, tprintf("Transmit Lock: %s", buff));
if (chp->recv_lock)
buff = unparse_boolexp(player, chp->recv_lock);
else
buff = (char *) "*UNLOCKED*";
notify(player, tprintf("Receive Lock: %s", buff));
if (chp->descrip)
notify(player, tprintf("Description: %s", chp->descrip));
return;
}
if (key & CLIST_FULL) {
notify(player, "Channel Flags Locks Charge Balance Users Messages Owner");
} else if (key & CLIST_HEADER) {
notify(player, "Channel Owner Header");
} else {
notify(player, "Channel Owner Description");
}
for (chp = (CHANNEL *) hash_firstentry(&mod_comsys_comsys_htab);
chp != NULL;
chp = (CHANNEL *) hash_nextentry(&mod_comsys_comsys_htab)) {
if ((chp->flags & CHAN_FLAG_PUBLIC) ||
Comm_All(player) || (chp->owner == player)) {
if (key & CLIST_FULL) {
notify(player,
tprintf("%-20s %c%c%c%c%c%c%c%c%c %c%c%c %6d %7d %5d %8d #%d",
chp->name,
(chp->flags & CHAN_FLAG_PUBLIC) ? 'P' : '-',
(chp->flags & CHAN_FLAG_LOUD) ? 'L' : '-',
(chp->flags & CHAN_FLAG_SPOOF) ? 'S' : '-',
(chp->flags & CHAN_FLAG_P_JOIN) ? 'J' : '-',
(chp->flags & CHAN_FLAG_P_TRANS) ? 'X' : '-',
(chp->flags & CHAN_FLAG_P_RECV) ? 'R' : '-',
(chp->flags & CHAN_FLAG_O_JOIN) ? 'j' : '-',
(chp->flags & CHAN_FLAG_O_TRANS) ? 'x' : '-',
(chp->flags & CHAN_FLAG_O_RECV) ? 'r' : '-',
(chp->join_lock) ? 'J' : '-',
(chp->trans_lock) ? 'X' : '-',
(chp->recv_lock) ? 'R' : '-',
chp->charge,
chp->charge_collected,
chp->num_who,
chp->num_sent,
chp->owner));
} else {
notify(player,
tprintf("%-20s %-18s %-38.38s",
chp->name, Name(chp->owner),
((key & CLIST_HEADER) ? chp->header :
(chp->descrip ? chp->descrip : " "))));
}
count++;
}
}
if (Comm_All(player)) {
notify(player, tprintf("There %s %d %s.",
(count == 1) ? "is" : "are",
count,
(count == 1) ? "channel" : "channels"));
} else {
notify(player, tprintf("There %s %d %s visible to you.",
(count == 1) ? "is" : "are",
count,
(count == 1) ? "channel" : "channels"));
}
}
void do_comlist(player, cause, key)
dbref player, cause;
int key;
{
COMLIST *clist, *cl_ptr;
int count = 0;
clist = lookup_clist(player);
if (!clist) {
notify(player, "You are not on any channels.");
return;
}
notify(player, "Alias Channel Title");
for (cl_ptr = clist; cl_ptr != NULL; cl_ptr = cl_ptr->next) {
/* We are guaranteed alias and channel lengths that are not truncated.
* We need to truncate title.
*/
notify(player,
tprintf("%-10s %-20s %-40.40s %s",
cl_ptr->alias_ptr->alias,
cl_ptr->alias_ptr->channel->name,
(cl_ptr->alias_ptr->title) ? cl_ptr->alias_ptr->title :
(char *) " ",
(is_listenchannel(player, cl_ptr->alias_ptr->channel)) ?
"[on]" : " "));
count++;
}
notify(player, tprintf("You have %d channel %s.",
count,
(count == 1) ? "alias" : "aliases"));
}
void do_allcom(player, cause, key, cmd)
dbref player, cause;
int key;
char *cmd;
{
COMLIST *clist, *cl_ptr;
clist = lookup_clist(player);
if (!clist) {
notify(player, "You are not on any channels.");
return;
}
for (cl_ptr = clist; cl_ptr != NULL; cl_ptr = cl_ptr->next)
process_comsys(player, cmd, cl_ptr->alias_ptr);
}
int mod_comsys_process_command(player, cause, interactive, in_cmd,
args, nargs)
dbref player, cause;
int interactive;
char *in_cmd, *args[];
int nargs;
{
/* Return 1 if we got something, 0 if we didn't. */
char *arg;
COMALIAS *cap;
char cmd[LBUF_SIZE]; /* temp -- can't nibble our input */
if (!in_cmd || !*in_cmd || Slave(player))
return 0;
strcpy(cmd, in_cmd);
arg = cmd;
while (*arg && !isspace(*arg))
arg++;
if (*arg)
*arg++ = '\0';
cap = lookup_calias(player, cmd);
if (!cap)
return 0;
while (*arg && isspace(*arg))
arg++;
if (!*arg) {
notify(player, "No message.");
return 1;
}
process_comsys(player, arg, cap);
return 1;
}
/* --------------------------------------------------------------------------
* Command tables.
*/
NAMETAB cboot_sw[] = {
{(char *)"quiet", 1, CA_PUBLIC, CBOOT_QUIET},
{ NULL, 0, 0, 0}};
NAMETAB cemit_sw[] = {
{(char *)"noheader", 1, CA_PUBLIC, CEMIT_NOHEADER},
{ NULL, 0, 0, 0}};
NAMETAB channel_sw[] = {
{(char *)"charge", 1, CA_PUBLIC, CHANNEL_CHARGE},
{(char *)"desc", 1, CA_PUBLIC, CHANNEL_DESC},
{(char *)"header", 1, CA_PUBLIC, CHANNEL_HEADER},
{(char *)"lock", 1, CA_PUBLIC, CHANNEL_LOCK},
{(char *)"owner", 1, CA_PUBLIC, CHANNEL_OWNER},
{(char *)"set", 1, CA_PUBLIC, CHANNEL_SET},
{(char *)"join", 1, CA_PUBLIC, CHANNEL_JOIN | SW_MULTIPLE},
{(char *)"transmit", 1, CA_PUBLIC, CHANNEL_TRANS | SW_MULTIPLE},
{(char *)"receive", 1, CA_PUBLIC, CHANNEL_RECV | SW_MULTIPLE},
{ NULL, 0, 0, 0}};
NAMETAB clist_sw[] = {
{(char *)"full", 1, CA_PUBLIC, CLIST_FULL},
{(char *)"header", 1, CA_PUBLIC, CLIST_HEADER},
{ NULL, 0, 0, 0}};
NAMETAB cwho_sw[] = {
{(char *)"all", 1, CA_PUBLIC, CWHO_ALL},
{ NULL, 0, 0, 0}};
CMDENT mod_comsys_cmdtable[] = {
{(char *)"@cboot", cboot_sw, CA_NO_SLAVE|CA_NO_GUEST,
0, CS_TWO_ARG,
NULL, NULL, NULL, {do_cboot}},
{(char *)"@ccreate", NULL, CA_NO_SLAVE|CA_NO_GUEST,
0, CS_ONE_ARG,
NULL, NULL, NULL, {do_ccreate}},
{(char *)"@cdestroy", NULL, CA_NO_SLAVE|CA_NO_GUEST,
0, CS_ONE_ARG,
NULL, NULL, NULL, {do_cdestroy}},
{(char *)"@cemit", cemit_sw, CA_NO_SLAVE|CA_NO_GUEST,
0, CS_TWO_ARG,
NULL, NULL, NULL, {do_cemit}},
{(char *)"@channel", channel_sw, CA_NO_SLAVE|CA_NO_GUEST,
0, CS_TWO_ARG|CS_INTERP,
NULL, NULL, NULL, {do_channel}},
{(char *)"@clist", clist_sw, CA_NO_SLAVE,
0, CS_ONE_ARG,
NULL, NULL, NULL, {do_clist}},
{(char *)"@cwho", cwho_sw, CA_NO_SLAVE,
0, CS_ONE_ARG,
NULL, NULL, NULL, {do_cwho}},
{(char *)"addcom", NULL, CA_NO_SLAVE,
0, CS_TWO_ARG|CS_ARGV,
NULL, NULL, NULL, {do_addcom}},
{(char *)"allcom", NULL, CA_NO_SLAVE,
0, CS_ONE_ARG,
NULL, NULL, NULL, {do_allcom}},
{(char *)"comlist", NULL, CA_NO_SLAVE,
0, CS_NO_ARGS,
NULL, NULL, NULL, {do_comlist}},
{(char *)"comtitle", NULL, CA_NO_SLAVE,
0, CS_TWO_ARG,
NULL, NULL, NULL, {do_comtitle}},
{(char *)"clearcom", NULL, CA_NO_SLAVE,
0, CS_NO_ARGS,
NULL, NULL, NULL, {do_clearcom}},
{(char *)"delcom", NULL, CA_NO_SLAVE,
0, CS_ONE_ARG,
NULL, NULL, NULL, {do_delcom}},
{(char *)NULL, NULL, 0,
0, 0,
NULL, NULL, NULL, {NULL}}};
/* --------------------------------------------------------------------------
* Initialization, and other fun with files.
*/
void mod_comsys_dump_database(fp)
FILE *fp;
{
CHANNEL *chp;
COMALIAS *cap;
fprintf(fp, "+V4\n");
for (chp = (CHANNEL *) hash_firstentry(&mod_comsys_comsys_htab);
chp != NULL;
chp = (CHANNEL *) hash_nextentry(&mod_comsys_comsys_htab)) {
putstring(fp, chp->name);
putref(fp, chp->owner);
putref(fp, chp->flags);
putref(fp, chp->charge);
putref(fp, chp->charge_collected);
putref(fp, chp->num_sent);
putstring(fp, chp->descrip);
putstring(fp, chp->header);
putboolexp(fp, chp->join_lock);
fprintf(fp, "-\n");
putboolexp(fp, chp->trans_lock);
fprintf(fp, "-\n");
putboolexp(fp, chp->recv_lock);
fprintf(fp, "-\n");
fprintf(fp, "<\n");
}
fprintf(fp, "+V1\n");
for (cap = (COMALIAS *) hash_firstentry(&mod_comsys_calias_htab);
cap != NULL;
cap = (COMALIAS *) hash_nextentry(&mod_comsys_calias_htab)) {
putref(fp, cap->player);
putstring(fp, cap->channel->name);
putstring(fp, cap->alias);
putstring(fp, cap->title);
putref(fp, is_listening_disconn(cap->player, cap->channel));
fprintf(fp, "<\n");
}
fprintf(fp, "*** END OF DUMP ***\n");
}
static void comsys_flag_convert(chp)
CHANNEL *chp;
{
/* Convert MUX-style comsys flags to the new style. */
int old_flags, new_flags;
old_flags = chp->flags;
new_flags = 0;
if (old_flags & 0x200)
new_flags |= CHAN_FLAG_PUBLIC;
if (old_flags & 0x100)
new_flags |= CHAN_FLAG_LOUD;
if (old_flags & 0x1)
new_flags |= CHAN_FLAG_P_JOIN;
if (old_flags & 0x2)
new_flags |= CHAN_FLAG_P_TRANS;
if (old_flags & 0x4)
new_flags |= CHAN_FLAG_P_RECV;
if (old_flags & (0x10 * 0x1))
new_flags |= CHAN_FLAG_O_JOIN;
if (old_flags & (0x10 * 0x2))
new_flags |= CHAN_FLAG_O_TRANS;
if (old_flags & (0x10 * 0x4))
new_flags |= CHAN_FLAG_O_RECV;
chp->flags = new_flags;
}
static void comsys_data_update(chp, obj)
CHANNEL *chp;
dbref obj;
{
/* Copy data from a MUX channel object to a new-style channel. */
char *key;
dbref aowner;
int aflags, alen;
key = atr_get(obj, A_LOCK, &aowner, &aflags, &alen);
chp->join_lock = parse_boolexp(obj, key, 1);
free_lbuf(key);
key = atr_get(obj, A_LUSE, &aowner, &aflags, &alen);
chp->trans_lock = parse_boolexp(obj, key, 1);
free_lbuf(key);
key = atr_get(obj, A_LENTER, &aowner, &aflags, &alen);
chp->recv_lock = parse_boolexp(obj, key, 1);
free_lbuf(key);
key = atr_pget(obj, A_DESC, &aowner, &aflags, &alen);
if (*key)
chp->descrip = XSTRDUP(key, "comsys_data_update.desc");
else
chp->descrip = NULL;
free_lbuf(key);
}
static void read_comsys(fp, com_ver)
FILE *fp;
int com_ver;
{
CHANNEL *chp;
COMALIAS *cap;
COMLIST *clist;
CHANWHO *wp;
char c, *s, buf[LBUF_SIZE];
int done;
done = 0;
c = getc(fp);
if (c == '+') /* do we have any channels? */
done = 1;
ungetc(c, fp);
/* Load up the channels */
while (!done) {
chp = (CHANNEL *) XMALLOC(sizeof(CHANNEL), "load_comsys.channel");
chp->name = XSTRDUP(getstring_noalloc(fp, 1), "load_comsys.name");
chp->owner = getref(fp);
if (!Good_obj(chp->owner) || !isPlayer(chp->owner))
chp->owner = GOD; /* sanitize */
chp->flags = getref(fp);
if (com_ver == 1)
comsys_flag_convert(chp);
chp->charge = getref(fp);
chp->charge_collected = getref(fp);
chp->num_sent = getref(fp);
chp->header = NULL;
if (com_ver == 1) {
comsys_data_update(chp, getref(fp));
} else {
s = (char *) getstring_noalloc(fp, 1);
if (s && *s)
chp->descrip = XSTRDUP(s, "read_comsys.desc");
else
chp->descrip = NULL;
if (com_ver > 3) {
s = (char *) getstring_noalloc(fp, 1);
if (s && *s)
chp->header = XSTRDUP(s, "read_comsys.header");
}
if (com_ver == 2) {
/* Inherently broken behavior. Can't deal with eval locks,
* among other things.
*/
chp->join_lock = getboolexp1(fp);
getc(fp); /* eat newline */
chp->trans_lock = getboolexp1(fp);
getc(fp); /* eat newline */
chp->recv_lock = getboolexp1(fp);
getc(fp); /* eat newline */
} else {
chp->join_lock = getboolexp1(fp);
if (getc(fp) != '\n') {
/* Uh oh. Format error. Trudge on valiantly... probably
* won't work, but we can try.
*/
fprintf(mainlog_fp,
"Missing newline while reading join lock for channel %s\n",
chp->name);
}
c = getc(fp);
if (c == '\n') {
getc(fp); /* eat the dash on the next line */
getc(fp); /* eat the newline on the next line */
} else if (c == '-') {
getc(fp); /* eat the next newline */
} else {
fprintf(mainlog_fp,
"Expected termination sequence while reading join lock for channel %s\n",
chp->name);
}
chp->trans_lock = getboolexp1(fp);
if (getc(fp) != '\n') {
fprintf(mainlog_fp,
"Missing newline while reading transmit lock for channel %s\n",
chp->name);
}
c = getc(fp);
if (c == '\n') {
getc(fp); /* eat the dash on the next line */
getc(fp); /* eat the newline on the next line */
} else if (c == '-') {
getc(fp); /* eat the next newline */
} else {
fprintf(mainlog_fp,
"Expected termination sequence while reading transmit lock for channel %s\n",
chp->name);
}
chp->recv_lock = getboolexp1(fp);
if (getc(fp) != '\n') {
fprintf(mainlog_fp,
"Missing newline while reading receive lock for channel %s\n",
chp->name);
}
c = getc(fp);
if (c == '\n') {
getc(fp); /* eat the dash on the next line */
getc(fp); /* eat the newline on the next line */
} else if (c == '-') {
getc(fp); /* eat the next newline */
} else {
fprintf(mainlog_fp,
"Expected termination sequence while reading receive lock for channel %s\n",
chp->name);
}
}
}
if (!chp->header) {
sprintf(buf, "[%s]", chp->name);
chp->header = XSTRDUP(buf, "read_comsys.header");
}
chp->who = NULL;
chp->num_who = 0;
chp->connect_who = NULL;
chp->num_connected = 0;
hashadd(chp->name, (int *) chp, &mod_comsys_comsys_htab, 0);
getstring_noalloc(fp, 0); /* discard the < */
c = getc(fp);
if (c == '+') /* look ahead for the end of the channels */
done = 1;
ungetc(c, fp);
}
getstring_noalloc(fp, 0); /* discard the version string */
done = 0;
c = getc(fp);
if (c == '*') /* do we have any aliases? */
done = 1;
ungetc(c, fp);
/* Load up the aliases */
while (!done) {
cap = (COMALIAS *) XMALLOC(sizeof(COMALIAS), "load_comsys.alias");
cap->player = getref(fp);
cap->channel = lookup_channel((char *) getstring_noalloc(fp, 1));
cap->alias = XSTRDUP(getstring_noalloc(fp, 1), "load_comsys.alias_str");
s = (char *) getstring_noalloc(fp, 1);
if (s && *s)
cap->title = XSTRDUP(s, "load_comsys.title");
else
cap->title = NULL;
hashadd(tprintf("%d.%s", cap->player, cap->alias), (int *) cap,
&mod_comsys_calias_htab, 0);
clist = (COMLIST *) XMALLOC(sizeof(COMLIST), "load_comsys.clist");
clist->alias_ptr = cap;
clist->next = lookup_clist(cap->player);
if (clist->next == NULL)
nhashadd((int) cap->player, (int *) clist, &mod_comsys_comlist_htab);
else
nhashrepl((int) cap->player, (int *) clist,
&mod_comsys_comlist_htab);
if (!is_onchannel(cap->player, cap->channel)) {
wp = (CHANWHO *) XMALLOC(sizeof(CHANWHO), "load_comsys.who");
wp->player = cap->player;
wp->is_listening = getref(fp);
if ((cap->channel->num_who == 0) || (cap->channel->who == NULL)) {
wp->next = NULL;
} else {
wp->next = cap->channel->who;
}
cap->channel->who = wp;
cap->channel->num_who++;
} else {
getref(fp); /* toss the value */
}
getstring_noalloc(fp, 0); /* discard the < */
c = getc(fp);
if (c == '*') /* look ahead for the end of the aliases */
done = 1;
ungetc(c, fp);
}
s = (char *) getstring_noalloc(fp, 0);
if (strcmp(s, (char *) "*** END OF DUMP ***")) {
STARTLOG(LOG_STARTUP, "INI", "COM")
log_printf("Aborted load on unexpected line: %s", s);
ENDLOG
}
}
static void sanitize_comsys()
{
/* Because we can run into situations where the comsys db and
* regular database are not in sync (ex: restore from backup),
* we need to sanitize the comsys data structures at load time.
* The comlists we have represent the dbrefs of objects on channels.
* Thus we can just look at what objects are there, and work
* accordingly.
*/
int i, count;
NHSHTAB *htab;
NHSHENT *hptr;
int *ptab;
count = 0;
htab = &mod_comsys_comlist_htab;
ptab = (int *) XCALLOC(htab->entries, sizeof(int), "sanitize_comsys");
for (i = 0; i < htab->hashsize; i++) {
for (hptr = htab->entry[i]; hptr != NULL; hptr = hptr->next) {
if (!Good_obj(hptr->target.i)) {
ptab[count] = hptr->target.i;
count++;
}
}
}
/* Have to do this separately, so we don't mess up the hashtable
* linking while we're trying to traverse it.
*/
for (i = 0; i < count; i++)
channel_clr(ptab[i]);
XFREE(ptab, "sanitize_comsys");
}
void mod_comsys_make_minimal()
{
CHANNEL *chp;
do_ccreate(GOD, GOD, 0, mod_comsys_config.public_channel);
chp = lookup_channel(mod_comsys_config.public_channel);
if (chp) { /* should always be true, but be safe */
chp->flags |= CHAN_FLAG_PUBLIC;
}
do_ccreate(GOD, GOD, 0, mod_comsys_config.guests_channel);
chp = lookup_channel(mod_comsys_config.guests_channel);
if (chp) { /* should always be true, but be safe */
chp->flags |= CHAN_FLAG_PUBLIC;
}
}
void mod_comsys_load_database(fp)
FILE *fp;
{
char buffer[2 * MBUF_SIZE + 8]; /* depends on max length of params */
fgets(buffer, sizeof(buffer), fp);
if (!strncmp(buffer, (char *) "+V", 2)) {
read_comsys(fp, atoi(buffer + 2));
sanitize_comsys();
} else {
STARTLOG(LOG_STARTUP, "INI", "COM")
log_printf("Unrecognized comsys format.");
ENDLOG
mod_comsys_make_minimal();
}
}
/* --------------------------------------------------------------------------
* User functions.
*/
#define Grab_Channel(p) \
chp = lookup_channel(fargs[0]); \
if (!chp) { \
safe_str((char *) "#-1 CHANNEL NOT FOUND", buff, bufc); \
return; \
} \
if ((!Comm_All(p) && ((p) != chp->owner))) { \
safe_str((char *) "#-1 NO PERMISSION TO USE", buff, bufc); \
return; \
}
#define Comsys_User(p,t) \
t = lookup_player(p, fargs[0], 1); \
if (!Good_obj(t) || (!Controls(p,t) && !Comm_All(p))) { \
safe_str((char *) "#-1 NO PERMISSION TO USE", buff, bufc); \
return; \
}
#define Grab_Alias(p,n) \
cap = lookup_calias(p,n); \
if (!cap) { \
safe_str((char *) "#-1 NO SUCH ALIAS", buff, bufc); \
return; \
}
FUNCTION(fun_comlist)
{
CHANNEL *chp;
char *bb_p;
Delim osep;
VaChk_Only_Out(1);
bb_p = *bufc;
for (chp = (CHANNEL *) hash_firstentry(&mod_comsys_comsys_htab);
chp != NULL;
chp = (CHANNEL *) hash_nextentry(&mod_comsys_comsys_htab)) {
if ((chp->flags & CHAN_FLAG_PUBLIC) ||
Comm_All(player) || (chp->owner == player)) {
if (*bufc != bb_p) {
print_sep(&osep, buff, bufc);
}
safe_str(chp->name, buff, bufc);
}
}
}
FUNCTION(fun_cwho)
{
CHANNEL *chp;
char *bb_p;
int i;
Grab_Channel(player);
bb_p = *bufc;
for (i = 0; i < chp->num_connected; i++) {
if (chp->connect_who[i]->is_listening &&
(!isPlayer(chp->connect_who[i]->player) ||
(Connected(chp->connect_who[i]->player) &&
(!Hidden(chp->connect_who[i]->player) || See_Hidden(player))))) {
if (*bufc != bb_p)
safe_chr(' ', buff, bufc);
safe_dbref(buff, bufc, chp->connect_who[i]->player);
}
}
}
FUNCTION(fun_cwhoall)
{
CHANNEL *chp;
CHANWHO *wp;
char *bb_p;
Grab_Channel(player);
bb_p = *bufc;
for (wp = chp->who; wp != NULL; wp = wp->next) {
if (*bufc != bb_p)
safe_chr(' ', buff, bufc);
safe_dbref(buff, bufc, wp->player);
}
}
FUNCTION(fun_comowner)
{
CHANNEL *chp;
Grab_Channel(player);
safe_dbref(buff, bufc, chp->owner);
}
FUNCTION(fun_comdesc)
{
CHANNEL *chp;
Grab_Channel(player);
if (chp->descrip)
safe_str(chp->descrip, buff, bufc);
}
FUNCTION(fun_comheader)
{
CHANNEL *chp;
Grab_Channel(player);
if (chp->header)
safe_str(chp->header, buff, bufc);
}
FUNCTION(fun_comalias)
{
dbref target;
COMLIST *clist, *cl_ptr;
char *bb_p;
Comsys_User(player, target);
clist = lookup_clist(target);
if (!clist)
return;
bb_p = *bufc;
for (cl_ptr = clist; cl_ptr != NULL; cl_ptr = cl_ptr->next) {
if (*bufc != bb_p)
safe_chr(' ', buff, bufc);
safe_str(cl_ptr->alias_ptr->alias, buff, bufc);
}
}
FUNCTION(fun_cominfo)
{
dbref target;
COMALIAS *cap;
Comsys_User(player, target);
Grab_Alias(target, fargs[1]);
safe_str(cap->channel->name, buff, bufc);
}
FUNCTION(fun_comtitle)
{
dbref target;
COMALIAS *cap;
Comsys_User(player, target);
Grab_Alias(target, fargs[1]);
if (cap->title)
safe_str(cap->title, buff, bufc);
}
FUNCTION(fun_cemit)
{
CHANNEL *chp;
Grab_Channel(player);
com_message(chp, fargs[1], player);
}
FUN mod_comsys_functable[] = {
{"CEMIT", fun_cemit, 2, 0, CA_PUBLIC, NULL},
{"COMALIAS", fun_comalias, 1, 0, CA_PUBLIC, NULL},
{"COMDESC", fun_comdesc, 1, 0, CA_PUBLIC, NULL},
{"COMHEADER", fun_comheader, 1, 0, CA_PUBLIC, NULL},
{"COMINFO", fun_cominfo, 2, 0, CA_PUBLIC, NULL},
{"COMLIST", fun_comlist, 0, FN_VARARGS, CA_PUBLIC, NULL},
{"COMOWNER", fun_comowner, 1, 0, CA_PUBLIC, NULL},
{"COMTITLE", fun_comtitle, 2, 0, CA_PUBLIC, NULL},
{"CWHO", fun_cwho, 1, 0, CA_PUBLIC, NULL},
{"CWHOALL", fun_cwhoall, 1, 0, CA_PUBLIC, NULL},
{NULL, NULL, 0, 0, 0, NULL}};
/* --------------------------------------------------------------------------
* Initialization.
*/
void mod_comsys_cleanup_startup()
{
update_comwho_all();
}
void mod_comsys_create_player(creator, player, isrobot, isguest)
dbref creator, player;
int isrobot, isguest;
{
if (isguest && (player != 1)) {
if (*mod_comsys_config.guests_channel)
join_channel(player, mod_comsys_config.guests_channel,
mod_comsys_config.guests_calias, NULL);
} else if (player != 1) { /* avoid problems with minimal db */
if (*mod_comsys_config.public_channel)
join_channel(player, mod_comsys_config.public_channel,
mod_comsys_config.public_calias, NULL);
}
}
void mod_comsys_destroy_obj(player, obj)
dbref player, obj;
{
channel_clr(obj);
}
void mod_comsys_destroy_player(player, victim)
dbref player, victim;
{
comsys_chown(victim, Owner(player));
}
void mod_comsys_init()
{
mod_comsys_config.public_channel = XSTRDUP("Public", "cf_string");
mod_comsys_config.guests_channel = XSTRDUP("Guests", "cf_string");
mod_comsys_config.public_calias = XSTRDUP("pub", "cf_string");
mod_comsys_config.guests_calias = XSTRDUP("g", "cf_string");
register_hashtables(mod_comsys_hashtable, mod_comsys_nhashtable);
register_commands(mod_comsys_cmdtable);
register_functions(mod_comsys_functable);
}