/* 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;
}