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/
#include <lib.h>
#include <daemons.h>
#include <commands.h>
#include <save.h>
#include "./server_log.h"

inherit LIB_DAEMON;
static string router_port = "8888";
mapping mudinfo;

object cmd = find_object(CMD_IMC_SERVER_D);
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 IMC2_SERVER_D.","red");
        error("Illegal attempt to access IMC2 server daemon: "+get_stack()+
          " "+identify(previous_object(-1)));
    }
}

void create(){
    ::create();
    if(object_file(SAVE_IMC2_SERVER)){
        unguarded( (: restore_object, SAVE_IMC2_SERVER, 1 :) );
    }
}

mapping query_mudinfo(){
    return copy(mudinfo);
}

string imc2_name(string str){
    return replace_string(str," ","_");
}

string i3_name(string str){
    string ret = replace_string((str || ""),"_"," ");
    if(ROUTER_D->query_mudinfo()[str]) return str; 
    if(ROUTER_D->query_mudinfo()[ret]) return ret; 
    return str;
}

int is_lpmud(string mud){
    string family, truename = i3_name(mud);
    mapping info = ROUTER_D->query_mudinfo()[truename];
    if(!info) return 0;
    if(stringp(info["mud_type"]) && grepp(info["mud_type"],"LP")){
        tc("woot 1","red");
        return 1;
    }
    if(info["other_data"] && stringp(info["other_data"]["versionid"])){
        if(!strsrch(info["other_data"]["versionid"],"Tim's LPC")){
            tc("woot 2","red");
            return 1;
        }
    }
    return 0;
}

string unescape(string str){
    string a,b=str,output="";
    while(sscanf(b,"%s\\%s",a,b)==2){
        output += a;
        if(sizeof(b)){
            switch(b[0]){
            case 34 : output += "\""; break; // '\"' makes warnings.
            case '\\' : output += "\\"; break;
            case 'n' : output += "\n"; break;
            case 'r' : output += "\r"; break;
            }
        }
        b=b[1..];
    }
    output += b;
    output = replace_string(output,"\n\r","\n");
    if((sizeof(explode(output," "))==1) && sscanf(output,"\\\"%*s\\\"") )
        output = "\""+output+"\"";
    return output;
}

mapping string_to_mapping(string str){
    // Picks first element off of string and then repeats?
    mapping out=([]);
    int i;
    string what,data,rest;

    rest = str;

    while(sizeof(rest)>0){
        sscanf(rest, "%s=%s", what, rest);
        /*
                        write("what="+what+", rest="+rest+"\n");
        */
        // At this point, what is the key, rest is value plus rest.
        if(rest[0]==34){ // value is in quotes, tons of fun!
            // find first quote without a backslash in front?
            /*
                                    write("rest begings with a quote\n");
            */
            i = 1;
            while(((rest[i]!=34) || (rest[i-1]==92)) && (i<sizeof(rest))){ // 34 = ", 92 = \
                // While this is not a quote, or if this is an escaped quote, keep looking.
                i++;
            }
            // now are 1 space past quote
            //                    write("i="+i+"\n");
            data=rest[1..(i-1)]; // skip opening and closing quotes
            rest=rest[(i+2)..]; // skip past space
            //                    write("new data="+data+"\n");
            //                    write("new rest="+rest+"\n");
            // Data is now what was in the quotes... now to un-escape the
            //data...
            out[what]=unescape(data);
        }
        else{ // value is not in quotes, tons of actual non-sarcastic fun!                  // just split it at the first space
            if(sscanf(rest,"%s %s",data,rest)!=2){ // break at first space                      data = rest;
                rest = "";
            }
            if( !data || ((sscanf(data,"%d",i)==1) 
                && (sprintf("%d",i)==data)))//just number
                out[what]=i;
            else // not just a number
                out[what]=data;
        }
    }
    return out;
}

mixed packetize(string str){
    mixed *ret;
    mixed src, targ, who, whom;
    string tmp, seq, route, type, data;
    int i = sscanf(str,"%s %s %s %s %s %s", src, seq, route, type, targ, data);
    if(i < 6){
        //tc("BAD PACKET SIZE","red");
        return 0;
    }
    if( (i = sscanf(src,"%s@%s",who, tmp)) == 2) src = tmp;
    if( (i = sscanf(targ,"%s@%s",whom, tmp)) == 2) targ = tmp;
    if(src && src == "*") src = 0;
    if(targ && targ == "*") targ = 0;
    if(who && who == "*") who = 0;
    if(who && who == "*") who = 0;
    if(whom && whom == "*") whom = 0;
    ret = ({ type, 5, src, who, i3_name(targ), whom, data });
    return ret;
}    

mapping parse_info(string str){
    string url, versionid, host;
    int port;
    mapping ret = ([]);
    str = " " + str + " ";
    if(sscanf(str,"%*surl=%s ", url)) ret["url"] = url;
    else if(sscanf(str,"%*surl=%s\n%*s", url)) ret["url"] = url;
    else if(sscanf(str,"%*surl=%s %*s", url)) ret["url"] = url;
    if(sscanf(str,"%*sversionid=\"%s\"%*s", versionid)) 
        ret["versionid"] = versionid;
    else if(sscanf(str,"%*sversionid=%s ", versionid)) 
        ret["versionid"] = versionid;
    else if(sscanf(str,"%*sversionid=%s %*s", versionid))
        ret["versionid"] = versionid;
    if(sscanf(str,"%*shost=\"%s\"%*s", host)) ret["host"] = host;
    else if(sscanf(str,"%*shost=%s ", host)) ret["host"] = host;
    else if(sscanf(str,"%*shost=%s %*s", host)) ret["host"] = host;
    if(sscanf(str,"%*sport=%d ", port)) ret["port"] = port;
    else if(sscanf(str,"%*sport=%d %*s", port)) ret["port"] = port;
    if(url) ret["url"] = url;
    if(versionid) ret["versionid"] = versionid;
    if(host) ret["host"] = host;
    if(port) ret["port"] = port;
    return ret;
}

mixed translate_packet(mixed data, int fd){
    string targ = ROUTER_D->query_connected_fds()[fd];
    mapping i3info, tmpinfo;
    mixed ret = ({});
    string router_name = ROUTER_D->GetRouterName();
    //trr("IMC2_SERVER_D data to translate: "+identify(data),"blue");
    if(!mudinfo) mudinfo = ([]);
    tmpinfo = copy(mudinfo);
    if(stringp(data)) return data;
    if(!arrayp(data) || sizeof(data) < 6) return 0;
    if(data[0] == "mudlist"){
        mapping tmpret = data[7];
        //tc("%^B_RED%^%^YELLOW%^"+identify(data));
        foreach(mixed key, mixed val in tmpret){
            //tc("%^B_WHITE%^key: "+key+", val[0]: "+val[0],"black");
            if(!key) continue;
            if(sizeof(val) && val[0] > -1 ){
                //trr("%^B_YELLOW%^%^BLACK%^"+identify(key));
                ret += ({ "*@" + router_name + " " + time() + " " +
                  router_name + " close-notify *@* host=" + imc2_name(key) });
            }
            else {
                //trr("%^B_RED%^%^YELLOW%^"+identify(key));
                //trr("%^B_YELLOW%^%^RED%^"+identify(val));
                if(!tmpinfo[key]){
                    i3info = ROUTER_D->query_mudinfo()[key];
                    if(!sizeof(i3info)) continue;
                    tmpinfo[key] = ([]);
                    tmpinfo[key]["versionid"] = i3info["mudlib"];
                    tmpinfo[key]["url"] = "http://"+i3info["ip"];
                    tmpinfo[key]["host"] = i3info["ip"];
                    tmpinfo[key]["port"] = i3info["player_port"];
                }
                ret += ({ "*@"+imc2_name(key)+" "+time()+" "+imc2_mud_name()+
                  //"!"+router_name+
                  " is-alive *@"+ targ + " versionid=\""+
                  tmpinfo[key]["versionid"]+
                  "\" networkname=\"LPMuds.net\" url=\""+
                  tmpinfo[key]["url"]+"\""+
                  " sha256=0"+
                  " host=\""+tmpinfo[key]["host"]+"\" "+
                  "port="+tmpinfo[key]["port"]+
                  "\n\r" });
            }
        }
        //tc("mudlist packet: "+identify(ret),"yellow");
    }
    else trr("IMC2_SERVER_D data to translate: "+identify(data),"blue");
    if(data[0] == "chanlist-reply"){
        //tc("packet: "+identify(data));
        foreach(mixed foo in data){
            string hub = data[2];
            string rname = "LPMuds.net";
            mapping chans = data[7];
            foreach(string chan, mixed arr in chans){
                string owner = replace_string(arr[0]," ","_");
                int priv = arr[1];
                ret += ({
                  "ICE@"+hub+" "+time()+" "+hub+" ice-update *@"+
                  data[4]+" channel="+hub+":"+chan+" owner=admin@"+owner+
                  " operators=admin@"+owner+" policy="+
                  (priv ? "private invited=" : "open excluded=")+
                  "\"a@b\" level=Imm localname="+chan });
            }
        }
    }
    if(data[0] == "tell"){
        string tmp = replace_string(data[7],"\"","'");
        ret = data[3]+"@"+imc2_name(data[2])+" "+time()+" "+
        imc2_name(data[2])+"!"+router_name+" tell "+
        data[5]+"@"+data[4]+" text=\""+unescape(tmp)+"\"";
    }
    if(data[0][0..7] == "channel-"){
        string tmp = replace_string(data[8],"\"","'");
        if(data[0][8..8] == "e"){
            tmp = replace_string(data[8],"$N",data[7]);
        }
        ret = data[7]+"@"+imc2_name(data[2])+" "+time()+" "+
        imc2_name(data[2])+ " ice-msg-b *@* channel="+router_name+
        ":"+data[6]+" text=\""+unescape(tmp)+"\"";
    }
    if(data[0] == "who-req"){
        ret = data[3]+"@"+imc2_name(data[2])+" "+time()+" "+
        imc2_name(data[2])+" who *@"+imc2_name(data[4])+
        " type=who";
    }
    if(data[0] == "who-reply"){
        string who_ret = "Who list for "+data[2]+"\n";
        foreach(mixed element in data[6]){
            who_ret += element[0] + " idle: "+
            time_elapsed(element[1])+"\n";
        }
        who_ret = replace_string(who_ret,"\"","'");
        who_ret = replace_string(who_ret,"\r","");
        if(!is_lpmud(data[4])){
            who_ret = replace_string(who_ret,"\n","\\n");
        }
        //who_ret = "fart";
        tc("who_ret: "+identify(who_ret));
        ret = "*@"+imc2_name(data[2])+" "+time()+" "+
        imc2_name(data[2])+" who-reply "+ capitalize(data[5])+
        "@"+imc2_name(data[4])+" text=\""+ who_ret+"\"";
    }
    if(sizeof(ret)) return ret;
    return 0;
}

varargs void write_data(int fd, mixed data){
    mixed *sstat = socket_status(fd);
    object ssock = find_object(SSOCKET_D);
    mixed ret;
    string targetmud;
    validate();
    targetmud = ROUTER_D->query_connected_fds()[fd];
    if(arrayp(data)){
        //tc("avoiding array write to "+fd+", type: "+data[0],"green");
        if(!(ret = translate_packet(data, fd))){
            //tc("Packet translation failure.");
            return;
        }
    }
    if(ret) data = ret;
    if(!arrayp(data)) data = ({ data });
    //tc("targetmud: "+identify(targetmud));
    //tc("fd: "+identify(fd));
    if(!sstat || sstat[1] != "DATA_XFER" || !ssock || sstat[5] != ssock) return;
    if(member_array(fd, keys(ROUTER_D->query_irn_sockets())) == -1){ 
        foreach(mixed element in data){
            if(sizeof(element) && last(element,2) != "\n\r") element += "\n\r";
            //if(sizeof(element) && last(element,2) != "\r") element += "\r";
            //if(sizeof(element) && last(element,2) != "\n") element += "\n";
            //tc("writing "+identify(element)+" to "+identify(fd),"red");
            SSOCKET_D->write_data(fd, element);
        }
    }
    else  {
        //tc("wtf");
    }
}

static void close_connection(int fd){
    //trr("connection close request for fd "+fd+", stack: "+get_stack(),"red");
    SSOCKET_D->close_connection(fd);
}

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

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

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

int GetMaxRetries(){
    return 5;
}

varargs void construct_startup(mixed fd, mixed info, string client){
    string router = ROUTER_D->GetRouterName();
    string s1, s2, s3, s4, s5;
    mixed packet;
    int passwd, pport, scan, newmud;
    mapping other = ([]);
    string *nix = ({ "clientpass", "serverpass", "password" });
    if(!mudinfo) mudinfo = ([]);
    trr("construct_startup hit");
    scan = sscanf(info,"PW %s %s %s %s %s", s1, s2, s3, s4, s5);
    if(scan != 5) scan = sscanf(info,"PW %s %s %s %s", s1, s2, s3, s4);
    if(scan < 4) scan = sscanf(info,"PW %s %s %s", s1, s2, s3);
    if(scan < 3 || (!mudinfo[s1] && scan != 5)){
        trr("info: "+identify(info));
        trr("client: "+identify(client));
        trr("s1: "+s1);
        trr("s2: "+s2);
        trr("s3: "+s3);
        trr("s4: "+s4);
        trr("s5: "+s5);
        trr("wrong size packet");
        return;
    }
    if(!s5 && mudinfo[s1]) s5 = mudinfo[s1]["serverpass"];
    s5 = replace_string(s5,"\n","");
    s5 = replace_string(s5," SHA256","");
    if(!mudinfo[s1]){
        newmud = 1;
        trr("NEW MUD "+s1);
        mudinfo[s1] = ([ "clientpass" : s2, "serverpass" : s5, 
          "password" : random_numbers(9,1) ]);
}
else {
    if(mudinfo[s1]["clientpass"] != s2){ 
        trr("BAD PASSWORD! Wanted "+mudinfo[s1]["clientpass"]+
          ", got: "+s2,"red");
        mudinfo[s1]["password"] = random_numbers(10,1);
    }
    else trr("Good password for "+s1);
    trr(s1 + " i3 password: "+mudinfo[s1]["password"]);
}
passwd = mudinfo[s1]["password"];
if(mudinfo[s1]["port"]) pport = mudinfo[s1]["port"];
//tc("other: "+identify(other));
foreach(mixed key, mixed val in mudinfo[s1]){
    if(member_array(key, nix) == -1){
        //tc("key: "+key+", val: "+val);
        other[key] = val;
    }
}
//tc("other: "+identify(other));
packet = ({ "startup-req-3", 5, s1, 0, router, 0, passwd, 1, 1,
  pport, 0, 0, " ", " ", " ", " ", " ", " ",
  (["channel" : 1, "who" : 1, "tell" : 1]), other });
this_object()->acknowledge_startup(fd, s1);
ROUTER_D->read_callback(fd, packet);
ROUTER_D->update_imc2(s1, other);
mudinfo[s1]["password"] = ROUTER_D->query_mudinfo()[s1]["password"];
trr(s1 + " i3 password: "+mudinfo[s1]["password"]);
}

void acknowledge_startup(int fd, string mud){
    string passwd = "flan";
    string server = ROUTER_D->GetRouterName();
    trr("ack");
    if(mudinfo && mudinfo[mud] && mudinfo[mud]["serverpass"])
        passwd = mudinfo[mud]["serverpass"];
    trr("PW "+server+" "+passwd+" version=2 LPMuds.net");
    write_data(fd, "PW "+server+" "+passwd+" version=2 LPMuds.net");
}

void read_callback(mixed fd, mixed info){
    mixed packet = packetize(info);
    mixed ret;
    mapping datamap = string_to_mapping(info);
    trr("packet: "+identify(packet),"yellow");
    if(stringp(info) && last(info,1) == "\n") info = truncate(info,1);
    trr("whee! read_callback("+fd+", "+identify(info)+")","cyan");
    trr("Mapping form: "+identify(datamap)+")","cyan");
    if(stringp(info) && !strsrch(info,"PW ")){
        info = replace_string(info,"\n","");
        info = replace_string(info,"\n","");
        construct_startup(fd, info);
        return;
    }
    if(!mudinfo[packet[2]] || 
      ROUTER_D->query_connected_fds()[fd] != packet[2]){
        write_data(fd, "Your connection isn't registered as "+packet[2]);
        close_connection(fd);
        return;
    }
    if(packet[0] == "ice-msg-b"){
        string tmp;
        sscanf(packet[6],"%*shannel=%*s:%s %*s",tmp);
        ret = ({ "channel-m" });
        ret += packet[1..5];
        if(!stringp(datamap["text"])) datamap["text"] = itoa(datamap["text"]);
        ret += ({ tmp, capitalize(packet[3]), datamap["text"] });
        ROUTER_D->read_callback(fd, ret);
        return;
    }
    if(packet[0] == "is-alive" || packet[0] == "keepalive-request"){
        int update;
        mixed data;
        trr(packet[0],"white");
        if(sizeof(packet) < 6){
            trr("Wrong packet size.","white");
            write_data(fd, "Wrong packet size.");
            return;
        }
        data = parse_info(packet[6]);
        trr("data: "+identify(data));
        foreach(mixed key, mixed val in data){
            trr("key: "+identify(key)+", val: "+identify(val),"green");
            if(!mudinfo[packet[2]][key] ||
              mudinfo[packet[2]][key] != val){
                trr("key: "+identify(key)+", val: "+identify(val),"red");
                update = 1;
                mudinfo[packet[2]][key] = val;
            }
        }
        if(update){
            trr("trying to update","white");
            ROUTER_D->update_imc2(packet[2], data);
        }
    }
    if(packet[0] == "keepalive-request"){
        trr("trying to send mudlist");
        ROUTER_D->send_full_mudlist(packet[2]);
        return;
    }
    if(packet[0] == "ice-refresh"){
        trr("trying to send chanlist");
        ROUTER_D->broadcast_chanlist("foo",packet[2]);
        return;
    }
    if(packet[0] == "tell"){
        string msg;
        trr("trying to send tell");
        msg = replace_string(packet[6],"text=","");
        ret = ({ "tell", 5, packet[2], packet[3], packet[4], 
          packet[5], packet[3], msg });
        ROUTER_D->read_callback(fd, ret);
        return;
    }
    if(packet[0] == "who-reply"){
        string *tmp_ret = ({});
        string tmp = packet[6];
        tmp = replace_string(tmp,"text=","");
        if(is_lpmud(packet[4])){
            tmp = replace_string(tmp,"\\n","\n");
        }
        foreach(string element in explode(tmp,"\n")){
            tmp_ret += ({ ({ element, 0, "" }) });
        }
        tc("tmp_ret: "+identify(tmp_ret));
        ret = ({ "who-reply", 5, packet[2], 0, packet[4],
          packet[5], tmp_ret });
        tc("trying to write to fd "+fd+" "+identify(ret),"white");
        ROUTER_D->read_callback(fd, ret);
    }
    if(packet[0] == "who"){
        tc("PACKIT: "+identify(packet));
        ret = ({ "who-req", 5, packet[2], packet[3], packet[4], 0 });
        ROUTER_D->read_callback(fd, ret);
    }
}