ds2.9a12/bin/
ds2.9a12/extra/
ds2.9a12/extra/crat/
ds2.9a12/extra/creremote/
ds2.9a12/extra/mingw/
ds2.9a12/extra/wolfpaw/
ds2.9a12/fluffos-2.14-ds13/
ds2.9a12/fluffos-2.14-ds13/Win32/
ds2.9a12/fluffos-2.14-ds13/compat/
ds2.9a12/fluffos-2.14-ds13/compat/simuls/
ds2.9a12/fluffos-2.14-ds13/include/
ds2.9a12/fluffos-2.14-ds13/testsuite/
ds2.9a12/fluffos-2.14-ds13/testsuite/clone/
ds2.9a12/fluffos-2.14-ds13/testsuite/command/
ds2.9a12/fluffos-2.14-ds13/testsuite/data/
ds2.9a12/fluffos-2.14-ds13/testsuite/etc/
ds2.9a12/fluffos-2.14-ds13/testsuite/include/
ds2.9a12/fluffos-2.14-ds13/testsuite/inherit/
ds2.9a12/fluffos-2.14-ds13/testsuite/inherit/master/
ds2.9a12/fluffos-2.14-ds13/testsuite/log/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/compiler/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/efuns/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/operators/
ds2.9a12/fluffos-2.14-ds13/testsuite/u/
ds2.9a12/lib/cmds/admins/
ds2.9a12/lib/cmds/common/
ds2.9a12/lib/cmds/creators/include/
ds2.9a12/lib/daemon/services/
ds2.9a12/lib/daemon/tmp/
ds2.9a12/lib/doc/
ds2.9a12/lib/doc/bguide/
ds2.9a12/lib/doc/efun/all/
ds2.9a12/lib/doc/efun/arrays/
ds2.9a12/lib/doc/efun/buffers/
ds2.9a12/lib/doc/efun/compile/
ds2.9a12/lib/doc/efun/floats/
ds2.9a12/lib/doc/efun/functions/
ds2.9a12/lib/doc/efun/general/
ds2.9a12/lib/doc/efun/mixed/
ds2.9a12/lib/doc/efun/numbers/
ds2.9a12/lib/doc/efun/parsing/
ds2.9a12/lib/doc/hbook/
ds2.9a12/lib/doc/help/classes/
ds2.9a12/lib/doc/help/races/
ds2.9a12/lib/doc/lfun/
ds2.9a12/lib/doc/lfun/all/
ds2.9a12/lib/doc/lfun/lib/abilities/
ds2.9a12/lib/doc/lfun/lib/armor/
ds2.9a12/lib/doc/lfun/lib/bank/
ds2.9a12/lib/doc/lfun/lib/bot/
ds2.9a12/lib/doc/lfun/lib/clay/
ds2.9a12/lib/doc/lfun/lib/clean/
ds2.9a12/lib/doc/lfun/lib/clerk/
ds2.9a12/lib/doc/lfun/lib/client/
ds2.9a12/lib/doc/lfun/lib/combat/
ds2.9a12/lib/doc/lfun/lib/connect/
ds2.9a12/lib/doc/lfun/lib/container/
ds2.9a12/lib/doc/lfun/lib/corpse/
ds2.9a12/lib/doc/lfun/lib/creator/
ds2.9a12/lib/doc/lfun/lib/daemon/
ds2.9a12/lib/doc/lfun/lib/damage/
ds2.9a12/lib/doc/lfun/lib/deterioration/
ds2.9a12/lib/doc/lfun/lib/donate/
ds2.9a12/lib/doc/lfun/lib/door/
ds2.9a12/lib/doc/lfun/lib/equip/
ds2.9a12/lib/doc/lfun/lib/file/
ds2.9a12/lib/doc/lfun/lib/fish/
ds2.9a12/lib/doc/lfun/lib/fishing/
ds2.9a12/lib/doc/lfun/lib/flashlight/
ds2.9a12/lib/doc/lfun/lib/follow/
ds2.9a12/lib/doc/lfun/lib/ftp_client/
ds2.9a12/lib/doc/lfun/lib/ftp_data_connection/
ds2.9a12/lib/doc/lfun/lib/fuel/
ds2.9a12/lib/doc/lfun/lib/furnace/
ds2.9a12/lib/doc/lfun/lib/genetics/
ds2.9a12/lib/doc/lfun/lib/holder/
ds2.9a12/lib/doc/lfun/lib/id/
ds2.9a12/lib/doc/lfun/lib/interactive/
ds2.9a12/lib/doc/lfun/lib/lamp/
ds2.9a12/lib/doc/lfun/lib/leader/
ds2.9a12/lib/doc/lfun/lib/light/
ds2.9a12/lib/doc/lfun/lib/limb/
ds2.9a12/lib/doc/lfun/lib/living/
ds2.9a12/lib/doc/lfun/lib/load/
ds2.9a12/lib/doc/lfun/lib/look/
ds2.9a12/lib/doc/lfun/lib/manipulate/
ds2.9a12/lib/doc/lfun/lib/meal/
ds2.9a12/lib/doc/lfun/lib/messages/
ds2.9a12/lib/doc/lfun/lib/player/
ds2.9a12/lib/doc/lfun/lib/poison/
ds2.9a12/lib/doc/lfun/lib/position/
ds2.9a12/lib/doc/lfun/lib/post_office/
ds2.9a12/lib/doc/lfun/lib/potion/
ds2.9a12/lib/doc/lfun/lib/room/
ds2.9a12/lib/doc/lfun/lib/server/
ds2.9a12/lib/doc/lfun/lib/spell/
ds2.9a12/lib/doc/lfun/lib/torch/
ds2.9a12/lib/doc/lfun/lib/vendor/
ds2.9a12/lib/doc/lfun/lib/virt_sky/
ds2.9a12/lib/doc/lfun/lib/weapon/
ds2.9a12/lib/doc/lfun/lib/worn_storage/
ds2.9a12/lib/doc/lpc/basic/
ds2.9a12/lib/doc/lpc/concepts/
ds2.9a12/lib/doc/lpc/constructs/
ds2.9a12/lib/doc/lpc/etc/
ds2.9a12/lib/doc/lpc/intermediate/
ds2.9a12/lib/doc/lpc/types/
ds2.9a12/lib/doc/misc/
ds2.9a12/lib/doc/old/
ds2.9a12/lib/domains/
ds2.9a12/lib/domains/Praxis/adm/
ds2.9a12/lib/domains/Praxis/attic/
ds2.9a12/lib/domains/Praxis/cemetery/mon/
ds2.9a12/lib/domains/Praxis/data/
ds2.9a12/lib/domains/Praxis/death/
ds2.9a12/lib/domains/Praxis/mountains/
ds2.9a12/lib/domains/Praxis/obj/armour/
ds2.9a12/lib/domains/Praxis/obj/magic/
ds2.9a12/lib/domains/Praxis/obj/weapon/
ds2.9a12/lib/domains/Praxis/orc_valley/
ds2.9a12/lib/domains/Ylsrim/
ds2.9a12/lib/domains/Ylsrim/adm/
ds2.9a12/lib/domains/Ylsrim/armor/
ds2.9a12/lib/domains/Ylsrim/broken/
ds2.9a12/lib/domains/Ylsrim/fish/
ds2.9a12/lib/domains/Ylsrim/meal/
ds2.9a12/lib/domains/Ylsrim/npc/
ds2.9a12/lib/domains/Ylsrim/obj/
ds2.9a12/lib/domains/Ylsrim/virtual/
ds2.9a12/lib/domains/Ylsrim/weapon/
ds2.9a12/lib/domains/campus/adm/
ds2.9a12/lib/domains/campus/etc/
ds2.9a12/lib/domains/campus/meals/
ds2.9a12/lib/domains/campus/save/
ds2.9a12/lib/domains/campus/txt/ai/charles/
ds2.9a12/lib/domains/campus/txt/ai/charles/bak2/
ds2.9a12/lib/domains/campus/txt/ai/charles/bak2/bak1/
ds2.9a12/lib/domains/campus/txt/ai/charly/
ds2.9a12/lib/domains/campus/txt/ai/charly/bak/
ds2.9a12/lib/domains/campus/txt/jenny/
ds2.9a12/lib/domains/cave/doors/
ds2.9a12/lib/domains/cave/etc/
ds2.9a12/lib/domains/cave/meals/
ds2.9a12/lib/domains/cave/weap/
ds2.9a12/lib/domains/default/creator/
ds2.9a12/lib/domains/default/doors/
ds2.9a12/lib/domains/default/etc/
ds2.9a12/lib/domains/default/vehicles/
ds2.9a12/lib/domains/default/virtual/
ds2.9a12/lib/domains/default/weap/
ds2.9a12/lib/domains/town/txt/shame/
ds2.9a12/lib/domains/town/virtual/
ds2.9a12/lib/domains/town/virtual/bottom/
ds2.9a12/lib/domains/town/virtual/space/
ds2.9a12/lib/estates/
ds2.9a12/lib/ftp/
ds2.9a12/lib/lib/comp/
ds2.9a12/lib/lib/daemons/
ds2.9a12/lib/lib/daemons/include/
ds2.9a12/lib/lib/lvs/
ds2.9a12/lib/lib/user/
ds2.9a12/lib/lib/virtual/
ds2.9a12/lib/log/
ds2.9a12/lib/log/adm/
ds2.9a12/lib/log/archive/
ds2.9a12/lib/log/chan/
ds2.9a12/lib/log/errors/
ds2.9a12/lib/log/law/adm/
ds2.9a12/lib/log/law/email/
ds2.9a12/lib/log/law/names/
ds2.9a12/lib/log/law/sites-misc/
ds2.9a12/lib/log/law/sites-register/
ds2.9a12/lib/log/law/sites-tempban/
ds2.9a12/lib/log/law/sites-watch/
ds2.9a12/lib/log/open/
ds2.9a12/lib/log/reports/
ds2.9a12/lib/log/router/
ds2.9a12/lib/log/secure/
ds2.9a12/lib/log/watch/
ds2.9a12/lib/obj/book_source/
ds2.9a12/lib/obj/include/
ds2.9a12/lib/powers/prayers/
ds2.9a12/lib/powers/spells/
ds2.9a12/lib/realms/template/adm/
ds2.9a12/lib/realms/template/area/armor/
ds2.9a12/lib/realms/template/area/npc/
ds2.9a12/lib/realms/template/area/obj/
ds2.9a12/lib/realms/template/area/room/
ds2.9a12/lib/realms/template/area/weap/
ds2.9a12/lib/realms/template/bak/
ds2.9a12/lib/realms/template/cmds/
ds2.9a12/lib/save/kills/o/
ds2.9a12/lib/secure/cfg/classes/
ds2.9a12/lib/secure/cmds/builders/
ds2.9a12/lib/secure/cmds/creators/include/
ds2.9a12/lib/secure/cmds/players/
ds2.9a12/lib/secure/cmds/players/include/
ds2.9a12/lib/secure/daemon/imc2server/
ds2.9a12/lib/secure/daemon/include/
ds2.9a12/lib/secure/lib/
ds2.9a12/lib/secure/lib/include/
ds2.9a12/lib/secure/lib/net/include/
ds2.9a12/lib/secure/lib/std/
ds2.9a12/lib/secure/log/adm/
ds2.9a12/lib/secure/log/bak/
ds2.9a12/lib/secure/log/intermud/
ds2.9a12/lib/secure/log/network/
ds2.9a12/lib/secure/modules/
ds2.9a12/lib/secure/npc/
ds2.9a12/lib/secure/obj/include/
ds2.9a12/lib/secure/room/
ds2.9a12/lib/secure/save/
ds2.9a12/lib/secure/save/backup/
ds2.9a12/lib/secure/save/boards/
ds2.9a12/lib/secure/tmp/
ds2.9a12/lib/secure/upgrades/files/
ds2.9a12/lib/secure/verbs/creators/
ds2.9a12/lib/std/board/
ds2.9a12/lib/std/lib/
ds2.9a12/lib/tmp/
ds2.9a12/lib/verbs/admins/include/
ds2.9a12/lib/verbs/builders/
ds2.9a12/lib/verbs/common/
ds2.9a12/lib/verbs/common/include/
ds2.9a12/lib/verbs/creators/
ds2.9a12/lib/verbs/creators/include/
ds2.9a12/lib/verbs/rooms/
ds2.9a12/lib/verbs/rooms/include/
ds2.9a12/lib/www/client/
ds2.9a12/lib/www/errors/
ds2.9a12/lib/www/images/
ds2.9a12/lib/www/lpmuds/downloads_files/
ds2.9a12/lib/www/lpmuds/intermud_files/
ds2.9a12/lib/www/lpmuds/links_files/
ds2.9a12/win32/
// I3 server.
// This file written mostly by Tim Johnson (Tim@TimMUD)
// Started by Tim on May 7, 2003.
// http://cie.imaginary.com/protocols/intermud3.html#errors
#include <lib.h>
#include <commands.h>
#include <daemons.h>
#include <socket.h>
#define MAXMUDS 32	// Max number of muds allowed from one IP
#define ROUTER_BLACKLIST "/secure/daemon/i3router/blacklist.cfg" //bad IP's
#define DEB_IN  1	// //trr-Incoming
#define DEB_OUT 2	// //trr-Outgoing
#define DEB_OUT_LOG 0	// //logging stuff
#define DEB_INVALID 3	// //trr-Invalid
#define DEB_OTHER 0	// //trr-Other
#define DEBUGGER_GUY "cratylus"	// Name of who to display //trrging info to.
#define MAXIMUM_RETRIES 20
// SEND_WHOLE_MUDLIST makes it act like the official I3 server instead of like the I3 specs
#define SEND_WHOLE_MUDLIST
// SEND_WHOLE_CHANLIST makes it act like the official I3 server instead of like the I3 specs
#define SEND_WHOLE_CHANLIST
inherit LIB_DAEMON;

object cmd = load_object(CMD_ROUTER);
object rsocket = find_object(RSOCKET_D);
object ssocket = find_object(SSOCKET_D);

static void validate(){
    if( previous_object() != cmd && previous_object() != rsocket &&
      previous_object() != this_object() && previous_object() != ssocket &&
      !((int)master()->valid_apply(({ "ASSIST" }))) ){
        trr("SECURITY ALERT: validation failure in ROUTER_D.","red");
        error("Illegal attempt to access router daemon: "+get_stack()+
          " "+identify(previous_object(-1)));
    }
}

// Saved variables...
mapping listening;
// list of muds listening to each channel
// (key=chan name, value=mud array)
mapping connected_muds;
// muds that have successfully done a startup
// (key=mudname, value=fd)
string router_name; // Name of the router.
string router_ip;
static string *router_list = ({}); // Ordered list of routers to use.
mapping mudinfo = ([]); // Info about all the muds which the router knows about.
mapping channels; // Info about all the channels the router handles.
mapping channel_updates; // Tells when a channel was last changed.
int channel_update_counter; // Counter for the most recent change.
// Why is this not a part of the channels mapping?
// Because I need to remember that some channels got deleted.
mapping mudinfo_updates; // Like channel_updates except for muds.
int mudinfo_update_counter; // Similar to channel_update_counter
static int max_age = 604800;
//static int max_age = 86400;
mapping Blacklist = ([]);

// Prototypes
static mapping muds_on_this_fd(int fd);
static mapping muds_not_on_this_fd(int fd);
void write_data(int fd, mixed data);
static void close_connection(int fd);
static void broadcast_data(mapping targets, mixed data);

// Ones with their own files...
string clean_fd(string fd);
static void broadcast_chanlist(string channame);
void broadcast_mudlist(string mudname);
static varargs void Debug(string str, int level);
static void process_channel(int fd, mixed *info);
static void process_startup_req(int protocol, mixed info, int fd);
static void read_callback(int fd, mixed info);
static void remove_mud(string mudname, int forced);
static void send_chanlist_reply(string mudname, int old_chanid);
static void send_mudlist(string mudname);
static void send_mudlist_updates(string updating_mudname, int old_mudlist_id);
static void send_startup_reply(string mudname);
static void send_error(string mud, string user, string errcode, string errmsg, mixed *info);
void send_full_mudlist(string mud);
// core_stuff.h...
static void create();
static void setup();
void remove();
// funcs.h...
static mapping muds_on_this_fd(int fd);
int value_equals(string a,int b, int c);
static mapping muds_not_on_this_fd(int fd);
int value_not_equals(string a,int b, int c);
// socket_stuff.h
varargs string *SetList();

// Code for all the stuff in the prototypes...
#include "./server_log.h"
#include "./irn.h"
#include "./clean_fd.h"
#include "./broadcast_chanlist.h"
#include "./broadcast_mudlist.h"
#include "./debug.h"
#include "./process_channel.h"
#include "./process_startup_req.h"
#include "./read_callback.h"
#include "./remove_mud.h"
#include "./send_chanlist_reply.h"
#include "./send_mudlist_updates.h"
#include "./send_startup_reply.h"
#include "./send_error.h"

#include "./core_stuff.h"
#include "./funcs.h"
#include "./hosted_channels.h"
#include "./send_full_mudlist.h"

static void close_connection(int fd){
    if(!socket_status(fd) || socket_status(fd)[0] == -1) return;
    if(base_name(socket_status(fd)[5]) == RSOCKET_D){
        RSOCKET_D->close_connection(fd);
    }
    if(base_name(socket_status(fd)[5]) == SSOCKET_D){
        SSOCKET_D->close_connection(fd);
    }
}

varargs void write_data(int fd, mixed data, int override){
    mixed *sstat = socket_status(fd);
    object rsock = find_object(RSOCKET_D);
    string targetmud;
#if DEB_OUT_LOG
    write_file("/secure/log/tmp.txt","Trying to send at "+ctime(time()));
    write_file("/secure/log/tmp.txt"," packet size:"+sizeof(data));
    write_file("/secure/log/tmp.txt"," "+identify(data)+"\n---\n\n");
#endif
    if(data[0] == "irn-startup-req"){
        int ok = 0;
        string tmpsstat;
        if(socket_status(fd)) tmpsstat = socket_status(fd)[4];
        foreach(mixed element in ok_ips){
            if(tmpsstat && !strsrch(tmpsstat,element+".")) ok = 1;
        }
        if(!ok){
            trr("%^B_GREEN%^ATTENTION: IRN startup aborted for "+
              tmpsstat,"red");
            return;
        }
        trr("%^B_YELLOW%^ATTENTION: IRN startup about to be sent to fd"+fd+":%^RESET%^ "+ identify(data)+", "+identify(socket_status(fd)),"red");
    }
    targetmud = this_object()->query_connected_fds()[fd];
    if(!sstat || sstat[1] != "DATA_XFER") return;
    if(!fd || (!data[4] && targetmud) ||  member_array(fd, keys(irn_sockets)) != -1 ||
      (targetmud && targetmud == data[4]) ){  
        if(rsock && sstat[5] == rsock) RSOCKET_D->write_data(fd, data);
        else IMC2_SERVER_D->write_data(fd, data);
    }
    else  {
    }
}

static void broadcast_data(mapping targets, mixed data){
    object ssock = find_object(SSOCKET_D);
    RSOCKET_D->broadcast_data(targets, data);
    if(ssock) IMC2_SERVER_D->broadcast_data(targets, data);
}

// debugging stuff...
mapping query_mudinfo(){ validate(); return copy(mudinfo); }
mapping query_mud(string str){ validate(); return copy(mudinfo[str]); }
mapping query_connected_muds(){ validate(); return copy(connected_muds); }
mapping query_chaninfo(){ return ([ "listening" : listening, "channels" : channels ]); }
mapping query_socks(){
    object ssock = find_object(SSOCKET_D);
    object rsock = find_object(RSOCKET_D);
    mapping ret = ([]);
    validate();
    if(rsock) ret = RSOCKET_D->query_socks();
    if(ssock) ret += SSOCKET_D->query_socks();
    return ret;
}

mapping query_connected_fds(){
    mapping RetMap = ([]);
    validate();
    foreach(mixed key, mixed val in connected_muds){
        RetMap[val] = key;
    }
    return copy(RetMap);
}

int *open_socks(){
    int *ret = ({});
    validate();
    foreach(mixed element in socket_names()){
        if(intp(element[0]) && element[0] != -1 && !grepp(element[3],"*") &&
          last_string_element(element[3],".") == router_port &&
          member_array(element[0],
            keys(this_object()->query_irn_sockets())) == -1) {
            ret += ({ element[0] });
        }
    }
    return ret;
}

int query_imc(mixed mud){
    mixed *sstat;
    object ssock = find_object(SSOCKET_D);
    if(stringp(mud)) mud = query_connected_muds()[mud];
    if(!intp(mud)) return 0;
    sstat = socket_status(mud);
    if(!ssock ||!sstat || sizeof(sstat) != 6 || sstat[0] == -1) return 0;
    if(sstat[5] == ssock) return 1;
    return 0;
}

mixed get_info(int auto) {
    mixed *socky = sort_array(values(connected_muds), 1);
    mixed *muddy = sort_array(keys(connected_muds), 1);
    string socks = implode(socky, " ");
    string muddies = implode(muddy, ", ");
    int socknum = sizeof(socky);
    string sret = "", ret = "";
    validate();

    if(find_object(SSOCKET_D)){
        sret = "\nIMC2 server socket daemon uptime: "+
        time_elapsed(time()-SSOCKET_D->GetInceptDate())+
        ", up since "+ctime(SSOCKET_D->GetInceptDate());
    }
    socks += "\nTotal number of connected muds: "+socknum+"\n";
    ret = "router_name: "+router_name+
    "\nrouter_ip: "+router_ip+
    "\nrouter_port: "+router_port+
    "\nrouter_list"+identify(router_list)+
    "\nchannel_update_counter: "+ channel_update_counter+
    ((sizeof(channels)) ? "\nchannels:"+implode(sort_array(keys(channels),1),", ") : "")+
    "\nmudinfo_update_counter: "+ mudinfo_update_counter+
    "\nsockets: "+socks+
    "\nmuds: "+muddies+
    "\nRouter socket daemon uptime: "+
    time_elapsed(time()-RSOCKET_D->GetInceptDate())+
    ", up since "+ctime(RSOCKET_D->GetInceptDate())+
    sret+
    "\n"+Report();

    if(auto) return ret;
    write(ret);
    return 1;
}

void clear(){ 
    string mudname; 
    validate();
    server_log("%^RED%^Clearing all mud data!"); 
    foreach(mudname in keys(mudinfo)) remove_mud(mudname,1); 
    save_object(SAVE_ROUTER);    
}

string GetRouterName(){
    validate();
    return router_name;
}

string SetRouterName(string str){
    validate();
    if(first(str,1) != "*") str = "*"+str;
    router_name = str;
    server_log(" setting router name to: "+str); 
    SetList();
    return router_name;
}

string GetRouterIP(){
    validate();
    return router_ip;
}

string SetRouterIP(string str){
    validate();
    router_ip = str;
    server_log("Setting router IP to: "+str);
    SetList();
    return router_ip;
}

string GetRouterPort(){
    validate();
    return router_port;
}

string SetRouterPort(string str){
    validate();
    router_port = str;
    server_log("Setting router port to: "+str);
    SetList();
    return router_port;
}

string *GetRouterList(){
    validate();
    return router_list;
}

varargs string *SetList(){
    string tmp;
    string tmp_port = router_port;
    string tmp_ip = router_ip;
    validate();
    if(!strsrch(router_name,"*")) tmp = router_name;
    else tmp = "*"+router_name;
    if(lower_case(mud_name()) == "frontiers" ||
      lower_case(mud_name()) == "*yatmim"){
        tmp_port = "23";
        tmp_ip = "149.152.218.102";
        tmp = "*yatmim";
    }
    if(!sizeof(router_list)){
        router_list = ({ ({ tmp, tmp_ip+" "+tmp_port }) });
    }
    server_log("Setting router list to: "+identify(router_list));
    save_object(SAVE_ROUTER);
    return router_list;
}

mapping GetConnectedMuds(){
    validate();
    return copy(connected_muds);
}

string *GetBannedMuds(){
    validate();
    return banned_muds;
}

string *AddBannedMud(string str){
    validate();
    banned_muds += ({ str });
    server_log(str+" has been BANNED");
    save_object(SAVE_ROUTER);
    return banned_muds;
}

string *RemoveBannedMud(string str){
    validate();
    banned_muds -= ({ str });
    server_log(str+" has been unbanned.");
    save_object(SAVE_ROUTER);
    return banned_muds;
}

string *GetBlacklistedMuds(){
    validate();
    return blacklisted_muds;
}

varargs string *AddBlacklistedMud(string str, int perma){
    validate();
    if(!Blacklist) Blacklist = ([]);
    if(Blacklist[str]) return blacklisted_muds;
    if(perma) perma = 0;
    else perma = time();
    Blacklist[str] = perma;
    blacklisted_muds += ({ str });
    server_log(str+" has been BLACKLISTED");
    save_object(SAVE_ROUTER);
    return blacklisted_muds;
}

string *RemoveBlacklistedMud(string str){
    validate();
    if(!Blacklist) Blacklist = ([]);
    if(Blacklist[str]) map_delete(Blacklist, str);
    blacklisted_muds -= ({ str });
    server_log(str+" has been unblacklisted.");
    save_object(SAVE_ROUTER);
    return blacklisted_muds;
}

mixed GetRemoteIP(int fd){
    mixed conn = socket_status(fd);
    string ret;
    if(!conn || !sizeof(conn)) return 0;
    if(!conn[4] || conn[4] == "*.*") return 0;
    conn = explode(conn[4],".");
    ret = implode(conn[0..3],".");
    return ret;
}  

void check_blacklist(){
    int timenow = time();
    string *bms;
    trr("Checking old blacklists");
    if(!Blacklist) Blacklist = ([]);
    blacklisted_muds = distinct_array(blacklisted_muds);
    bms = keys(Blacklist);

    foreach(string element in blacklisted_muds){
        if(member_array(element, bms) == -1)
            Blacklist[element] = 0;
    }

    foreach(string key, int val in Blacklist){
        if((timenow - val) == timenow) continue;
        if((timenow - val) > max_age){
            RemoveBlacklistedMud(key);
            trr("%^B_BLACK%^Removed "+key+" from blacklist.","white");
        }
    }
}

void check_graylist(){
    //tc("Checking graylist");
    if(sizeof(graylisted_muds)){
        graylisted_muds = distinct_array(graylisted_muds);
        tc("removing graylisted  "+graylisted_muds[0]);
        graylisted_muds -= ({ graylisted_muds[0] });
    }
}

void check_discs(){
    int *fds = values(connected_muds);
    int i = 1;

    foreach(string mudname in keys(mudinfo)){
        if(!connected_muds[mudname] && mudinfo[mudname]["router"]){
            if(mudinfo[mudname]["router"] != my_name &&
              member_array(mudinfo[mudname]["router"],keys(irn_connections)) == -1){
                if(!mudinfo[mudname]["disconnect_time"]){
                    trr("killing "+mudname);
                    mudinfo[mudname]["disconnect_time"] = 100;
                    if(mudinfo[mudname]["connect_time"])
                        mudinfo[mudname]["connect_time"] = 0;
                    schedule_broadcast(mudname);
                }
            }
        }
    }

    if(sizeof(fds))
        foreach(mixed element in sort_array(fds,1)){
            string lost_mud;
            if(!intp(element)) continue;
            if(!socket_status(element) ||
              socket_status(element)[1] == "CLOSED" || !GetRemoteIP(element) ||
              GetRemoteIP(element) != mudinfo[query_connected_fds()[element]]["ip"]){
                foreach(string key, mixed val in mudinfo){
                    if(!connected_muds[key] && mudinfo[key]["router"]){
                        if(mudinfo[key]["router"] == my_name){
                            server_log("Cleaning connection info from "+key);
                            if(!mudinfo[key]["disconnect_time"])
                                mudinfo[key]["disconnect_time"] = time();
                            if(mudinfo[key]["connect_time"]) 
                                mudinfo[key]["connect_time"] = 0;
                        }
                        else {
                            server_log("Cleaning connection info from "+key);
                            if(!mudinfo[key]["disconnect_time"])
                                mudinfo[key]["disconnect_time"] = 0;
                            if(mudinfo[key]["connect_time"]) 
                                mudinfo[key]["connect_time"] = 0;
                        }
                    }
                }

                foreach(string key, int val in connected_muds){
                    if(val == element){
                        trr("REMOVING DISCONNECTED: "+key+" from "+val);
                        i++;
                        call_out( (: disconnect_mud :), i, key);
                    }
                }
            }
        }
}

void clean_ghosts(){
    int tmp,i;
    object rsockd = find_object(RSOCKET_D);
    object ssockd = find_object(SSOCKET_D);
    mixed array incoming = socket_status();
    mixed *legit_socks = keys(this_object()->query_socks());
    legit_socks += keys(this_object()->query_irn_sockets());

    if(!rsockd && !ssockd) return;

    tmp =sizeof(socket_status())-1;

    for(i=0;i < tmp;i++){ 
        if(!incoming[i][5] || 
          (incoming[i][5] != rsockd && incoming[i][5] != ssockd)) continue;
        if(member_array(i, legit_socks) == -1 && incoming[i][1] == "DATA_XFER"){ 
            this_object()->close_connection(i);
        }
    }
}

void clean_chans(){
    string *cleaned = ({});
    foreach(mixed key, mixed val in listening){
        if(arrayp(val)){
            foreach(string listening_mud in val){
                if(!mudinfo[listening_mud]){
                    listening[key] -= ({ listening_mud });
                    cleaned += ({ listening_mud });
                }
            }
        }
        else trr("huh? not an array?");
    }
    foreach(mixed key, mixed val in channels){
        mixed *tmp_chan = ({});
        if(sizeof(val) == 3){
            tmp_chan = ({ val[0], val[1], distinct_array(val[2]) });
            channels[key] = tmp_chan;
        }
    }
    cleaned = distinct_array(cleaned);
    save_object(SAVE_ROUTER);
    trr("channel cleanup: cleaned from listening: "+implode(cleaned,"\n"));
}

void clear_discs(){ 
    string mudname; 
    int i = 1;

    validate();

    trr("%^B_BLACK%^Discarding excessively old disconnects.","white");
    trr("%^B_BLACK%^Max age is "+time_elapsed(max_age)+".","white");
    foreach(mudname in keys(mudinfo)) {
        int deadsince = time() - mudinfo[mudname]["disconnect_time"];

        if(!connected_muds[mudname] && mudinfo[mudname]["router"]){
            if(mudinfo[mudname]["router"] != my_name && 
              member_array(mudinfo[mudname]["router"],keys(irn_connections)) == -1){
                if(!mudinfo[mudname]["disconnect_time"])
                    mudinfo[mudname]["disconnect_time"] = 0;
                if(mudinfo[mudname]["connect_time"])
                    mudinfo[mudname]["connect_time"] = 0;
            }
        }

        if(!mudinfo[mudname]["disconnect_time"]) continue;
        trr("%^B_BLACK%^"+mudname+": "+ctime((mudinfo[mudname]["disconnect_time"]) || time()),"white");
        trr("%^B_BLACK%^dead age: "+time_elapsed(deadsince),"white");
        if(mudinfo[mudname]["disconnect_time"] && deadsince > max_age ){
            trr(mudname+": "+time_elapsed(deadsince),"red");
            server_log("I want to remove "+mudname+". Its disconnect time is "+ctime(query_mud(mudname)["disconnect_time"]));
            server_log("Which was "+time_elapsed(time() - query_mud(mudname)["disconnect_time"])+" ago.");
            i = i+1;
            remove_mud(mudname,1);
        }

        if(mudinfo[mudname] && mudinfo[mudname]["disconnect_time"] > 0 &&
          mudinfo[mudname]["connect_time"] > 0){
            i = 1;
            server_log("I want to remove "+mudname+". It is in a paradox state.");
            if(member_array(mudname,keys(query_connected_muds())) != -1){
                trr("%^B_BLACK%^Its fd is: "+query_connected_muds()[mudname],"white");
            }
            else {
                trr("%^B_BLACK%^It is not listed as a connected mud.","white");
            }
            server_log("Removing disconnected mud: "+identify(mudname));
            i = i+2;
            remove_mud(mudname,1);
        }
    }
}

int eventDestruct(){
    validate();
    save_object(SAVE_ROUTER);
    server_log("I am being destructed by: \n"+get_stack()+
      "\n"+identify(previous_object(-1)));
    daemon::eventDestruct();
}

string query_fd_info(mixed foo){
    int num, i;
    string ret = "";    validate();
    if(stringp(foo)) if(sscanf(foo,"%d",num) != 1) return "foo";
    if(intp(foo)) num = foo;
    for(i=0;i<num;i++){
        mapping bar = query_connected_fds();
        ret += i+" "+socket_address(i)+" "+(bar[i]+"" || "")+"\n";
    }
    return ret;
}

int GetMaxRetries(){ return MAXIMUM_RETRIES; }

varargs void ReceiveList(mixed data, string type, string who){
    string *cmuds = keys(connected_muds);
    //trr("ReceiveList("+identify(data)+", "+identify(type)+", "+identify(who)+")");
    if(!type || !sizeof(type)) type = "mudlist";
    if(!mapp(data)){
        return;
    }
    if(type == "mudlist"){
        foreach(mixed key, mixed val in data){
            if(member_array(key,cmuds) != -1) continue;
            if(val && val["router"] && val["router"] == router_name) continue;
            if(mudinfo[key] && (!val || intp(val))){
                trr("ROUTER_D: deleting "+key);
                remove_mud(key,1);
                continue;
            }
            if(!mapp(val)){
                return;
            }
            mudinfo_update_counter++;
            //if(mudinfo[key] && mudinfo[key]["router"] &&
            //  mudinfo[key]["router"] == router_name) continue;
            if(!connected_muds[key]){
                trr("%^B_GREEN%^%^BLACK%^accepting "+key+
                  " update from "+(mudinfo[key] ? mudinfo[key]["router"] : who ));
                mudinfo_updates[key] = mudinfo_update_counter;
                mudinfo[key]=val;
                schedule_broadcast(key, 1);
            }
            else {
                trr("%^RESET%^Looks like I'm %^B_RED%^%^BLACK%^REJECTING%^RESET%^ "+key+"."
                  +mudinfo[key]["router"]+" update from "+who);
            }
        }
    }
    else if(type == "chanlist"){
        if(data["listening"] && sizeof(data["listening"]) && mapp(data["listening"])){
            foreach(mixed key, mixed val in data["listening"]){
                trr("listening update: "+key+" is "+identify(val)+" val is a "+typeof(val),"yellow");
            }
        }
        if(data["channels"] && sizeof(data["channels"]) && mapp(data["channels"])){
            foreach(mixed key, mixed val in data["channels"]){
                string ownermud = data["channels"][key][1];
                if(!key || !val || !stringp(key)){
                    map_delete(channels, key);
                    map_delete(listening, key);
                    continue;
                }
                if(member_array(ownermud, cmuds) != -1) continue;
                if(val == -1) map_delete(channels, key);
                if(val == -1) map_delete(listening, key);
                else {
                    val[2] = distinct_array(val[2]);
                    channels[key] = val;
                }
                broadcast_chanlist(key);
                trr("chan update: "+key+" is "+identify(val)+" val is a "+typeof(val),"green");
            }
        }
    }
    save_object(SAVE_ROUTER);
}

int purge_crud(){
    foreach(mixed key, mixed val in mudinfo){
        if(!mapp(val)) map_delete(mudinfo, key);
    }
    save_object(SAVE_ROUTER);
    return sizeof(mudinfo);
}

varargs int purge_ip(string ip, int rude, mixed *sock_array){
    validate();
    if(!sock_array || !sizeof(sock_array)) sock_array = socket_names();
    foreach(mixed element in sock_array){
        int fd = element[0];
        if(last_string_element(element[3],".") != router_port) continue;
        if(member_array(fd, keys(irn_sockets)) != -1) continue;
        if(ip == "*") continue;
        if(clean_fd(socket_address(fd)) == ip){
            if(query_connected_fds()[fd]){
                if(rude){
                    server_log("router: fd to be rudely purged: "+fd+", "+query_connected_fds()[fd]);
                    disconnect_mud(query_connected_fds()[fd]);
                }
            }
        }
    }
    return 1;
}

varargs int purge_ips(int rude){
    mixed *sockies = socket_names();
    foreach(mixed element in sockies){
        string ip_address = element[3];
        if(last_string_element(ip_address,".") == router_port ){
            ip_address = replace_string(element[4],"."+last_string_element(element[4],"."),"");
            purge_ip(ip_address,(rude || 0), sockies);
        }
    }
    return 1;
}

void update_imc2(string mud, mapping foo){
    //tc("update_imc2("+identify(mud)+", "+identify(foo)+")");
    validate(); 
    if(!mudinfo[mud]) return;
    if(!mudinfo[mud]["other_data"]) mudinfo[mud]["other_data"] = ([]);
    //tc("update_imc2("+identify(mud)+", "+identify(foo)+")","white");
    foreach(mixed key, mixed val in foo){
        mudinfo[mud]["other_data"][key] = val;
        if(key == "port") mudinfo[mud]["player_port"] = val;
        if(key == "versionid"){
            mudinfo[mud]["base_mudlib"] = val;
            mudinfo[mud]["mudlib"] = val;
            mudinfo[mud]["mud_type"] = "n/a";
            mudinfo[mud]["driver"] = "IMC2 client";
        }
    }
    //tc(mud + " info: "+identify(mudinfo[mud]),"white");
    this_object()->broadcast_mudlist(mud);
}