Don't like ads? PRO users don't see any ads ;-)
Guest

LPC xterm-256color imc2 daemon

By: a guest on Jun 18th, 2012  |  syntax: C  |  size: 53.96 KB  |  hits: 22  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. // Tim Johnson
  2. // Started on May 1, 2004.
  3. // Use this however you want.
  4.  
  5. #include <lib.h>
  6. #include <logs.h>
  7. #include <cgi.h>
  8. #include <save.h>
  9. #include NETWORK_H
  10. #include <socket_err.h>
  11. #include <daemons.h>
  12. #include <message_class.h>
  13.  
  14. inherit LIB_DAEMON;
  15.  
  16. #define IMC2_C
  17. #include <pinkfish.h>
  18.  
  19. //Which IMC version you wish to support.
  20. #define IMC_VERSION 2
  21.  
  22. // Connection data for Davion's server
  23. // HostIP overrides HOSTNAME, in case the mud doesn't want to resolve addresses
  24. //#define HOSTNAME "server01.mudbytes.net"
  25. #ifndef IMCTESTING
  26. #define HOSTPORT 5000
  27. #define HOSTIP "74.207.247.83"
  28. #endif
  29.  
  30. // Connection data for Davion's other server
  31. // HostIP overrides HOSTNAME, in case the mud doesn't want to resolve addresses
  32. //#define HOSTPORT 9000
  33. //#define HOSTIP "74.207.247.83"
  34.  
  35. // Connection data for Kayle and Samson's server
  36. // HostIP overrides HOSTNAME, in case the mud doesn't want to resolve addresses
  37. //#define HOSTPORT 9000
  38. //#define HOSTIP "66.218.49.113"
  39.  
  40. //This is the LPMuds.net experimental IMC2 server. If you are
  41. //already connected to LPMuds.net intermud using Intermud-3, do
  42. //not use the LPMuds.net IMC2 server.
  43. #ifdef IMCTESTING
  44. #define HOSTPORT 8888
  45. #define HOSTIP "97.107.133.86"
  46. #endif
  47.  
  48. // In theory, live server
  49. #define HOSTPORT 5000
  50. #define HOSTIP "96.126.117.92"
  51.  
  52. // What name the network knows your mud as. Replace MUD_NAME with "whatever" if you want it to be different.
  53. #define MUDNAME (imc2_mud_name())
  54.  
  55. // COMMAND_NAME is the command that people type to use this network.
  56. #define COMMAND_NAME "imc2"
  57.  
  58. // NETWORK_ID is what your mud calls this network.
  59. // This is the prefix that comes up on all messages to identify the IMC2 network.
  60. // Tells for example look like:
  61. // NETWORK_ID- Tim@TimMUD tells you: hi
  62. // Make it similar to the command name, so players will understand.
  63. // **OUTDATED
  64. #define NETWORK_ID "IMC2"
  65.  
  66. // DATA_LOG is where packets are logged to.
  67. // Turn IMC2_logging off when not working on the system, as it invades privacy.
  68. // Comment this out to turn it off.
  69. #define IMC2_LOGGING
  70.  
  71. #ifndef LOG_IMC2
  72. #define DATA_LOG "/secure/log/intermud/imc2"
  73. #else
  74. #define DATA_LOG LOG_IMC2
  75. #endif
  76.  
  77. // UNKNOWN_DATA_LOG is where unrecognized packets are logged to.
  78. // I wrote handlers for all packets I know of, so this should only pick
  79. // up tests and possibly if anyone is creating new packets.
  80. #define UNKNOWN_DATA_LOG DATA_LOG
  81.  
  82. // Your MUD's URL is shared with other muds when building the mud list.
  83. // This you could also put this in your who reply.
  84. #define URL "http://dead-souls.net"
  85.  
  86. // ANNOUNCE_LOG is where network announcements get logged to.
  87. // I suggest you keep this turned on.
  88. // These announcements seem to be about channels being created and
  89. // deleted, but may possibly have more.
  90. #define ANNOUNCE_LOG "IMC2_ANNOUNCEMENTS"
  91.  
  92. // How many lines you want the backlog to be.
  93. #define BACKLOG_SIZE 20
  94.  
  95. // Minimum permission number for channel to be viewable on the web.
  96. #define BACKLOG_WEB_LEVEL 0
  97.  
  98. // What's the file for the channel daemon?
  99. #ifndef CHANNEL_BOT
  100. #define CHANNEL_BOT CHAT_D
  101. #endif
  102. #ifndef STREAM
  103. #define STREAM 1
  104. #endif
  105. #ifndef EESUCCESS
  106. #define EESUCCESS 1 /* Call was successful */
  107. #endif
  108. #ifndef THIS_PLAYER
  109. #define THIS_PLAYER this_player()
  110. #endif
  111. #ifndef FIND_PLAYER
  112. #define FIND_PLAYER(x) find_player(x)
  113. #endif
  114. #ifndef GET_CAP_NAME
  115. #define GET_CAP_NAME(x) replace_string(x->GetName()," ","")
  116. #endif
  117. #ifndef GET_NAME
  118. #define GET_NAME(x) convert_name(x->GetName())
  119. #endif
  120. #ifndef IMC2_MSG
  121. #define IMC2_MSG(x,y) foreach(tmpstr in explode(x,"\n")){ message("IMC2",tmpstr,y); }
  122. #endif
  123. #ifndef GET_GENDER
  124. #define GET_GENDER(x) x->GetGender()
  125. #endif
  126. #ifndef ADMIN
  127. #define ADMIN(x) archp(x)
  128. #endif
  129. #ifndef VERSION
  130. #define VERSION "Tim's LPC IMC2 client 30-Jan-05 / Dead Souls integrated"
  131. #endif
  132.  
  133. #define HTML_LOCATION "http://dead-souls.net/"
  134.  
  135. // Other things that could be #define'd...
  136. // INVIS(x) !visible(x)
  137. // TELL_BOT "/u/t/timbot/imc2_invalidtells.c"
  138. // TELL_BOTS ([ "timbot" : "/u/t/timbot/imc2_tellbot.c" ])
  139. // CHAN_BOT "/u/t/timbot/imc2_chans.c"
  140. // CHAN_BOTS ([ "ichat" : "/u/t/timbot/imc2_ichat.c" ])
  141. // USER_EXISTS(x) user_exists(x)
  142.  
  143. // Mode decides what kind of packet is expected
  144. #define NO_UIDS
  145. #define MODE_CONNECTED 1
  146. #define MODE_WAITING_ACCEPT 2
  147. #define MODE_CONNECT_ERROR 3
  148. // Not used yet, I need to see what the hub sends.
  149. #define MODE_HUB_DOWN 4
  150. // Not used yet either.
  151. #define MODE_BANNED 5
  152.  
  153. string tmpstr, host, who_str;
  154. static string SaveFile, serverpass, clientpass;
  155.  
  156. static int socket_num, counter;
  157. static int heart_count = 0;
  158. int mode, autodisabled = 1;
  159. mapping ping_requests; // Keeps track of who sent a ping request.
  160. // Ping requests aren't labelled with names, so replies are destined to this MUD
  161. // with no idea why, unless we keep track.
  162. string buf=""; // Buffer for incoming packets (aren't always sent 1 at a time)
  163.  
  164. // Variables
  165. static int xmit_to_network_room = 1; //enable this to make a lot of noise
  166. static string hub_name, network_name;
  167. float server_version;
  168. static string lterm;
  169. static float client_version = IMC_VERSION;
  170. static mapping chaninfo;
  171. mapping localchaninfo; // (["chan": ([ "perm":1, "name":"something", "users":({ }) ]) ])
  172. mapping mudinfo;
  173. mapping genders;
  174. mapping tells;
  175. int sequence;
  176.  
  177. // Prototypes :)
  178. void create();
  179. void Setup();
  180. void remove();
  181. string pinkfish_to_imc2(string str);
  182. string imc2_to_pinkfish(string str);
  183. string escape(string str);
  184. string unescape(string str);
  185. mapping string_to_mapping(string str);
  186. string main_help();
  187. void keepalive(string args, object who);
  188.  
  189. string clean_str(string str);
  190. private void send_packet(string sender, string packet_type, string target, string destination, string data);
  191. private void send_text(string text);
  192. private void got_packet(string info);
  193. private void start_logon();
  194. private varargs void send_is_alive(string origin);
  195. private void channel_in(string fromname, string frommud, mapping data);
  196. private void tell_in(string sender, string origin, string target, mapping data);
  197. private void beep_in(string sender, string origin, string target, mapping data);
  198. private void who_reply_in(string origin, string target, mapping data);
  199. private void whois_in(string fromname, string frommud, string targ, mapping data);
  200. private void whois_reply_in(string targ,string fromname,string frommud,mapping data);
  201. private void ping_reply_in(string sender,string origin,string target,mapping data);
  202. private void chanwho_reply_in(string origin, string target, mapping data);
  203. private void send_keepalive_request();
  204. private int chan_perm_allowed(object user, string chan);
  205. private string localize_channel(string str);
  206. private void chan_who_in(string fromname, string frommud, mapping data);
  207. private void send_ice_refresh();
  208. private void resolve_callback(string address, string resolved, int key);
  209.  
  210. // Sanity check: a null socket write tends to be a crasher
  211. varargs static void validate(int i){
  212.     if(i){
  213.         if(!socket_status(i) || !socket_status(i)[5]){
  214.             tn("%^RED%^BAD SOCKET ALERT. fd "+i+": "+
  215.               identify(socket_status(i)),"red");
  216.             error("Bad socket, fd "+i);
  217.         }
  218.     }
  219. }
  220.  
  221. varargs void write_to_log(string type, string wat){
  222.     if(xmit_to_network_room){
  223.         tn(wat);
  224.     }
  225.     unguarded( (: log_file($(type), $(wat)) :) );
  226. }
  227.  
  228. // Functions for users to change.
  229. int can_use(object user){ return 1; } // Is this person allowed to use IMC2 at all? This function determines if tells can be sent to the person and such.
  230.  
  231. int level(object ob){
  232.     // Outgoing packets are marked with the user's level.
  233.     // This function figures it out.
  234.     // If you have different ways of ranking, make this function convert them to what IMC2 uses.
  235.     // IMC2 uses: Admin=5, Imp=4, Imm=3, Mort=2, or None=1
  236.     if(ADMIN(ob)) return 5; // Admin
  237.     //TMI-2: if(wizardp(ob)) return 3;
  238.     //TMI-2: if(userp(ob)) return 2;
  239.     //Discworld: if(ob->query_creator()) return 3;
  240.     if(this_player()) return 2;
  241.     return 1; // None
  242. }
  243.  
  244. string chan_perm_desc(int i){
  245.     // Given the permission level assigned locally to a channel, return a short
  246.     // string describing what the number means. The number means nothing
  247.     // outside of this MUD. Also, they are independant of each other, and
  248.     // so you can do groups without having to always do subgroups of higher
  249.     // ones or anything like 'levels'. BACKLOG_WEB_LEVEL is the only one of
  250.     // significance, as it's the only one that the web backlog thing works on.
  251.     switch(i){
  252.     case 2: return "arch";
  253.     case 1: return "creator";
  254.     case 0: return "public";
  255.     }
  256.     return "invalid";
  257. }
  258.  
  259. int chan_perm_allowed(object user, string chan){
  260.     // Using the permission level assigned locally to a channel,
  261.     // return 1 if user is allowed to use the channel, 0 if not.
  262.     switch(localchaninfo[chan]["perm"]){
  263.     case 2: if(archp(user)) return 1; return 0;
  264.     case 1: if(creatorp(user)) return 1; return 0;
  265.     case 0: return 1;
  266.     }
  267. }
  268.  
  269.  
  270. // Shouldn't have to change anything beyond this point.
  271.  
  272. void write_callback(int fd){
  273. #ifdef IMC2_LOGGING
  274.     write_to_log(DATA_LOG,"Write_Callback. \n");
  275. #endif
  276.     start_logon();
  277. }
  278.  
  279. void read_callback(int socket, mixed info){
  280.     string a,b,tmp;
  281.     int done=0;
  282.     counter = time();
  283.  
  284. //tc("read_callback","white");
  285. //tmp = replace_string(info, "\n", "(LF)");
  286. //tmp = replace_string(tmp, "\r", "(CR)");
  287. //tc("read_callback: "+tmp+"\n");
  288.  
  289. #ifdef IMC2_LOGGING
  290.     write_to_log(DATA_LOG,"SERVER: "+info+"\n");
  291. #endif
  292.  
  293.     if(!sizeof(info)) return 0;
  294.     buf += info;
  295.  
  296.     // The hub groups packets, unfortunately.
  297.     switch(mode){
  298.     case MODE_WAITING_ACCEPT: // waiting for Hub to send autosetup
  299.         if(sscanf(info, "autosetup %s accept %s"+lterm,
  300.             hub_name, network_name)==2){
  301.             mode = MODE_CONNECTED;
  302.             if(network_name) network_name = clean_str(network_name);
  303.             send_is_alive("*");
  304.             send_keepalive_request();
  305.             send_ice_refresh();
  306.         } else if(sscanf(info, "PW %s %s version=%f %s"+lterm[0..0],
  307.             hub_name, serverpass, server_version, network_name)==4){
  308.             mode = MODE_CONNECTED;
  309.             if(network_name) network_name = clean_str(network_name);
  310.             send_is_alive("*");
  311.             send_keepalive_request();
  312.             send_ice_refresh();
  313.         } else{ // Failed login sends plaintext error message.
  314.             tn("IMC2 Failed to connect... "+info);
  315.             mode = MODE_CONNECT_ERROR;
  316.             set_heart_beat(22000); //Try again much later
  317.             return;
  318.         }
  319.         buf=""; // clear buffer
  320.         break;
  321.     case MODE_CONNECTED:
  322.         while(!done){
  323.             if(sscanf(buf,"%s\n\r%s",a,b)==2){ // found a break...
  324.                 //if(IMC_VERSION > 2) tc("LFCR: "+a,"red");
  325.                 got_packet(a);
  326.                 buf=b;
  327.             }
  328.             else if(sscanf(buf,"%s\r\n%s",a,b)==2){ // found a break...
  329.                 //if(IMC_VERSION < 2.1) tc("CRLF","green");
  330.                 got_packet(a);
  331.                 buf=b;
  332.             }
  333.             else { // no break...
  334.                 if(sizeof(b)){
  335.                     tmp = replace_string(b, "\n", "(LF)");
  336.                     tmp = replace_string(tmp, "\r", "(CR)");
  337.                     //tc("remainder: "+identify(tmp));
  338.                     got_packet(b+lterm);
  339.                 }
  340.                 done = 1;
  341.             }
  342.         }
  343.         break;
  344.     }
  345.     return;
  346. }
  347.  
  348. private void got_packet(string info){
  349.     string str, tmp;
  350.     string a,b, my_ip;
  351.     int i;
  352.     string sender, origin, route, packet_type, target, destination, strdata;
  353.     int sequence, my_port;
  354.     mapping data;
  355.     object who;
  356.     if(!sizeof(info)){
  357.         tn("No info?");
  358.         return;
  359.     }
  360. #ifdef IMC2_LOGGING
  361.     tmp = replace_string(info,"\n","(LF)");
  362.     tmp = replace_string(tmp,"\r","(CR)");
  363.     write_to_log(DATA_LOG,"GOT PACKET: "+tmp+"\n");
  364. #endif
  365.  
  366.     counter = time();
  367.     str = info;
  368.     // messages end with " \n\r" or "\n" or sometimes just a space
  369.     sscanf(str, "%s\n^", str);
  370.     sscanf(str, "%s\r^", str);
  371.     sscanf(str, "%s ^", str);
  372.     sscanf(str, "%s ^", str);
  373.     if(sscanf(str, "%s %d %s %s %s %s",
  374.         a, sequence, route, packet_type,
  375.         b, strdata)==6){ // matches
  376.         if(sscanf(b,"%s@%s",target,destination)!=2){
  377.             // Could be MUD instead of Name@MUD or *@MUD
  378.             target="*"; destination=b;
  379.         }
  380.         if(sscanf(a,"%s@%s",sender,origin)!=2){
  381.             sender="*"; origin=a;
  382.         }
  383.         data = string_to_mapping(strdata);
  384.         if(!mudinfo[origin]) mudinfo[origin] = ([ ]);
  385.  
  386.         switch(packet_type){
  387.         case "is-alive": // For making a MUD list.
  388.             if(!mudinfo[origin]) mudinfo[origin] = ([ ]);
  389.             // example of info:
  390.             // versionid=\"IMC2 AntiFreeze CL-2 SWR 1.0\" url=none md5=1
  391.             //mudinfo[origin]["version"]="blah";
  392.             if(!mudinfo[origin]["online"]){
  393.                 CHAT_D->eventSendChannel(origin+"@IMC2","muds",
  394.                 "%^BOLD%^%^BLUE%^online%^RESET%^",0);
  395.             }
  396.             mudinfo[origin]+=data;
  397.             mudinfo[origin]["online"]=1;
  398.             break;
  399.         case "close-notify": // Someone disconnected.
  400.             if(!mudinfo[data["host"]]) mudinfo[data["host"]] = ([]);
  401.             if(mudinfo[data["host"]]["online"]){
  402.                 CHAT_D->eventSendChannel(data["host"]+"@IMC2","muds",
  403.                 "%^BOLD%^%^RED%^offline%^RESET%^",0);
  404.             }
  405.             mudinfo[data["host"]]["online"]=0;
  406.             break;
  407.         case "keepalive-request": // Request for is-alive.
  408.             send_is_alive(origin);
  409.             break;
  410.         case "ice-msg-b": // Broadcast channel message.
  411.             channel_in(sender, origin, data);
  412.             break;
  413.         case "tell": // Tells or emotes.
  414.             tell_in(sender, origin, target, data);
  415.             break;
  416.         case "who-reply":
  417.             who_reply_in(origin,target,data);
  418.             break;
  419.         case "whois": // Like I3's locate
  420.             whois_in(sender,origin,target,data);
  421.             break;
  422.         case "whois-reply":
  423.             whois_reply_in(target,sender,origin,data);
  424.             break;
  425.         case "beep":
  426.             beep_in(sender, origin, target, data);
  427.             break;
  428.         case "ice-update": // Reply from ice-refresh.
  429.             chaninfo[data["channel"]]=data;
  430.             break;
  431.         case "wHo": // Drop-through
  432.         case "who":
  433.             if(sizeof(host)) my_ip = host;
  434.             else my_ip = INTERMUD_D->GetMyIp();
  435.             if(my_ip == "127.0.0.1"){
  436.                 my_ip = "dead-souls.net";
  437.                 my_port = 8000;
  438.             }
  439.             else my_port = query_host_port();
  440.             who_str=CGI_WHO->gateway(1)+URL+"\ntelnet://"+my_ip+":"+my_port+"\n";
  441.             who_str += repeat_string("_", 75);
  442.             send_packet("*","who-reply",sender,origin,
  443.               "text="+escape(pinkfish_to_imc2(who_str)));
  444.             CHAT_D->eventSendChannel("SYSTEM","intermud","[" + capitalize(sender)+"@"+origin+
  445.               " requests the IMC2 who list]",0);
  446.             break;
  447.         case "ice-destroy": // Deleting channel.
  448.             map_delete(chaninfo,data["channel"]);
  449.             break;
  450.         case "user-cache": // User info, like I3's ucache service.
  451.             if(!genders[origin]) genders[origin]=([ ]);
  452.             genders[origin][sender]=data["gender"];
  453.             break;
  454.         case "user-cache-reply": // Reply with user info
  455.             if(!genders[origin]) genders[origin]=([ ]);
  456.             genders[origin][data["user"]]=data["gender"];
  457.             break;
  458.         case "user-cache-request": // Request for user info
  459.             sscanf(data["user"],"%s@%*s",str);
  460.             if(str) who = FIND_PLAYER(lower_case(str));
  461.             if(who
  462. #ifdef INVIS
  463.               && !INVIS(who)
  464. #endif
  465.             ){
  466.                 switch(GET_GENDER(who)){
  467.                 case "male" : i=0; break;
  468.                 case "female" : i=1; break;
  469.                 default : i=2; break;
  470.                 }
  471.                 send_packet("*","user-cache-reply",sender,
  472.                   origin,sprintf("gender=%d",i));
  473.             }
  474.             break;
  475.         case "ping":
  476.             send_packet("*","ping-reply",sender,origin,
  477.               sprintf("path=\"%s\"",route));
  478.             break;
  479.         case "ping-reply":
  480.             ping_reply_in(sender,origin,target,data);
  481.             break;
  482.         case "ice-msg-r": // Relayed channel message.
  483.             channel_in(sender, origin, data);
  484.             CHAT_D->eventSendChannel("IMC2Test:"+sender,"admin","ice-msg-r sender:"+sender+"; origin:" +origin+"; data:"+(string)data);
  485.             break;
  486.         case "ice-chan-whoreply": // Tell who's listening to a channel.
  487.             chanwho_reply_in(origin,target,data);
  488.             break;
  489.         case "emote": // Channel creation/destruction message... anything else?
  490.             CHAT_D->eventSendChannel("IMC2:"+NETWORK_ID,"admin",data["text"]);
  491. #ifdef ANNOUNCE_LOG
  492.             write_to_log(ANNOUNCE_LOG,ctime(time())+": "+data["text"]+"\n");
  493. #endif
  494.             break;
  495.         case "ice-chan-who": // Check who's listening to a channel.
  496.             chan_who_in(sender,origin,data);
  497.             break;
  498.         case "channel-notify":
  499.             // Don't care about this. Useful only if you care when
  500.             // people on other MUDs start/stop listening to a channel.
  501.             // example: Someone@SomeMUD 1087076772 SomeMUD channel-notify *@* channel=Hub01:ichat status=0
  502.             break;
  503.             // The following packets shouldn't be incoming.
  504.         case "ice-cmd": // Remote channel administration
  505.         case "remote-admin": // For controlling the hub
  506.         case "ice-refresh": // Request data about channels
  507.         case "ice-msg-p": // Private channel message
  508.             break;
  509.         default:
  510. #ifdef UNKNOWN_DATA_LOG
  511.             write_to_log(UNKNOWN_DATA_LOG,"Unknown packet: "+escape(info)+"\n\n");
  512. #endif
  513.             break;
  514.         }
  515.     }
  516.     else{
  517.         buf += info;
  518. #ifdef BAD_PACKET
  519.         write_to_log(BAD_PACKET,"Doesn't match incoming pattern: "+str+"\n");
  520. #endif
  521.     }
  522. }
  523.  
  524. private int close_callback(object socket){
  525. // Connection was closed.
  526. #ifdef IMC2_LOGGING
  527.     write_to_log(DATA_LOG,"DISCONNECTED\n");
  528. #endif
  529.     socket_close(socket_num);
  530.     mode = MODE_WAITING_ACCEPT;
  531.     create();
  532.     return 1;
  533. }
  534.  
  535. private void send_text(string text){
  536.     string tmp;
  537.     text = clean_str(text);
  538.     text += lterm;
  539.     tmp = replace_string(text, "\n", "(LF)");
  540.     tmp = replace_string(tmp, "\r", "(CR)");
  541. // Send a literal string.
  542. // Almost everything should use the send_packet function instead of this.
  543. #ifdef IMC2_LOGGING
  544.     write_to_log(DATA_LOG,"CLIENT: "+save_variable(tmp)+"\n");
  545. //tc("send_text: "+tmp);
  546. #endif
  547.     validate(socket_num);
  548.     socket_write(socket_num,text);
  549.     return;
  550. }
  551.  
  552. void create(){
  553.     SaveFile = save_file(SAVE_IMC2);
  554.     SetSaveFile(SaveFile);
  555.     if(unguarded( (: file_exists(SaveFile) :) ))
  556.         RestoreObject(SaveFile);
  557.     if(!file_exists(SaveFile) && file_exists(old_savename(SaveFile))){
  558.         cp(old_savename(SaveFile), SaveFile);
  559.     }
  560. #if DISABLE_IMC2
  561. //tn("IMC2: destruicted "+ctime(time()));
  562. //unguarded( (: destruct() :) );
  563. #else
  564.     if(mode != MODE_CONNECT_ERROR){
  565.         set_heart_beat(10);
  566.     }
  567.     counter = time();
  568.     tn("IMC2: created "+ctime(time()));
  569. #endif
  570.     if(client_version > 2){
  571.         lterm = "\r\n";
  572.     }
  573.     else {
  574.         lterm = "\n\r";
  575.     }
  576.     call_out( (: Setup :), 1);
  577. }
  578.  
  579. void Setup(){
  580.     int temp, kill, my_port;
  581.     string my_ip;
  582.  
  583. #ifdef DISABLE_IMC2
  584.     if(DISABLE_IMC2){
  585.         kill = 1;
  586.     }
  587. #endif
  588.  
  589.     if(DISABLE_INTERMUD == 1){
  590.         kill = 1;
  591.     }
  592.  
  593.     if(kill || autodisabled){
  594.         call_out( (: eventDestruct() :), 5);
  595.         return;
  596.     }
  597.     else {
  598.         clientpass = SECRETS_D->GetSecret("IMC2_CLIENT_PW");
  599.         serverpass = SECRETS_D->GetSecret("IMC2_SERVER_PW");
  600.         if(sizeof(host)) my_ip = host;
  601.         else my_ip = INTERMUD_D->GetMyIp();
  602.         if(my_ip == "127.0.0.1"){
  603.             my_ip = "dead-souls.net";
  604.             my_port = 8000;
  605.         }
  606.         else my_port = query_host_port();
  607.         who_str = CGI_WHO->gateway(1)+URL+"\ntelnet://"+my_ip+":"+my_port+"\n";
  608.         who_str += repeat_string("_", 75);
  609.     }
  610.     tn("IMC2: setup "+ctime(time()));
  611. #ifndef NO_UIDS
  612.     seteuid(getuid());
  613. #endif
  614.     tn("Creating IMC2 object at "+ctime(time())+".\n");
  615. #ifdef IMC2_LOGGING
  616.     write_to_log(DATA_LOG,"Creating IMC2 object at "+ctime(time())+".\n");
  617. #endif
  618.     if(!mudinfo) mudinfo = ([ ]);
  619.     if(!chaninfo) chaninfo = ([ ]);
  620.     if(!localchaninfo) localchaninfo = ([ ]);
  621.     if(!genders) genders = ([ ]);
  622.     if(!tells) tells=([ ]);
  623.     ping_requests=([ ]);
  624.     mode = MODE_WAITING_ACCEPT;
  625.     if(my_ip == "dead-souls.net"){
  626.         catch( host = query_intermud_ip() );
  627.         if(!sizeof(host)) host = "dead-souls.net";
  628.     }
  629.     else host = my_ip;
  630. #ifdef HOSTIP
  631.     // We already know the IP, go straight to the connecting, just do callback as if it found the IP.
  632.     resolve_callback(HOSTIP,HOSTIP,1);
  633. #else
  634.     temp = resolve(HOSTNAME, "resolve_callback");
  635.     if(temp == 0){
  636. #ifdef IMC2_LOGGING
  637.         write_to_log(DATA_LOG,"Addr_server is not running, resolve failed.\n");
  638. #endif
  639.         eventDestruct();
  640.         return;
  641.     }
  642. #endif
  643.     SaveObject(SaveFile, 1);
  644. }
  645.  
  646. void heart_beat(){
  647.     int lastmsg = time() - counter;
  648.     heart_count++;
  649.     if(heart_count > 4){
  650.         mixed sstat = socket_status(socket_num);
  651.         heart_count = 0;
  652.         if( lastmsg > 300 && lastmsg < 900){
  653.             tn("IMC2 heartbeat: Last message "+time_elapsed(lastmsg)+" ago.");
  654.             tn("sending keepalive");
  655.             send_keepalive_request();
  656.         }
  657.         if( lastmsg > 400
  658.           ||!sstat || sstat[1] != "DATA_XFER"){
  659.             socket_close(socket_num);
  660.             tn("IMC2 heartbeat: reloading IMC2_D due to timeout");
  661. #ifdef IMC2_LOGGING
  662.             write_to_log(DATA_LOG,"IMC2 TIMEOUT! Reloading.\n");
  663. #endif
  664.  
  665.             RELOAD_D->eventReload(this_object(), 2, 1);
  666.         }
  667.     }
  668. }
  669.  
  670. void remove(){
  671.     // This object is getting destructed.
  672.     tn("removing imc2. stack: "+get_stack(1));
  673.     mode=2;
  674.     SaveObject(SaveFile, 1);
  675.     socket_close(socket_num);
  676. #ifdef IMC2_LOGGING
  677.     write_to_log(DATA_LOG,"IMC2 OBJECT REMOVED\n");
  678. #endif
  679. }
  680.  
  681. static mixed GetChanInfo(){
  682.     mixed foo = copy(chaninfo);
  683.     return 1;
  684. }
  685.  
  686. string *GetChanList(){
  687.     if(!chaninfo) return ({});
  688.     return keys(chaninfo);
  689. }
  690.  
  691. int eventDestruct(){
  692.     remove();
  693.     return ::eventDestruct();
  694. }
  695.  
  696. void resolve_callback( string address, string resolved, int key ) {
  697.     // Figured out what the IP is for the address.
  698.     int error;
  699.  
  700.     write_file("/tmp/imc2.log","address: "+address+"\n");
  701.     write_file("/tmp/imc2.log","resolved: "+resolved+"\n");
  702.     write_file("/tmp/imc2.log","key: "+key+"\n");
  703.  
  704.     socket_num = socket_create(STREAM, "read_callback", "close_callback");
  705.     if (socket_num < 0) {
  706. #ifdef IMC2_LOGGING
  707.         write_to_log(DATA_LOG,"socket_create: " + socket_error(socket_num) + "\n");
  708. #endif
  709.         return;
  710.     }
  711. #ifdef IMC2_LOGGING
  712.     write_to_log(DATA_LOG,"socket_create: Created Socket " + socket_num + "\n");
  713. #endif
  714.  
  715.     error = socket_connect(socket_num, resolved+" "+HOSTPORT, "read_callback", "write_callback");
  716.     if (error != EESUCCESS) {
  717. #ifdef IMC2_LOGGING
  718.         write_to_log(DATA_LOG,"socket_connect: " + socket_error(error) + "\n");
  719. #endif
  720.         socket_close(socket_num);
  721.         //Timeouts on windows lag the whole mud. This disables the daemon
  722.         //to prevent that.
  723.         if(query_os_type() == "windows" && grepp(socket_error(error), "Problem with connect")){
  724.             load_object("/secure/cmds/admins/mudconfig")->cmd("imc2 disable");
  725.         }
  726.         return;
  727.     }
  728. #ifdef IMC2_LOGGING
  729.     write_to_log(DATA_LOG,"socket_connect: connected socket " + socket_num + " to " + resolved+" "+HOSTPORT + "\n");
  730. #endif
  731. }
  732.  
  733. void start_logon(){
  734. #if NOT_AUTO
  735.     //tc("1, version: "+IMC_VERSION);
  736.     send_text(sprintf("PW %s %s version=%s"+lterm,
  737. #else
  738.     //tc("2, version: "+IMC_VERSION);
  739.     send_text(sprintf("PW %s %s version=%s autosetup %s"+lterm,
  740. #endif
  741.             MUDNAME,
  742.             clientpass, IMC_VERSION+""
  743. #ifndef NOT_AUTO
  744.             , serverpass
  745. #endif
  746.           ));
  747.         buf="";
  748.         /* For invitation-only networks.
  749. //tc("3, wtf");
  750. send_text(sprintf("PW %s %s version=%s %s"+lterm,
  751. MUDNAME,
  752. clientpass, IMC_VERSION+"", "hub03"
  753. ));
  754. */
  755.         sequence=time();
  756.     }
  757.       string clean_str(string str){
  758.           while(sizeof(str) && str[<1] < 32){
  759.              str = str[0..<2];
  760.           }
  761.           return str;
  762.       }
  763.  
  764.       string escape(string str){
  765.           str=replace_string(str,"\\","\\\\");
  766.           str=replace_string(str,"\"","\\\"");
  767.           str=replace_string(str,"\n","\\n");
  768.           str=replace_string(str,"\r","\\r");
  769.           if(sizeof(explode(str," "))!=1) str = "\""+str+"\"";
  770.           return str;
  771.       }
  772.  
  773.       string unescape(string str){
  774.           string a,b=str,output="";
  775.           while(sscanf(b,"%s\\%s",a,b)==2){
  776.               output += a;
  777.               if(sizeof(b)){
  778.                   switch(b[0]){
  779.                   case 34 : output += "\""; break; // '\"' makes warnings.
  780.                   case '\\' : output += "\\"; break;
  781.                   case 'n' : output += "\n"; break;
  782.                   case 'r' : output += "\r"; break;
  783.                   }
  784.               }
  785.               b=b[1..];
  786.           }
  787.           output += b;
  788.           output = replace_string(output,"\n\r","\n");
  789.           if((sizeof(explode(output," "))==1) && sscanf(output,"\\\"%*s\\\"") ) output = "\""+output+"\"";
  790.           return output;
  791.       }
  792.  
  793.       mapping string_to_mapping(string str){
  794.           // Picks first element off of string and then repeats?
  795.           mapping out=([]);
  796.           int i;
  797.           string what,data,rest;
  798.  
  799.           rest = str;
  800.  
  801.           while(sizeof(rest)>0){
  802.               sscanf(rest, "%s=%s", what, rest);
  803.               /*
  804. write("what="+what+", rest="+rest+"\n");
  805. */
  806.               // At this point, what is the key, rest is value plus rest.
  807.               if(rest[0]==34){ // value is in quotes, tons of fun!
  808.                   // find first quote without a backslash in front?
  809.                   /*
  810. write("rest begings with a quote\n");
  811. */
  812.                   i = 1;
  813.                   while(((rest[i]!=34) || (rest[i-1]==92)) && (i<sizeof(rest))){ // 34 = ", 92 = \
  814. // While this is not a quote, or if this is an escaped quote, keep looking.
  815.                       i++;
  816.                   }
  817.                   // now are 1 space past quote
  818.                   data=rest[1..(i-1)]; // skip opening and closing quotes
  819.                   rest=rest[(i+2)..]; // skip past space
  820.                   // Data is now what was in the quotes... now to un-escape the data...
  821.                   out[what]=unescape(data);
  822.               }
  823.               else{ // value is not in quotes, tons of actual non-sarcastic fun!
  824.                   // just split it at the first space
  825.                   if(sscanf(rest,"%s %s",data,rest)!=2){ // break at first space
  826.                       data = rest;
  827.                       rest = "";
  828.                   }
  829.                   if((sscanf(data,"%d",i)==1) && (sprintf("%d",i)==data)) // is just number
  830.                       out[what]=i;
  831.                   else // not just a number
  832.                       out[what]=data;
  833.               }
  834.           }
  835.           return out;
  836.       }
  837.  
  838.       void send_packet(string sender, string packet_type, string target, string destination, string data){
  839.           // Sends a packet in the format that IMC2 uses.
  840.           sequence++;
  841.           send_text(sprintf("%s@%s %d %s %s %s@%s %s",
  842.               sender, MUDNAME, sequence, MUDNAME, packet_type,
  843.               target, destination, clean_str(data)));
  844.       }
  845.  
  846.       void send_keepalive_request(){
  847.           // Ask all the muds to tell us that they're alive.
  848.           string mud;
  849.           // Mark all the muds as offline until they respond.
  850.           foreach(mud in keys(mudinfo)){
  851.               mudinfo[mud]["online"]=0;
  852.           }
  853.           send_packet("*","keepalive-request","*","*","");
  854.       }
  855.  
  856.       varargs void send_is_alive(string origin){
  857.           // Sends an is-alive packet to whoever requested it, or else broadcasts it.
  858.           send_packet("*","is-alive","*", (origin ? origin : "*"),
  859. #if 1
  860.             "versionid=\""+VERSION+"\" networkname="+network_name+
  861.             " url="+URL+" host="+host+" port="+query_host_port());
  862. #else
  863.           "foo");
  864. #endif
  865.   }
  866.  
  867.     void channel_in(string fromname, string frommud, mapping data){
  868.         string sender;
  869.         string localchan;
  870.  
  871.         int emote=0;
  872.  
  873.         sender=fromname+"@"+frommud;
  874.         if(data["sender"]) sender = data["sender"];
  875.         if(data["realfrom"]) sender = data["realfrom"];
  876.         if(intp(data["text"])) data["text"]=sprintf("%d",data["text"]);
  877.  
  878.         if(data["emote"]) emote = data["emote"];
  879.         //Following fix courtesy of Tricky
  880.         if (emote == 1 && strsrch(data["text"], "$N") == -1)
  881.             data["text"] = "$N " + data["text"];
  882.  
  883.         localchan = CHANNEL_BOT->GetLocalChannel(data["channel"]);
  884.         CHANNEL_BOT->eventSendChannel(sender, localchan,
  885.          imc2_to_pinkfish(data["text"]), emote, "", "");
  886.     }
  887.  
  888.     void channel_out(string user,string chan,string msg,int emote){
  889.         // Send outgoing channel message.
  890.         send_packet(user,"ice-msg-b","*","*", sprintf("channel=%s text=%s emote=%d echo=0", chan,escape(pinkfish_to_imc2(msg)),emote));
  891.     }
  892.  
  893.     varargs static void tell_out(object from, string targname, string targmud, string msg, int reply, int emote){
  894.         string ret = "%^BOLD%^RED%^You tell " + capitalize(targname) +
  895.           "@" + targmud + ":%^RESET%^ " + msg;
  896.         // Send outgoing tell.
  897.         if(!reply) reply=0;
  898.         send_packet(capitalize(from->GetKeyName()),"tell",targname,targmud,
  899.           "text="+escape(msg));
  900.         from->eventPrint(ret, MSG_CONV);
  901.         from->eventTellHist(ret);
  902.     }
  903.  
  904.     void tell_in(string sender, string origin, string target, mapping data){
  905.         // Incoming tell. Parse and display to user
  906.         object who;
  907.         int sz;
  908.         string blmsg, ret, eret;
  909.         if(target){
  910.             who = FIND_PLAYER(lower_case(target));
  911.             INSTANCES_D->SendTell(lower_case(target), data["text"],
  912.               sender + "@" + origin);
  913.         }
  914.         if(who) target = GET_CAP_NAME(who);
  915.         else return;
  916.         data["text"]=imc2_to_pinkfish(data["text"]);
  917.         who->SetProperty("reply",sender+"@"+origin);
  918.         who->SetProperty("reply_time", time());
  919.         eret = "%^BOLD%^RED%^" + sender + "@" + origin +
  920.           " %^RESET%^ " + data["text"];
  921.         ret = "%^BOLD%^RED%^" + sender + "@" + origin +
  922.           " tells you:%^RESET%^ " + data["text"];
  923.         if(who
  924. #ifdef INVIS
  925.           && VISIBLE(who)
  926. #endif
  927.           && can_use(who)
  928.         ){
  929.             switch(data["isreply"]){
  930.             case 2: // emote
  931.                 ret = eret;
  932.                 who->eventPrint(ret, MSG_CONV);
  933.                 blmsg=sprintf("%s@%s %s\n",sender,origin,data["text"]);
  934.                 break;
  935.             case 1: // reply
  936.                 who->eventPrint(ret, MSG_CONV);
  937.                 blmsg=sprintf("%s@%s replied to you: %s\n", NETWORK_ID,sender,origin,data["text"]);
  938.                 break;
  939.             default:
  940.                 who->eventPrint(ret , MSG_CONV);
  941.                 blmsg=sprintf("%s@%s told you: %s\n", sender,origin,data["text"]);
  942.                 break;
  943.             }
  944.             who->eventTellHist(ret);
  945.             if(!tells[target])
  946.                 tells[target]=([ ]);
  947.             tells[target]["reply"] = sprintf("%s@%s",sender,origin);
  948.             if(!tells[target]["backlog"] || !arrayp(tells[target]["backlog"]))
  949.                 tells[target]["backlog"]=({ });
  950.             tells[target]["backlog"] += ({ blmsg });
  951.             sz = sizeof(tells[target]["backlog"]);
  952.             if(sz>BACKLOG_SIZE)
  953.                 tells[target]["backlog"]=tells[target]["backlog"][(sz-BACKLOG_SIZE)..sz];
  954.         } else {
  955. #ifdef TELL_BOTS
  956.             // Can have a mapping of bots which can get tells.
  957.             if(TELL_BOTS[target]){
  958.                 call_other(TELL_BOTS[target],"got_tell",sender, origin, target, data["text"]);
  959.                 return;
  960.             }
  961. #endif
  962. #ifdef TELL_BOT
  963.             // They should have got_tell(fromname, frommud, target, text)
  964.             // I'll assume bots don't care about emote/reply... tell me if you do?
  965.             // TELL_BOT will get told of all tells which don't have a valid target, and
  966.             // then the sender will be notified that it didn't get to anyone.
  967.             // This would be useful if for example you wanted all tells sent to
  968.             // offline people to be mudmailed to them or something.
  969.             call_other(TELL_BOT,"got_tell",sender, origin, target, data["text"]);
  970. #endif
  971.             send_packet("*","tell",sender,origin,
  972.               sprintf("level=-1 text=\"%s is not online on this mud.\" isreply=1",target));
  973.         }
  974.     }
  975.  
  976.     int tell(mixed arg, object who){
  977.         string targmud,targplayer,msg;
  978.         int i;
  979.         if(!who || !this_player() || who != this_player()) return 0;
  980.         if(who->GetForced()){
  981.             return 0;
  982.         }
  983.         i = sscanf(arg,"%s@%s %s",targplayer, targmud, msg);
  984.         if(i != 3 ){
  985.             write("There was an error in your message. See: \"help imc2\"");
  986.             return 1;
  987.         }
  988.         tell_out(who, targplayer, this_object()->find_mud(targmud), msg, 0, 0);
  989.         return 1;
  990.     }
  991.  
  992.     string pinkfish_to_imc2(string str){
  993.         // Convert RGB to xterm256 so the other mapping can handle it.
  994.         str = "/lib/interface"->rgb_downto_xterm256( str );
  995.         str = replace_strings( str, pinkfish_imc );
  996.         str = replace_strings( str, pinkfish_imc_cleanup );
  997.         return str;
  998.     }
  999.  
  1000.     string imc2_to_pinkfish(string str){
  1001.         str = replace_strings( str, imc_pinkfish );
  1002.         return str;
  1003.     }
  1004.  
  1005.     private void who_reply_in(string origin, string target, mapping data){
  1006.         string output;
  1007.         object targuser;
  1008.         if(target) targuser = FIND_PLAYER(lower_case(target));
  1009.         if(targuser){
  1010.             output = NETWORK_ID+" who reply from: %^CYAN%^"+origin+"%^RESET%^\n";
  1011.             output += imc2_to_pinkfish(data["text"])+"\n";
  1012.             IMC2_MSG(output,targuser);
  1013.         }
  1014.     }
  1015.  
  1016.     private void whois_in(string fromname, string frommud, string targ, mapping data){
  1017.         if(targ && FIND_PLAYER(lower_case(targ))
  1018. #ifdef INVIS
  1019.           && !INVIS(FIND_PLAYER(lower_case(targ)))
  1020. #endif
  1021.           && can_use(FIND_PLAYER(lower_case(targ)))
  1022.         ){
  1023.             send_packet(targ,"whois-reply",fromname,frommud,"text=Online");
  1024.         }
  1025. #ifdef USER_EXISTS
  1026.         else if(USER_EXISTS(lower_case(targ))){
  1027.             send_packet(targ,"whois-reply",fromname,frommud,
  1028.               "text=\"Exists but is offline\"");
  1029.         }
  1030. #endif
  1031.     }
  1032.  
  1033.     private void whois_reply_in(string targ,string fromname,string frommud,mapping data){
  1034.         object who;
  1035.         if(targ) who = FIND_PLAYER(lower_case(targ));
  1036.         if(who){
  1037.             IMC2_MSG(sprintf("%s whois reply: %s@%s: %s\n",
  1038.                 NETWORK_ID,fromname,frommud,data["text"]),who);
  1039.         }
  1040.     }
  1041.  
  1042.     private void beep_in(string sender, string origin, string target, mapping data){
  1043.         object who;
  1044.         if(target) who = FIND_PLAYER(lower_case(target));
  1045.         if(who && can_use(who)
  1046. #ifdef INVIS
  1047.           && VISIBLE(who)
  1048. #endif
  1049.         ){
  1050.             IMC2_MSG(sprintf("%s- %%^CYAN%%^%s@%s%%^RESET%%^ \abeeps you.\n",
  1051.                 NETWORK_ID,sender,origin,data["text"]), who);
  1052.         }
  1053.         else{
  1054.             send_packet("*","tell",sender,origin,
  1055.               sprintf("level=-1 text=\"%s is not online.\" isreply=1",target));
  1056.         }
  1057.     }
  1058.  
  1059.     void beep_out(object from, string targname, string targmud){
  1060.         send_packet(GET_CAP_NAME(from),"beep",targname,targmud,
  1061.           sprintf("level=%d ", level(from)));
  1062.     }
  1063.  
  1064.     void send_ice_refresh(){ send_packet("*","ice-refresh","*","*",""); }
  1065.  
  1066.     void ping_reply_in(string sender,string origin,string target,mapping data){
  1067.         object who;
  1068.         if((target=="*") && target=ping_requests[sender]){
  1069.             target=ping_requests[sender];
  1070.             map_delete(ping_requests,sender);
  1071.         }
  1072.         if(target) who = FIND_PLAYER(lower_case(target));
  1073.         if(who){
  1074.             IMC2_MSG(sprintf("%s route to %%^CYAN%%^%s%%^RESET%%^ is: %s\n",
  1075.                 NETWORK_ID,origin,data["path"]), who);
  1076.         }
  1077.     }
  1078.  
  1079.     void ping_out(string from,string targmud){ send_packet(from,"ping","*",targmud,""); }
  1080.     varargs void who_out(string from,string targmud,string type){ send_packet(from,"who","*",targmud,(type ? sprintf("type=\"%s\"",type) : "type=who")); }
  1081.  
  1082.     void chanwho_out(object from,string chan,string mud){
  1083.         send_packet(GET_CAP_NAME(from),"ice-chan-who","*",mud,
  1084.           sprintf("level=%d channel=%s lname=%s",level(from),
  1085.             localchaninfo[chan]["name"],chan));
  1086.     }
  1087.  
  1088.     void chanwho_reply_in(string origin, string target, mapping data){
  1089.         string output;
  1090.         object targuser;
  1091.         if(target) targuser = FIND_PLAYER(lower_case(target));
  1092.         if(targuser){
  1093.             output = NETWORK_ID+" chanwho reply from "+origin+" for "+data["channel"]+"\n";
  1094.             output += imc2_to_pinkfish(data["list"]);
  1095.             IMC2_MSG(output,targuser);
  1096.         }
  1097.     }
  1098.  
  1099.     void chan_who_in(string fromname, string frommud, mapping data){
  1100.         // Handles an incoming channel who request.
  1101.     }
  1102.  
  1103.     string find_mud(string str){
  1104.         // Makes case-insensitive mud name into the actual mud name, or else 0.
  1105.         string mud;
  1106.         string *mudses = filter(keys(mudinfo), (: mudinfo[$1] &&
  1107.             mudinfo[$1]["online"] == 1 :) );
  1108.         if (!str) return 0;
  1109.         if(member_array(str, mudses) != -1) return str;
  1110.         str = lower_case(str);
  1111.         foreach(mud in mudses){
  1112.             if ( lower_case(mud)==str ) return mud;
  1113.         }
  1114.         return 0;
  1115.     }
  1116.  
  1117.     string localize_channel(string str){
  1118.         // Tells what the local name for a channel is, given the network name for it, or else 0.
  1119.         string a;
  1120.         foreach(a in keys(localchaninfo)){
  1121.             if(lower_case(localchaninfo[a]["name"])==lower_case(str)) return a;
  1122.         }
  1123.         return 0;
  1124.     }
  1125.  
  1126.     //Returns mud list to user object 'towho'
  1127.     void mudlist(object towho) {
  1128.         int x,y;
  1129.         string output;
  1130.         string mud,*muds;
  1131.  
  1132.         muds = sort_array(filter(keys(mudinfo), (: stringp($1) :)),1);
  1133.         if (!mode==MODE_CONNECTED) {
  1134.             message("system",MUDNAME+" is not connected to the "+NETWORK_ID+" network!\n",towho);
  1135.             return;
  1136.         } else if (!sizeof(mudinfo)){
  1137.             message("system","There are no muds on the "+NETWORK_ID+" network!\n",towho);
  1138.             return;
  1139.         } else {
  1140.             x=0; y=0;
  1141.             output=sprintf("[%s] %-20s %-20s %-20s\n","U/D?","Name","Network","IMC2 Version");
  1142.             foreach (mud in filter(muds, (: mudinfo[$1]["online"] :) )){
  1143.                 if(!mudinfo[mud]) output += "Error on mud: "+mud+"\n";
  1144.                 else {
  1145.                     if(mudinfo[mud]["online"]) x++; else y++;
  1146.                     output += sprintf("[%s] %-20s %-20s %-20s\n",
  1147.                       (mudinfo[mud]["online"] ? "%^GREEN%^ UP %^RESET%^" : "%^RED%^DOWN%^RESET%^"),
  1148.                       mud, mudinfo[mud]["networkname"], mudinfo[mud]["versionid"]);
  1149.                 }
  1150.             }
  1151.             output += sprintf("%d of %d MUDs are online.\n",x,x+y);
  1152.             message("system",output,towho);
  1153.             return;
  1154.         }
  1155.     }
  1156.  
  1157.     mapping getmudinfo() {
  1158.         return mudinfo;
  1159.     }
  1160.  
  1161.     //pings mud 'mudname', returns it to user object 'towho'
  1162.     void mudinfo(string args,object towho) {
  1163.         string str;
  1164.         string output;
  1165.  
  1166.         if(!args) {
  1167.             message("system","See info for which MUD?",towho);
  1168.             return;
  1169.         }
  1170.  
  1171.         str=find_mud(args);
  1172.         if(!str) {
  1173.             message("system","MUD isn't known on "+NETWORK_ID+".",towho);
  1174.             return;
  1175.         }
  1176.  
  1177.         output=("Mud info for: "+str+"\nStatus: ");
  1178.  
  1179.         if(mudinfo[str]["online"]) output+=("%^GREEN%^Online%^RESET%^\n");
  1180.         else output+=("%^RED%^Offline%^RESET%^\n");
  1181.  
  1182.         if(mudinfo[str]["versionid"]) output+=("Version ID: "+mudinfo[str]["versionid"]+"\n");
  1183.         if(mudinfo[str]["url"]) output+=("URL: "+mudinfo[str]["url"]+"\n");
  1184.         if(mudinfo[str]["networkname"]) output+=("Network name: "+mudinfo[str]["networkname"]+"\n");
  1185.  
  1186.         message("system",output,towho);
  1187.         return;
  1188.     }
  1189.  
  1190.     //Gets WHO list from mud 'mudname', returns it to user object 'towho'
  1191.     void mudwho(string mudname,object towho) {
  1192.         string str;
  1193.         if(!mudname) {
  1194.             message("system","Send who request to which MUD?",towho);
  1195.             return;
  1196.         }
  1197.         str=find_mud(mudname);
  1198.         if(!str) {
  1199.             message("system","MUD isn't known on "+NETWORK_ID+".",towho);
  1200.             return;
  1201.         }
  1202.         if(!mudinfo[str]["online"]) {
  1203.             message("system",str+" is offline right now.",towho);
  1204.             return;
  1205.         }
  1206.         who_out(capitalize(this_player()->GetKeyName()),str);
  1207.         message("system",NETWORK_ID+" sent a who request to "+str+"\n",towho);
  1208.         return;
  1209.     }
  1210.  
  1211.     //pings mud 'mudname', returns it to user object 'towho'
  1212.     mixed pingmud(string mudname,object towho) {
  1213.         string str;
  1214.         if(!mudname) {
  1215.             message("system","Send ping to which MUD?",towho);
  1216.             return;
  1217.         }
  1218.         str=find_mud(mudname);
  1219.         if(!str) {
  1220.             message("system","MUD isn't known on "+NETWORK_ID+".",towho);
  1221.             return;
  1222.         }
  1223.         if(!mudinfo[str]["online"]) {
  1224.             message("system",str+" is offline right now.",towho);
  1225.             return;
  1226.         }
  1227.         ping_out(capitalize(towho->GetKeyName()),str);
  1228.         message("system","Sent a ping to "+str+".",towho);
  1229.         return;
  1230.     }
  1231.  
  1232.     //Displays status to user object 'towho'
  1233.     void getstatus(object towho) {
  1234.         string output;
  1235.  
  1236.         output=sprintf(@EndText
  1237.             IMC2 NETWORK INFORMATION
  1238.             ------------------------
  1239.             Status: %s
  1240.             Hub address: %s
  1241.             Hub port: %d
  1242.             The hub calls itself: %s
  1243.             The network calls itself: %s
  1244.             Command to use this network: %s
  1245.             The MUD calls this connection: %s
  1246.  
  1247.             Packet logging: %s
  1248.  
  1249.             The network calls the MUD: %s
  1250.             The MUD's Version ID: %s
  1251.            The MUD's URL: %s
  1252. EndText,
  1253.           ((mode==MODE_CONNECTED) ? "Connected" : "Not connected"),
  1254. #ifdef HOSTNAME
  1255.           HOSTNAME,
  1256. #else
  1257.           HOSTIP,
  1258. #endif
  1259.           HOSTPORT,hub_name,network_name,COMMAND_NAME,NETWORK_ID,
  1260. #ifdef IMC2_LOGGING
  1261.           "on",
  1262. #else
  1263.           "off",
  1264. #endif
  1265.           MUDNAME,VERSION,URL);
  1266.  
  1267.         message("system",output,towho);
  1268.     }
  1269.  
  1270.     //return mode
  1271.     int getonline() {
  1272.         return mode;
  1273.     }
  1274.  
  1275.     //Beeps user@mud stored in 'args', returns it to user object 'towho'
  1276.     void interbeep(string args, object towho) {
  1277.         string a,b;
  1278.         if(!args || sscanf(args,"%s@%s",a,b)!=2) {
  1279.             message("system","Invalid syntax.",towho);
  1280.             return;
  1281.         }
  1282.         b=find_mud(b);
  1283.         if(!b) {
  1284.             message("system","MUD isn't known on "+NETWORK_ID+".",towho);
  1285.             return;
  1286.         }
  1287.         if(!mudinfo[b]["online"]) {
  1288.             message("system",b+" is offline right now.",towho);
  1289.             return;
  1290.         }
  1291.         beep_out(towho,a,b);
  1292.         message("system",sprintf("You beep %s@%s.",capitalize(a),b),towho);
  1293.         return;
  1294.     }
  1295.  
  1296.     //Fingers user@mud stored in 'args', returns it to user object 'towho'
  1297.     void finger(string args, object towho) {
  1298.         string a,b;
  1299.  
  1300.         if(!args) return notify_fail("Send finger request to who@where?\n");;
  1301.         if(sscanf(args,"%s@%s",a,b)!=2){
  1302.             return notify_fail("Send finger request to who@where?\n");;
  1303.         }
  1304.         b=find_mud(b);
  1305.         who_out(GET_CAP_NAME(towho),b,"finger "+a);
  1306.         IMC2_MSG(NETWORK_ID"- Sent a finger request to "+a+"@"+b+"\n",THIS_PLAYER);
  1307.     }
  1308.  
  1309.     //Gets whois for user@mud stored in 'args', returns it to user object 'towho'
  1310.     void whois(string args, object towho) {
  1311.         if(!args || !sizeof(args)) return notify_fail("Locate who?\n");
  1312.  
  1313.         send_packet(GET_CAP_NAME(THIS_PLAYER),"whois",args,"*", sprintf("level=%d ",level(THIS_PLAYER)));
  1314.  
  1315.         IMC2_MSG("Sent a request on "+NETWORK_ID+" looking for "+args+"\n",THIS_PLAYER);
  1316.     }
  1317.  
  1318.     //Gets MUD Info for a mud 'args', returns it to user object 'towho'
  1319.     void getremotemudinfo(string args, object towho) {
  1320.         string str;
  1321.  
  1322.         if(!args) return notify_fail("Send info request to which MUD?\n");
  1323.  
  1324.         str=find_mud(args);
  1325.  
  1326.         if(!str) return notify_fail("MUD isn't known on "+NETWORK_ID+".\n");
  1327.         if(!mudinfo[str]["online"]) return notify_fail(NETWORK_ID+"- "+str+" is offline right now.\n");
  1328.  
  1329.         who_out(GET_CAP_NAME(THIS_PLAYER),str,"info");
  1330.         IMC2_MSG(NETWORK_ID"- Sent an info request to "+str+"\n",THIS_PLAYER);
  1331.     }
  1332.  
  1333.     //Write list of all channels to screen
  1334.     void allchans(string args, object towho) {
  1335.         string output;
  1336.         string a;
  1337.  
  1338.         output=NETWORK_ID+" channels:\n";
  1339.         output += sprintf("%-23s %-17s %-7s %-6s %-10s %-10s\n",
  1340.           "Name","Owner","Policy","Level","Suggested","Local Name");
  1341.  
  1342.         foreach(a in sort_array(
  1343.             filter(keys(chaninfo), (: stringp($1) :) ),1)){
  1344.             output += sprintf("%-23s %-17s %-7s %-6s %-10s %-10s\n",
  1345.               a,chaninfo[a]["owner"],
  1346.               chaninfo[a]["policy"],
  1347.               chaninfo[a]["level"],chaninfo[a]["localname"],
  1348.               (localize_channel(a) ? localize_channel(a) : "<none>"));
  1349.         }
  1350.         IMC2_MSG(output,THIS_PLAYER);
  1351.  
  1352.     }
  1353.  
  1354.     //Send a channel command to the server
  1355.     void chancmd(string args, object towho) {
  1356.         string a, b, c;
  1357.  
  1358.         if(!ADMIN(THIS_PLAYER)) return notify_fail("You aren't allowed to use chancmd.\n");
  1359.         if(!args || (sscanf(args,"%s:%s %s",a,b,c)!=3))
  1360.             return notify_fail("Syntax: "+COMMAND_NAME+" chancmd hub:channel command\n");
  1361.         if(!chaninfo[a+":"+b])
  1362.             write(a+" is not listed as a channel, sending command anyway...\n");
  1363.         send_packet(GET_CAP_NAME(THIS_PLAYER),"ice-cmd","IMC",b, sprintf("channel=%s command=\"%s\"", a+":"+b,escape(c)));
  1364.  
  1365.         IMC2_MSG(sprintf("%s- Sent a command for the %s channel to %s\n",NETWORK_ID,a,GET_CAP_NAME(THIS_PLAYER),b),THIS_PLAYER);
  1366.     }
  1367.  
  1368.     int command(string str){
  1369.         // Takes the arguments given to the command which does IMC2 stuff.
  1370.         string cmd, args;
  1371.         string output;
  1372.         string a,b,c;
  1373.         int x,y;
  1374.         int emote,reply;
  1375.         object usr, *usrs=({ });
  1376.  
  1377.         if(IMC2_D->getonline() != 1) return 0;
  1378.  
  1379.         if(!str) str = "help";
  1380.         sscanf(str,"%s %s",cmd,args);
  1381.         if(!cmd) cmd=str;
  1382.         if(!args) args = "";
  1383.  
  1384.         switch(cmd){
  1385.         case "list":
  1386.             mudlist(this_player());
  1387.             return 1;
  1388.             break;
  1389.         case "setup":
  1390.             getstatus(this_player());
  1391.             return 1;
  1392.             break;
  1393.         case "info":
  1394.             mudinfo(args,this_player());
  1395.             return 1;
  1396.             break;
  1397.         case "ping":
  1398.             pingmud(args,this_player());
  1399.             return 1;
  1400.             break;
  1401.         case "who":
  1402.             mudwho(args,this_player());
  1403.             return 1;
  1404.             break;
  1405.         case "tell":
  1406.             tell(args,this_player());
  1407.             return 1;
  1408.             break;
  1409.         case "finger":
  1410.             finger(args,this_player());
  1411.             return 1;
  1412.             break;
  1413.         case "mudinfo":
  1414.             getremotemudinfo(args,this_player());
  1415.             return 1;
  1416.             break;
  1417.         case "allchans": case "ice-update":
  1418.             allchans(args,this_player());
  1419.             return 1;
  1420.             break;
  1421.         case "beep":
  1422.             interbeep(args,this_player());
  1423.             return 1;
  1424.             break;
  1425.         case "whois":
  1426.             whois(args,this_player());
  1427.             return 1;
  1428.             break;
  1429.         case "chancmd":
  1430.             chancmd(args,this_player());
  1431.             return 1;
  1432.             break;
  1433.         case "keepalive":
  1434.             send_keepalive_request();
  1435.             return 1;
  1436.             break;
  1437.         case "is-alive":
  1438.             send_is_alive();
  1439.             return 1;
  1440.             break;
  1441.         case "help": // drop through to default
  1442.         default:
  1443.             IMC2_MSG(main_help(),THIS_PLAYER);
  1444.             return 1;
  1445.             break;
  1446.         }
  1447.     }
  1448.  
  1449.     string main_help(){
  1450.         return sprintf(@EndText
  1451.             IMC2 system by Tim, set up for %s.
  1452.             To use this, type the command '%s' followed by one of the following:
  1453.             info (name) - lists information about a MUD
  1454.             list - lists the MUDs on this network
  1455.             finger (name)@(mud) - send a finger request for information about name@mud
  1456.             ping (mud) - pings a mud
  1457.             setup - shows information about this IMC2 network
  1458.             help - see this help message
  1459. EndText, NETWORK_ID,COMMAND_NAME);
  1460.     }
  1461.  
  1462.     string html(string str){
  1463.         string mud, *muds;
  1464.         string a,b;
  1465.         int x=0,y=0;
  1466.         string output="";
  1467.         if(!str) str="";
  1468.         sscanf(str,"%s_%s",a,b);
  1469.         if(!a) a=str;
  1470.         switch(a){
  1471.         case "list":
  1472.             muds = sort_array(keys(mudinfo),1);
  1473.             if (!sizeof(mudinfo)){
  1474.                 return ("There are no muds on the "+NETWORK_ID+" network!\n");
  1475.             }
  1476.             else{
  1477.                 output="<tr><td><b>Status</b></td><td><b>Name</b></td><td><b>Network</b></td><td><b>IMC2 Version</b></td></tr>\n";
  1478.                 foreach (mud in muds){
  1479.                     if(mudinfo[mud]["online"]) x++; else y++;
  1480.                     output += sprintf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\n",
  1481.                       (mudinfo[mud]["online"] ? "UP" : "DOWN"),
  1482.                       mudinfo[mud]["url"] ? "<a href=\""+
  1483.                       ((mudinfo[mud]["url"][0..6]!="http://") ? "http://"+mudinfo[mud]["url"] : mudinfo[mud]["url"])
  1484.                       +"\">"+mud+"</a>" : mud,
  1485.                       mudinfo[mud]["networkname"],
  1486.                       mudinfo[mud]["versionid"]);
  1487.                 }
  1488.             }
  1489.             return sprintf("<B>%d of %d MUDs are on %s.</B.\n<BR><table>%s</table>",
  1490.               x,x+y,NETWORK_ID,output);
  1491.             break;
  1492.         case "backlog":
  1493.             if(b && localchaninfo[b] && (localchaninfo[b]["perm"]==BACKLOG_WEB_LEVEL)){
  1494.                 // Show backlog for channel.
  1495.                 return b+" channel backlog:\n"+implode(localchaninfo[b]["backlog"],"\n");
  1496.             }
  1497.             // List the channels
  1498.             output = NETWORK_ID+" channels on "+MUDNAME+":\n";
  1499.             foreach(b in sort_array(keys(localchaninfo),1)){
  1500.                 if(localchaninfo[b]["perm"]==BACKLOG_WEB_LEVEL) // is public
  1501.                     output += "<a href=\""+HTML_LOCATION+"backlog_"+b+"\">";
  1502.                 output += b;
  1503.                 output += " - "+chan_perm_desc(localchaninfo[b]["perm"]);
  1504.                 if(localchaninfo[b]["perm"]==BACKLOG_WEB_LEVEL) // is public
  1505.                     output += " - on web</a>";
  1506.                 else
  1507.                     output += " - not on web";
  1508.                 output += "\n";
  1509.             }
  1510.             return output;
  1511.             break;
  1512.         default:
  1513.             return MUDNAME+" uses Tim's LPC IMC2 system to connect to the "+
  1514.             NETWORK_ID+" network.\n"+
  1515.             "(version:"+VERSION+")\n"+
  1516.             "From here, you can look at the <a href=\""+HTML_LOCATION+"list\">list of muds</a>,\n"+
  1517.             "view the <a href=\""+HTML_LOCATION+"backlog\">public channel backlogs</a>,\n"+
  1518.             "or go to the main <a href=\""+URL+"\">"+MUDNAME+"</a> web site.\n";
  1519.             break;
  1520.         }
  1521.     }
  1522.  
  1523.     int clean_up(){ return 0; }
  1524.  
  1525.  
  1526.     void forget_user(string str){ map_delete(tells,str); }
  1527.  
  1528.     static void eventChangeIMC2Passwords(){
  1529.         clientpass = alpha_crypt(10);
  1530.         serverpass = alpha_crypt(10);
  1531.         SECRETS_D->SetSecret("IMC2_CLIENT_PW", clientpass);
  1532.         SECRETS_D->SetSecret("IMC2_SERVER_PW", serverpass);
  1533.         return;
  1534.     }
  1535.  
  1536.     int UnSetAutoDisabled(int x){
  1537.         //This is just for taking away automatic disablement.
  1538.         //For enabling/disabling, see the mudconfig command.
  1539.         if(!autodisabled) return 0;
  1540.         autodisabled = 0;
  1541.         if(x){
  1542.             eventChangeIMC2Passwords();
  1543.         }
  1544.         if(unguarded((:directory_exists(path_prefix(SaveFile)):))){
  1545.             SaveObject(SaveFile,1);
  1546.             RELOAD_D->eventReload(this_object(), 2, 1);
  1547.         }
  1548.         return autodisabled;
  1549.     }
  1550.  
  1551.     void keepalive(string args, object who){
  1552.         send_packet(who->GetName(), "keepalive-request", "*", "*", 0);
  1553.     }
  1554.  
  1555.     int GetEnabled(){
  1556.         return !(DISABLE_IMC2);
  1557.     }
  1558.  
  1559. varargs string *GetMudList(int online){
  1560.    mixed muds = filter(keys(mudinfo), (: stringp($1) :));
  1561.    if(online) muds = filter(muds, (: mudinfo[$1]["online"] :));
  1562.    return muds;
  1563. }
  1564.  
  1565. varargs string GetMudName(string name, int online){
  1566.    mixed muds = filter(keys((mudinfo || ([]))), (: stringp($1) :));
  1567.    if(member_array(name, muds) != -1) return name;
  1568.    foreach(string mud in muds){
  1569.        if(lower_case(mud) == lower_case(name)){
  1570.            if(!online) return mud;
  1571.            else if(mudinfo[mud]["online"]) return mud;
  1572.        }
  1573.    }
  1574.    return 0;
  1575. }