mux2.0/game/
mux2.0/game/data/
mux2.0/src/tools/
// comsys.cpp
//
// * $Id: comsys.cpp,v 1.12 2000/09/07 07:31:38 sdennis Exp $
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"

#include <sys/types.h>

#include "ansi.h"
#include "db.h"
#include "interface.h"
#include "attrs.h"
#include "match.h"
#include "config.h"
#include "flags.h"
#include "powers.h"
#include "functions.h"

#include "comsys.h"

int num_channels;
int max_channels;
struct channel **channels;
comsys_t *comsys_table[NUM_COMSYS];

// Return value must be free_lbuf'ed.
//
char *RestrictTitleValue(char *pTitleRequest)
{
    // First, remove all '\r\n\t' from the string.
    //
    char *pNewTitle = RemoveSetOfCharacters(pTitleRequest, "\r\n\t");

    // Optimize/terminate any ANSI in the string.
    //
    char NewTitle_ANSI[MAX_TITLE_LEN+1];
    int nVisualWidth;
    int nLen = ANSI_TruncateToField(pNewTitle, sizeof(NewTitle_ANSI), NewTitle_ANSI, sizeof(NewTitle_ANSI), &nVisualWidth, FALSE);
    memcpy(pNewTitle, NewTitle_ANSI, nLen+1);
    return pNewTitle;
}

void do_setnewtitle(dbref player, struct channel *ch, char *pValidatedTitle)
{
    struct comuser *user = select_user(ch, player);

    if (ch && user)
    {
        if (user->title)
        {
            MEMFREE(user->title);
        }
        user->title = StringClone(pValidatedTitle);
    }
}

void load_comsys(char *filename)
{
    int i;
    char buffer[200];
    
    for (i = 0; i < NUM_COMSYS; i++)
    {
        comsys_table[i] = NULL;
    }
    
    FILE *fp = fopen(filename, "rb");
    if (!fp)
    {
        Log.printf("Error: Couldn't find %s.\n", filename);
    }
    else
    {
        DebugTotalFiles++;
        Log.printf("LOADING: %s\n", filename);
        if (fscanf(fp, "*** Begin %s ***\n", buffer) == 1 && !strcmp(buffer, "COMMAC"))
        {
            load_old_channels(fp);
        }
        else if (!strcmp(buffer, "CHANNELS"))
        {
            load_channels(fp);
        }
        else
        {
            Log.printf("Error: Couldn't find Begin CHANNELS in %s.", filename);
            return;
        }
        
        if (fscanf(fp, "*** Begin %s ***\n", buffer) == 1 && !strcmp(buffer, "COMSYS"))
        {
            load_comsystem(fp);
        }
        else
        {
            Log.printf("Error: Couldn't find Begin COMSYS in %s.", filename);
            return;
        }
        
        if (fclose(fp) == 0)
        {
            DebugTotalFiles--;
        }
        Log.printf("LOADING: %s (done)\n", filename);
    }
}

void save_comsys(char *filename)
{
    char buffer[500];
    
    sprintf(buffer, "%s.#", filename);
    FILE *fp = fopen(buffer, "wb");
    if (!fp)
    {
        Log.printf("Unable to open %s for writing.\n", buffer);
        return;
    }
    DebugTotalFiles++;
    fprintf(fp, "*** Begin CHANNELS ***\n");
    save_channels(fp);
    
    fprintf(fp, "*** Begin COMSYS ***\n");
    save_comsystem(fp);
    
    if (fclose(fp) == 0)
    {
        DebugTotalFiles--;
    }
    ReplaceFile(buffer, filename);
}

// Aliases must be between 1 and 5 characters. No spaces. No ANSI.
//
char *MakeCanonicalComAlias(const char *pAlias, int *nValidAlias, BOOL *bValidAlias)
{
    static char Buffer[6];
    *nValidAlias = 0;
    *bValidAlias = FALSE;

    if (!pAlias)
    {
        return NULL;
    }
    const char *p = pAlias;
    char *q = Buffer;
    int n = 0;
    while (*p)
    {
        if (!Tiny_IsPrint[(unsigned char)*p] || *p == ' ')
        {
            return NULL;
        }
        if (n <= 5)
        {
            n++;
            *q++ = *p;
        }
        p++;
    }
    *q = '\0';
    if (n < 1 || 5 < n)
    {
        return FALSE;
    }
    *nValidAlias = n;
    *bValidAlias = TRUE;
    return Buffer;
}

BOOL ParseChannelLine(char *pBuffer, char *pAlias5, char **ppChannelName)
{
    // Fetch alias portion. We need to find the first space.
    //
    char *p = strchr(pBuffer, ' ');
    if (!p) return FALSE;

    *p = '\0';
    BOOL bValidAlias;
    int  nValidAlias;
    char *pValidAlias = MakeCanonicalComAlias(pBuffer, &nValidAlias, &bValidAlias);
    if (!bValidAlias)
    {
        return FALSE;
    }
    strcpy(pAlias5, pValidAlias);

    // Skip any leading space before the channel name.
    //
    p++;
    while (Tiny_IsSpace[(unsigned char)*p])
    {
        p++;
    }

    if (*p == '\0')
    {
        return FALSE;
    }

    // The rest of the line is the channel name.
    //
    *ppChannelName = StringClone(p);
    return TRUE;
}

void load_channels(FILE *fp)
{
    int i, j;
    char buffer[LBUF_SIZE];
    int np;
    comsys_t *c;

    fscanf(fp, "%d\n", &np);
    for (i = 0; i < np; i++)
    {
        c = create_new_comsys();
        fscanf(fp, "%d %d\n", &(c->who), &(c->numchannels));
        c->maxchannels = c->numchannels;
        if (c->maxchannels > 0)
        {
            c->alias = (char *)MEMALLOC(c->maxchannels * 6);
            ISOUTOFMEMORY(c->alias);
            c->channels = (char **)MEMALLOC(sizeof(char *) * c->maxchannels);
            ISOUTOFMEMORY(c->channels);
            
            for (j = 0; j < c->numchannels; j++)
            {
                fscanf(fp, "%[^\n]\n", buffer);
                if (!ParseChannelLine(buffer, c->alias + j * 6, c->channels+j))
                {
                    c->numchannels--;
                    j--;
                }
            }
            sort_com_aliases(c);
        }
        else
        {
            c->alias = NULL;
            c->channels = NULL;
        }
        if (c->who >= 0 && c->who < mudstate.db_top)
        {
            if ((Typeof(c->who) == TYPE_PLAYER) || (!God(Owner(c->who))) || ((!Going(c->who))))
            {
                add_comsys(c);
            }
        }
        else
        {
            Log.printf("dbref %d out of range [0, %d)\n", c->who, mudstate.db_top);
        }
        purge_comsystem();
    }
}

void load_old_channels(FILE *fp)
{
    int i, j, k;
    char buffer[LBUF_SIZE];
    int np;
    comsys_t *c;
    char *t;
    char in;
    
    fscanf(fp, "%d\n", &np);
    for (i = 0; i < np; i++)
    {
        c = create_new_comsys();
        /* Trash the old values! */
        fscanf(fp, "%d %d %d %d %d %d %d %d\n", &(c->who), &(c->numchannels), &k, &k, &k, &k, &k, &k);
        c->maxchannels = c->numchannels;
        if (c->maxchannels > 0)
        {
            c->alias = (char *)MEMALLOC(c->maxchannels * 6);
            ISOUTOFMEMORY(c->alias);
            c->channels = (char **)MEMALLOC(sizeof(char *) * c->maxchannels);
            ISOUTOFMEMORY(c->channels);
            
            for (j = 0; j < c->numchannels; j++)
            {
                t = c->alias + j * 6;
                while ((in = fgetc(fp)) != ' ')
                {
                    *t++ = in;
                }
                *t = 0;
                
                fscanf(fp, "%[^\n]\n", buffer);
                c->channels[j] = StringClone(buffer);
            }
            sort_com_aliases(c);
        }
        else
        {
            c->alias = NULL;
            c->channels = NULL;
        }
        if (c->who >= 0 && c->who < mudstate.db_top)
        {
            if ((Typeof(c->who) == TYPE_PLAYER) || (!God(Owner(c->who))) || ((!Going(c->who))))
            {
                add_comsys(c);
            }
        }
        else
        {
            Log.printf("load_old_channels: dbref %d out of range [0, %d)\n", c->who, mudstate.db_top);
        }
        purge_comsystem();
    }
}

void purge_comsystem(void)
{
    comsys_t *c;
    comsys_t *d;
    int i;
    
#ifdef ABORT_PURGE_COMSYS
    return;
    #endif /*
    * * ABORT_PURGE_COMSYS  
    */
    
    for (i = 0; i < NUM_COMSYS; i++)
    {
        c = comsys_table[i];
        while (c)
        {
            d = c;
            c = c->next;
            if (d->numchannels == 0)
            {
                del_comsys(d->who);
                continue;
            }
            /*
            * if ((Typeof(d->who) != TYPE_PLAYER) && (God(Owner(d->who))) &&
            * * (Going(d->who))) 
            */
            if (Typeof(d->who) == TYPE_PLAYER)
                continue;
            if (God(Owner(d->who)) && Going(d->who))
            {
                del_comsys(d->who);
                continue;
            }
        }
    }
}

void save_channels(FILE *fp)
{
    int np;
    comsys_t *c;
    int i, j;
    
    purge_comsystem();
    np = 0;
    for (i = 0; i < NUM_COMSYS; i++)
    {
        c = comsys_table[i];
        while (c)
        {
            np++;
            c = c->next;
        }
    }
    
    fprintf(fp, "%d\n", np);
    for (i = 0; i < NUM_COMSYS; i++)
    {
        c = comsys_table[i];
        while (c)
        {
            fprintf(fp, "%d %d\n", c->who, c->numchannels);
            for (j = 0; j < c->numchannels; j++)
            {
                fprintf(fp, "%s %s\n", c->alias + j * 6, c->channels[j]);
            }
            c = c->next;
        }
    }
}

comsys_t *create_new_comsys(void)
{
    comsys_t *c;
    
    c = (comsys_t *)MEMALLOC(sizeof(comsys_t));
    ISOUTOFMEMORY(c);
    
    c->who = -1;
    c->numchannels = 0;
    c->maxchannels = 0;
    c->alias = NULL;
    c->channels = NULL;
    
    c->next = NULL;
    return c;
}

comsys_t *get_comsys(dbref which)
{
    comsys_t *c;
    
    if (which < 0)
        return NULL;
    
    c = comsys_table[which % NUM_COMSYS];
    
    while (c && (c->who != which))
        c = c->next;
    
    if (!c)
    {
        c = create_new_comsys();
        c->who = which;
        add_comsys(c);
    }
    return c;
}

void add_comsys(comsys_t *c)
{
    if (c->who < 0 || c->who >= mudstate.db_top)
    {
        Log.printf("add_comsys: dbref %d out of range [0, %d)\n", c->who, mudstate.db_top);
        return;
    }
    
    c->next = comsys_table[c->who % NUM_COMSYS];
    comsys_table[c->who % NUM_COMSYS] = c;
}

void del_comsys(dbref who)
{
    comsys_t *c;
    comsys_t *last;
    
    if (who < 0 || who >= mudstate.db_top)
    {
        Log.printf("del_comsys: dbref %d out of range [0, %d)\n", who, mudstate.db_top);
        return;
    }
    
    c = comsys_table[who % NUM_COMSYS];
    
    if (c == NULL)
        return;
    
    if (c->who == who)
    {
        comsys_table[who % NUM_COMSYS] = c->next;
        destroy_comsys(c);
        return;
    }
    last = c;
    c = c->next;
    while (c)
    {
        if (c->who == who)
        {
            last->next = c->next;
            destroy_comsys(c);
            return;
        }
        last = c;
        c = c->next;
    }
}

void destroy_comsys(comsys_t *c)
{
    int i;

    if (c->alias)
    {
        MEMFREE(c->alias);
    }
    for (i = 0; i < c->numchannels; i++)
    {
        MEMFREE(c->channels[i]);
    }
    if (c->channels)
    {
        MEMFREE(c->channels);
    }
    MEMFREE(c);
}

void sort_com_aliases(comsys_t *c)
{
    int i;
    int cont;
    char buffer[10];
    char *s;
    
    cont = 1;
    while (cont)
    {
        cont = 0;
        for (i = 0; i < c->numchannels - 1; i++)
        {
            if (strcmp(c->alias + i * 6, c->alias + (i + 1) * 6) > 0)
            {
                strcpy(buffer, c->alias + i * 6);
                strcpy(c->alias + i * 6, c->alias + (i + 1) * 6);
                strcpy(c->alias + (i + 1) * 6, buffer);
                s = c->channels[i];
                c->channels[i] = c->channels[i + 1];
                c->channels[i + 1] = s;
                cont = 1;
            }
        }
    }
}

char *get_channel_from_alias(dbref player, char *alias)
{
    comsys_t *c;
    int first, last, current;
    int dir;
    
    c = get_comsys(player);
    
    current = 0;
    first = 0;
    last = c->numchannels - 1;
    dir = 1;
    
    while (dir && (first <= last))
    {
        current = (first + last) / 2;
        dir = strcmp(alias, c->alias + 6 * current);
        if (dir < 0)
            last = current - 1;
        else
            first = current + 1;
    }
    
    if (!dir)
        return c->channels[current];
    else
        return "";
}

void load_comsystem(FILE *fp)
{
    int i, j, dummy;
    int nc, new0 = 0;
    struct channel *ch;
    struct comuser *user;
    char temp[1000];
    char buf[8];
    
    num_channels = 0;
    
    
    fgets(buf, sizeof(buf), fp);
    if (!strncmp(buf, "+V1", 3))
    {
        new0 = 1;
        fscanf(fp, "%d\n", &nc);
    }
    else
    {
        nc = Tiny_atol(buf);
    }
    
    num_channels = nc;
    
    for (i = 0; i < nc; i++)
    {
        ch = (struct channel *)MEMALLOC(sizeof(struct channel));
        ISOUTOFMEMORY(ch);
        
        fscanf(fp, "%[^\n]\n", temp);
        strcpy(ch->name, temp);
        ch->on_users = NULL;
        
        hashaddLEN(ch->name, strlen(ch->name), (int *)ch, &mudstate.channel_htab);
        
        if (new0)
        {
            fscanf(fp, "%d %d %d %d %d %d %d %d\n",
                &(ch->type), &(ch->temp1), &(ch->temp2),
                &(ch->charge), &(ch->charge_who),
                &(ch->amount_col), &(ch->num_messages), &(ch->chan_obj));
        }
        else
        {
            fscanf(fp, "%d %d %d %d %d %d %d %d %d %d\n",
                &(ch->type), &(dummy), &(ch->temp1), &(ch->temp2),
                &(dummy), &(ch->charge), &(ch->charge_who),
                &(ch->amount_col), &(ch->num_messages), &(ch->chan_obj));
        }
        
        fscanf(fp, "%d\n", &(ch->num_users));
        ch->max_users = ch->num_users;
        if (ch->num_users > 0)
        {
            ch->users = (struct comuser **)calloc(ch->max_users, sizeof(struct comuser *));
            
            for (j = 0; j < ch->num_users; j++)
            {
                user = (struct comuser *)MEMALLOC(sizeof(struct comuser));
                ISOUTOFMEMORY(user);
                
                ch->users[j] = user;
                
                if (new0)
                {
                    fscanf(fp, "%d %d\n", &(user->who), &(user->bUserIsOn));
                }
                else
                {
                    fscanf(fp, "%d %d %d", &(user->who), &(dummy), &(dummy));
                    fscanf(fp, "%d\n", &(user->bUserIsOn));
                }
                
                fscanf(fp, "%[^\n]\n", temp);
                user->title = StringClone(temp+2);

                if (user->who >= 0 && user->who < mudstate.db_top)
                {
                    if (  !(isPlayer(user->who))
                       && !(Going(user->who)
                       && (God(Owner(user->who)))))
                    {
                        do_joinchannel(user->who, ch);
                        user->on_next = ch->on_users;
                        ch->on_users = user;
                    }
                    else
                    {
                        user->on_next = ch->on_users;
                        ch->on_users = user;
                    }
                }
                else
                {
                    Log.printf("load_comsystem: dbref %d out of range [0, %d)\n", user->who, mudstate.db_top);
                }
            }
            sort_users(ch);
        }
        else
        {
            ch->users = NULL;
        }
    }
}

void save_comsystem(FILE *fp)
{
    struct channel *ch;
    struct comuser *user;
    int j;
    
    fprintf(fp, "+V1\n");
    fprintf(fp, "%d\n", num_channels);
    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab); ch; ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        fprintf(fp, "%s\n", ch->name);
        
        fprintf(fp, "%d %d %d %d %d %d %d %d\n", ch->type, ch->temp1, ch->temp2, ch->charge, ch->charge_who, ch->amount_col, ch->num_messages, ch->chan_obj);
        
        // Count the number of 'valid' users to dump.
        //
        int nUsers = 0;
        for (j = 0; j < ch->num_users; j++)
        {
            user = ch->users[j];
            if (user->who >= 0 && user->who < mudstate.db_top)
            {
                nUsers++;
            }
        }

        fprintf(fp, "%d\n", nUsers);
        for (j = 0; j < ch->num_users; j++)
        {
            user = ch->users[j];
            if (user->who >= 0 && user->who < mudstate.db_top)
            {
                user = ch->users[j];
                fprintf(fp, "%d %d\n", user->who, user->bUserIsOn);
                if (user->title[0] != '\0')
                    fprintf(fp, "t:%s\n", user->title);
                else
                    fprintf(fp, "t:\n");
            }
        }
    }
}

void do_processcom(dbref player, char *arg1, char *arg2)
{
    char *mess, *bp;
    
    if ((strlen(arg1) + strlen(arg2)) > 3500)
    {
        arg2[3500] = '\0'; // TODO: Incorrect logic that doesn't hurt anything.
    }
    if (!*arg2)
    {
        raw_notify(player, "No message.");
        return;
    }
    struct channel *ch = select_channel(arg1);
    if (!ch)
    {
        raw_notify(player, tprintf("Unknown channel %s.", arg1));
        return;
    }
    struct comuser *user = select_user(ch, player);
    if (!user)
    {
        raw_notify(player, "You are not listed as on that channel.  Delete this alias and readd.");
        return;
    }
    if (!strcmp(arg2, "on"))
    {
        do_joinchannel(player, ch);
    }
    else if (!strcmp(arg2, "off"))
    {
        do_leavechannel(player, ch);
    }
    else if (!user->bUserIsOn)
    {
        raw_notify(player, tprintf("You must be on %s to do that.", arg1));
        return;
    }
    else if (!strcmp(arg2, "who"))
    {
        do_comwho(player, ch);
    }
    else if (!(do_test_access(player, CHANNEL_TRANSMIT, ch)))
    {
        raw_notify(player, "That channel type cannot be transmitted on.");
        return;
    }
    else
    {
        if (!payfor(player, Guest(player) ? 0 : ch->charge))
        {
            notify(player, tprintf("You don't have enough %s.", mudconf.many_coins));
            return;
        }
        else
        {
            ch->amount_col += ch->charge;
            giveto(ch->charge_who, ch->charge);
        }
        
        bp = mess = alloc_lbuf("do_processcom");
        
        if ((*arg2) == ':')
        {
            if (user->title[0] != '\0')
            {
                // There is a comtitle.
                //
                if (ch->type & CHANNEL_SPOOF)
                {
                    safe_tprintf_str(mess, &bp, "[%s] %s %s", arg1, user->title, arg2 + 1);
                }
                else
                {
                    safe_tprintf_str(mess, &bp, "[%s] %s %s %s", arg1, user->title, Name(player), arg2 + 1);
                }
            }
            else
            {
                safe_tprintf_str(mess, &bp, "[%s] %s %s", arg1, Name(player), arg2 + 1);
            }
        }
        else if ((*arg2) == ';')
        {
            if (user->title[0] != '\0')
            {
                // There is a comtitle
                //
                if (ch->type & CHANNEL_SPOOF)
                {
                    safe_tprintf_str(mess, &bp, "[%s] %s%s", arg1, user->title, arg2 + 1);
                }
                else
                {
                    safe_tprintf_str(mess, &bp, "[%s] %s %s%s", arg1, user->title, Name(player), arg2 + 1);
                }
            }
            else
            {
                safe_tprintf_str(mess, &bp, "[%s] %s%s", arg1, Name(player), arg2 + 1);
            }
        }
        else
        {
            if (user->title[0] != '\0')
            {
                // There is a comtitle
                //
                if (ch->type & CHANNEL_SPOOF)
                {
                    safe_tprintf_str(mess, &bp, "[%s] %s says, \"%s\"", arg1, user->title, arg2);
                }
                else
                {
                    safe_tprintf_str(mess, &bp, "[%s] %s %s says, \"%s\"", arg1, user->title, Name(player), arg2);
                }
            }
            else
            {
                safe_tprintf_str(mess, &bp, "[%s] %s says, \"%s\"", arg1, Name(player), arg2);
            }
        }
        
        do_comsend(ch, mess);
        free_lbuf(mess);
    }
}

void do_comsend(struct channel *ch, char *msgNormal)
{
    struct comuser *user;
    
    ch->num_messages++;
    for (user = ch->on_users; user; user = user->on_next)
    {
        if (user->bUserIsOn)
        {
            if (do_test_access(user->who, CHANNEL_RECEIVE, ch))
            {
                if ((Typeof(user->who) == TYPE_PLAYER) && Connected(user->who)) 
                    raw_notify(user->who, msgNormal);
                else
                    notify(user->who, msgNormal);
            }
        }
    }
}

void do_joinchannel(dbref player, struct channel *ch)
{
    char temp[1000];
    struct comuser *user;
    struct comuser **cu;
    int i;
    
    user = select_user(ch, player);
    
    if (!user)
    {
        ch->num_users++;
        if (ch->num_users >= ch->max_users)
        {
            ch->max_users += 10;
            cu = (struct comuser **)MEMALLOC(sizeof(struct comuser *) * ch->max_users);
            ISOUTOFMEMORY(cu);
            
            for (i = 0; i < (ch->num_users - 1); i++)
            {
                cu[i] = ch->users[i];
            }
            MEMFREE(ch->users);
            ch->users = cu;
        }
        user = (struct comuser *)MEMALLOC(sizeof(struct comuser));
        ISOUTOFMEMORY(user);
        
        for (i = ch->num_users - 1; i > 0 && ch->users[i - 1]->who > player; i--)
        {
            ch->users[i] = ch->users[i - 1];
        }
        ch->users[i] = user;
        
        user->who = player;
        user->bUserIsOn = 1;

        user->title = StringClone("");
        
        // if (Connected(player))&&(isPlayer(player))
        //
        if UNDEAD(player)
        {
            user->on_next = ch->on_users;
            ch->on_users = user;
        }
    }
    else if (!user->bUserIsOn)
    {
        user->bUserIsOn = 1;
    }
    else
    {
        raw_notify(player, tprintf("You are already on channel %s.", ch->name));
        return;
    }
    
    if (!Dark(player))
    {
        if (user->title[0] != '\0')
        {
            // There is a comtitle
            //
            if (ch->type & CHANNEL_SPOOF)
            {
                sprintf(temp, "[%s] %s has joined this channel.", ch->name, user->title);
            }
            else
            {
                sprintf(temp, "[%s] %s %s has joined this channel.", ch->name, user->title, Name(player));
            }
        }
        else
        {
            sprintf(temp, "[%s] %s has joined this channel.", ch->name, Name(player));
        }
        do_comsend(ch, temp);
    }
}

void do_leavechannel(dbref player, struct channel *ch)
{
    struct comuser *user;
    char temp[1000];
    
    user = select_user(ch, player);
    
    raw_notify(player, tprintf("You have left channel %s.", ch->name));
    
    if ((user->bUserIsOn) && (!Dark(player)))
    { 
        if (user->title[0] != '\0')
        {
            // There is a comtitle
            //
            if (ch->type & CHANNEL_SPOOF)
            {
                sprintf(temp, "[%s] %s has left this channel.", ch->name, user->title);
            }
            else
            {
                sprintf(temp, "[%s] %s %s has left this channel.", ch->name, user->title, Name(player));
            }
        }
        else
        {
            sprintf(temp, "[%s] %s has left this channel.", ch->name, Name(player));
        }
        do_comsend(ch, temp);
    }
    user->bUserIsOn = 0;
}

void do_comwho_line
(
    dbref player,
    struct channel *ch,
    struct comuser *user
)
{
    char *msg;
    char *buff = NULL;

    if (user->title[0] != '\0')
    {
        // There is a comtitle
        //
        if (Staff(player))
        {
            buff = unparse_object(player, user->who, 0);
            if (ch->type & CHANNEL_SPOOF)
            {
                msg = tprintf("%s as %s", buff, user->title);
            }
            else
            {
                msg = tprintf("%s as %s %s", buff, user->title, buff);
            }
        }
        else
        {
            if (ch->type & CHANNEL_SPOOF)
            {
                msg = user->title;
            }
            else
            {
                buff = unparse_object(player, user->who, 0);
                msg = tprintf("%s %s", user->title, buff);
            }
        }
    }
    else
    {
        buff = unparse_object(player, user->who, 0);
        msg = buff;
    }

    raw_notify(player, msg);
    if (buff) 
    {
        free_lbuf(buff);
    }
}

void do_comwho(dbref player, struct channel *ch)
{
    struct comuser *user;

    raw_notify(player, "-- Players --");
    for (user = ch->on_users; user; user = user->on_next)
    {
        if (Typeof(user->who) == TYPE_PLAYER)
        {
            if (Connected(user->who) && (!Dark(user->who) || Wizard_Who(player)))
            {
                if (user->bUserIsOn)
                {
                    do_comwho_line(player, ch, user);
                }
            }
            else if (!Dark(user->who))
            {
                do_comdisconnectchannel(user->who, ch->name);
            }
        }
    }
    raw_notify(player, "-- Objects --");
    for (user = ch->on_users; user; user = user->on_next)
    {
        if (Typeof(user->who) != TYPE_PLAYER)
        {
            if ((Going(user->who)) && (God(Owner(user->who))))
            {
                do_comdisconnectchannel(user->who, ch->name);
            }
            else if (user->bUserIsOn)
            {
                do_comwho_line(player, ch, user);
            }
        }
    }
    raw_notify(player, tprintf("-- %s --", ch->name));
}

struct channel *select_channel(char *channel)
{
    struct channel *cp;
    
    cp = (struct channel *)hashfindLEN(channel, strlen(channel), &mudstate.channel_htab);
    if (!cp)
        return NULL;
    else
        return cp;
}

struct comuser *select_user(struct channel *ch, dbref player)
{
    int first, last, current;
    int dir;
    
    if (!ch)
        return NULL;
    
    first = 0;
    last = ch->num_users - 1;
    dir = 1;
    current = (first + last) / 2;
    
    while (dir && (first <= last))
    {
        current = (first + last) / 2;
        if (ch->users[current] == NULL)
        {
            last--;
            continue;
        }
        if (ch->users[current]->who == player)
            dir = 0;
        else if (ch->users[current]->who < player)
        {
            dir = 1;
            first = current + 1;
        }
        else
        {
            dir = -1;
            last = current - 1;
        }
    }
    
    if (!dir)
        return ch->users[current];
    else
        return NULL;
}

void do_addcom(dbref player, dbref cause, int key, char *arg1, char *arg2)
{
    char channel[MAX_CHANNEL_LEN+1];
    char title[MAX_TITLE_LEN+1];
    char Buffer[MAX_CHANNEL_LEN+1];
    struct channel *ch;
    char *s, *t;
    int i, j, where;
    comsys_t *c;
    char *na;
    char **nc;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    BOOL bValidAlias;
    int  nValidAlias;
    char *pValidAlias = MakeCanonicalComAlias(arg1, &nValidAlias, &bValidAlias);
    if (!bValidAlias)
    {
        raw_notify(player, "You need to specify a valid alias.");
        return;
    }
    s = arg2;
    if (!*s)
    {
        raw_notify(player, "You need to specify a channel.");
        return;
    }
    t = channel;
    while (*s && (*s != ',') && ((t - channel) < MAX_CHANNEL_LEN))
    {
        if (*s != ' ')
            *t++ = *s++;
        else
            s++;
    }
    *t = '\0';
    
    t = title;
    *t = '\0';
    if (*s)
    {   
        // Read title 
        //
        s++;
        while (*s && ((t - title) < MAX_TITLE_LEN))
        {
            *t++ = *s++;
        }
        *t = '\0';
    }
    ch = select_channel(channel);
    if (!ch)
    {
        int nVisualWidth;
        ANSI_TruncateToField(channel, sizeof(Buffer), Buffer, sizeof(Buffer), &nVisualWidth, FALSE);
        raw_notify(player, tprintf("Channel %s does not exist yet.", Buffer));
        return;
    }
    if (!(do_test_access(player, CHANNEL_JOIN, ch)))
    {
        raw_notify(player, "Sorry, this channel type does not allow you to join.");
        return;
    }
    if (select_user(ch, player))
    {
        raw_notify(player, tprintf("Warning: You are already on that channel."));
    }
    c = get_comsys(player);
    for (j = 0; j < c->numchannels && (strcmp(pValidAlias, c->alias + j * 6) > 0); j++)
    {
        // Nothing
        ;
    }
    if (j < c->numchannels && !strcmp(pValidAlias, c->alias + j * 6))
    {
        char *p = tprintf("That alias is already in use for channel %s.", c->channels[j]);
        raw_notify(player, p);
        return;
    }
    if (c->numchannels >= c->maxchannels)
    {
        c->maxchannels += 10;
        
        na = (char *)MEMALLOC(6 * c->maxchannels);
        ISOUTOFMEMORY(na);
        nc = (char **)MEMALLOC(sizeof(char *) * c->maxchannels);
        ISOUTOFMEMORY(nc);
        
        for (i = 0; i < c->numchannels; i++)
        {
            strcpy(na + i * 6, c->alias + i * 6);
            nc[i] = c->channels[i];
        }
        if (c->alias)
        {
            MEMFREE(c->alias);
        }
        if (c->channels)
        {
            MEMFREE(c->channels);
        }
        c->alias = na;
        c->channels = nc;
    }
    where = c->numchannels++;
    for (i = where; i > j; i--)
    {
        strcpy(c->alias + i * 6, c->alias + (i - 1) * 6);
        c->channels[i] = c->channels[i - 1];
    }
    
    where = j;
    memcpy(c->alias + where * 6, pValidAlias, nValidAlias);
    *(c->alias + where * 6 + nValidAlias) = '\0';
    c->channels[where] = StringClone(channel);
    
    do_joinchannel(player, ch);
    char *pValidatedTitleValue = RestrictTitleValue(title);
    do_setnewtitle(player, ch, pValidatedTitleValue);
    
    if (pValidatedTitleValue[0] == '\0')
    {
        raw_notify(player, tprintf("Channel %s added with alias %s.", channel, pValidAlias));
    }
    else
    {
        raw_notify(player, tprintf("Channel %s added with alias %s and title %s.", channel, pValidAlias, pValidatedTitleValue));
    }
}

void do_delcom(dbref player, dbref cause, int key, char *arg1)
{
    int i;
    comsys_t *c;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    if (!arg1)
    {
        raw_notify(player, "Need an alias to delete.");
        return;
    }
    c = get_comsys(player);
    
    for (i = 0; i < c->numchannels; i++)
    {
        if (!strcmp(arg1, c->alias + i * 6))
        {
            do_delcomchannel(player, c->channels[i]);
            raw_notify(player, tprintf("Channel %s deleted.", c->channels[i]));
            MEMFREE(c->channels[i]);
            
            c->numchannels--;
            for (; i < c->numchannels; i++)
            {
                strcpy(c->alias + i * 6, c->alias + i * 6 + 6);
                c->channels[i] = c->channels[i + 1];
            }
            return;
        }
    }
    raw_notify(player, "Unable to find that alias.");
}

void do_delcomchannel(dbref player, char *channel)
{
    struct comuser *user;
    int i;
    int j;
    
    struct channel *ch = select_channel(channel);
    if (!ch)
    {
        raw_notify(player, tprintf("Unknown channel %s.", channel));
    }
    else
    {
        j = 0;
        for (i = 0; i < ch->num_users && !j; i++)
        {
            user = ch->users[i];
            if (user->who == player)
            {
                do_comdisconnectchannel(player, channel);
                if (user->bUserIsOn && (!Dark(player)))
                {
                    char *p;
                    if (user->title[0] != '\0')
                    {
                        // There is a comtitle.
                        //
                        if (ch->type & CHANNEL_SPOOF)
                        {
                            p = tprintf("[%s] %s has left this channel.", channel, user->title);
                        }
                        else
                        {
                            p = tprintf("[%s] %s %s has left this channel.", channel, user->title, Name(player));
                        }
                    }
                    else
                    {
                        p = tprintf("[%s] %s has left this channel.", channel, Name(player));
                    }
                    do_comsend(ch, p);
                }
                raw_notify(player, tprintf("You have left channel %s.", channel));
                
                if (user->title)
                {
                    MEMFREE(user->title);
                }
                MEMFREE(user);
                j = 1;
            }
        }
        
        if (j)
        {
            ch->num_users--;
            for (i--; i < ch->num_users; i++)
            {
                ch->users[i] = ch->users[i + 1];
            }
        }
    }
}

void do_createchannel(dbref player, dbref cause, int key, char *channel)
{
    struct channel *newchannel;

    if (select_channel(channel))
    {
        raw_notify(player, tprintf("Channel %s already exists.", channel));
        return;
    }
    if (!*channel)
    {
        raw_notify(player, "You must specify a channel to create.");
        return;
    }
    if (!(Comm_All(player)))
    {
        raw_notify(player, "You do not have permission to do that.");
        return;
    }
    newchannel = (struct channel *)MEMALLOC(sizeof(struct channel));
    ISOUTOFMEMORY(newchannel);
    
    strncpy(newchannel->name, channel, MAX_CHANNEL_LEN);
    newchannel->name[MAX_CHANNEL_LEN] = '\0';
    newchannel->type = 127;
    newchannel->temp1 = 0;
    newchannel->temp2 = 0;
    newchannel->charge = 0;
    newchannel->charge_who = player;
    newchannel->amount_col = 0;
    newchannel->num_users = 0;
    newchannel->max_users = 0;
    newchannel->users = NULL;
    newchannel->on_users = NULL;
    newchannel->chan_obj = NOTHING;
    newchannel->num_messages = 0;
    
    num_channels++;
    
    hashaddLEN(newchannel->name, strlen(newchannel->name), (int *)newchannel, &mudstate.channel_htab);
    
    raw_notify(player, tprintf("Channel %s created.", channel));
}

void do_destroychannel(dbref player, dbref cause, int key, char *channel)
{
    struct channel *ch;
    int j;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    ch = (struct channel *)hashfindLEN(channel, strlen(channel), &mudstate.channel_htab);
    
    if (!ch)
    {
        raw_notify(player, tprintf("Could not find channel %s.", channel));
        return;
    }
    else if (!(Comm_All(player)) && (player != ch->charge_who))
    {
        raw_notify(player, "You do not have permission to do that. ");
        return;
    }
    num_channels--;
    hashdeleteLEN(channel, strlen(channel), &mudstate.channel_htab);
    
    for (j = 0; j < ch->num_users; j++)
    {
        MEMFREE(ch->users[j]);
    }
    MEMFREE(ch->users);
    MEMFREE(ch);
    raw_notify(player, tprintf("Channel %s destroyed.", channel));
}

#if 0
void do_cleanupchannels(void)
{
    struct channel *ch;
    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         ch; ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        struct comuser *user, *prevuser = NULL;
        for (user = ch->on_users; user; )
        {
            if (Typeof(user->who) == TYPE_PLAYER)
            {
                if (!(do_test_access(user->who, CHANNEL_JOIN, ch)))
                //if (!Connected(user->who))
                {
                    // Go looking for user in the array.
                    //
                    int bFound = FALSE;
                    int iPos;
                    for (iPos = 0; iPos < ch->num_users && !bFound; iPos++)
                    {
                        if (ch->users[iPos] == user)
                        {
                            bFound = TRUE;
                        }
                    }

                    if (bFound)
                    {
                        // Remove user from the array.
                        //
                        ch->num_users--;
                        for (iPos--; iPos < ch->num_users; iPos++)
                        {
                            ch->users[iPos] = ch->users[iPos+1];
                        }

                        // Save user pointer for later reporting and freeing.
                        //
                        struct comuser *cuVictim = user;

                        // Unlink user from the list, and decide who to look at next.
                        //
                        if (prevuser)
                        {
                            prevuser->on_next = user->on_next;
                        }
                        else
                        {
                            ch->on_users = user->on_next;
                        }
                        user = user->on_next;

                        // Reporting
                        //
                        if (!Dark(cuVictim->who))
                        {
                            char buff[LBUF_SIZE];
                            if (cuVictim->title[0] != '\0')
                            {
                                // There is a comtitle.
                                //
                                if (ch->type & CHANNEL_SPOOF)
                                {
                                    sprintf(buff, "[%s] The system boots %s off the channel.", ch->name, cuVictim->title);
                                }
                                else
                                {
                                    sprintf(buff, "[%s] The system boots %s %s off the channel.", ch->name, cuVictim->title, Name(cuVictim->who));
                                }
                            }
                            else
                            {
                                sprintf(buff, "[%s] The system boots %s off the channel.", ch->name, Name(cuVictim->who));
                            }
                            do_comsend(ch, buff);
                        }
                        raw_notify(cuVictim->who, tprintf("The system has booted you off channel %s.", ch->name));

                        // Freeing
                        //
                        if (cuVictim->title)
                        {
                            MEMFREE(cuVictim->title);
                        }
                        MEMFREE(cuVictim);
    
                        continue;
                    }
                }
            }

            prevuser = user;
            user = user->on_next;
        }
    }
}
#endif

void do_listchannels(dbref player)
{
    struct channel *ch;
    char temp[LBUF_SIZE];
    
    int perm = Comm_All(player);
    if (!perm)
    {
        raw_notify(player, "Warning: Only public channels and your channels will be shown.");
    }
    raw_notify(player, "*** Channel      --Flags--  Obj   Own   Charge  Balance  Users   Messages");
    
    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         ch; ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        if (perm || (ch->type & CHANNEL_PUBLIC) || ch->charge_who == player)
        {
            
            sprintf(temp, "%c%c%c %-13.13s %c%c%c/%c%c%c %5d %5d %8d %8d %6d %10d",
                (ch->type & (CHANNEL_PUBLIC)) ? 'P' : '-',
                (ch->type & (CHANNEL_LOUD)) ? 'L' : '-',
                (ch->type & (CHANNEL_SPOOF)) ? 'S' : '-',
                ch->name,
                (ch->type & (CHANNEL_PL_MULT * CHANNEL_JOIN)) ? 'J' : '-',
                (ch->type & (CHANNEL_PL_MULT * CHANNEL_TRANSMIT)) ? 'X' : '-',
                (ch->type & (CHANNEL_PL_MULT * CHANNEL_RECEIVE)) ? 'R' : '-',
                (ch->type & (CHANNEL_OBJ_MULT * CHANNEL_JOIN)) ? 'j' : '-',
                (ch->type & (CHANNEL_OBJ_MULT * CHANNEL_TRANSMIT)) ? 'x' : '-',
                (ch->type & (CHANNEL_OBJ_MULT * CHANNEL_RECEIVE)) ? 'r' : '-',
                (ch->chan_obj != NOTHING) ? ch->chan_obj : -1,
                ch->charge_who, ch->charge, ch->amount_col, ch->num_users, ch->num_messages);
            raw_notify(player, temp);
        }
    }
    raw_notify(player, "-- End of list of Channels --");
}

void do_comtitle(dbref player, dbref cause, int key, char *arg1, char *arg2)
{
    char channel[MAX_CHANNEL_LEN+1];
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    if (!*arg1)
    {
        raw_notify(player, "Need an alias to do comtitle.");
        return;
    }
    strcpy(channel, get_channel_from_alias(player, arg1));
    
    if (channel[0] == '\0')
    {
        raw_notify(player, "Unknown alias");
        return;
    }
    struct channel *ch = select_channel(channel);
    if (ch)
    {
        if (select_user(ch, player))
        {
            char *pValidatedTitleValue = RestrictTitleValue(arg2);
            do_setnewtitle(player, ch, pValidatedTitleValue);
            raw_notify(player, tprintf("Title set to '%s' on channel %s.",
                       pValidatedTitleValue, channel));
            free_lbuf(pValidatedTitleValue);
        }
    }
    else
    {
        raw_notify(player, "Illegal comsys alias, please delete.");
    }
}

void do_comlist(dbref player, dbref cause, int key)
{
    comsys_t *c;
    int i;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    c = get_comsys(player);
    
    raw_notify(player, "Alias     Channel            Status Title");
    
    for (i = 0; i < c->numchannels; i++)
    {
        struct comuser *user = select_user(select_channel(c->channels[i]), player);
        if (user)
        {
            char *p = tprintf("%-9.9s %-18.18s %-6.6s %s", c->alias + i * 6, c->channels[i], (user->bUserIsOn ? "on" : "off"), user->title);
            raw_notify(player, p);
        }
        else
        {
            raw_notify(player, tprintf("Bad Comsys Alias: %s for Channel: %s", c->alias + i * 6, c->channels[i]));
        }
    }
    raw_notify(player, "-- End of comlist --");
}

void do_channelnuke(dbref player)
{
    struct channel *ch;
    int j;
    
    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         ch; ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        if (ch->charge_who == player)
        {
            num_channels--;
            hashdeleteLEN(ch->name, strlen(ch->name), &mudstate.channel_htab);
            
            for (j = 0; j < ch->num_users; j++)
            {
                MEMFREE(ch->users[j]);
            }
            MEMFREE(ch->users);
            MEMFREE(ch);
        }
    }
}

void do_clearcom(dbref player, dbref unused1, int unused2)
{
    int i;
    comsys_t *c;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    c = get_comsys(player);
    
    for (i = (c->numchannels) - 1; i > -1; --i)
    {
        do_delcom(player, player, 0, c->alias + i * 6);
    }
}

void do_allcom(dbref player, dbref cause, int key, char *arg1)
{
    int i;
    comsys_t *c;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    c = get_comsys(player);
    
    if (  strcmp(arg1, "who") != 0
       && strcmp(arg1, "on")  != 0
       && strcmp(arg1, "off") != 0)
    {
        raw_notify(player, "Only options available are: on, off and who.");
        return;
    }
    for (i = 0; i < c->numchannels; i++)
    {
        do_processcom(player, c->channels[i], arg1);
        if (strcmp(arg1, "who") == 0)
        {
            raw_notify(player, "");
        }
    }
}

void sort_users(struct channel *ch)
{
    int i;
    int nu;
    int done;
    struct comuser *user;
    
    nu = ch->num_users;
    done = 0;
    while (!done)
    {
        done = 1;
        for (i = 0; i < (nu - 1); i++)
        {
            if (ch->users[i]->who > ch->users[i + 1]->who)
            {
                user = ch->users[i];
                ch->users[i] = ch->users[i + 1];
                ch->users[i + 1] = user;
                done = 0;
            }
        }
    }
}

void do_channelwho(dbref player, dbref cause, int key, char *arg1)
{
    struct comuser *user;
    char channel[MAX_CHANNEL_LEN+1];
    int flag;
    char *s;
    char *t;
    char *buff;
    char temp[LBUF_SIZE];
    int i;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    s = arg1;
    t = channel;
    while (*s && *s != '/' && ((t - channel) < MAX_CHANNEL_LEN))
    {
        *t++ = *s++;
    }
    *t = 0;
    
    flag = 0;
    if (*s && *(s + 1))
        flag = (*(s + 1) == 'a');
    
    struct channel *ch = select_channel(channel);
    if (!ch)
    {
        raw_notify(player, tprintf("Unknown channel %s.", channel));
        return;
    }
    if (!((Comm_All(player)) || (player == ch->charge_who)))
    {
        raw_notify(player, "You do not have permission to do that. (Not owner or admin.)");
        return;
    }
    raw_notify(player, tprintf("-- %s --", ch->name));
    raw_notify(player, tprintf("%-29.29s %-6.6s %-6.6s", "Name", "Status", "Player"));
    for (i = 0; i < ch->num_users; i++)
    {
        user = ch->users[i];
        if ((flag || UNDEAD(user->who)) && (!Dark(user->who) || Wizard_Who(player)))
        {
            buff = unparse_object(player, user->who, 0);
            sprintf(temp, "%-29.29s %-6.6s %-6.6s", strip_ansi(buff), ((user->bUserIsOn) ? "on " : "off"), (Typeof(user->who) == TYPE_PLAYER) ? "yes" : "no ");
            raw_notify(player, temp);
            free_lbuf(buff);
        }
    }
    raw_notify(player, tprintf("-- %s --", ch->name));
}

void do_comdisconnectraw_notify(dbref player, char *chan)
{
    struct channel *ch = select_channel(chan);
    if (!ch) return;

    struct comuser *cu = select_user(ch, player);
    if (!cu) return;
    
    if ((ch->type & CHANNEL_LOUD) && (cu->bUserIsOn) && (!Dark(player)))
    {
        char *buff = alloc_lbuf("do_comconnect");
        if (cu->title[0] != '\0')
        {
            // There is a comtitle.
            //
            if (ch->type & CHANNEL_SPOOF)
            {
                sprintf(buff, "[%s] %s has disconnected.", ch->name, cu->title);
            }
            else
            {
                sprintf(buff, "[%s] %s %s has disconnected.", ch->name, cu->title, Name(player));
            }
        }
        else
        {
            sprintf(buff, "[%s] %s has disconnected.", ch->name, Name(player));
        }
        do_comsend(ch, buff);
        free_lbuf(buff);
    }
}

void do_comconnectraw_notify(dbref player, char *chan)
{
    char *buff;
    
    struct channel *ch = select_channel(chan);
    if (!ch) return;
    struct comuser *cu = select_user(ch, player);
    if (!cu) return;
    
    if ((ch->type & CHANNEL_LOUD) && (cu->bUserIsOn) && (!Dark(player)))
    {
        buff = alloc_lbuf("do_comconnect");
        if (cu->title[0] != '\0')
        {
            // There is a comtitle.
            //
            if (ch->type & CHANNEL_SPOOF)
            {
                sprintf(buff, "[%s] %s has connected.", ch->name, cu->title);
            }
            else
            {
                sprintf(buff, "[%s] %s %s has connected.", ch->name, cu->title, Name(player));
            }
        }
        else
        {
            sprintf(buff, "[%s] %s has connected.", ch->name, Name(player));
        }
        do_comsend(ch, buff);
        free_lbuf(buff);
    }
}

void do_comconnectchannel(dbref player, char *channel, char *alias, int i)
{
    struct comuser *user;
    
    struct channel *ch = select_channel(channel);
    if (ch)
    {
        for (user = ch->on_users;
        user && user->who != player;
        user = user->on_next) ;
        
        if (!user)
        {
            user = select_user(ch, player);
            if (user)
            {
                user->on_next = ch->on_users;
                ch->on_users = user;
            }
            else
            {
                raw_notify(player, tprintf("Bad Comsys Alias: %s for Channel: %s", alias + i * 6, channel));
            }
        }
    }
    else
    {
        raw_notify(player, tprintf("Bad Comsys Alias: %s for Channel: %s", alias + i * 6, channel));
    }
}

void do_comdisconnect(dbref player)
{
    int i;
    comsys_t *c;
    
    c = get_comsys(player);
    
    for (i = 0; i < c->numchannels; i++)
    {
        do_comdisconnectchannel(player, c->channels[i]);
#ifdef CHANNEL_LOUD
        do_comdisconnectraw_notify(player, c->channels[i]);
#endif
    }
}

void do_comconnect(dbref player)
{
    comsys_t *c;
    int i;
    
    c = get_comsys(player);
    
    for (i = 0; i < c->numchannels; i++)
    {
        do_comconnectchannel(player, c->channels[i], c->alias, i);
        do_comconnectraw_notify(player, c->channels[i]);
    }
}


void do_comdisconnectchannel(dbref player, char *channel)
{
    struct comuser *prevuser = NULL;
    struct channel *ch = select_channel(channel);
    if (!ch)
        return;

    struct comuser *user;
    for (user = ch->on_users; user;)
    {
        if (user->who == player)
        {
            if (prevuser)
                prevuser->on_next = user->on_next;
            else
                ch->on_users = user->on_next;
            return;
        }
        else
        {
            prevuser = user;
            user = user->on_next;
        }
    }
}

void do_editchannel(dbref player, dbref cause, int flag, char *arg1, char *arg2)
{
    char *s;
    int add_remove = 1;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    struct channel *ch = select_channel(arg1);
    if (!ch)
    {
        raw_notify(player, tprintf("Unknown channel %s.", arg1));
        return;
    }
    if (!((Comm_All(player)) || (player == ch->charge_who)))
    {
        raw_notify(player, "You do not have permission to do that. (Not owner or Admin.)");
        return;
    }
    s = arg2;
    if (*s == '!')
    {
        add_remove = 0;
        s++;
    }
    switch (flag)
    {
    case 0:
        {
            int who = lookup_player(player, arg2, 1);
            if (NOTHING == who)
            {
                raw_notify(player, "Invalid player.");
            }
            else
            {
                ch->charge_who = who;
                raw_notify(player, "Set.");
            }
        }
        break;

    case 1:
        ch->charge = Tiny_atol(arg2);
        raw_notify(player, "Set.");
        break;

    case 3:
        if (strcmp(s, "join") == 0)
        {
            if (add_remove)
            {
                ch->type |= (CHANNEL_PL_MULT * CHANNEL_JOIN);
                raw_notify(player, "@cpflags: Set.");
            }
            else
            {
                ch->type &= ~(CHANNEL_PL_MULT * CHANNEL_JOIN);
                raw_notify(player, "@cpflags: Cleared.");
            }
        }
        else if (strcmp(s, "receive") == 0)
        {
            if (add_remove)
            {
                ch->type |= (CHANNEL_PL_MULT * CHANNEL_RECEIVE);
                raw_notify(player, "@cpflags: Set.");
            }
            else
            {
                ch->type &= ~(CHANNEL_PL_MULT * CHANNEL_RECEIVE);
                raw_notify(player, "@cpflags: Cleared.");
            }
        }
        else if (strcmp(s, "transmit") == 0)
        {
            if (add_remove)
            {
                ch->type |= (CHANNEL_PL_MULT * CHANNEL_TRANSMIT);
                raw_notify(player, "@cpflags: Set.");
            }
            else
            {
                ch->type &= ~(CHANNEL_PL_MULT * CHANNEL_TRANSMIT);
                raw_notify(player, "@cpflags: Cleared.");
            }
        }
        else
        {
            raw_notify(player, "@cpflags: Unknown Flag.");
        }
        break;

    case 4:
        if (strcmp(s, "join") == 0)
        {
            if (add_remove)
            {
                ch->type |= (CHANNEL_OBJ_MULT * CHANNEL_JOIN);
                raw_notify(player, "@coflags: Set.");
            }
            else
            {
                ch->type &= ~(CHANNEL_OBJ_MULT * CHANNEL_JOIN);
                raw_notify(player, "@coflags: Cleared.");
            }
        }
        else if (strcmp(s, "receive") == 0)
        {
            if (add_remove)
            {
                ch->type |= (CHANNEL_OBJ_MULT * CHANNEL_RECEIVE);
                raw_notify(player, "@coflags: Set.");
            }
            else
            {
                ch->type &= ~(CHANNEL_OBJ_MULT * CHANNEL_RECEIVE);
                raw_notify(player, "@coflags: Cleared.");
            }
        }
        else if (strcmp(s, "transmit") == 0)
        {
            if (add_remove)
            {
                ch->type |= (CHANNEL_OBJ_MULT * CHANNEL_TRANSMIT);
                raw_notify(player, "@coflags: Set.");
            }
            else
            {
                ch->type &= ~(CHANNEL_OBJ_MULT * CHANNEL_TRANSMIT);
                raw_notify(player, "@coflags: Cleared.");
            }
        }
        else
        {
            raw_notify(player, "@coflags: Unknown Flag.");
        }
        break;
    }
}

int do_test_access(dbref player, long access, struct channel *chan)
{
    long flag_value = access;
    
    if (Comm_All(player))
    {
        return 1;
    }

    // Channel objects allow custom locks for channels.  The normal
    // lock is used to see if they can join that channel. The enterlock
    // is checked to see if they can receive messages on it. The
    // Uselock is checked to see if they can transmit on it. Note:
    // These checks do not supercede the normal channel flags. If a
    // channel is set JOIN for players, ALL players can join the
    // channel, whether or not they pass the lock.  Same for all
    // channel object locks.
    //
    if (chan->chan_obj != NOTHING && chan->chan_obj != 0)
    {
        if (flag_value & CHANNEL_JOIN)
        {
            if (could_doit(player, chan->chan_obj, A_LOCK))
                return 1;
        }
        if (flag_value & CHANNEL_TRANSMIT)
        {
            if (could_doit(player, chan->chan_obj, A_LUSE))
                return 1;
        }
        if (flag_value & CHANNEL_RECEIVE)
        {
            if (could_doit(player, chan->chan_obj, A_LENTER))
                return 1;
        }
    }

    if (Typeof(player) == TYPE_PLAYER)
    {
        flag_value *= CHANNEL_PL_MULT;
    }
    else
    {
        flag_value *= CHANNEL_OBJ_MULT;
    }

    // Mask out CHANNEL_PUBLIC, CHANNEL_LOUD, and CHANNEL_SPOOF
    //
    flag_value &= 0xFF;
        
    return (((long)chan->type & flag_value));
}

// 1 means continue, 0 means stop
//
int do_comsystem(dbref who, char *cmd)
{
    char *t;
    char *alias = alloc_lbuf("do_comsystem");
    char *s = alias;
    for (t = cmd; *t && *t != ' '; *s++ = *t++)
    {
        ; // Nothing.
    }
    
    *s = '\0';
    
    if (*t)
    {
        t++;
    }
    
    char *ch = get_channel_from_alias(who, alias);
    if (ch[0] != '\0')
    {
        do_processcom(who, ch, t);
        free_lbuf(alias);
        return 0;
    }
    else
    {
        free_lbuf(alias);
    }
    return 1;
}

void do_cemit(dbref player, dbref cause, int key, char *chan, char *text)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    struct channel *ch = select_channel(chan);
    if (!ch)
    {
        raw_notify(player, tprintf("Channel %s does not exist.", chan));
        return;
    }
    if ((player != ch->charge_who) && (!Comm_All(player)))
    {
        raw_notify(player, NOPERM_MESSAGE);
        return;
    }
    if (key == CEMIT_NOHEADER)
    {
        do_comsend(ch, text);
    }
    else
    {
        do_comsend(ch, tprintf("[%s] %s", chan, text));
    }
}

void do_chopen(dbref player, dbref cause, int key, char *chan, char *object)
{
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    if (key == CSET_LIST)
    {
        do_chanlist(player, NOTHING, 1);
        return;
    }

    char *msg = NULL;
    char *buff;
    dbref thing;

    struct channel *ch = select_channel(chan);
    if (!ch)
    {
        msg = tprintf("@cset: Channel %s does not exist.", chan);
        raw_notify(player, msg);
        return;
    }
    if ((player != ch->charge_who) && (!Comm_All(player)))
    {
        raw_notify(player, NOPERM_MESSAGE);
        return;
    }
    switch (key)
    {
    case CSET_PUBLIC:
        ch->type |= CHANNEL_PUBLIC;
        msg = tprintf("@cset: Channel %s placed on the public listings.", chan);
        break;

    case CSET_PRIVATE:
        ch->type &= ~CHANNEL_PUBLIC;
        msg = tprintf("@cset: Channel %s taken off the public listings." ,chan);
        break;

    case CSET_LOUD:
        ch->type |= CHANNEL_LOUD;
        msg = tprintf("@cset: Channel %s now sends connect/disconnect msgs.", chan);
        break;

    case CSET_QUIET:
        ch->type &= ~CHANNEL_LOUD;
        msg = tprintf("@cset: Channel %s connect/disconnect msgs muted.", chan);
        break;

    case CSET_SPOOF:
        ch->type |= CHANNEL_SPOOF;
        msg = tprintf("@cset: Channel %s set spoofable.", chan);
        break;

    case CSET_NOSPOOF:
        ch->type &= ~CHANNEL_SPOOF;
        msg = tprintf("@cset: Channel %s set unspoofable.", chan);
        break;

    case CSET_OBJECT:
        init_match(player, object, NOTYPE);
        match_everything(0);
        thing = match_result();

        ch->chan_obj = thing;
        if (thing == NOTHING)
        {
            msg = tprintf("Channel %s is now disassociated from any channel object.", ch->name);
        }
        else
        {
            buff = unparse_object(player, thing, 0);
            msg = tprintf("Channel %s is now using %s as channel object.", ch->name, buff);
            free_lbuf(buff);
        }
        break;
    }
    raw_notify(player, msg);
}

void do_chboot(dbref player, dbref cause, int key, char *channel, char *victim)
{
    dbref thing;
    char buff[LBUF_SIZE];
    char buf2[LBUF_SIZE];

    // I sure hope it's not going to be that long.
    //
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    struct channel *ch = select_channel(channel);
    if (!ch)
    {
        raw_notify(player, "@cboot: Unknown channel.");
        return;
    }
    struct comuser *user = select_user(ch, player);
    if (!user)
    {
        raw_notify(player, "@cboot: You are not on that channel.");
        return;
    }
    if (!(ch->charge_who == player) && !Comm_All(player))
    {
        raw_notify(player, "@cboot:  You can't do that!");
        return;
    }
    thing = match_thing(player, victim);
    
    if (thing == NOTHING)
    {
        return;
    }
    struct comuser *vu = select_user(ch, thing);
    if (!vu)
    {
        raw_notify(player, tprintf("@cboot: %s in not on the channel.", Name(thing)));
        return;
    }

    // We should be in the clear now. ;)
    //
    if (user->title[0] != '\0')
    {
        // There is a comtitle.
        //
        if (ch->type & CHANNEL_SPOOF)
        {
            strcpy(buf2, user->title);
        }
        else
        {
            sprintf(buf2, "%s %s", user->title, Name(player));
        }
    }
    else
    {
        strcpy(buf2, Name(player));
    }
    if (vu->title[0] != '\0')
    {
        // There is a comtitle.
        //
        if (ch->type & CHANNEL_SPOOF)
        {
            sprintf(buff, "[%s] %s boots %s off the channel.", ch->name, buf2, vu->title);
        }
        else
        {
            sprintf(buff, "[%s] %s boots %s %s off the channel.", ch->name, buf2, vu->title, Name(thing));
        }
    }
    else
    {
        sprintf(buff, "[%s] %s boots %s off the channel.", ch->name, buf2, Name(thing));
    }
    do_comsend(ch, buff);
    do_delcomchannel(thing, channel);
}

void do_chanlist(dbref player, dbref cause, int key)
{
    dbref owner;
    struct channel *ch;
    int flags;
    char *temp;
    char *buf;
    char *atrstr;
    
    if (!mudconf.have_comsys)
    {
        raw_notify(player, "Comsys disabled.");
        return;
    }
    flags = 0;
    
    if (key & CLIST_FULL)
    {
        do_listchannels(player);
        return;
    }
    temp = alloc_mbuf("do_chanlist_temp");
    buf = alloc_mbuf("do_chanlist_buf");
    
    raw_notify(player, "*** Channel       Owner           Description");
    
    for (ch = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         ch; ch = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        if (Comm_All(player) || (ch->type & CHANNEL_PUBLIC) ||
            ch->charge_who == player)
        {
            atrstr = atr_pget(ch->chan_obj, A_DESC, &owner, &flags);
            if ((ch->chan_obj == NOTHING) || !*atrstr)
                strcpy(buf, "No description.");
            else
                sprintf(buf, "%-54.54s", atrstr);
            
            free_lbuf(atrstr);
            sprintf(temp, "%c%c%c %-13.13s %-15.15s %-45.45s",
                (ch->type & (CHANNEL_PUBLIC)) ? 'P' : '-',
                (ch->type & (CHANNEL_LOUD)) ? 'L' : '-',
                (ch->type & (CHANNEL_SPOOF)) ? 'S' : '-',
                ch->name, Name(ch->charge_who), buf);
            
            raw_notify(player, temp);
        }
    }
    free_mbuf(temp);
    free_mbuf(buf);
    raw_notify(player, "-- End of list of Channels --");
}

// Returns a player's comtitle for a named channel.
//
FUNCTION(fun_comtitle)
{
    if (!mudconf.have_comsys)
    {
        safe_str("#-1 COMSYS DISABLED", buff, bufc);
        return;
    }

    dbref victim = lookup_player(player, fargs[0], 1);
    comsys_t *c = get_comsys(player);

    if (victim == NOTHING)
    {
        safe_str("#-1 PLAYER DOES NOT EXIST", buff, bufc);
        return;
    }

    struct channel *chn = select_channel(fargs[1]);
    if (!chn)
    {
        safe_str("#-1 CHANNEL DOES NOT EXIST", buff, bufc);
        return;
    }

    struct comuser *user;

    int i;
    int onchannel = 0;
    if (Wizard(player))
    {
        onchannel = 1;
    }
    else
    {
        for (i = 0; i < c->numchannels; i++)
        {
            user = select_user(chn, player);
            if (user)
            {
                onchannel = 1;
                break;
            }
        }
    }

    if (!onchannel)
    {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }

    for (i = 0; i < c->numchannels; i++)
    {
        user = select_user(chn, victim);
        if (user)
        {
            safe_str(user->title, buff, bufc);
            return;
        }
    }
    safe_str("#-1 PLAYER NOT ON THAT CHANNEL", buff, bufc);
}

// Returns a player's comsys alias for a named channel.
//
FUNCTION(fun_comalias)
{
    if (!mudconf.have_comsys)
    {
        safe_str("#-1 COMSYS DISABLED", buff, bufc);
        return;
    }

    dbref victim = lookup_player(player, fargs[0], 1);
    comsys_t *c = get_comsys(player);

    if (victim == NOTHING)
    {
        safe_str("#-1 PLAYER DOES NOT EXIST", buff, bufc);
        return;
    }

    struct channel *chn = select_channel(fargs[1]);
    if (!chn)
    {
        safe_str("#-1 CHANNEL DOES NOT EXIST", buff, bufc);
        return;
    }

    int i;
    struct comuser *user;
    int onchannel = 0;
    if (Wizard(player))
    {
        onchannel = 1;
    }
    else
    {
        for (i = 0; i < c->numchannels; i++)
        {
            user = select_user(chn, player);
            if (user)
            {
                onchannel = 1;
                break;
            }
        }
    }

    if (!onchannel)
    {
        safe_str("#-1 PERMISSION DENIED", buff, bufc);
        return;
    }

    comsys_t *cc = get_comsys(victim);
    for (i = 0; i < cc->numchannels; i++)
    {
        if (!strcmp(fargs[1], cc->channels[i]))
        {
            safe_str(cc->alias + i * 6, buff, bufc);
            return;
        }
    }
    safe_str("#-1 PLAYER NOT ON THAT CHANNEL", buff, bufc);
}

// Returns a list of channels.
//
FUNCTION(fun_channels)
{
    if (!mudconf.have_comsys)
    {
        safe_str("#-1 COMSYS DISABLED", buff, bufc);
        return;
    }

    char *outbuff = alloc_lbuf("fun_comlist_outbuff");
    *outbuff='\0';

    struct channel *chn;
    for (chn = (struct channel *)hash_firstentry(&mudstate.channel_htab);
         chn;
         chn = (struct channel *)hash_nextentry(&mudstate.channel_htab))
    {
        if (  Comm_All(player)
           || (chn->type & CHANNEL_PUBLIC)
           || chn->charge_who == player)
        {
            strcat(outbuff, chn->name);
            strcat(outbuff, " ");
        }
    }
    safe_str(outbuff, buff, bufc);
    free_lbuf(outbuff);
}