tmi2_fluffos_v2/
tmi2_fluffos_v2/bin/
tmi2_fluffos_v2/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/ChangeLog.old/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/Win32/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/compat/simuls/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/clone/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/command/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/data/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/etc/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/include/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/inherit/master/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/log/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/compiler/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/efuns/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/single/tests/operators/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/testsuite/u/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/tmp/
tmi2_fluffos_v2/fluffos-2.7-ds2.018/windows/
tmi2_fluffos_v2/lib/
tmi2_fluffos_v2/lib/adm/
tmi2_fluffos_v2/lib/adm/daemons/languages/
tmi2_fluffos_v2/lib/adm/daemons/network/I3/
tmi2_fluffos_v2/lib/adm/daemons/virtual/
tmi2_fluffos_v2/lib/adm/daemons/virtual/template/
tmi2_fluffos_v2/lib/adm/news/
tmi2_fluffos_v2/lib/adm/obj/
tmi2_fluffos_v2/lib/adm/obj/master/
tmi2_fluffos_v2/lib/adm/priv/
tmi2_fluffos_v2/lib/adm/shell/
tmi2_fluffos_v2/lib/adm/tmp/
tmi2_fluffos_v2/lib/cmds/
tmi2_fluffos_v2/lib/d/
tmi2_fluffos_v2/lib/d/Conf/
tmi2_fluffos_v2/lib/d/Conf/adm/
tmi2_fluffos_v2/lib/d/Conf/boards/
tmi2_fluffos_v2/lib/d/Conf/cmds/
tmi2_fluffos_v2/lib/d/Conf/data/
tmi2_fluffos_v2/lib/d/Conf/logs/
tmi2_fluffos_v2/lib/d/Conf/obj/
tmi2_fluffos_v2/lib/d/Conf/text/help/
tmi2_fluffos_v2/lib/d/Fooland/adm/
tmi2_fluffos_v2/lib/d/Fooland/data/
tmi2_fluffos_v2/lib/d/Fooland/data/attic/
tmi2_fluffos_v2/lib/d/Fooland/items/
tmi2_fluffos_v2/lib/d/TMI/
tmi2_fluffos_v2/lib/d/TMI/adm/
tmi2_fluffos_v2/lib/d/TMI/boards/
tmi2_fluffos_v2/lib/d/TMI/data/
tmi2_fluffos_v2/lib/d/TMI/rooms/
tmi2_fluffos_v2/lib/d/grid/
tmi2_fluffos_v2/lib/d/grid/adm/
tmi2_fluffos_v2/lib/d/grid/data/
tmi2_fluffos_v2/lib/d/std/
tmi2_fluffos_v2/lib/d/std/adm/
tmi2_fluffos_v2/lib/data/adm/
tmi2_fluffos_v2/lib/data/adm/daemons/
tmi2_fluffos_v2/lib/data/adm/daemons/doc_d/
tmi2_fluffos_v2/lib/data/adm/daemons/emoted/
tmi2_fluffos_v2/lib/data/adm/daemons/network/http/
tmi2_fluffos_v2/lib/data/adm/daemons/network/services/mail_q/
tmi2_fluffos_v2/lib/data/adm/daemons/network/smtp/
tmi2_fluffos_v2/lib/data/adm/daemons/news/archives/
tmi2_fluffos_v2/lib/data/attic/connection/
tmi2_fluffos_v2/lib/data/attic/user/
tmi2_fluffos_v2/lib/data/std/connection/b/
tmi2_fluffos_v2/lib/data/std/connection/l/
tmi2_fluffos_v2/lib/data/std/user/a/
tmi2_fluffos_v2/lib/data/std/user/b/
tmi2_fluffos_v2/lib/data/std/user/d/
tmi2_fluffos_v2/lib/data/std/user/f/
tmi2_fluffos_v2/lib/data/std/user/l/
tmi2_fluffos_v2/lib/data/std/user/x/
tmi2_fluffos_v2/lib/data/u/d/dm/working/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/doc_d/
tmi2_fluffos_v2/lib/data/u/l/leto/smtp/
tmi2_fluffos_v2/lib/doc/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/
tmi2_fluffos_v2/lib/doc/driverdoc/applies/interactive/
tmi2_fluffos_v2/lib/doc/driverdoc/concepts/
tmi2_fluffos_v2/lib/doc/driverdoc/driver/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/arrays/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/buffers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/compile/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/ed/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/filesystem/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/floats/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/functions/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/general/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/mappings/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/numbers/
tmi2_fluffos_v2/lib/doc/driverdoc/efuns/parsing/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/constructs/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/preprocessor/
tmi2_fluffos_v2/lib/doc/driverdoc/lpc/types/
tmi2_fluffos_v2/lib/doc/driverdoc/platforms/
tmi2_fluffos_v2/lib/doc/mudlib/
tmi2_fluffos_v2/lib/ftp/
tmi2_fluffos_v2/lib/include/driver/
tmi2_fluffos_v2/lib/log/
tmi2_fluffos_v2/lib/log/driver/
tmi2_fluffos_v2/lib/obj/net/
tmi2_fluffos_v2/lib/obj/shells/
tmi2_fluffos_v2/lib/obj/tools/
tmi2_fluffos_v2/lib/std/adt/
tmi2_fluffos_v2/lib/std/board/
tmi2_fluffos_v2/lib/std/body/
tmi2_fluffos_v2/lib/std/fun/
tmi2_fluffos_v2/lib/std/living/
tmi2_fluffos_v2/lib/std/object/
tmi2_fluffos_v2/lib/std/shop/
tmi2_fluffos_v2/lib/std/socket/
tmi2_fluffos_v2/lib/std/user/
tmi2_fluffos_v2/lib/std/virtual/
tmi2_fluffos_v2/lib/student/
tmi2_fluffos_v2/lib/student/kalypso/
tmi2_fluffos_v2/lib/student/kalypso/armor/
tmi2_fluffos_v2/lib/student/kalypso/rooms/
tmi2_fluffos_v2/lib/student/kalypso/weapons/
tmi2_fluffos_v2/lib/u/l/leto/
tmi2_fluffos_v2/lib/u/l/leto/cmds/
tmi2_fluffos_v2/lib/www/errors/
tmi2_fluffos_v2/lib/www/gateways/
tmi2_fluffos_v2/lib/www/images/
tmi2_fluffos_v2/old/
tmi2_fluffos_v2/win32/
//      File:                   channels.c  (A daemon)
//      Author:                 Inspiral@Tabor, et al
//      Created:                93-05-10
//      Purpose:                To efficiently handle player communication.
//
// REVISON HISTORY:
// 930725  - Fixed problem with creating spurious channels!    -- Inspiral
// 930801  - Added scan_config()                               -- Inspiral
// 930803  - Reformatted data structure to use
//           more arrays than mappings                         -- Inspiral
// 930811  - Removed all reliances on query() and set() lfuns. -- Inspiral
// 930818  - Added log(), tell_user(), secure()                -- Inspiral
// 930826  - fixed bug in del user                             -- Pallando
// 931004  - added emotes                                      -- Rust
// 940322  - Rewritten to be faster, and to index users by
//           object                                            -- Inspiral
// 940911  - Added missing prototpye                           -- Leto
// 950312  - cleaned up format & changed channels variable set
//           in the player to a mapping type instead of an
//           array since Earthmud uses the mapping version     -- Zifi@earthmud
// 951001  - Added parse_amcp_channel                          -- Leto@Earthmud
// 960325  - Added backlog for each channel                    -- Perrin@Earthmud
// 960429  - Fixed channel emotes and formatting problems      -- Emeradii@Earthmud
// 960501  - Added support for I3 channels		       -- Brainstorm@SILKE

#include <channels.h>
#include <commands.h>
#include <net/i3modules.h>
#include <emoted.h>
#include <daemons.h>
#include <net/daemons.h>
#include <driver/origin.h>

varargs mixed display_channel(string chan, object viewer);
varargs 
int register_channel(string chan, object user, int state, mixed *action);
int log(string log_mesg);
void add_chan_backlog(string chan,string mesg);
void process_alias();

mapping channels;  // contains all our channel stats
mapping alias;     // a rude alias mechanism

#define BACKLOG_MAX 15

private static int
tell_user(object user, string mesg)
{
    tell_object(user, wrap(sprintf("CHANNELS:\t%s\n", mesg)));
    return 1;
}

#define NO_NEW_CHANNELS

// Begin admin_channel() - only applies to I3 at the moment but who knows
private static
int admin_channel( string chan, object user, string* add_list,
                                                string* rem_list )
{ string ext_ob, err;

  if( !channels[chan] ) return 0;

  // send admin command to router
  if( !undefinedp( ( ext_ob = channels[chan]["object"] ) ) ) {
      sscanf( ext_ob, "%s#%s", chan, ext_ob );

      if( err = catch( ext_ob->
        daemon_apply( user, ADM, ({ user->query("name"), chan, add_list,
                                rem_list }) ) ) )
          log( sprintf( "parse_channel:\tError in %s:\n%s",
            ext_ob, err ) );
  }
 return 1;
}


private static varargs int
create_channel(string chan, object user, string group, int mode,
		object ref )
{
    if (channels[chan])
        return 1;

    if( living(user) && ref ) {
        // Create channel came from a real user
        I3_CHANNEL->daemon_apply( user, CRE, 
		({ user->query("name"), chan, mode }) );
        return 1;
    } 

    channels[chan] = ([ ]);
    channels[chan]["vis"] = ({  });
    channels[chan]["backlog"] = ({ });

    if( group )
        channels[chan]["priv"]=group;
    if( ref )
        channels[chan]["object"]=chan+"#"+base_name(ref)+".c";

    return 1;
}


varargs private static int
kill_user(object user, string chan, int onquit)
{
    string ext_ob, err;
    string *usrchanlist;

    if (undefinedp(channels[chan]))
        return 0;

    channels[chan]["vis"] -= ({user});

    // Delete the users from the channel
    if( user && !onquit) {
      usrchanlist = user->query("channels/toggle") - ({ chan });
      user->set("channels/toggle", usrchanlist );
    }

    if( channels[chan]["vis"] == ({ }) ) {
      // deregister channel with router
      if( !undefinedp( ( ext_ob = channels[chan]["object"] ) ) ) {
          sscanf( ext_ob, "%s#%s", chan, ext_ob );

          if( err = catch( ext_ob->
            daemon_apply( this_object(), LIS, ({ chan, 0 })) ) )
              log( sprintf( "parse_channel:\tError in %s:\n%s",
                ext_ob, err ) );
      }
    }

    return 1;
}


varargs int
delete_channel(string chan, object user)
{
    int i;
    object *chuser;

    if( alias[chan] ) chan=alias[chan];
    if (undefinedp(channels[chan]))
        return 0;

    chuser=channels[chan]["vis"];
    for( i=0; i<sizeof(chuser); i++ ) {
        kill_user( chuser[i], chan );
    }

    if( channels[chan]["object"] && user && living(user) ) {
        I3_CHANNEL->daemon_apply( user, DEL, ({ user->query("name"), chan }) );
    }

    log( "Deleting channel "+chan+" by "+identify(user) );

    map_delete(channels, chan);

    return 1;
}


private static varargs int
add_user(string chan, object user)
{
    string *toggle_list, priv, ext_ob, err, pchan;

    if (!chan || !user)
        return 0;

    if (!pointerp(toggle_list = (string *) user->query("channels/toggle")))
    {
	user->set("channels/toggle",({  }));
	toggle_list = ({  });
    }

    // chan doesn't exist...do we have permission to create it?
    if (undefinedp(channels[chan]))
    {
#ifdef NO_NEW_CHANNELS
	return 0;
#else
	create_channel(chan, user);
#endif
    }

    if (!undefinedp(priv = channels[chan]["priv"]) && !member_group(geteuid(user), priv))
        return 0;

    if (member_array(chan,toggle_list) == -1)
	user->add("channels/toggle",({chan}));

     if( channels[chan]["vis"] == ({ }) ) {
       // Send a channel listen if we are the first in this channel
       if( !undefinedp( ( ext_ob = channels[chan]["object"] ) ) ) {
          sscanf( ext_ob, "%s#%s", pchan, ext_ob );

          if( err = catch( ext_ob->
            daemon_apply( user, LIS, ({ pchan, 1 })) ) )
              log( sprintf( "parse_channel:\tError in %s:\n%s",
                ext_ob, err ) );
       }
     }

    channels[chan]["vis"] = uniq_array(channels[chan]["vis"] + ({user}));

    return 1;
}


private static int
kill_users(mixed chan)
{
    int i;

    if (!chan) chan = keys(channels);
    else chan = ({chan});

    i = sizeof(chan);
    while(i--)
	channels[chan[i]]["vis"] -= ({ 0 });
    
    return 1;
}

// Clean up the channel list on quit
// So we are able to deregister unused channels with the
// Router - Brainstorm

int
cleanup_user( object whom )
{  string *current_list;
   int i;

   if( !whom || previous_object() != whom ) return 0;
   current_list=whom->query("channels/toggle");
   i=sizeof(current_list);
   while(i--)
	kill_user( whom, current_list[i], 1);

   return 1;

}

varargs int
initialize_user(object whom)
{
    int i, j;
    object *list;
    string priv;
    string *current_list;

    if (!whom || !living(whom)) list = users();
    else list = ({whom});  // We have one user.

    i = sizeof(list);
    while(i--)
    {
	current_list = (string *) list[i]->query("channels/toggle");

	if (!pointerp(current_list))
	{
	    list[i]->set("channels/toggle",({  }));
	    current_list = ({  });
	}

	j = sizeof(current_list);
	while(j--)
	    register_channel(current_list[j], list[i], ADD);
    }

    return 1;
}


varargs int
parse_channel(string chan, string mesg, string name, int emote)
{
    int i;
    string *ignore;
    object *list;        // Holds the list of all players currently on chan
    string ext_ob, err;
    string plural;       // Holds the plural form of the channel name, chan
    string verb, cmd;

    if (alias[chan]) chan = alias[chan];

    if (undefinedp(channels[chan]))
        return 0;

    kill_users(chan);

    if (undefinedp((list = channels[chan]["vis"])))
        return 0;

    i = sizeof(list);  // Optimize is.

    // Channel format is wrong in the daemon..Attempt a fix, but log.
    if (!pointerp(list))
    {
	channels[chan]["vis"] = ({  });
	initialize_user();  // Shall we reconfigure?
	return log(sprintf("parse_channel: channel %s is not a pointer!  Fixing.", identify(chan)));
    }

    // Is the name flag not given? or, is name not even in the list?
    if (this_player() && member_array(this_player(),list) == -1) 
        return 0;

    if (this_player() && !name)
	name = (string) this_player()->query("cap_name");

    if (!mesg || mesg == "")
    {
	CMD_SHOW->cmd_show(chan);
	return 1;
    }

    // Output backlog stuff here
    if (mesg[0..4]=="/last")
    {
        if (!sizeof(channels[chan]["backlog"]))
    	    message("channels","No backlog yet, sorry.\n",this_player());
        else
    	    message("channels",sprintf("Backlog of channel %s:\n%s",chan,
    	            implode(channels[chan]["backlog"],"")+"\n"),this_player());
        return 1;
    }

    if (undefinedp((plural = channels[chan]["plural"])))
	plural = pluralize_verb(chan);

    if (mesg[0] == ';')
    {
        if (!sscanf(mesg[1..],"%s %s",verb,cmd))
	    verb = mesg[1..];

	mesg = EMOTE_D->return_parse(verb,cmd);

	if (!mesg)
        {
            if (!cmd) cmd = "";
            mesg = name + " " + verb + " " + cmd;
        }

	emote = 1;
    }
    else if (mesg[0..5] == ":emote")
    {
    	if (!sscanf(mesg[7..],"%s %s",verb,cmd))
	    verb = mesg[7..];

	mesg = EMOTE_D->return_parse(verb,cmd);

	if (!mesg)
        {
            if (!cmd) cmd = "";
            mesg = name + " " + verb + " " + cmd;
        }

	emote = 1;
    }
    else if (emote)
    {
        mesg = name + " " + mesg;
    }

    if( emote )
	add_chan_backlog(chan,iwrap(capitalize(chan)+": "+mesg));
    else
        add_chan_backlog(chan,iwrap(name+" "+plural+": "+mesg));


    while(i--)
    {
        if (!objectp(list[i]))
	    continue;

	if (list[i] == this_player())
	{
	    if (emote)
		message("channels",iwrap(capitalize(chan)+": "+mesg),list[i]);
	    else
		message("channels",iwrap("You "+chan+": "+mesg),list[i]);
	}
	else
	{
	    ignore = (string *) list[i]->query("ignore");

	    if (!pointerp(ignore))
	        ignore = ({ });

	    if (member_array(lower_case(name), ignore) != -1)
	        continue;

	    if (list[i]->getenv("add_newline"))
		tell_object(list[i], "\n");

	    if (emote) message("channels",iwrap(capitalize(chan)+": "+mesg),list[i]);
	    else message("channels",iwrap(name+" "+plural+": "+mesg),list[i]);

	    continue;
	}

    } // while()

    // Do we have an apply to an external object?
    chan = lower_case(chan);
    if (!undefinedp((ext_ob = channels[chan]["object"])))
    {
	// format of object flag:  alias#object
	sscanf(ext_ob,"%s#%s", chan, ext_ob);

	// Attempt the apply, log all errors
	if (err = catch(ext_ob->daemon_apply(this_player(), MSG, 
			({ chan, mesg, emote}) )))
	    log(sprintf("parse_channel:\tError in %s:\n%s", ext_ob, err));
    }
    return 1;
}

//
int display_i3channel( string chan, string mud, object viewer )
{ string ext_ob, err;

  if( !channels[chan] ) return 0;
  if( !undefinedp( ( ext_ob = channels[chan]["object"] ) ) ) {
// format of object flag:  alias#object
        sscanf( ext_ob, "%s#%s", chan, ext_ob );

// Attempt the apply, log all errors
        if( err = catch( ext_ob->
          daemon_apply( this_player(), WHOREQ, 
		({ this_player()->query("name"), mud, chan })) ) )
            log( sprintf( "parse_channel:\tError in %s:\n%s",
              ext_ob, err ) );
    }
}


varargs mixed display_channel(string chan, object viewer) {
    mixed list;

    if(!chan || chan == "") list = keys(channels);
    else {
	if( alias[chan] ) chan=alias[chan];
	if(undefinedp(list = channels[chan]))
	    return sprintf("There is no such channel:  %s.",identify(chan));
	list = channels[chan]["vis"];
    }
    if(!pointerp(list) || !sizeof(list)) {
	channels[chan]["vis"] = ({  });
	// Return something printable for a user, and an empty set for a
	// daemon.
	return viewer && living(viewer) ? 
	sprintf("CHANNELS:\tNo Users Subscribed to %s", identify(chan)) : "";
    }
    if(chan && chan != "") 
	list = filter_array(list, "flush_list", this_object(), viewer);
    list = map_array(list, "get_cap", this_object());
    list = (implode(list,",")); // Leto.. +"   <End>");
    return list;
}

// Bootflag is set if this is the first channellist after
// a startupreq from the I3 DAEMON. In that case we need
// to tune the channels in again ;)

varargs
int add_i3channels( mapping chanlist, int bootflag )
{ string *i3list, line;
  int j;

    if( sizeof(chanlist) ) {
        i3list=keys( chanlist );
        for( j=0; j<sizeof(i3list); j++ ) {
                line=i3list[j];
		if( !alias[line] && channels[line] && 
		    channels[line]["object"] ) continue;
		if( alias[line] ) line=alias[line];

                if( ! ( channels[line] && !channels[line]["object"]) ) {
                // Its not there so lets add it or we get an
                // information update on it in which case we dont
                // want to boot our users from the channel
                if( !channels[line] )
                channels[line] = allocate_mapping( 7 );
                if( !channels[line]["vis"] || bootflag )
                  channels[line]["vis"] = ({  });

                // channels[line]["priv"] = "wiz";
                channels[line]["object"] = i3list[j]+"#"+I3_CHANNEL+".c";
                channels[line]["creator"] = chanlist[i3list[j]][0];
                channels[line]["mode"] = chanlist[i3list[j]][1];
                }
        }
    }
	// call process_alias again in case we got new channels
	process_alias();
    if(bootflag)
	call_out( "initialize_user", 2);
}


private static int scan_config() {
    string *list, *split_line, line;
    int i, j;

    list = update_file(CHANNELS_CONFIG);
    i = sizeof(list);

    // Once through the i for each non-"#" line in config file
    while(i--) {
	line = list[i];
	if(line) {
	    split_line = explode(line, ":");
	    j = sizeof(split_line);
	    channels[split_line[0]] = allocate_mapping(6);
	    channels[split_line[0]]["vis"] = ({  });
	    while(j--) {
		if((split_line[j] != "X") && (CHANNELS_INFO[j] != ""))
		    channels[split_line[0]][CHANNELS_INFO[j]] = split_line[j];
	    } // while(j)
	}  // IF (LINE)
    } // while(i)
    return 1;
}


int log(string log_mesg) {
    if(!log_mesg || log_mesg == "") return 0;
    log_file(CHANNELS_LOG, sprintf("%s %O - %s\n", intl_date_stamp(1),
	previous_object(), log_mesg));
    return 0;
}


mapping q() {
    return copy(channels);
}


mapping a() {
    return copy(alias);
}


void
process_alias() {
    int i;
    string *list, temp1;

    list = keys(channels);
    i = sizeof(list);
    while(i--) {
	if(!undefinedp(temp1 = channels[list[i]]["object"]))
	    if(sscanf(temp1, "%s#%*s", temp1) == 2)
		alias += ([temp1 : list[i]]);
    }
}


string get_cap(object ob) {
    return (string) ob->query("cap_name");
}


int flush_list(object user, object viewer) {
    return (objectp(user) && visible(user, viewer) && interactive(user));
}


void create() {
    channels = allocate_mapping(20);
    alias = allocate_mapping(20);
    seteuid(getuid());
    scan_config();
    process_alias();
    add_i3channels(I3_DAEMON->query_chanlist());
    call_out("initialize_user", 2);
}


varargs int
register_channel(string chan, object user, int state, mixed *action )
{
    int ret, mode;
    string group;
    object ref;

    if( origin() != ORIGIN_LOCAL && previous_object(1) && 
	!adminp(previous_object(1)) && previous_object(1) != user) return 0;
 
    if (nullp(state))
	return notify_fail("register_channel:  no state given!");

    switch(state)
    {
        case DEL: ret = delete_channel(chan, user);
	          break;
        case ADM: if( sizeof( action ) != 2 ) ret = 0;
                  else
                    ret=admin_channel( chan, user, action[0], action[1] );
                  break;
        case CRE: switch( sizeof( action ) ) {
                        case 3: ref = action[2];
                        case 2: mode = action[1];
                        case 1: group = action[0];
                  }
                  ret = create_channel( chan, user, group, mode, ref ); 
		  break;
        case ADD: ret = add_user(chan, user);
	          break;
        case KIL: ret = kill_user(user, chan, sizeof(action) ? action[0] : 0 );
	          break;
        default : ret = 0;
	          break;
    }

    return ret;
}


// If all parsing of <foo>bar fails, we arrive at this function to see
// if the request might be a valid message class. We return 0 if it
// wasn't, and 1 if it was. We're assuming all strings are valid.

int parse_amcp_channel(string command, string mesg) {

    string *channels, control, channelname;

    channels = previous_object()->query("channels/toggle");
    command = lower_case(command);

    if(member_array(command,channels)!=-1)
    {
	if (!mesg) this_player()->receive_message(command,display_channel(
		command,this_player() ));
	return parse_channel(command,mesg); // channel message sent.
    }
    // is it a control message then ?
    if(sscanf(command,"channel&%s",control)!=1) return 0; // We don't get it.
    //write("CHAN:"+control);
    if(control=="list")
    {
	this_player()->dump_amcp_channels();
	return 1;
    }
    if(sscanf(control,"%s&%s",control,channelname)!=2)
    {
	this_player()->receive_message("error","Unknown channel control message.\n");
	return 1;
    }
    if(control=="subscribe") return CMD_TUNE->cmd_tune(channelname+" in");
    if(control=="unsubscribe") return CMD_TUNE->cmd_tune(channelname+" out");
    // Guess it's an unknown control message.
    this_player()->receive_message("error","Unknown channel control message.\n");
    return 1;

}

void add_chan_backlog(string chan,string mesg)
{
    string *backlog = channels[chan]["backlog"];
    int count = sizeof(channels[chan]["backlog"]);

    // if(undefinedp(channels[chan]["backlog"]))
    if(!count)
	channels[chan]["backlog"] = ({});

    if(count >= BACKLOG_MAX) backlog = backlog[1..];
    if (!backlog)
	backlog = ({ mesg });
    else
	backlog = backlog + ({ mesg });

    channels[chan]["backlog"] = backlog;
}