lima-1.0b5/
lima-1.0b5/driver/
lima-1.0b5/driver/ChangeLog.old/
lima-1.0b5/driver/Win32/
lima-1.0b5/driver/compat/
lima-1.0b5/driver/include/
lima-1.0b5/driver/testsuite/
lima-1.0b5/driver/testsuite/clone/
lima-1.0b5/driver/testsuite/command/
lima-1.0b5/driver/testsuite/data/
lima-1.0b5/driver/testsuite/etc/
lima-1.0b5/driver/testsuite/include/
lima-1.0b5/driver/testsuite/inherit/
lima-1.0b5/driver/testsuite/inherit/master/
lima-1.0b5/driver/testsuite/log/
lima-1.0b5/driver/testsuite/single/
lima-1.0b5/driver/testsuite/single/tests/compiler/
lima-1.0b5/driver/testsuite/single/tests/efuns/
lima-1.0b5/driver/testsuite/single/tests/operators/
lima-1.0b5/driver/testsuite/u/
lima-1.0b5/driver/tmp/
lima-1.0b5/etc/
lima-1.0b5/lib/WWW/help/
lima-1.0b5/lib/cmds/
lima-1.0b5/lib/cmds/create/
lima-1.0b5/lib/cmds/player/attic/
lima-1.0b5/lib/contrib/bboard/
lima-1.0b5/lib/contrib/boards/
lima-1.0b5/lib/contrib/marriage/
lima-1.0b5/lib/contrib/roommaker/
lima-1.0b5/lib/contrib/transient_effect/
lima-1.0b5/lib/daemons/channel/
lima-1.0b5/lib/daemons/imud/
lima-1.0b5/lib/data/
lima-1.0b5/lib/data/config/
lima-1.0b5/lib/data/links/
lima-1.0b5/lib/data/news/
lima-1.0b5/lib/data/players/
lima-1.0b5/lib/data/secure/
lima-1.0b5/lib/domains/
lima-1.0b5/lib/domains/std/2.4.5/maze1/
lima-1.0b5/lib/domains/std/2.4.5/npc/
lima-1.0b5/lib/domains/std/2.4.5/post_dir/
lima-1.0b5/lib/domains/std/2.4.5/sub/
lima-1.0b5/lib/domains/std/camera/
lima-1.0b5/lib/domains/std/config/
lima-1.0b5/lib/domains/std/cult/
lima-1.0b5/lib/domains/std/effects/
lima-1.0b5/lib/domains/std/misc/
lima-1.0b5/lib/domains/std/monsters/
lima-1.0b5/lib/domains/std/recorder/
lima-1.0b5/lib/domains/std/rooms/
lima-1.0b5/lib/domains/std/rooms/beach/
lima-1.0b5/lib/domains/std/rooms/labyrinth/
lima-1.0b5/lib/domains/std/school/
lima-1.0b5/lib/domains/std/school/O/
lima-1.0b5/lib/domains/std/spells/
lima-1.0b5/lib/domains/std/spells/stock-mage/
lima-1.0b5/lib/domains/std/spells/stock-priest/
lima-1.0b5/lib/help/
lima-1.0b5/lib/help/admin/
lima-1.0b5/lib/help/hints/General_Questions/
lima-1.0b5/lib/help/hints/Pirate_Quest/
lima-1.0b5/lib/help/player/
lima-1.0b5/lib/help/player/bin/
lima-1.0b5/lib/help/player/quests/
lima-1.0b5/lib/help/wizard/
lima-1.0b5/lib/help/wizard/coding/guilds/
lima-1.0b5/lib/help/wizard/coding/rooms/
lima-1.0b5/lib/help/wizard/lib/daemons/
lima-1.0b5/lib/help/wizard/lib/lfun/
lima-1.0b5/lib/help/wizard/lib/std/
lima-1.0b5/lib/help/wizard/mudos_doc/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/interactive/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/concepts/
lima-1.0b5/lib/help/wizard/mudos_doc/driver/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/arrays/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/buffers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/compile/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/filesystem/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/floats/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/functions/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/general/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mappings/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mixed/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/numbers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/constructs/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/types/
lima-1.0b5/lib/include/driver/
lima-1.0b5/lib/log/
lima-1.0b5/lib/obj/admtool/
lima-1.0b5/lib/obj/admtool/internal/
lima-1.0b5/lib/obj/admtool/mudinfo/
lima-1.0b5/lib/obj/admtool/secure/
lima-1.0b5/lib/obj/secure/
lima-1.0b5/lib/obj/secure/cmd/
lima-1.0b5/lib/obj/secure/mailers/
lima-1.0b5/lib/obj/secure/shell/
lima-1.0b5/lib/obj/secure/shell/classes/
lima-1.0b5/lib/obj/tasktool/
lima-1.0b5/lib/obj/tasktool/internal/
lima-1.0b5/lib/open/
lima-1.0b5/lib/secure/
lima-1.0b5/lib/secure/cgi/
lima-1.0b5/lib/secure/modules/
lima-1.0b5/lib/secure/simul_efun/
lima-1.0b5/lib/std/adversary/
lima-1.0b5/lib/std/adversary/advancement/
lima-1.0b5/lib/std/adversary/armor/
lima-1.0b5/lib/std/adversary/blows/
lima-1.0b5/lib/std/adversary/death/
lima-1.0b5/lib/std/adversary/formula/
lima-1.0b5/lib/std/adversary/health/
lima-1.0b5/lib/std/adversary/pulse/
lima-1.0b5/lib/std/adversary/wield/
lima-1.0b5/lib/std/classes/event_info/
lima-1.0b5/lib/std/container/
lima-1.0b5/lib/std/living/
lima-1.0b5/lib/std/modules/contrib/
lima-1.0b5/lib/std/patterns/
lima-1.0b5/lib/std/race/
lima-1.0b5/lib/std/race/restricted/
lima-1.0b5/lib/std/room/
lima-1.0b5/lib/tmp/
lima-1.0b5/lib/trans/
lima-1.0b5/lib/trans/admincmds/
lima-1.0b5/lib/trans/obj/
lima-1.0b5/lib/wiz/
/* Do not remove the headers from this file! see /USAGE for more info. */

/*
** channel_d.c
**
** Daemon for handling channels.
**
** Basic architecture: this daemon records all active channels for
** registered listener objects.  Incoming tells/emotes are then passed
** to the objects.  In particular, player objects register at login
** time and unregister at exit time.
**
** Note: (un)registration uses the previous object
**
** Client interface:
**	register_channels()	register a set of channels
**	unregister_channels()	unregister a set of channels
**
**	deliver_string()	deliver a raw string to listeners
**	deliver_channel()	deliver a string to a channel
**	deliver_tell()		deliver a (default-format) tell
**	deliver_emote()		deliver a (default-format) emote
**	deliver_soul()		deliver a (default-format) soul
**	deliver_notice()	deliver a (default-format) system notice
**
**	cmd_channel()		standard channel processing
**
** Listener interface:
**	channel_rcv_string()	receive a simple string
**	channel_rcv_soul()	receive a soul data array
**	channel_rcv_data()	receive data defining a channel use
**
** 05-Nov-94. Created. Deathblade.
*/
#pragma warnings

#define CHANNEL_FORMAT(chan) (chan[0..4] == "imud_" ? "%%^CHANNEL_IMUD%%^[%s]%%^RESET%%^ %s\n" : "%%^CHANNEL%%^[%s]%%^RESET%%^ %s\n")

#include <security.h>
#include <channel.h>


inherit M_DAEMON_DATA;
inherit M_GRAMMAR;
inherit CLASS_CHANNEL_INFO;   //picked up from channel/cmd
inherit __DIR__ "channel/cmd";
inherit __DIR__ "channel/moderation";


/*
** This channel information.  It specifies channel_name -> channel_info.
*/
private nosave mapping info;

/*
** This mapping contains which channels should not be auto-purged (keys)
** and their flags (values)
*/
private mapping permanent_channels = ([ ]);

/*
** The listener information is only saved on a "nice" shutdown (remove()).
** On a hard shutdown (mud crash), we don't care cuz the listeners and
** hooks will reattach when they load.  So you'll find changes to the
** permanent_channels will save, but these two variables will usually be
** zero at those times.
*/
class listener_pair
{
    string channel_name;
    string filename;
}
private class listener_pair *	saved_listeners;
private class listener_pair *	saved_hooks;


private nomask string extract_channel_name(string channel_name)
{
  int idx = strsrch(channel_name, "_", 1);
  if ( idx == -1 )
    return channel_name;

  /* If the channel is an intermud channel, prepend an 'i' to the name */
  if(channel_name[0..4]=="imud_")
    return "i"+channel_name[idx+1..];

  return channel_name[idx+1..];
}

protected nomask class channel_info query_channel_info(string channel_name)
{
    return info[channel_name];
}

protected nomask void create_channel(string channel_name)
{
    class channel_info	ci;

    if ( info[channel_name] )
	error("channel already exists\n");

    ci = new(class channel_info);
    ci->name		= extract_channel_name(channel_name);
    ci->listeners	= ({ });
    ci->hooked		= ({ });
    ci->history		= ({ });

    info[channel_name] = ci;
}

private void register_one(int add_hook, object listener, string channel_name)
{
    class channel_info	ci = info[channel_name];

    if ( !ci )
    {
	/*
	** Note: the registration interface allows for auto-creation.
	** The user commands request that /new be used, though.  This
	** allows a user to create a channel and listen to it, and it
	** will "remain" even across sessions.
	*/
	create_channel(channel_name);
	ci = info[channel_name];
    }

    /* enforce the channel restrictions now */
    /* ### not super secure, but screw it :-) */
    if ( (ci->flags & CHANNEL_WIZ_ONLY) && !wizardp(this_user()) )
    {
	/* ### don't error... might prevent somebody from logging in */
	return;
    }
    if ( (ci->flags & CHANNEL_ADMIN_ONLY) && !adminp(this_user()) && member_array(this_user()->query_userid(), SECURE_D->query_domain_members("admin-channels")) == -1)
    {
	/* ### don't error... might prevent somebody from logging in */
	return;
    }

    if ( add_hook )
    {
	if ( member_array(listener, ci->hooked) == -1 )
	    ci->hooked += ({ listener });
    }
    else
    {
	if ( member_array(listener, ci->listeners) == -1 )
	    ci->listeners += ({ listener });
    }
}

protected nomask void test_for_purge(string channel_name)
{
    class channel_info ci = info[channel_name];

    if ( sizeof(ci->listeners) + sizeof(ci->hooked) == 0 )
	map_delete(info, channel_name);
}

private void unregister_one(string channel_name, object listener)
{
    class channel_info ci = info[channel_name];

    if ( ci )
    {
	ci->listeners -= ({ listener });

	/* purge the channel if it isn't permanent */
	if ( undefinedp(permanent_channels[channel_name]) )
	    test_for_purge(channel_name);
    }
}

private void register_body(object body)
{
    string * names;

    if ( !(names = body->query_channel_list()) )
	return;

    map_array(names, (: register_one, 0, body :));
}

protected nomask void set_permanent(string channel_name, int is_perm)
{
    int no_exist = undefinedp(permanent_channels[channel_name]);

    if ( is_perm && no_exist )
    {
	class channel_info ci = info[channel_name];

	permanent_channels[channel_name] = ci->flags;
	save_me();
    }
    else if ( !is_perm && !no_exist )
    {
	map_delete(permanent_channels, channel_name);
	save_me();
    }
}

protected nomask void set_flags(string channel_name, int flags)
{
    class channel_info ci = info[channel_name];

    ci->flags = flags;

    if ( !undefinedp(permanent_channels[channel_name]) )
    {
	permanent_channels[channel_name] = flags;
	save_me();
    }
}

/*
** user_channel_name()
**
** Return a channel name that a user might like.  Internal names are
** not "human readable"
*/
nomask string user_channel_name(string channel_name)
{
    class channel_info	ci = info[channel_name];

    if ( ci )
	return ci->name;

    return extract_channel_name(channel_name);
}

/*
** register_channels()
**
** Register previous_object() with a specific set of channels.
*/
nomask void register_channels(string * names)
{
  /* First filter out the channels that don't exist, otherwise the channel 
   * will be created.  If the channel's not created, we don't want it to be
   * done so automatically here. */
  //  names=names&keys(info);
  map_array(names, (: register_one, 0, previous_object() :));
}

/*
** unregister_channels()
**
** Un-register previous_object() from a specific set of channels.  If
** the list is 0, then unregister from all channels.
*/
nomask void unregister_channels(string * names)
{
    if ( !names )
	names = keys(info);

    map_array(names, (: unregister_one :), previous_object());
}

/*
** find_sender_name()
**
** Find the sender's name
*/
private nomask string find_sender_name(string sender_name)
{
    if ( sender_name )
	return sender_name;

    if ( this_body() )
	if ( sender_name = this_body()->query_name() )
	    return sender_name;

    if ( !(sender_name = previous_object()->query_name()) )
	sender_name = "<unknown>";

    return sender_name;
}

/*
** deliver_string()
**
** Deliver a raw string over a channel.
*/
nomask void deliver_string(string channel_name, string str)
{
    class channel_info ci = info[channel_name];
    if ( !ci ||	sizeof(ci->listeners) == 0 )
	return;

    ci->history += ({ str });
    if ( sizeof(ci->history) > CHANNEL_HISTORY_SIZE )
	ci->history[0..0] = ({ });
    ci->listeners->channel_rcv_string(channel_name, str);
}

/*
** deliver_channel()
**
** Deliver a string to a channel, prepending the channel name
*/
nomask void deliver_channel(string channel_name, string str)
{
    deliver_string(channel_name,
		   sprintf(CHANNEL_FORMAT(channel_name),
			   user_channel_name(channel_name),
			   str));
}

/*
** deliver_raw_soul()
**
** Deliver raw "soul" data over a channel.
*/
nomask void deliver_raw_soul(string channel_name, mixed * data)
{
    class channel_info ci = info[channel_name];

    if ( !ci ||	sizeof(ci->listeners) == 0 )
	return;

    ci->history += ({ data[1][<1] });
    if ( sizeof(ci->history) > CHANNEL_HISTORY_SIZE )
	ci->history[0..0] = ({ });

    ci->listeners->channel_rcv_soul(channel_name, data);
}

/*
** deliver_data()
**
** Deliver unformatted channel data to the listeners
*/
private nomask void deliver_data(string channel_name,
				 string sender_name,
				 string type,
				 mixed data)
{
    class channel_info ci = info[channel_name];

    if ( !ci ||	sizeof(ci->listeners) == 0 )
	return;

    ci->listeners->channel_rcv_data(channel_name, sender_name, type, data);
}

/*
** deliver_tell()
**
** Deliver a standard-formatted "tell" over a channel
*/
nomask void deliver_tell(string channel_name,
			 string sender_name,
			 string message)
{
    sender_name = find_sender_name(sender_name);

    deliver_data(channel_name, sender_name, "tell", message);
    deliver_string(channel_name,
		   sprintf(CHANNEL_FORMAT(channel_name),
			   user_channel_name(channel_name),
			   sender_name + ": " + punctuate(message)));
}

/*
** deliver_emote()
**
** Deliver a standard-formatted "emote" over a channel
*/
nomask void deliver_emote(string channel_name,
			  string sender_name,
			  string message)
{
    if( !sizeof( message ))
    {
    write( "Emote what?\n" );
        return;
}
    sender_name = find_sender_name(sender_name);

    deliver_data(channel_name, sender_name, "emote", message);
    deliver_string(channel_name,
		   sprintf(CHANNEL_FORMAT(channel_name), 
			   user_channel_name(channel_name),
			   sender_name + " " + punctuate(message)));
}

/*
** deliver_soul()
**
** Deliver a standard-formatted "soul" over a channel
*/
nomask void deliver_soul(string channel_name, mixed * soul)
{
    string user_channel_name;

    deliver_data(channel_name, find_sender_name(0), "soul", soul);

    user_channel_name = user_channel_name(channel_name);
    soul = ({ soul[0] }) +
	({ map_array(soul[1],
		     (: sprintf(CHANNEL_FORMAT($(channel_name)), $(user_channel_name), $1[0..<2]) :))
       });

    deliver_raw_soul(channel_name, soul);
}

/*
** deliver_notice()
**
** Deliver a standard-formatted "system notice" over a channel
*/
nomask void deliver_notice(string channel_name,
			   string message)
{
    deliver_string(channel_name,
		   sprintf(CHANNEL_FORMAT(channel_name), 
			   user_channel_name(channel_name),
			   "(" + message + ")"));
}

void create()
{
  class listener_pair pair;
  
  info = ([ ]);
  map_array(users(), (: register_body :));
  ::create();
  if ( saved_listeners )
    {
      foreach ( pair in saved_listeners )
	{
	  object ob = find_object(pair->filename);
	  
	  if ( ob )
	    register_one(0, ob, pair->channel_name);
	}
      
      saved_listeners = 0;
    }
  if ( saved_hooks )
    {
      foreach ( pair in saved_hooks )
	{
	  object ob = find_object(pair->filename);
	  
	  if ( ob )
	    register_one(1, ob, pair->channel_name);
	}
      
      saved_hooks = 0;
    }
  
  foreach ( string channel_name, int flags in permanent_channels )
    {
      class channel_info ci;
      
      if ( !info[channel_name] )
	create_channel(channel_name);
      
      ci = info[channel_name];
      ci->flags = flags;
    }
}

/*
** remove()
**
** Write out all listeners and hooks that are blueprints.  We'll reset
** them at creation time.
*/
void remove() {
  string channel_name;
  class channel_info ci;

  saved_listeners = ({ });
  saved_hooks = ({ });

  foreach ( channel_name, ci in info )  {
    object ob;

    foreach ( ob in ci->listeners - ({ 0 }) ) {
      string fname = file_name(ob);
      
      if ( member_array('#', fname) == -1 ) {
	class listener_pair pair = new(class listener_pair);
	
	pair->channel_name = channel_name;
	pair->filename = fname;
	saved_listeners += ({ pair });
      }
    }
    
    foreach ( ob in ci->hooked - ({ 0 }) ) {
      string fname = file_name(ob);
      
      if ( member_array('#', fname) == -1 ) {
	class listener_pair pair = new(class listener_pair);
	
	pair->channel_name = channel_name;
	pair->filename = fname;
	saved_hooks += ({ pair });
      }
    }
  }
  
  save_me();
}


/*
** query_channels()
**
** Return the list of active channels
*/
nomask string * query_channels()
{
    return keys(info);
}

/*
** query_listeners()
**
** Return the listeners of a particular channel
*/
nomask object * query_listeners(string channel_name)
{
    if ( check_previous_privilege(1) ||
	 previous_object() == find_object(IMUD_D) )
    {
	class channel_info ci = info[channel_name];

	if ( ci )
	    return ci->listeners;
    }

    return 0;
}

/*
** query_flags()
**
** Return the flags for a particular channel.  0 is returned for unknown
** channels.
*/
nomask int query_flags(string channel_name)
{
    class channel_info ci = info[channel_name];
    int flags;

    if ( !ci )
	return 0;

    flags = ci->flags;
    if ( !undefinedp(permanent_channels[channel_name]) )
	flags |= CHANNEL_PERMANENT;

    return flags;
}

/*
** make_name_list()
**
** Make a list of names given an array of players.
*/
nomask string make_name_list(mixed * list)
{
    /*
    ** Remove null objects, objects with no links (to interactive users),
    ** and link obs that are no longer interactive.
    */
    list = filter_array(list, (: $1 && interactive($1) :));
    return implode(list->query_userid(), ", ");
}

/*
** is_valid_channel()**
** Is the given string a valid channel name (as in command name) ?  A list
** of internal names should be provided (such as the list a player is
** registered with).  The internal channel name will be returned, or 0
** if the name does not correspond to a channel.
**
** Note: permanent channels will always be valid.
*/
nomask string is_valid_channel(string which, string * list)
{
  if(list)
    if(sizeof(list)>0)
    foreach ( string name in list )
	if ( info[name] && ((class channel_info)info[name])->name == which )
	    return name;
    foreach ( string name in keys(permanent_channels) )
	if ( info[name] && ((class channel_info)info[name])->name == which )
	    return name;
    return 0;
}