Author Topic: Problem with Tim's LPC IMC2 client  (Read 8226 times)

Offline Newt

  • Pottymouth
  • *
  • Posts: 19
    • View Profile
Problem with Tim's LPC IMC2 client
« on: March 03, 2010, 11:26:15 AM »
I can't figure out why this isn't working.  No errors or anything...just never get callbacks.  Spoke with Davion and he confirmed that it does connect...but then times out and is closed.


Code: [Select]
// Tim Johnson
// Started on May 1, 2004.
// Use this however you want.
#include <socket.h>
#include <network.h>
#include <security.h>
#include <save.h>
#include <config.h>
#include <daemons.h>
#include <std.h>

inherit DAEMON;

//#include <save.h> //need to add: #define SAVE_IMC2 DIR_SECURE_DAEMONS_SAVE "/imc2"

// File to save data to, .o will be added automatically to the end.
// This will have private stuff in it, don't put this in a directory where your wizards can read it.
#define SAVE_FILE "/adm/save/daemons/imc2" //SAVE_IMC2

//#define NO_AUTO 1

// Connection data...
#define HOSTNAME "server01.mudbytes.net"
#define HOSTPORT 5000
// HostIP overrides HOSTNAME, in case the mud doesn't want to resolve addresses.
#define HOSTIP "74.207.247.83"

// What name the network knows your mud as...
#define MUDNAME replace_string(mud_name(), " ", "")

// Passwords...
#define CLIENT_PW <password removed for posting>
#define SERVER_PW CLIENT_PW

// COMMAND_NAME is the command that people type to use this network.
#define COMMAND_NAME "imc2"

// NETWORK_ID is what your mud calls this network.
// This is the prefix that comes up on all messages to identify the IMC2 network.
// Tells for example look like:
// NETWORK_ID- Tim@TimMUD tells you: hi
// Make it similar to the command name, so players will understand.
#define NETWORK_ID "IMC2"

// DATA_LOG is where packets are logged to.
// Turn this off when not working on the system, as it invades privacy.
// Comment this out to turn it off.
#define DATA_LOG "imc2/general"

// UNKNOWN_DATA_LOG is where unrecognized packets are logged to.
// I wrote handlers for all packets I know of, so this should only pick
// up tests and possibly if anyone is creating new packets.
#define UNKNOWN_DATA_LOG "imc2/IMC2_UNKNOWN"

// Your MUD's URL is shared with other muds when building the mud list.
// This you could also put this in your who reply.
#define URL "http://shadowmudii.genesismuds.com/"

// ANNOUNCE_LOG is where network announcements get logged to.
// I suggest you keep this turned on.
// These announcements seem to be about channels being created and
// deleted, but may possibly have more.
#define ANNOUNCE_LOG "imc2/IMC2_ANNOUNCEMENTS"

// How many lines you want the backlog to be.
#define BACKLOG_SIZE 20

// Minimum permission number for channel to be viewable on the web.
#define BACKLOG_WEB_LEVEL 0

// If you use the web page with the mud list and channels and stuff,
// this'll be the URL for it, up to the point where the arguments
// are passed.
#define HTML_LOCATION URL //this is most likely borked


// WHO_STR is the code that you want a who request to display.
#define WHO_STR "/cmds/mortal/_who.c;who_list;80"

#ifndef VERSION
#define VERSION "Tim's LPC IMC2 client - Jan 30, 2005"
#endif

string tmpstr;

// Mode decides what kind of packet is expected
#define MODE_CONNECTED 1
#define MODE_WAITING_ACCEPT 2
#define MODE_CONNECT_ERROR 3 // Not used yet, I need to see what the hub sends.
#define MODE_HUB_DOWN 4 // Not used yet either.

#ifndef STREAM
#define STREAM 1
#endif
#ifndef EESUCCESS
#define EESUCCESS         1     /* Call was successful */
#endif
#ifndef THIS_PLAYER
#define THIS_PLAYER this_player()
#endif
#ifndef FIND_PLAYER
#define FIND_PLAYER(x) find_player(x)
#endif
#ifndef GET_CAP_NAME
#define GET_CAP_NAME(x) x->query_cap_name()
#endif
#ifndef GET_NAME
#define GET_NAME(x) x->query_name()
#endif
#ifndef IMC2_MSG
#define IMC2_MSG(x,y) foreach(tmpstr in explode(x,"\n")){ message("IMC2",tmpstr,y); }
#endif
#ifndef GET_GENDER
#define GET_GENDER(x) x->query_gender()
#endif
#ifndef ADMIN
#define ADMIN(x) creatorp(x)
#endif

// Other things that could be #define'd...
// INVIS(x) !visible(x)
// TELL_BOT "/u/t/timbot/imc2_invalidtells.c"
// TELL_BOTS ([ "timbot" : "/u/t/timbot/imc2_tellbot.c" ])
// CHAN_BOT "/u/t/timbot/imc2_chans.c"
// CHAN_BOTS ([ "ichat" : "/u/t/timbot/imc2_ichat.c" ])
// USER_EXISTS(x) user_exists(x)

// Debugging
#define DEB_IN  1
#define DEB_OUT 2
#define DEB_PAK 3
#define DEB_OTHER 0

nosave int socket_num;
int mode;
mapping ping_requests; // Keeps track of who sent a ping request.
// Ping requests aren't labelled with names, so replies are destined to this MUD
// with no idea why, unless we keep track.
string buf=""; // Buffer for incoming packets (aren't always sent 1 at a time)
 
// Variables
string hub_name, network_name;
string server_pass, server_version;
mapping chaninfo;
mapping localchaninfo; // (["chan": ([ "perm":1, "name":"something", "users":({ }) ]) ])
mapping mudinfo;
mapping genders;
mapping tells;
int sequence;

// Prototypes :)
void create();
void remove();
string pinkfish_to_imc2(string str);
string imc2_to_pinkfish(string str);
string escape(string str);
string unescape(string str);
mapping string_to_mapping(string str);
string main_help();

private void send_packet(string sender, string packet_type, string target, string destination, string data);
private void send_text(string text);
private void got_packet(string info);
private void start_logon();
private varargs void send_is_alive(string origin);
private void channel_in(string fromname, string frommud, mapping data);
private void tell_in(string sender, string origin, string target, mapping data);
private void beep_in(string sender, string origin, string target, mapping data);
private void who_reply_in(string origin, string target, mapping data);
private void whois_in(string fromname, string frommud, string targ, mapping data);
private void whois_reply_in(string targ,string fromname,string frommud,mapping data);
private void ping_reply_in(string sender,string origin,string target,mapping data);
private void chanwho_reply_in(string origin, string target, mapping data);
private void send_keepalive_request();
private int chan_perm_allowed(object user, string chan);
private string localize_channel(string str);
private void chan_who_in(string fromname, string frommud, mapping data);
private void send_ice_refresh();
private void resolve_callback(string address, string resolved, int key);

private varargs void debug(mixed msg, int x){
    seteuid(UID_LOG);
    log_file(DATA_LOG, "DEBUG: "+msg+" "+x+"\n");
    seteuid(UID_SOCKET);
// Add stuff in here if you want to see messages.
}

// Functions for users to change.
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.
int level(object ob){
        // Outgoing packets are marked with the user's level.
        // This function figures it out.
        // If you have different ways of ranking, make this function convert them to what IMC2 uses.
        // IMC2 uses: Admin=5, Imp=4, Imm=3, Mort=2, or None=1
        if(ADMIN(ob)) return 5; // Admin
        //TMI-2: if(wizardp(ob)) return 3;
        //TMI-2: if(userp(ob)) return 2;
//Discworld: if(ob->query_creator()) return 3;
if(this_player()) return 2;
        return 1; // None
}

string chan_perm_desc(int i){
        // Given the permission level assigned locally to a channel, return a short
        // string describing what the number means.  The number means nothing
// outside of this MUD.  Also, they are independant of each other, and
// so you can do groups without having to always do subgroups of higher
// ones or anything like 'levels'.  BACKLOG_WEB_LEVEL is the only one of
// significance, as it's the only one that the web backlog thing works on.
        switch(i){
                case 2: return "admin";
                case 1: return "wizards";
                case 0: return "public";
        }
        return "invalid";
}

int chan_perm_allowed(object user, string chan){
        // Using the permission level assigned locally to a channel,
        // return 1 if user is allowed to use the channel, 0 if not.
        switch(localchaninfo[chan]["perm"]){
                case 2: if(ADMIN(user)) return 1; return 0;
case 1:
                // TMI-2: case 1: if(wizardp(user)) return 1; return 0;
                case 0: return 1;
        }
}


// Shouldn't have to change anything beyond this point.

private int read_callback(int socket, mixed info){
// string str, *strs;
string a,b; int done=0;

debug("Begin read_cb", socket_num);

if(!sizeof(info)) return 0;
debug(save_variable(info),DEB_IN);
#ifdef DATA_LOG
    seteuid(UID_LOG);
    log_file(DATA_LOG,"SERVER: "+save_variable(info)+"\n");
    seteuid(UID_SOCKET);
#endif
buf += info;
// The hub groups packets, unfortunately.
switch(mode){
case MODE_WAITING_ACCEPT: // waiting for Hub to send autosetup
if(sscanf(info, "autosetup %s accept %s\n\r",
hub_name, network_name)==2){
debug("Connected, hub is "+hub_name+", network is "+network_name);
mode = MODE_CONNECTED;
send_is_alive("*");
send_keepalive_request();
send_ice_refresh();
}
else if(sscanf(info, "PW %s %s version=%d %s\n",
hub_name, server_pass, server_version, network_name)==4){
mode = MODE_CONNECTED;
send_is_alive("*");
send_keepalive_request();
send_ice_refresh();
}
else{ // Failed login sends plaintext error message.
debug("Failed to connect... "+info);
}
buf=""; // clear buffer
break;
case MODE_CONNECTED:
while(!done){
if(sscanf(buf,"%s\n\r%s",a,b)==2){ // found a break...
got_packet(a);
buf=b;
}
else{ // no break...
done = 1;
}
}
/*
strs = explode(info,"\n\r");
foreach(str in strs){
got_packet(str);
}
buf="";
*/
break;
}
return 1;
}

private void got_packet(string info){
string str;
string a,b;
int i;
string sender, origin, route, packet_type, target, destination, strdata;
int sequence;
mapping data;
object who;
if(!sizeof(info)) return;
debug(save_variable(info),DEB_PAK);

str = info;
// messages end with " \n\r" or "\n" or sometimes just a space
sscanf(str, "%s\n^", str);
sscanf(str, "%s\r^", str);
sscanf(str, "%s ^", str);
sscanf(str, "%s ^", str);
if(sscanf(str, "%s %d %s %s %s %s",
a, sequence, route, packet_type,
b, strdata)==6){ // matches
if(sscanf(b,"%s@%s",target,destination)!=2){
// Could be MUD instead of Name@MUD or *@MUD
target="*"; destination=b;
}
if(sscanf(a,"%s@%s",sender,origin)!=2){
sender="*"; origin=a;
}
data = string_to_mapping(strdata);
// debug("sender="+sender);
// debug("origin="+origin);
// debug("sequence="+sequence);
// debug("route="+route);
// debug("packet_type="+packet_type);
// debug("data="+save_variable(data));
if(!mudinfo[origin]) mudinfo[origin] = ([ ]);

switch(packet_type){
case "is-alive": // For making a MUD list.
if(!mudinfo[origin]) mudinfo[origin] = ([ ]);
// example of info:
// versionid=\"IMC2 AntiFreeze CL-2 SWR 1.0\" url=none md5=1
//mudinfo[origin]["version"]="blah";
mudinfo[origin]+=data;
mudinfo[origin]["online"]=1;
debug("handled is-alive for mud "+origin);
break;
case "close-notify": // Someone disconnected.
if(!mudinfo[data["host"]]) mudinfo[data["host"]] = ([]);
mudinfo[data["host"]]["online"]=0;
break;
case "keepalive-request": // Request for is-alive.
send_is_alive(origin);
break;
case "ice-msg-b": // Broadcast channel message.
channel_in(sender, origin, data);
break;
case "tell": // Tells or emotes.
tell_in(sender, origin, target, data);
break;
case "who-reply":
who_reply_in(origin,target,data);
break;
case "whois": // Like I3's locate
whois_in(sender,origin,target,data);
break;
case "whois-reply":
whois_reply_in(target,sender,origin,data);
break;
case "beep":
beep_in(sender, origin, target, data);
break;
case "ice-update": // Reply from ice-refresh.
chaninfo[data["channel"]]=data;
break;
case "wHo": // Drop-through
case "who":
send_packet("*","who-reply",sender,origin,
"text="+escape(pinkfish_to_imc2(WHO_STR)));
break;
case "ice-destroy": // Deleting channel.
map_delete(chaninfo,data["channel"]);
break;
case "user-cache": // User info, like I3's ucache service.
if(!genders[origin]) genders[origin]=([ ]);
genders[origin][sender]=data["gender"];
break;
case "user-cache-reply": // Reply with user info
if(!genders[origin]) genders[origin]=([ ]);
genders[origin][data["user"]]=data["gender"];
break;
case "user-cache-request": // Request for user info
sscanf(data["user"],"%s@%*s",str);
who = FIND_PLAYER(lower_case(str));
if(who
#ifdef INVIS
&& !INVIS(who)
#endif
){
switch(GET_GENDER(who)){
case "male" : i=0; break;
case "female" : i=1; break;
default : i=2; break;
}
send_packet("*","user-cache-reply",sender,
origin,sprintf("gender=%d",i));
}
break;
case "ping":
send_packet("*","ping-reply",sender,origin,
sprintf("path=\"%s\"",route));
break;
case "ping-reply":
ping_reply_in(sender,origin,target,data);
break;
case "ice-msg-r": // Relayed channel message.
channel_in(sender, origin, data);
break;
case "ice-chan-whoreply": // Tell who's listening to a channel.
chanwho_reply_in(origin,target,data);
break;
case "emote": // Channel creation/destruction message... anything else?
IMC2_MSG(NETWORK_ID+" announces: "+data["text"]+"\n",
filter_array(users(), (: ADMIN($1) :)));
#ifdef ANNOUNCE_LOG
                    seteuid(UID_LOG);
log_file(ANNOUNCE_LOG,ctime(time())+": "+data["text"]+"\n");
seteuid(UID_SOCKET);
#endif
break;
case "ice-chan-who": // Check who's listening to a channel.
chan_who_in(sender,origin,data);
break;
case "channel-notify":
// Don't care about this.  Useful only if you care when
// people on other MUDs start/stop listening to a channel.
// example: Someone@SomeMUD 1087076772 SomeMUD channel-notify *@*  channel=Hub01:ichat status=0
break;
// The following packets shouldn't be incoming.
case "ice-cmd": // Remote channel administration
case "remote-admin": // For controlling the hub
case "ice-refresh": // Request data about channels
case "ice-msg-p": // Private channel message
debug("This packet isn't supposed to be incoming: "+packet_type);
break;
default:
#ifdef UNKNOWN_DATA_LOG
                seteuid(UID_LOG);
                log_file(UNKNOWN_DATA_LOG,"Unknown packet: "+escape(info)+"\n\n");
                seteuid(UID_SOCKET);
#endif
debug("Unlisted packet type: "+packet_type);
break;
}
}
else{
buf += info;
debug("Doesn't match incoming pattern, so putting on buffer: "+str);
#ifdef BAD_PACKET
            seteuid(UID_LOG);
log_file(BAD_PACKET,"Doesn't match incoming pattern: "+str+"\n");
seteuid(UID_SOCKET);
#endif
}
}
 
private int close_callback(int socket){
// Connection was closed.
#ifdef DATA_LOG
    seteuid(UID_LOG);
log_file(DATA_LOG,"DISCONNECTED\n");
seteuid(UID_SOCKET);
#endif
socket_close(socket_num);
// socket->remove();
create();
return 1;
}

private void send_text(string text){
// Send a literal string.
// Almost everything should use the send_packet function instead of this.
#ifdef DATA_LOG
    seteuid(UID_LOG);
log_file(DATA_LOG,"CLIENT: "+save_variable(text)+"\n");
seteuid(UID_SOCKET);
#endif
debug(save_variable(text), DEB_OUT);
// debug("writing to socket: "+socket_num);
socket_write(socket_num,text);
// imc2_socket->send(text);
return;
}

void create(){
#ifdef DATA_LOG
    seteuid(UID_LOG);
log_file(DATA_LOG,"-------------------------\nStarting IMC2...\n");
seteuid(UID_SOCKET);
#endif
    if(sizeof(get_dir(SAVE_FILE+".o"))) restore_object(SAVE_FILE);
if(!mudinfo) mudinfo = ([ ]);
if(!chaninfo) chaninfo = ([ ]);
if(!localchaninfo) localchaninfo = ([ ]);
if(!genders) genders = ([ ]);
if(!tells) tells=([ ]);
ping_requests=([ ]);
mode = MODE_WAITING_ACCEPT;
#ifdef HOSTIP
    // We already know the IP, go straight to the connecting, just do callback as if it found the IP.
    resolve_callback(HOSTIP,HOSTIP,1);
#else
    if(!resolve(HOSTNAME, "resolve_callback")){
        //Debug("Addr_server is not running, resolve failed.");
#ifdef DATA_LOG
        log_file(DATA_LOG,"Addr_server is not running, resolve failed.\n");
#endif
        remove();
        return;
    }
#endif
debug("Finished creating IMC2 object");
}

void remove(){
// This object is getting destructed.
if(!socket_num) socket_close(socket_num);
debug("In remove()", socket_num);
seteuid(UID_LOG);
log_file(DATA_LOG,"IMC2 OBJECT REMOVED\n");
seteuid(UID_SOCKET);

save_object(SAVE_FILE);
destruct(this_object());

}

private void resolve_callback( string address, string resolved, int key ) {
// Figured out what the IP is for the address.
int error;
debug("Resolved to: "+resolved, socket_num);

//seteuid(UID_SOCKET);
socket_num = socket_create(STREAM, "read_callback", "close_callback");
//seteuid(getuid());
if (socket_num < 0) {
#ifdef DATA_LOG
        seteuid(UID_LOG);
log_file(DATA_LOG,"socket_create: " + socket_error(socket_num) + "\n");
seteuid(UID_SOCKET);
#endif
debug("socket_create: " + socket_error(socket_num) + "\n");
return;
}
debug("Created socket descriptor ", socket_num);

error = socket_bind(socket_num, 0);
if (error != EESUCCESS) {
#ifdef DATA_LOG
        seteuid(UID_LOG);
log_file(DATA_LOG,"socket_bind error: " + socket_error(error) + "\n");
seteuid(UID_SOCKET);
#endif
socket_close(socket_num);
return;
}

error = socket_connect(socket_num, resolved+" "+HOSTPORT, "read_callback", "write_callback");
if (error != EESUCCESS) {
#ifdef DATA_LOG
        seteuid(UID_LOG);
log_file(DATA_LOG,"socket_connect error: " + socket_error(error) + "\n");
seteuid(UID_SOCKET);
#endif
socket_close(socket_num);
return;
}
}

private void write_callback(int socket){
start_logon();
}

private void start_logon(){
debug("Gonna try logging in, sending the PW thing...");
#if NOT_AUTO
send_text(sprintf("PW %s %s version=%d\n",
#else
send_text(sprintf("PW %s %s version=%d autosetup %s\n",
#endif
MUDNAME,
CLIENT_PW, 2
#ifndef NOT_AUTO
, SERVER_PW
#endif
));
buf="";
/* For invitation-only networks.
send_text(sprintf("PW %s %s version=%d %s\n",
MUDNAME,
CLIENT_PW, 2, "hub03"
));
*/
sequence=time();
}

At one point I changed things assuming the socket was ready and called start_logon()  it sent the autosetup stuff and the status page for the IMC2 server said I was online...the socket stayed open at this point but still just sat there on my end.

Any help will be greatly appreciated,
Newt
« Last Edit: March 03, 2010, 11:43:13 AM by Newt »

Offline cratylus

  • Your favorite and best
  • Administrator
  • ***
  • Posts: 1020
  • Cratylus@Dead Souls <ds> np
    • View Profile
    • About Cratylus
Re: Problem with Tim's LPC IMC2 client
« Reply #1 on: March 03, 2010, 01:19:12 PM »
You've left out some pieces of information, such as what lib you've
got this in, which driver, which operating system.

Absent that information, I have a quickie suggestion to eliminate
your network as the culprit. Install the current version of DS somewhere,
boot, log in, then change the name to something unique like "newt_ds_test"
and restart it.

mudconfig mudname unique_name_here
mudconfig imc2 enable
shutdown


then log back in and:
arch

or

goto /secure/room/network

If that new mud connects to imc2, then you know you can disregard your
OS/network/router/ISP as the point of failure. Probably worth getting that
out of the way before chasing yourself in circles with code that's not broken.

If it turns out that your test DS mud does not have that problem, you know
that there's something different between the DS imc2.c and the one you're
using that makes things go. Study the DS version for clues, if this is the case.

-Crat


Offline Newt

  • Pottymouth
  • *
  • Posts: 19
    • View Profile
Re: Problem with Tim's LPC IMC2 client
« Reply #2 on: March 04, 2010, 12:02:04 AM »
Sorry about that, Crat.  Will make sure to include proper info next time.

At any rate, the problem was found.  The callback functions were declared as private.