#include "copyrite.h" #include "config.h" #include <ctype.h> #ifdef I_STDLIB #include <stdlib.h> #endif #ifdef I_STRING #include <string.h> #else #include <strings.h> #endif #ifdef I_SYS_TYPES #include <sys/types.h> #endif #include "mushdb.h" #include "intrface.h" #include "match.h" #include "conf.h" #include "externs.h" #include "extchat.h" #include "ansi.h" #include "privtab.h" #include "mymalloc.h" #include "pueblo.h" #include "confmagic.h" #ifdef CHAT_SYSTEM static CHAN *new_channel _((void)); static CHANLIST *new_chanlist _((void)); static CHANUSER *new_user _((dbref who)); static void free_channel _((CHAN *c)); static void free_chanlist _((CHANLIST * cl)); static void free_user _((CHANUSER *u)); static int load_channel _((FILE * fp, CHAN *ch)); static int load_chanusers _((FILE * fp, CHAN *ch)); static void insert_channel _((CHAN **ch)); static void remove_channel _((CHAN *ch)); static void insert_obj_chan _((dbref who, CHAN **ch)); static void remove_obj_chan _((dbref who, CHAN *ch)); void remove_all_obj_chan _((dbref thing)); static void chan_chown _((CHAN *c, dbref victim)); void chan_chownall _((dbref old, dbref new)); static int insert_user _((CHANUSER *user, CHAN *ch)); static int remove_user _((CHANUSER *u, CHAN *ch)); static int save_channel _((FILE * fp, CHAN *ch)); static int save_chanuser _((FILE * fp, CHANUSER *user)); static void channel_wipe _((dbref player, CHAN *chan)); static int yesno _((const char *str)); static int canstilladd _((dbref player)); extern void do_channel_who _((dbref player, CHAN *chan)); #define YES 1 #define NO 0 #define ERR -1 #define insert_user_by_dbref(who,chan) \ insert_user(new_user(who),chan) #define remove_user_by_dbref(who,chan) \ remove_user(onchannel(who,chan),chan) int num_channels; CHAN *channels; static PRIV priv_table[] = { {"Disabled", 'D', CHANNEL_DISABLED, CHANNEL_DISABLED}, {"Admin", 'A', CHANNEL_ADMIN | CHANNEL_PLAYER, CHANNEL_ADMIN}, {"Wizard", 'W', CHANNEL_WIZARD | CHANNEL_PLAYER, CHANNEL_WIZARD}, {"Player", 'P', CHANNEL_PLAYER, CHANNEL_PLAYER}, {"Object", 'O', CHANNEL_OBJECT, CHANNEL_OBJECT}, {"Quiet", 'Q', CHANNEL_QUIET, CHANNEL_QUIET}, {"Hide_Ok", 'H', CHANNEL_CANHIDE, CHANNEL_CANHIDE}, {NULL, '\0', 0, 0} }; /* Is the player on the channel? If so, return a pointer to player's * CHANUSER */ CHANUSER * onchannel(who, ch) dbref who; CHAN *ch; { static CHANUSER *u; for (u = ChanUsers(ch); u; u = u->next) { if (CUdbref(u) == who) { return u; } } return NULL; } /* A macro to test if a channel exists and, if not, to notify */ #define test_channel(player,name,chan) \ do { \ chan = NULL; \ switch (find_channel(name,&chan)) { \ case CMATCH_NONE: \ notify(player, "CHAT: I don't recognize that channel."); \ return; \ case CMATCH_AMBIG: \ notify(player, "CHAT: I don't know which channel you mean."); \ return; \ } \ } while (0) /*---------------------------------------------------------- * Loading and saving the chatdb * The chatdb's format is pretty straightforward * Return 1 on success, 0 on failure */ void init_chatdb() { num_channels = 0; channels = NULL; } int load_chatdb(fp) FILE *fp; { int i; CHAN *ch; /* How many channels? */ num_channels = getref(fp); if (num_channels > MAX_CHANNELS) return 0; /* Load all channels */ for (i = 0; i < num_channels; i++) { if (feof(fp)) break; ch = new_channel(); if (!ch) break; if (!load_channel(fp, ch)) { fprintf(stderr, "Unable to load channel %d.", i); free_channel(ch); break; } insert_channel(&ch); } num_channels = i; return 1; } /* Malloc memory for a new channel, and initialize it */ static CHAN * new_channel() { CHAN *ch; ch = (CHAN *) mush_malloc(sizeof(CHAN), "CHAN"); if (!ch) return NULL; ch->name[0] = '\0'; ch->title[0] = '\0'; ChanType(ch) = CHANNEL_DEFAULT_FLAGS; ChanCreator(ch) = NOTHING; ChanCost(ch) = CHANNEL_COST; ChanNext(ch) = NULL; ChanNumMsgs(ch) = 0; /* By default channels are public but mod-lock'd to the creator */ ChanJoinLock(ch) = TRUE_BOOLEXP; ChanSpeakLock(ch) = TRUE_BOOLEXP; ChanSeeLock(ch) = TRUE_BOOLEXP; ChanHideLock(ch) = TRUE_BOOLEXP; ChanModLock(ch) = TRUE_BOOLEXP; ChanNumUsers(ch) = 0; ChanMaxUsers(ch) = 0; ChanUsers(ch) = NULL; return ch; } /* Malloc memory for a new user, and initialize it */ static CHANUSER * new_user(who) dbref who; { CHANUSER *u; u = (CHANUSER *) mush_malloc(sizeof(CHANUSER), "CHANUSER"); if (!u) return NULL; CUdbref(u) = who; CUtype(u) = CU_DEFAULT_FLAGS; u->title[0] = '\0'; CUnext(u) = NULL; return u; } /* Free memory from a channel */ static void free_channel(c) CHAN *c; { CHANUSER *u, *unext; if (!c) return; if (ChanJoinLock(c)) free(ChanJoinLock(c)); if (ChanSpeakLock(c)) free(ChanSpeakLock(c)); if (ChanHideLock(c)) free(ChanHideLock(c)); if (ChanSeeLock(c)) free(ChanSeeLock(c)); if (ChanModLock(c)) free(ChanModLock(c)); u = ChanUsers(c); while (u) { unext = u->next; free_user(u); u = unext; } return; } /* Free memory from a channel user */ static void free_user(u) CHANUSER *u; { if (u) mush_free(u, "CHANUSER"); } /* Load in a single channel into position i. Return 1 if * successful, 0 otherwise. */ static int load_channel(fp, ch) FILE *fp; CHAN *ch; { strcpy(ChanName(ch), getstring_noalloc(fp)); if (feof(fp)) return 0; strcpy(ChanTitle(ch), getstring_noalloc(fp)); ChanType(ch) = getref(fp); ChanCreator(ch) = getref(fp); ChanCost(ch) = getref(fp); ChanNumMsgs(ch) = 0; ChanJoinLock(ch) = getboolexp(fp); ChanSpeakLock(ch) = getboolexp(fp); ChanModLock(ch) = getboolexp(fp); ChanSeeLock(ch) = getboolexp(fp); ChanHideLock(ch) = getboolexp(fp); ChanNumUsers(ch) = getref(fp); ChanMaxUsers(ch) = ChanNumUsers(ch); ChanUsers(ch) = NULL; if (ChanNumUsers(ch) > 0) return (ChanNumUsers(ch) = load_chanusers(fp, ch)); return 1; } /* Load the *channel's user list. Return number of users on success, or 0 */ static int load_chanusers(fp, ch) FILE *fp; CHAN *ch; { int i, num = 0; CHANUSER *user; dbref player; for (i = 0; i < ChanNumUsers(ch); i++) { player = getref(fp); /* Don't bother if the player isn't a valid dbref or the wrong type */ if (GoodObject(player) && Chan_Ok_Type(ch, player)) { user = new_user(player); if (!user) return 0; CUtype(user) = getref(fp); strcpy(CUtitle(user), getstring_noalloc(fp)); CUnext(user) = NULL; if (insert_user(user, ch)) num++; } else { /* But be sure to read (and discard) the player's info */ do_log(LT_ERR, 0, 0, "Bad object #%d removed from channel %s", player, ChanName(ch)); (void) getref(fp); (void) getstring_noalloc(fp); } } return num; } /* Insert the channel onto the list of channels, sorted by name */ static void insert_channel(ch) CHAN **ch; { CHAN *p; if (!ch || !*ch) return; /* If there's no users on the list, or if the first user is already * alphabetically greater, user should be the first entry on the list */ /* No channels? */ if (!channels) { channels = *ch; channels->next = NULL; return; } p = channels; /* First channel? */ if (strcasecmp(ChanName(p), ChanName(*ch)) > 0) { channels = *ch; channels->next = p; return; } /* Otherwise, find which user this user should be inserted after */ for (; p->next && (strcasecmp(ChanName(p->next), ChanName(*ch)) < 0); p = p->next) ; (*ch)->next = p->next; p->next = *ch; return; } /* Remove a channel from the list, but don't free it */ static void remove_channel(ch) CHAN *ch; { CHAN *p; if (!ch) return; if (!channels) return; if (channels == ch) { /* First channel */ channels = ch->next; return; } /* Otherwise, find the channel before this one */ for (p = channels; p->next && (p->next != ch); p = p->next) ; if (p->next) { p->next = ch->next; } return; } /* Insert the channel onto the list of channels on a given object, * sorted by name */ static void insert_obj_chan(who, ch) dbref who; CHAN **ch; { CHANLIST *p; CHANLIST *tmp; if (!ch || !*ch) return; tmp = new_chanlist(); if (!tmp) return; tmp->chan = *ch; /* If there's no channels on the list, or if the first channel is already * alphabetically greater, user should be the first entry on the list */ /* No channels? */ if (!Chanlist(who)) { Chanlist(who) = tmp; Chanlist(who)->next = NULL; return; } p = Chanlist(who); /* First channel? */ if (strcasecmp(ChanName(p->chan), ChanName(*ch)) > 0) { Chanlist(who) = tmp; Chanlist(who)->next = p; return; } else if (!strcasecmp(ChanName(p->chan), ChanName(*ch))) { /* Don't add the same channel twice! */ free_chanlist(tmp); } else { /* Otherwise, find which user this user should be inserted after */ for (; p->next && (strcasecmp(ChanName(p->next->chan), ChanName(*ch)) < 0); p = p->next) ; if (p->next && !strcasecmp(ChanName(p->next->chan), ChanName(*ch))) { /* Don't add the same channel twice! */ free_chanlist(tmp); } else { tmp->next = p->next; p->next = tmp; } } return; } /* Remove a channel from the obj's chanlist, and free the chanlist ptr */ static void remove_obj_chan(who, ch) dbref who; CHAN *ch; { CHANLIST *p, *q; if (!ch) return; if (!Chanlist(who)) return; p = Chanlist(who); if (p->chan == ch) { /* First channel */ Chanlist(who) = p->next; free_chanlist(p); return; } /* Otherwise, find the channel before this one */ for (; p->next && (p->next->chan != ch); p = p->next) ; if (p->next) { q = p->next; p->next = p->next->next; free_chanlist(q); } return; } /* Remove all channels from the obj's chanlist, freeing them */ void remove_all_obj_chan(thing) dbref thing; { CHANLIST *p, *nextp; for (p = Chanlist(thing); p; p = nextp) { nextp = p->next; remove_user_by_dbref(thing, p->chan); } return; } static CHANLIST * new_chanlist() { CHANLIST *c; c = (CHANLIST *) mush_malloc(sizeof(CHANLIST), "CHANLIST"); if (!c) return NULL; c->chan = NULL; c->next = NULL; return c; } static void free_chanlist(cl) CHANLIST *cl; { mush_free(cl, "CHANLIST"); } /* Insert the user onto the channel's list, sorted by the user's name */ static int insert_user(user, ch) CHANUSER *user; CHAN *ch; { CHANUSER *p; if (!user || !ch) return 0; /* If there's no users on the list, or if the first user is already * alphabetically greater, user should be the first entry on the list */ p = ChanUsers(ch); if (!p || (strcasecmp(Name(CUdbref(p)), Name(CUdbref(user))) > 0)) { user->next = ChanUsers(ch); ChanUsers(ch) = user; } else { /* Otherwise, find which user this user should be inserted after */ for (; p->next && (strcasecmp(Name(CUdbref(p->next)), Name(CUdbref(user))) <= 0); p = p->next) ; if (CUdbref(p) == CUdbref(user)) { /* Don't add the same user twice! */ mush_free((Malloc_t) user, "CHANUSER"); return 0; } else { user->next = p->next; p->next = user; } } insert_obj_chan(CUdbref(user), &ch); return 1; } /* Remove a user from a channel list, and free it */ static int remove_user(u, ch) CHANUSER *u; CHAN *ch; { CHANUSER *p; dbref who; if (!ch || !u) return 0; p = ChanUsers(ch); if (!p) return 0; who = CUdbref(u); if (p == u) { /* First user */ ChanUsers(ch) = p->next; free_user(u); } else { /* Otherwise, find the user before this one */ for (; p->next && (p->next != u); p = p->next) ; if (p->next) { p->next = u->next; free_user(u); } else return 0; } /* Now remove the channel from the user's chanlist */ remove_obj_chan(who, ch); ChanNumUsers(ch)--; return 1; } int save_chatdb(fp) FILE *fp; { CHAN *ch; /* How many channels? */ putref(fp, num_channels); for (ch = channels; ch; ch = ch->next) { save_channel(fp, ch); } return 1; } /* Save a single channel. Return 1 if successful, 0 otherwise. */ static int save_channel(fp, ch) FILE *fp; CHAN *ch; { CHANUSER *cu; putstring(fp, ChanName(ch)); putstring(fp, ChanTitle(ch)); putref(fp, ChanType(ch)); putref(fp, ChanCreator(ch)); putref(fp, ChanCost(ch)); putboolexp(fp, ChanJoinLock(ch)); putboolexp(fp, ChanSpeakLock(ch)); putboolexp(fp, ChanModLock(ch)); putboolexp(fp, ChanSeeLock(ch)); putboolexp(fp, ChanHideLock(ch)); putref(fp, ChanNumUsers(ch)); for (cu = ChanUsers(ch); cu; cu = cu->next) save_chanuser(fp, cu); return 1; } /* Save the channel's user list. Return 1 on success, 0 on failure */ static int save_chanuser(fp, user) FILE *fp; CHANUSER *user; { putref(fp, CUdbref(user)); putref(fp, CUtype(user)); putstring(fp, CUtitle(user)); return 1; } /*-------------------------------------------------------------* * Some utility functions: * find_channel - given a name, return a channel * find_channel_partial - given a name and a player, return * the first channel that matches name that player is on. * onchannel - is player on channel? */ /* Given name and a chan pointer, set chan pointer to point to * channel if found (NULL otherwise), and return an indication * of how good the match was */ int find_channel(name, chan) const char *name; CHAN **chan; { CHAN *p; int count = 0; *chan = NULL; if (!name || !*name) return CMATCH_NONE; for (p = channels; p; p = p->next) { if (!strcasecmp(name, ChanName(p))) { *chan = p; return CMATCH_EXACT; } if (string_prefix(ChanName(p), name)) { *chan = p; count++; } } switch (count) { case 0: return CMATCH_NONE; case 1: return CMATCH_PARTIAL; } return CMATCH_AMBIG; } /* Given a name and a chan pointer, set chan pointer to point * to channel if found. If the channel is ambiguous, return * the first channel matched that the player is on. */ int find_channel_partial(name, chan, player) const char *name; CHAN **chan; dbref player; { CHAN *p; int count = 0; *chan = NULL; if (!name || !*name) return CMATCH_NONE; for (p = channels; p; p = p->next) { if (!strcasecmp(name, ChanName(p))) { *chan = p; return CMATCH_EXACT; } if (string_prefix(ChanName(p), name)) { /* If we've already found an ambiguous match that the * player is on, keep using that one. Otherwise, this is * our best candidate so far. */ if (!*chan || !onchannel(player, *chan)) *chan = p; count++; } } switch (count) { case 0: return CMATCH_NONE; case 1: return CMATCH_PARTIAL; } return CMATCH_AMBIG; } /*--------------------------------------------------------------* * User commands: * do_channel - @channel/on,off,who * do_chan_admin - @channel/add,delete,name,priv,quiet * do_chan_desc * do_chan_title * do_chan_lock * do_chan_boot * do_chan_wipe */ void do_channel(player, name, target, com) dbref player; const char *name; const char *target; const char *com; { /* join, quit, wipe, or who a channel */ CHAN *chan = NULL; dbref victim; if (!name && !*name) { notify(player, "You need to specify a channel."); return; } if (!com && !*com) { notify(player, "What do you want to do with that channel?"); return; } test_channel(player, name, chan); if (!Chan_Can_See(chan, player)) { if (onchannel(player, chan)) notify(player, "CHAT: You can't do that with that channel."); else notify(player, "CHAT: I don't recognize that channel."); return; } if (!strcasecmp(com, "who")) { do_channel_who(player, chan); return; } else if (!strcasecmp(com, "wipe")) { channel_wipe(player, chan); return; } /* It's on or off now */ /* Determine who is getting added or deleted. If we don't have * an argument, we assume it's the player. */ if (!target || !*target) victim = player; else if ((victim = lookup_player(target)) != NOTHING) ; else if (Channel_Object(chan)) victim = match_result(player, target, TYPE_THING, MAT_OBJECTS); else victim = NOTHING; if (!GoodObject(victim) || !controls(player, victim)) { notify(player, "Invalid target."); return; } if (!Chan_Ok_Type(chan, victim)) { notify(player, "Sorry, wrong type of thing for that channel."); return; } if (!strcasecmp("on", com)) { if (Guest(player)) { notify(player, "Guests are not allowed to join channels."); return; } /* Is victim already on the channel? */ if (onchannel(victim, chan)) { notify(player, "That player is already on that channel."); return; } /* Does victim pass the joinlock? */ if (!Chan_Can_Join(chan, victim)) { if (Wizard(player)) { /* Wizards can override join locks */ notify(player, "CHAT: Warning: Player does not meet channel join permissions (joining anyway)"); } else { notify(player, "Permission to join denied."); return; } } if (insert_user_by_dbref(victim, chan)) { if (victim != player) { notify(victim, tprintf("CHAT: %s joins you to channel <%s>.", Name(player), ChanName(chan))); notify(player, tprintf("CHAT: You join %s to channel <%s>.", Name(victim), ChanName(chan))); } else notify(victim, tprintf("CHAT: You join channel <%s>.", ChanName(chan))); if (!Channel_Quiet(chan) && !(Wizard(victim) && Dark(victim))) channel_broadcast(chan, 1, "<%s> %s has joined this channel.", ChanName(chan), Name(victim)); ChanNumUsers(chan)++; } else { notify(player, "That player is already on that channel."); } return; } else if (!strcasecmp("off", com)) { if (Guest(player)) { notify(player, "Guests may not leave channels."); return; } if (remove_user_by_dbref(victim, chan)) { if (!Channel_Quiet(chan) && !(Wizard(victim) && Dark(victim))) channel_broadcast(chan, 1, "<%s> %s has left this channel.", ChanName(chan), Name(victim)); if (victim != player) { notify(victim, tprintf("CHAT: %s removes you from channel <%s>.", Name(player), ChanName(chan))); notify(player, tprintf("CHAT: You remove %s from channel <%s>.", Name(victim), ChanName(chan))); } else notify(victim, tprintf("CHAT: You leave channel <%s>.", ChanName(chan))); } else { notify(player, "That player is not on that channel."); } return; } else { notify(player, "I don't understand what you want to do."); return; } } int do_chat_by_name(player, name, msg) dbref player; const char *name; const char *msg; { /* Parse the name and call do_chat. If name fails, * return silently. */ CHAN *c; c = NULL; if (!msg || !*msg) return 0; if (find_channel_partial(name, &c, player) == CMATCH_NONE) return 0; do_chat(player, c, msg); return 1; } void do_chat(player, chan, arg1) dbref player; CHAN *chan; const char *arg1; { /* send a message to a channel */ CHANUSER *u; int key; const char *gap; char *title; if (!Chan_Ok_Type(chan, player)) { notify(player, "Sorry, you're not the right type to be on that channel."); return; } if (!Chan_Can_Speak(chan, player)) { notify(player, "Sorry, you're not allowed to speak on that channel."); return; } /* figure out what kind of message we have */ gap = " "; switch (*arg1) { case SEMI_POSE_TOKEN: gap = ""; /* FALLTHRU */ case POSE_TOKEN: key = 1; arg1 = arg1 + 1; break; case '\0': key = 3; break; default: key = 2; break; } if ((u = onchannel(player, chan)) &&CUtitle(u) && *CUtitle(u)) title = CUtitle(u); else title = NULL; /* now send out the message. If the player isn't on that channel, tell * him what he said. */ switch (key) { case 1: channel_broadcast(chan, 0, "<%s> %s%s%s%s%s", ChanName(chan), title ? title : "", title ? " " : "", Name(player), gap, arg1); if (!onchannel(player, chan)) notify(player, tprintf("To channel %s: %s%s%s", ChanName(chan), Name(player), gap, arg1)); break; case 2: channel_broadcast(chan, 0, "<%s> %s%s%s says, \"%s\"", ChanName(chan), title ? title : "", title ? " " : "", Name(player), arg1); if (!onchannel(player, chan)) notify(player, tprintf("To channel %s: %s says, \"%s\"", ChanName(chan), Name(player), arg1)); break; case 3: notify(player, "What do you want to say to that channel?"); break; } ChanNumMsgs(chan)++; } void do_cemit(player, name, msg) dbref player; const char *name; const char *msg; { /* Send a message to a channel, no prefix. */ CHAN *chan = NULL; if (!Can_Cemit(player)) { notify(player, "You can't channel-surf that well."); return; } if (!name || !*name) { notify(player, "That is not a valid channel."); return; } switch (find_channel(name, &chan)) { case CMATCH_NONE: notify(player, "I don't recognize that channel."); return; case CMATCH_AMBIG: notify(player, "I don't know which channel you mean."); return; } if (!Chan_Can_See(chan, player)) { notify(player, "CHAT: I don't recognize that channel."); return; } if (!msg || !*msg) { notify(player, "What do you want to emit?"); return; } channel_broadcast(chan, 2, "%s", msg); if (!onchannel(player, chan)) notify(player, tprintf("Cemit to channel %s: %s", ChanName(chan), msg)); ChanNumMsgs(chan)++; return; } void do_chan_admin(player, name, perms, flag) dbref player; char *name; const char *perms; int flag; { /* Based on flag: * 0: add name=perms * 1: delete name * 2: rename name=newname * 3: priv name=newpriv * 4: quiet name=on/off */ CHAN *chan = NULL, *temp = NULL; long int type; int res; struct boolexp *key; if (!name || !*name) { notify(player, "You must specify a channel."); return; } if (Guest(player)) { notify(player, "Guests may not modify channels."); return; } if ((flag > 1) && (!perms || !*perms)) { notify(player, "What do you want to do with the channel?"); return; } /* Make sure we've got a unique channel name unless we're * adding a channel */ if (flag) test_channel(player, name, chan); switch (flag) { case 0: /* add a channel */ if (num_channels == MAX_CHANNELS) { notify(player, "No more room for channels."); return; } if (strlen(name) > CHAN_NAME_LEN - 1) { notify(player, "The channel needs a shorter name."); return; } if (!Hasprivs(player) && !canstilladd(player)) { notify(player, "You already own too many channels."); return; } res = find_channel(name, &chan); if (res != CMATCH_NONE) { notify(player, "CHAT: The channel needs a more unique name."); return; } /* get the permissions. Invalid specs default to the default */ type = string_to_privs(priv_table, perms, 0); if (!Chan_Can(player, type)) { notify(player, "You can't create channels of that type."); return; } if (type & CHANNEL_DISABLED) notify(player, "Warning: channel will be created disabled."); /* Can the player afford it? There's a cost */ if (!payfor(Owner(player), CHANNEL_COST)) { notify(player, tprintf("You can't afford the %d %s.", CHANNEL_COST, MONIES)); return; } /* Ok, let's do it */ chan = new_channel(); if (!chan) { notify(player, "CHAT: No more memory for channels!"); giveto(Owner(player), CHANNEL_COST); return; } key = parse_boolexp(player, tprintf("=#%d", player)); if (!key) { mush_free(chan, "CHAN"); notify(player, "CHAT: No more memory for channels!"); giveto(Owner(player), CHANNEL_COST); return; } ChanModLock(chan) = key; num_channels++; if (type) ChanType(chan) = type; ChanCreator(chan) = Owner(player); strcpy(ChanName(chan), name); insert_channel(&chan); notify(player, "CHAT: Channel created."); break; case 1: /* remove a channel */ /* Check permissions. Wizards and owners can remove */ if (!Chan_Can_Nuke(chan, player)) { notify(player, "Permission denied."); return; } /* remove everyone from the channel */ channel_wipe(player, chan); /* refund the owner's money */ giveto(ChanCreator(chan), ChanCost(chan)); /* zap the channel */ remove_channel(chan); free_channel(chan); num_channels--; notify(player, "Channel removed."); break; case 2: /* rename a channel */ /* Can the player do this? */ if (!Chan_Can_Modify(chan, player)) { notify(player, "Permission denied."); return; } /* make sure the channel name is unique */ if (find_channel(perms, &temp)) { /* But allow renaming a channel to a differently-cased version of * itself */ if (strcasecmp(perms, ChanName(temp)) != 0) { notify(player, "The channel needs a more unique new name."); return; } } if (strlen(perms) > CHAN_NAME_LEN - 1) { notify(player, "That name is too long."); return; } /* When we rename a channel, we actually remove it and re-insert it */ remove_channel(chan); strcpy(ChanName(chan), perms); insert_channel(&chan); notify(player, "Channel renamed."); break; case 3: /* change the permissions on a channel */ /* get the permissions. Invalid specs default to no change */ type = string_to_privs(priv_table, perms, ChanType(chan)); if (!Chan_Can_Priv(player, type)) { notify(player, "You can't make channels that type."); return; } if (type & CHANNEL_DISABLED) notify(player, "Warning: channel will be disabled."); ChanType(chan) = type; notify(player, "Permissions on channel changed."); break; case 4: /* Quiet a channel */ if (!Chan_Can_Modify(chan, player)) { notify(player, "Permission denied. Use @channel/mute <chan>=<y/n>"); return; } switch (yesno(perms)) { case YES: ChanType(chan) |= CHANNEL_QUIET; break; case NO: ChanType(chan) &= ~CHANNEL_QUIET; break; default: notify(player, "Quiet status must be 'yes' or 'no'."); return; } notify(player, "Quiet status changed."); break; } } void do_chan_user_flags(player, name, isyn, flag, silent) dbref player; char *name; const char *isyn; int flag; /* 0 = mute, 1 = hide, 2 = gag, 3 = notitles */ int silent; { CHAN *c = NULL; CHANUSER *u; if (!name || !*name) { notify(player, "You must specify a channel."); return; } test_channel(player, name, c); u = onchannel(player, c); if (!u) { if (!silent) notify(player, "You are not on that channel."); return; } switch (flag) { case 0: /* Mute */ switch (yesno(isyn)) { case YES: CUtype(u) |= CU_QUIET; if (!silent) notify(player, "You will not hear connection messages on that channel."); break; case NO: CUtype(u) &= ~CU_QUIET; if (!silent) notify(player, "You will hear connection messages on that channel."); break; default: if (!silent) notify(player, "Mute status must be 'yes' or 'no'."); return; } return; case 1: /* Hide */ switch (yesno(isyn)) { case YES: if (!Chan_Can_Hide(c, player) && !Wizard(player)) { if (!silent) notify(player, "You are not permitted to hide on that channel."); return; } CUtype(u) |=CU_HIDE; if (!silent) notify(player, "You no longer appear on the channel's who list."); break; case NO: CUtype(u) &= ~CU_HIDE; if (!silent) notify(player, "You now appear on the channel's who list."); break; default: if (!silent) notify(player, "Hide status must be 'yes' or 'no'."); return; } return; case 2: /* Gag */ switch (yesno(isyn)) { case YES: CUtype(u) |= CU_GAG; if (!silent) notify(player, "You will no longer hear messages on the channel."); break; case NO: CUtype(u) &= ~CU_GAG; if (!silent) notify(player, "You will now hear messages on the channel."); break; default: if (!silent) notify(player, "Gag status must be 'yes' or 'no'."); return; } return; } /* NOTREACHED */ return; } /* Set a user's title for the channel */ void do_chan_title(player, name, title) dbref player; const char *name; const char *title; { CHAN *c = NULL; CHANUSER *u; if (!name || !*name) { notify(player, "You must specify a channel."); return; } if (strlen(title) >= CU_TITLE_LEN) { notify(player, "Title too long."); return; } test_channel(player, name, c); u = onchannel(player, c); if (!u) { notify(player, "You are not on that channel."); return; } strcpy(CUtitle(u), title); notify(player, "Title set."); return; } /* List all the channels and their flags */ void do_channel_list(player, partname) dbref player; const char *partname; { CHAN *c; CHANUSER *u; char numusers[BUFFER_LEN]; if (SUPPORT_PUEBLO) notify_noenter(player, tprintf("%cPRE%c", TAG_START, TAG_END)); notify(player, tprintf("%-*s %-5s %8s %-15s %-8s", CHAN_NAME_LEN, "Name", "Users", "Msgs", "Chan Type", "Status")); for (c = channels; c; c = c->next) { if (Chan_Can_See(c, player) && string_prefix(ChanName(c), partname)) { u = onchannel(player, c); if (SUPPORT_PUEBLO) sprintf(numusers, "%cA XCH_CMD=\"@channel/who %s\" XCH_HINT=\"See who's on this channel now\"%c%5ld%c/A%c", TAG_START, ChanName(c), TAG_END, ChanNumUsers(c), TAG_START, TAG_END); else sprintf(numusers, "%5ld", ChanNumUsers(c)); notify(player, tprintf("%-30s %s %8d [%c%c%c%c%c%c %c%c%c%c%c%c] [%-3s %c%c]", ChanName(c), numusers, ChanNumMsgs(c), Channel_Disabled(c) ? 'D' : '-', Channel_Player(c) ? 'P' : '-', Channel_Object(c) ? 'O' : '-', Channel_Admin(c) ? 'A' : (Channel_Wizard(c) ? 'W' : '-'), Channel_Quiet(c) ? 'Q' : '-', Channel_CanHide(c) ? 'H' : '-', /* Locks */ ChanJoinLock(c) != TRUE_BOOLEXP ? 'j' : '-', ChanSpeakLock(c) != TRUE_BOOLEXP ? 's' : '-', ChanModLock(c) != TRUE_BOOLEXP ? 'm' : '-', ChanSeeLock(c) != TRUE_BOOLEXP ? 'v' : '-', ChanHideLock(c) != TRUE_BOOLEXP ? 'h' : '-', /* Does the player own it? */ ChanCreator(c) == player ? '*' : '-', /* User status */ u ? "On" : "Off", (u &&Chanuser_Quiet(u)) ? 'Q' : ' ', (u &&Chanuser_Hide(u)) ? 'H' : ' ' )); } } if (SUPPORT_PUEBLO) notify_noenter(player, tprintf("%c/PRE%c", TAG_START, TAG_END)); } /* Remove all players from a channel, notifying them. This is the * utility routine for handling it. The command @channel/wipe * calls do_chan_wipe, below */ static void channel_wipe(player, chan) dbref player; CHAN *chan; { CHANUSER *u, *nextu; dbref victim; /* This is easy. Just call remove_user on each user in the list */ if (!chan) return; for (u = ChanUsers(chan); u; u = nextu) { nextu = u->next; victim = CUdbref(u); if (remove_user(u, chan)) notify(victim, tprintf("CHAT: %s has removed all users from <%s>.", Name(player), ChanName(chan))); } ChanNumUsers(chan) = 0; return; } /* This is the user function for wiping a channel */ void do_chan_wipe(player, name) dbref player; const char *name; { CHAN *c; /* Find the channel */ test_channel(player, name, c); /* Check permissions */ if (!Chan_Can_Modify(c, player)) { notify(player, "CHAT: Wipe that silly grin off your face instead."); return; } /* Wipe it */ channel_wipe(player, c); notify(player, tprintf("CHAT: Channel <%s> wiped.", ChanName(c))); return; } /* Changing the owner of a channel */ void do_chan_chown(player, name, new) dbref player; const char *name; const char *new; { CHAN *c; dbref victim; /* Only a Wizard can do this */ if (!Wizard(player)) { notify(player, "CHAT: Only a Wizard can do that."); return; } /* Find the channel */ test_channel(player, name, c); /* Find the victim */ if ((victim = lookup_player(new)) == NOTHING) { notify(player, "CHAT: Invalid owner."); return; } /* We refund the original owner's money, but don't charge the * new owner. */ chan_chown(c, victim); notify(player, tprintf("CHAT: Channel <%s> now owned by %s.", ChanName(c), Name(ChanCreator(c)))); return; } /* Chown all of a player's channels. Usually done before destruction */ void chan_chownall(old, new) dbref old; dbref new; { CHAN *c; /* Run the channel list. If a channel is owned by old, chown it silently to new */ for (c = channels; c; c = c->next) { if (ChanCreator(c) == old) chan_chown(c, new); } } /* The actual chowning of a channel */ static void chan_chown(c, victim) CHAN *c; dbref victim; { giveto(ChanCreator(c), ChanCost(c)); ChanCreator(c) = victim; ChanCost(c) = 0; return; } /* Lock one of the channel's locks */ void do_chan_lock(player, name, lockstr, whichlock) dbref player; const char *name; const char *lockstr; int whichlock; { CHAN *c; struct boolexp *key; /* Make sure the channel exists */ test_channel(player, name, c); /* Make sure the player has permission */ if (!Chan_Can_Modify(c, player)) { notify(player, "CHAT: The channel resists."); return; } /* Ok, let's do it */ if (!lockstr || !*lockstr) { /* Unlock it */ key = TRUE_BOOLEXP; } else { key = parse_boolexp(player, lockstr); if (key == TRUE_BOOLEXP) { notify(player, "CHAT: I don't understand that key."); return; } } switch (whichlock) { case CL_JOIN: if (ChanJoinLock(c)) free(ChanJoinLock(c)); ChanJoinLock(c) = key; notify(player, tprintf("CHAT: Joinlock on <%s> %s.", ChanName(c), (key == TRUE_BOOLEXP) ? "reset" : "set")); break; case CL_SPEAK: if (ChanSpeakLock(c)) free(ChanSpeakLock(c)); ChanSpeakLock(c) = key; notify(player, tprintf("CHAT: Speaklock on <%s> %s.", ChanName(c), (key == TRUE_BOOLEXP) ? "reset" : "set")); break; case CL_SEE: if (ChanSeeLock(c)) free(ChanSeeLock(c)); ChanSeeLock(c) = key; notify(player, tprintf("CHAT: Seelock on <%s> %s.", ChanName(c), (key == TRUE_BOOLEXP) ? "reset" : "set")); break; case CL_HIDE: if (ChanHideLock(c)) free(ChanHideLock(c)); ChanHideLock(c) = key; notify(player, tprintf("CHAT: Hidelock on <%s> %s.", ChanName(c), (key == TRUE_BOOLEXP) ? "reset" : "set")); break; case CL_MOD: if (ChanModLock(c)) free(ChanModLock(c)); ChanModLock(c) = key; notify(player, tprintf("CHAT: Modlock on <%s> %s.", ChanName(c), (key == TRUE_BOOLEXP) ? "reset" : "set")); break; } return; } /* A channel list with names and descriptions only. */ void do_chan_what(player, partname) dbref player; const char *partname; { CHAN *c; if (ShowAnsi(player)) { for (c = channels; c; c = c->next) { if (Chan_Can_See(c, player) && string_prefix(ChanName(c), partname)) { notify(player, tprintf("%s<%s>%s", ANSI_HILITE, ChanName(c), ANSI_NORMAL)); notify(player, tprintf("Creator: %s", Name(ChanCreator(c)))); notify(player, privs_to_string(priv_table, ChanType(c))); notify(player, ChanTitle(c)); } } } else { for (c = channels; c; c = c->next) { if (Chan_Can_See(c, player) && string_prefix(ChanName(c), partname)) { notify(player, tprintf("<%s>", ChanName(c))); notify(player, tprintf("Creator: %s", Name(ChanCreator(c)))); notify(player, privs_to_string(priv_table, ChanType(c))); notify(player, ChanTitle(c)); } } } } /* A decompilation which one could recreate a channel with */ void do_chan_decompile(player, name) dbref player; const char *name; { CHAN *c; CHANUSER *u; int found; found = 0; for (c = channels; c; c = c->next) { if (string_prefix(ChanName(c), name)) { found++; if (!(Chan_Can_Modify(c, player) || (ChanCreator(c) == player))) { notify(player, tprintf("CHAT: No permission to decompile %s", ChanName(c))); continue; } notify(player, tprintf("@channel/add %s = %s", ChanName(c), privs_to_string(priv_table, ChanType(c)))); notify(player, tprintf("@channel/chown %s = %s", ChanName(c), Name(ChanCreator(c)))); if (ChanModLock(c) != TRUE_BOOLEXP) notify(player, tprintf("@clock/mod %s = %s", ChanName(c), unparse_boolexp(player, ChanModLock(c), 1))); if (ChanHideLock(c) != TRUE_BOOLEXP) notify(player, tprintf("@clock/hide %s = %s", ChanName(c), unparse_boolexp(player, ChanHideLock(c), 1))); if (ChanJoinLock(c) != TRUE_BOOLEXP) notify(player, tprintf("@clock/join %s = %s", ChanName(c), unparse_boolexp(player, ChanJoinLock(c), 1))); if (ChanSpeakLock(c) != TRUE_BOOLEXP) notify(player, tprintf("@clock/speak %s = %s", ChanName(c), unparse_boolexp(player, ChanSpeakLock(c), 1))); if (ChanSeeLock(c) != TRUE_BOOLEXP) notify(player, tprintf("@clock/see %s = %s", ChanName(c), unparse_boolexp(player, ChanSeeLock(c), 1))); for (u = ChanUsers(c); u; u = u->next) { if (!Chanuser_Hide(u) || Priv_Who(player)) notify(player, tprintf("@channel/on %s = %s", ChanName(c), Name(CUdbref(u)))); } } } if (!found) notify(player, "CHAT: No channel matches that string."); } /* Modify a channel's description */ void do_chan_desc(player, name, title) dbref player; const char *name; const char *title; { CHAN *c; /* Check new title length */ if (title && strlen(title) > CHAN_TITLE_LEN - 1) { notify(player, "CHAT: New title too long."); return; } /* Make sure the channel exists */ test_channel(player, name, c); /* Make sure the player has permission */ if (!Chan_Can_Modify(c, player)) { notify(player, "CHAT: Wipe that silly grin off your face instead."); return; } /* Ok, let's do it */ if (!title || !*title) { strcpy(ChanTitle(c), ""); notify(player, tprintf("CHAT: Channel <%s> title cleared.", ChanName(c))); } else { strcpy(ChanTitle(c), title); notify(player, tprintf("CHAT: Channel <%s> title set.", ChanName(c))); } } static int yesno(str) const char *str; { if (!str || !*str) return ERR; switch (str[0]) { case 'y': case 'Y': return YES; case 'n': case 'N': return NO; case 'o': case 'O': switch (str[1]) { case 'n': case 'N': return YES; case 'f': case 'F': return NO; default: return ERR; } default: return ERR; } } /* Can this player still add channels, or have they created their * limit already? */ static int canstilladd(player) dbref player; { CHAN *c; int num = 0; for (c = channels; c; c = c->next) { if (ChanCreator(c) == player) num++; } return (num < MAX_PLAYER_CHANS); } #endif /* CHAT_SYSTEM */