TinyMAZE/
TinyMAZE/config/
TinyMAZE/doc/
TinyMAZE/run/msgs/
TinyMAZE/src/
TinyMAZE/src/db/
TinyMAZE/src/ident/
TinyMAZE/src/io/
TinyMAZE/src/prog/
TinyMAZE/src/softcode/
TinyMAZE/src/util/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "externs.h"
#include "db.h"
#include "powers.h"
#include "com.h"

COM *chanlist = NULL;

extern int quiet_reboot;

void load_com_db()
{
  FILE *fp;
  COM *c, *cnew;
  COM_P *cp, *cpnew;
  char *filename = "db/com.db";
  char buf[4096];
  int num_channels, num_players;
  OBJ *who;

  if(!(fp = fopen(filename, "r")))
  {
    log_error(tprintf("Couldn't load \"%s\" for reading!", filename));
    return;
  }

  fgets(buf, sizeof(buf), fp);
  num_channels = atoi(buf);

  if(!quiet_reboot && num_channels)
  {
    notify_all2(tprintf("Loading com system: %d channel%s",
      num_channels, check_plural(num_channels, "", "s")), NULL);
    flush_all_output();
  }

  while(num_channels--)
  {
    fgets(buf, sizeof(buf), fp);
    num_players = atoi(buf);

    cnew = (COM *)stack_alloc(sizeof(COM), 1, 0);

    cnew->plist = NULL;
    cnew->next = NULL;

    fgets(buf, sizeof(buf), fp);
    *(buf+strlen(buf)-1) = '\0';
    SET(cnew->name, buf);

    fgets(buf, sizeof(buf), fp);
    cnew->owner = (who = find_object(atoi(buf)))?who:root_obj;

    fgets(buf, sizeof(buf), fp);
    *(buf+strlen(buf)-1) = '\0';
    SET(cnew->lock, buf);

    fgets(buf, sizeof(buf), fp);
    *(buf+strlen(buf)-1) = '\0';
    SET(cnew->join_msg, buf);

    while(num_players--)
    {
      cpnew = (COM_P *)stack_alloc(sizeof(COM_P), 1, 0);

      fgets(buf, sizeof(buf), fp);

      if(!(who = find_object(atoi(buf))) || Typeof(who) != TYPE_PLAYER)
      {
        stack_free(cpnew);
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        fgets(buf, sizeof(buf), fp);
        continue;
      }

      cpnew->player = who;

      fgets(buf, sizeof(buf), fp);
      *(buf+strlen(buf)-1) = '\0';
      SET(cpnew->color, buf);

      fgets(buf, sizeof(buf), fp);
      *(buf+strlen(buf)-1) = '\0';
      SET(cpnew->alias, buf);

      fgets(buf, sizeof(buf), fp);
      cpnew->flags = atoi(buf);

      cpnew->next = NULL;

      if(!cnew->plist)
        cnew->plist = cpnew;
      else
      {
        for(cp = cnew->plist;cp->next;cp = cp->next);
        cp->next = cpnew;
      }
    }
    if(!chanlist)
      chanlist = cnew;
    else
    {
      for(c = chanlist;c->next;c = c->next);
      c->next = cnew;
    }
  }
  fclose(fp);
}

void save_com_db()
{
  COM *c;
  COM_P *cp;
  int num_channels, num_players;
  FILE *fp;
  char *filename = "db/com.db";

  if(!(fp = fopen(filename, "w")))
  {
    log_error(tprintf("Couldn't open \"%s\" for writing!", filename));
    return;
  }

  num_channels = 0;
  for(c = chanlist;c;c = c->next)
    num_channels++;

  fprintf(fp, "%d\n", num_channels);

  for(c = chanlist;c;c = c->next)
  {
    num_players = 0;
    for(cp = c->plist;cp;cp = cp->next)
      num_players++;

    fprintf(fp, "%d\n", num_players);

    fprintf(fp, "%s\n", c->name);
    fprintf(fp, "%d\n", c->owner->dbref);
    fprintf(fp, "%s\n", c->lock);
    fprintf(fp, "%s\n", c->join_msg);

    for(cp = c->plist;cp;cp = cp->next)
    {
      fprintf(fp, "%d\n", cp->player->dbref);
      fprintf(fp, "%s\n", cp->color);
      fprintf(fp, "%s\n", cp->alias);
      fprintf(fp, "%d\n", cp->flags);
    }
  }
  fclose(fp);
}

/* See if this player can join this channel */
char chk_comlock(OBJ *player, COM *chan)
{
  return(eval_boolexp(player, player, chan->lock));
}

COM *find_channel(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp;

  for(c = chanlist;c;c = c->next)
  {
    if(!string_compare(c->name, chan))
      break;

    if(player)
      for(cp = c->plist;cp;cp = cp->next)
        if(cp->player == player)
          if(!string_compare(cp->alias, chan))
            return(c);
  }

  return(c);
}

COM_P *is_on_channel(OBJ *player, COM *c, char check)
{
  COM_P *cp;

  for(cp = c->plist;cp;cp = cp->next)
    if(cp->player == player)
      return((check)?((cp->flags & COM_OFF)?NULL:cp):cp);

  return(NULL);
}

static COM *find_default_channel(OBJ *player)
{
  COM *c;
  COM_P *cp;

  for(c = chanlist;c;c = c->next)
    for(cp = c->plist;cp;cp = cp->next)
      if(cp->player == player)
        if(cp->flags & COM_DEFAULT)
          return(c);

  return(c);
}

static int owns_channels(OBJ *player)
{
  COM *c;
  int total = 0;

  for(c = chanlist;c;c = c->next)
    if(c->owner == player)
      total++;

  return(total);
}

static int cquota_left(OBJ *player)
{
  return(atoi(atr_get(player, "Cquota"))-owns_channels(player));
}

static void chk_can_keep_com_alias(char *chan)
{
  COM *c;
  COM_P *cp;

  for(c = chanlist;c;c = c->next)
  {
    for(cp = c->plist;cp;cp = cp->next)
      if(!string_compare(cp->alias, chan))
      {
        cp->alias = stack_string_realloc(cp->alias, "");
        notify(cp->player,
          tprintf("Your alias for channel \"%s\" was removed.", c->name));
      }
  }
}

static int ok_channel_name(char *chan)
{
  if(!*chan || strchr(chan, ';') || strchr(chan, ' ') ||
    strchr(chan, '|'))
  {
    return(0);
  }

  return(1);
}

void do_create_channel(OBJ *player, char *chan)
{
  COM *c, *new;
  char cname[4096];

  strcpy(cname, strip_color(chan));

  if(cquota_left(player) <= 0 && !power(player, POW_NOQUOTA))
  {
    notify(player, "You have no quota left to create channels.");
    return;
  }

  if((get_class(player) != CLASS_DIR && *cname == '*') ||
    (get_class(player) < CLASS_ADMIN && *cname == '.'))
  {
    notify(player, "Channels beginning with '*' and '.' are reserved.");
    return;
  }

  if(!ok_channel_name(cname))
  {
    notify(player, "You can't name a channel that!");
    return;
  }

  if((c = find_channel(NULL, cname)))
  {
    notify(player, tprintf("Channel \"%s\" already exists.", c->name));
    return;
  }

  new = (COM *)stack_alloc(sizeof(COM), 1, 0);

  SET(new->name, cname);
  SET(new->lock, "");
  SET(new->join_msg, "");
  new->owner = player;
  new->plist = NULL;
  new->next = NULL;

/* Place the new channel on the end of the list */
  if(!chanlist)
    chanlist = new;
  else
  {
    for(c = chanlist;c->next;c = c->next);
    c->next = new;
  }

  notify(player, tprintf("Channel \"%s\" created.", cname));

  chk_can_keep_com_alias(cname);
}

void do_delete_channel(OBJ *player, char *chan)
{
  COM *c, *cprev = NULL;
  COM_P *cp, *cpnext;

  for(c = chanlist;c;c = c->next)
  {
    if(!string_compare(c->name, chan))
      break;
    cprev = c;
  }

  if(!c)
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(c->owner != player && !power(player, POW_CHANNEL))
  {
    notify(player, perm_denied());
    return;
  }

  com_send(chan, NULL, "* This channel is being deleted. Byebye.");

  notify(player, tprintf("Channel \"%s\" has been deleted.", c->name));

  if(cprev)
    cprev->next = c->next;
  else
    chanlist = c->next;

  for(cp = c->plist;cp;cp = cpnext)
  {
    cpnext = cp->next;
    stack_free(cp->alias);
    stack_free(cp->color);
    stack_free(cp);
  }

  stack_free(c->name);
  stack_free(c->lock);
  stack_free(c);
}

void com_send(char *chan, OBJ *player, char *msg)
{
  COM *c;
  COM_P *cp;
  char buf[4096];

  strcpy(buf, msg);

  if(!(c = find_channel(player, chan)))
    return;

  for(cp = c->plist;cp;cp = cp->next)
    if(!(cp->flags & COM_OFF))
      notify(cp->player, tprintf("|+W|[|%s|%s|+W|]|n| %s",
        cp->color, chan, buf));
}

static void do_list_channels(OBJ *player)
{
  COM *c;
  COM_P *cp;
  int total = 0;
  char buf[4096];

  for(c = chanlist;c;c = c->next)
    if(is_on_channel(player, c, 0))
      break;

  if(!c)
  {
    notify(player, "You're not on any channels.");
    return;
  }

  notify(player, tprintf("|+B|.%s.", my_string("-", 49)));
  notify(player, tprintf("|+B|||+W|%s|+B||",
    my_center("Channel List", 49)));
  notify(player, tprintf("|+B||%s|", my_string("-", 49)));
  notify(player,
    tprintf("|+B|||+W|%s|+B|||+W|%s|+B|||+W| Status |+B|||+W| Lock |+B||",
    my_center("Name", 25), my_center("Alias", 7)));
  notify(player, tprintf("|+B||%s|%s|--------|------|",
    my_string("-", 25), my_string("-", 7)));

  for(c = chanlist;c;c = c->next)
  {
    if((cp = is_on_channel(player, c, 0)))
    {
      total++;

      sprintf(buf,
        "|+B|| |%s|%s|+B|| |%s|%s|+B|||%s|%s|+B||",
        cp->color, my_ljust(c->name, 24), cp->color,
        my_ljust(cp->alias, 6), cp->color,
        my_center((cp->flags & COM_OFF)?"OFF":"ON", 8));
      sprintf(buf+strlen(buf), "|%s|%s|+B||",
        cp->color, my_center((*(c->lock))?"YES":"NO", 6));

      notify(player, buf);
    }
  }

  c = find_default_channel(player);

  notify(player, tprintf("|+B||%s|", my_string("-", 49)));
  notify(player,
    tprintf("|+B|| %s|+B||",
    my_ljust(tprintf("|+W|Total Channels : |+Y|%d", total), 48)));
  notify(player,
    tprintf("|+B|| %s|+B||",
    my_ljust(tprintf("|+W|Default Channel: |+Y|%s",
    (c)?c->name:"NONE"), 48)));
  notify(player, tprintf("|+B|`%s'", my_string("-", 49)));
}

void do_join_channel(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp, *new;
  char *alias, *color;

/* Parse up chan into channel name, alias, and color */
/* See if they specified an alias */
  if((alias = strchr(chan, ';')))
  {
    *alias++ = '\0';

    if((color = strchr(alias, ',')))
      *color++ = '\0';
    else
      color = "n";

    if(*alias)
    {
      for(c = chanlist;c;c = c->next)
        for(cp = c->plist;cp;cp = cp->next)
          if(cp->player == player)
            if(!string_compare(cp->alias, alias))
            {
              notify(player,
                tprintf("You already have \"%s\" as a channel alias!",
                alias));
              return;
            }
    }
  }
  else
  {
    alias = "";

    if((color = strchr(chan, ',')))
      *color++ = '\0';
    else
      color = "n";
  }

  if(*color)
    if(!my_is_color(color))
    {
      notify(player, tprintf("\"%s\" is not a valid color!", color));
      return;
    }

/* Reserve channels beginning with * for directors */
  if(*chan == '*')
    if(get_class(player) != CLASS_DIR && !power(player, POW_CHANNEL))
    {
      notify(player, perm_denied());
      return;
    }

/* Reserve channels beginning with . for directors and admins */
  if(*chan == '.')
    if(get_class(player) != CLASS_DIR &&
      get_class(player) != CLASS_ADMIN && !power(player, POW_CHANNEL))
    {
      notify(player, perm_denied());
      return;
    }

/* Reserve channels beginning with _ for anyone higher than a citizen */
  if(*chan == '_')
    if(get_class(player) < CLASS_PCITIZEN && !power(player, POW_CHANNEL))
    {
      notify(player, perm_denied());
      return;
    }

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(!chk_comlock(player, c) && !power(player, POW_CHANNEL))
  {
    notify(player, "You're not allowed to join that channel.");
    return;
  }

  if(is_on_channel(player, c, 0))
  {
    notify(player, "You're already on that channel.");
    return;
  }

  new = (COM_P *)stack_alloc(sizeof(COM_P), 1, 0);

  SET(new->color, color);
  SET(new->alias, alias);
  new->player = player;
  new->flags = 0;
  new->next = NULL;

  if(!c->plist)
    c->plist = new;
  else
  {
    for(cp = c->plist;cp->next;cp = cp->next);
    cp->next = new;
  }

  if(!*atr_get(player, "LHIDE"))
    com_send(c->name, NULL,
      tprintf("* %s has joined this channel.", name(player)));

  if(!find_default_channel(player))
  {
    new->flags |= COM_DEFAULT;
    notify(player, tprintf("\"%s\" is now your default channel.",
      c->name));
  }

  if(*(c->join_msg))
    notify(player, tprintf("|R|* |+B|Join Message|+W|: |n|%s",
      c->join_msg));
}

void do_leave_channel(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp, *cpprev = NULL;

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  for(cp = c->plist;cp;cp = cp->next)
  {
    if(cp->player == player)
      break;
    cpprev = cp;
  }

  if(!cp)
  {
    notify(player, "You're not on that channel.");
    return;
  }

  if(!*atr_get(player, "LHIDE"))
    com_send(c->name, NULL,
      tprintf("* %s has left this channel.", name(player)));

  if(!cpprev)
    c->plist = cp->next;
  else
    cpprev->next = cp->next;

  stack_free(cp->color);
  stack_free(cp->alias);
  stack_free(cp);
}

static void make_default_channel(OBJ *player, char *chan)
{
  COM *c, *cold;
  COM_P *cp, *cpold;

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(!(cp = is_on_channel(player, c, 0)))
  {
    notify(player, tprintf("You're not on that channel."));
    return;
  }

/* See if they already have a default channel */
  if((cold = find_default_channel(player)))
  {
    cpold = is_on_channel(player, cold, 0);
    cpold->flags &= ~COM_DEFAULT;
  }

  cp->flags |= COM_DEFAULT;

  notify(player, tprintf("Your default channel is now \"%s\".", c->name));
}

static void do_com_color(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp;
  char *color;

  if(!(color = strchr(chan, ',')) || !*(color+1))
  {
    notify(player, "You need to specify a color.");
    return;
  }

  *color++ = '\0';

  if(!my_is_color(color))
  {
    notify(player, tprintf("\"%s\" is not a valid color.", color));
    return;
  }

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(!(cp = is_on_channel(player, c, 0)))
  {
    notify(player, "You aren't on that channel.");
    return;
  }

  cp->color = stack_string_realloc(cp->color, color);

  notify(player, tprintf("Color for channel \"%s\" is now: |%s|%s",
    c->name, cp->color, cp->color));
}

static void do_com_alias(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp;
  char *alias;

  if(!(alias = strchr(chan, ',')) || !*(alias+1))
  {
    notify(player, "You need to specify an alias.");
    return;
  }

  *alias++ = '\0';

  for(c = chanlist;c;c = c->next)
    for(cp = c->plist;cp;cp = cp->next)
      if(!string_compare(c->name, alias) ||
        (cp->player == player && !string_compare(cp->alias, alias)))
      {
        notify(player, "You may not use that as an alias.");
        return;
      }

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(!(cp = is_on_channel(player, c, 0)))
  {
    notify(player, "You aren't on that channel.");
    return;
  }

  cp->alias = stack_string_realloc(cp->alias, alias);

  notify(player, tprintf("Alias for channel \"%s\" is now: %s",
    c->name, cp->alias));
}

static void do_com_list(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp;
  int total_players;
  DDATA *d;
  char cbuf[4096];

  if(*chan)
  {
    if(!power(player, POW_CHANNEL))
    {
      notify(player, perm_denied());
      return;
    }

    if(!(c = find_channel(player, chan)))
    {
      notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
      return;
    }

    notify(player, tprintf("Player list for channel \"%s\":", c->name));
    for(cp = c->plist;cp;cp = cp->next)
    {
      if((d = is_idle(cp->player)))
        sprintf(cbuf, "(%s) ", time_format(now-d->last_time, 1));
      else
        *cbuf = '\0';

      notify(player, tprintf("%s |+W|%s%s%s",
        unparse_object(player, cp->player),
        (is_connected_raw(cp->player))?"(CONNECTED) ":"", cbuf,
        (*atr_get(cp->player, "LHIDE"))?"(HIDDEN) ":""));
    }
    return;
  }

  notify(player, tprintf("|+B|.%s.", my_string("-", 57)));
  notify(player, tprintf("|+B|||+W|%s|+B||",
    my_center("Channel List", 57)));
  notify(player, tprintf("|+B||%s|", my_string("-", 57)));
  notify(player,
    tprintf("|+B|||+Y|%s|+B|||+Y|%s|+B|||+Y|%s|+B|||+Y|%s|+B||",
    my_center("NAME", 20), my_center("OWNER", 20),
    my_center("PLAYERS", 9), my_center("ON?", 5)));
  notify(player, tprintf("|+B||%s|", my_string("-", 57)));

  for(c = chanlist;c;c = c->next)
  {
    total_players = 0;

    for(cp = c->plist;cp;cp = cp->next)
      total_players++;

    notify(player,
      tprintf("|+B|| |+C|%s|+B|| %s|+B|||+C|%s |+B|| |+C|%s|+B||",
      my_ljust(c->name, 19),
      my_ljust(unparse_object(player, c->owner), 19),
      my_rjust(comma(tprintf("%d", total_players)), 8),
      my_ljust((is_on_channel(player, c, 0))?"YES":"NO", 4)));
  }

  notify(player, tprintf("|+B|`%s'", my_string("-", 57)));
}

static void do_com_boot(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp, *cpnext;
  char *p;
  OBJ *who;
  char *chanfull;

  if(!(p = strchr(chan, ',')))
  {
    notify(player, "You can't use this command that way!");
    return;
  }

  *p++ = '\0';

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(c->owner != player && !power(player, POW_CHANNEL))
  {
    notify(player, "You need to be the owner of the channel to do that.");
    return;
  }

  if(!strcmp(p, "ALL"))
  {
    for(cp = c->plist;cp;cp = cpnext)
    {
      cpnext = cp->next;

      if(cp->player == player)
        continue;

      notify(cp->player,
        tprintf("You have been booted from channel \"%s\"", c->name));
      do_leave_channel(cp->player, c->name);
    }

    log_important(tprintf("%s booted everybody from channel %s",
      name(player), c->name));

    return;
  }

  if(!(who = match_player(NULL, p)))
  {
    notify(player, tprintf("I don't know who \"%s\" is!", p));
    return;
  }

  if(player == who)
  {
    notify(player, "You can't boot yourself off a channel!");
    return;
  }

  if(!(cp = is_on_channel(who, c, 0)))
  {
    notify(player, tprintf("%s isn't on that channel!", name(who)));
    return;
  }

  chanfull = unparse_com_alias(player, chan);

  do_leave_channel(who, chanfull);

  notify(who,
    tprintf("You have been booted from channel \"%s\"", chanfull));
  notify(player, tprintf("%s has been booted from channel \"%s\"",
    name(who), chanfull));
  com_send(chanfull, NULL,
    tprintf("%s has been booted from this channel", name(who)));
  log_important(tprintf("%s booted %s from channel \"%s\"",
    name(player), name(who), chanfull));
}

static void do_com_lock(OBJ *player, char *chan)
{
  COM *c;
  char *lock;
  char *p;

  if(!(lock = strchr(chan, ',')))
  {
    if(!(c = find_channel(player, chan)))
    {
      notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
      return;
    }

    if(!*(c->lock))
      notify(player, tprintf("There is no lock for channel \"%s\".",
         c->name));
    else
      notify(player, tprintf("Lock for channel \"%s\" is: %s",
        c->name, unprocess_lock(player, c->lock)));

    return;
  }

  *lock++ = '\0';

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(c->owner != player && !power(player, POW_CHANNEL))
  {
    notify(player, "You need to be the owner of the channel to do that.");
    return;
  }

  if(!(p = process_lock(player, lock)))
    return;

  c->lock = stack_string_realloc(c->lock, p);

  if(!*p)
    notify(player,
      tprintf("Lock removed for channel \"%s\".", c->name));
  else
    notify(player, tprintf("Lock for channel \"%s\" is now: %s",
      c->name, unprocess_lock(player, c->lock)));
}

static void do_com_stat(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp;
  int ctr = 0;

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  for(cp = c->plist;cp;cp = cp->next)
    ctr++;

  notify(player, tprintf("|+B|Name        |+W|: |+C|%s", c->name));
  notify(player, tprintf("|+B|Owner       |+W|: %s",
    unparse_object(player, c->owner)));
  notify(player, tprintf("|+B|Players     |+W|: |+C|%d", ctr));
  notify(player, tprintf("|+B|Lock        |+W|: |+C|%s",
    (*c->lock)?unprocess_lock(player, c->lock):"NONE"));
  notify(player, tprintf("|+B|Join Message|+W|: |+C|%s",
    (*c->join_msg)?c->join_msg:"NONE"));
}

static void do_com_name(OBJ *player, char *chan)
{
  COM *c, *c2;
  char new_name[4096];
  char *p;

  if((p = strchr(chan, ',')))
  {
    *p++ = '\0';
    strcpy(new_name, strip_color(p));
  }

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(!p)
  {
    notify(player, tprintf("|+B|Channel name|+W|: |+C|%s", c->name));
    return;
  }

  if(c->owner != player && !power(player, POW_CHANNEL))
  {
    notify(player, "You need to be the owner of the channel to do that.");
    return;
  }

  if((get_class(player) != CLASS_DIR && *new_name == '*') ||
    (get_class(player) < CLASS_ADMIN && *new_name == '.'))
  {
    notify(player, "Channels beginning with '*' and '.' are reserved.");
    return;
  }

  if(!ok_channel_name(new_name))
  {
    notify(player, "You can't name a channel that!");
    return;
  }

  if((c2 = find_channel(NULL, new_name)))
  {
    if(string_compare(c2->name, new_name))
    {
      notify(player,
        tprintf("Channel \"%s\" already exists.", c2->name));
      return;
    }
  }

  com_send(c->name, NULL,
    tprintf("This channel is being renamed to: %s", new_name));

  c->name = stack_string_realloc(c->name, new_name);

  notify(player, tprintf("|+B|Channel name|+W|: |+C|%s", new_name));

  chk_can_keep_com_alias(new_name);
}

static void do_com_jmesg(OBJ *player, char *chan)
{
  COM *c;
  char *msg;

  if((msg = strchr(chan, ',')))
    *msg++ = '\0';

  if(!(c = find_channel(player, chan)))
  {
    notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
    return;
  }

  if(!msg)
  {
    notify(player, tprintf("|+B|Join message|+W|: |+C|%s", c->join_msg));
    return;
  }

  if(c->owner != player && !power(player, POW_CHANNEL))
  {
    notify(player, "You need to be the owner of the channel to do that.");
    return;
  }

  c->join_msg = stack_string_realloc(c->join_msg, msg);

  notify(player, tprintf("|+B|Join message|+W|: |+C|%s", msg));
}

/* Turn all channels on for player */
static void do_com_on(OBJ *player)
{
  COM *c;
  COM_P *cp;

  for(c = chanlist;c;c = c->next)
    if((cp = is_on_channel(player, c, 0)))
      if(cp->flags & COM_OFF)
      {
        if(!*atr_get(player, "LHIDE"))
          com_send(c->name, NULL,
            tprintf("* %s has turned this channel on.", name(player)));
        cp->flags &= ~COM_OFF;
      }

  notify(player, "All your channels have been turned on.");
}

/* Turn all channels off for player */
static void do_com_off(OBJ *player)
{
  COM *c;
  COM_P *cp;

  for(c = chanlist;c;c = c->next)
    if((cp = is_on_channel(player, c, 0)))
      if(!(cp->flags & COM_OFF))
      {
        cp->flags |= COM_OFF;
        if(!*atr_get(player, "LHIDE"))
          com_send(c->name, NULL,
            tprintf("* %s has turned this channel off.", name(player)));
      }

  notify(player, "All your channels have been turned off.");
}

void do_channel(OBJ *player, char *arg1, char *arg2)
{
  if(Typeof(player) != TYPE_PLAYER)
  {
    notify(player, "Only real players can use channels.");
    return;
  }

  if(!*arg1)
  {
    do_list_channels(player);
    return;
  }

  if(*arg1 == '+')
  {
    do_join_channel(player, arg1+1);
    return;
  }

  if(*arg1 == '-')
  {
    do_leave_channel(player, arg1+1);
    return;
  }

  if(!string_compare("list", arg1))
  {
    do_com_list(player, arg2);
    return;
  }

  if(!string_compare("on", arg1))
  {
    do_com_on(player);
    return;
  }

  if(!string_compare("off", arg1))
  {
    do_com_off(player);
    return;
  }

  if(!*arg2)
  {
    make_default_channel(player, arg1);
    return;
  }

  if(string_prefix("create", arg1))
  {
    do_create_channel(player, arg2);
    return;
  }

  if(string_prefix("delete", arg1))
  {
    do_delete_channel(player, arg2);
    return;
  }

  if(string_prefix("color", arg1))
  {
    do_com_color(player, arg2);
    return;
  }

  if(string_prefix("alias", arg1))
  {
    do_com_alias(player, arg2);
    return;
  }

  if(string_prefix("boot", arg1))
  {
    do_com_boot(player, arg2);
    return;
  }

  if(string_prefix("jmesg", arg1))
  {
    do_com_jmesg(player, arg2);
    return;
  }

  if(string_prefix("lock", arg1))
  {
    do_com_lock(player, arg2);
    return;
  }

  if(string_prefix("stat", arg1))
  {
    do_com_stat(player, arg2);
    return;
  }

  if(string_prefix("name", arg1))
  {
    do_com_name(player, arg2);
    return;
  }

  notify(player, "You can't use this command that way!");
}

static void do_com_who(OBJ *player, COM *c)
{
  char buf[4096];
  COM_P *cp;
  int num_hidden = 0, total = 0, num_active = 0;
  char idle_buf[4096];
  DDATA *d;

  notify(player, tprintf("|+W|Who's on channel \"%s\"?", c->name));

  for(cp = c->plist;cp;cp = cp->next)
  {
    if(!is_connected_raw(cp->player))
      continue;
    if(cp->flags & COM_OFF)
      continue;

    if((d = is_idle(cp->player)))
      sprintf(idle_buf, "(%s) ", time_format(now-d->last_time, 1));
    else
      *idle_buf = '\0';

    total++;

    if(!could_doit(player, cp->player, "LHIDE"))
    {
      if(controls(player, cp->player, POW_WHO))
      {
        notify(player, tprintf("%s |+W|%s(HIDDEN)",
          unparse_object(player, cp->player), idle_buf));
        if(!*idle_buf)
          num_active++;
      }
      num_hidden++;
    }
    else
    {
      notify(player, tprintf("%s |+W|%s",
        unparse_object(player, cp->player), idle_buf));
      if(!*idle_buf)
        num_active++;
    }
  }

   notify(player, "|+W|----------");
   sprintf(buf, "|+W|%d |+B|Total", total);
   if(num_hidden)
     strcat(buf, tprintf(", |+W|%d |+B|Hidden", num_hidden));
   if(num_active)
     strcat(buf, tprintf(", |+W|%d |+B|Active", num_active));
   notify(player, buf);
}

void do_com(OBJ *player, char *chan, char *msg)
{
  COM *c;
  COM_P *cp;
  char buf[4096], buf2[4096];
  int type;

  if(!*chan)
  {
    if(!(c = find_default_channel(player)))
    {
      notify(player, "You have no default channel.");
      return;
    }
    cp = is_on_channel(player, c, 0);
  }
  else
  {
    if(!(c = find_channel(player, chan)))
    {
      notify(player, tprintf("Channel \"%s\" doesn't exist.", chan));
      return;
    }

    if(!(cp = is_on_channel(player, c, 0)))
    {
      notify(player, "You aren't on that channel.");
      return;
    }
  }

  if(!string_compare(msg, "on"))
  {
    if(!(cp->flags & COM_OFF))
      notify(player, "You already have that channel turned on!");
    else
    {
      cp->flags &= ~COM_OFF;
      if(!*atr_get(player, "LHIDE"))
        com_send(c->name, NULL,
          tprintf("* %s has turned this channel on.", name(player)));
    }
    return;
  }

  if(!string_compare(msg, "off"))
  {
    if(cp->flags & COM_OFF)
      notify(player, "You already have that channel turned off!");
    else
    {
      if(!*atr_get(player, "LHIDE"))
        com_send(c->name, NULL,
          tprintf("* %s has turned this channel off.", name(player)));
      cp->flags |= COM_OFF;
    }
    return;
  }

  if(cp->flags & COM_OFF)
  {
    notify(player, "You have that channel turned off.");
    return;
  }

  if(!string_compare(msg, "who"))
  {
    do_com_who(player, c);
    return;
  }

  type = unparse_tothink(player, msg, buf2, 1, 0x3);

  if(type == 0)
    sprintf(buf, "%s: %s", name(player), buf2);
  else if(type == 1)
    sprintf(buf, "%s %s", name(player), buf2);
  else
    sprintf(buf, "%s %s", poss(player), buf2);

  com_send(c->name, player, buf);
}

char *unparse_com_alias(OBJ *player, char *chan)
{
  COM *c;
  COM_P *cp;

  for(c = chanlist;c;c = c->next)
    for(cp = c->plist;cp;cp = cp->next)
       if(cp->player == player)
          if(!string_compare(chan, c->name) ||
            !string_compare(chan, cp->alias))
          {
            return(c->name);
          }

  return("");
}

void do_cquota(OBJ *player, char *arg1, char *arg2)
{
  char buf[4096];
  OBJ *victim;
  int limit;

  if(!*arg1)
    victim = player;
  else
    if(!(victim = match_player(NULL, arg1)))
    {
      notify(player, no_match(arg1));
      return;
    }

  if(!controls(player, victim, POW_SETQUOTA))
  {
    notify(player, perm_denied());
    return;
  }

  if(!*arg2)
    limit = atoi(atr_get(victim, "CQUOTA"));
  else
  {
    if(!power(player, POW_SETQUOTA))
    {
      notify(player, perm_denied());
      return;
    }

    if((limit = atoi(arg2)) < 0)
    {
      notify(player,
        "Cannot set a player's cquota to a negative number!");
      return;
    }

    atr_add(victim, "CQUOTA", tprintf("%d", limit));
  }

  if(power(victim, POW_NOQUOTA))
    strcpy(buf, "UNLIMITED");
  else
    sprintf(buf, "%d", limit);

  notify(player,
    tprintf("|+B|Channels|+W|: |+C|%d   |+B|Limit|+W|: |+C|%s",
    owns_channels(victim), buf));
}

void rem_from_channels(OBJ *player)
{
  COM *c;

  for(c = chanlist;c;c = c->next)
    if(is_on_channel(player, c, 0))
      do_leave_channel(player, c->name);
}

void free_channels()
{
  COM *c, *cnext;
  COM_P *cp, *cpnext;

  for(c = chanlist;c;c = cnext)
  {
    cnext = c->next;

    for(cp = c->plist;cp;cp = cpnext)
    {
      cpnext = cp->next;

      stack_free(cp->color);
      stack_free(cp->alias);
      stack_free(cp);
    }

    stack_free(c->name);
    stack_free(c->lock);
    stack_free(c->join_msg);
    stack_free(c);
  }
}