/* * This will talk to gophers and get the menu stuff back in a nice * format for you. * * You send it the field to look up, the host to goto and the port to * look at, the function to call back on you, and optionaly the * search text... */ #include "gopher.h" #include "socket.h" #include "nroff.h" #ifdef DEBUG #define TP(STR) if (find_player("pinkfish")) tell_object(find_player("pinkfish"), STR) #else #define TP(STR) #endif mapping in_progress; #ifdef GOPHER_PORT string our_path; int our_socket, *closes; string tack_on_mess; void setup_gopher( int port, string path ); #endif void create() { seteuid( getuid() ); in_progress = ([ ]); closes = ({ }); #ifdef GOPHER_PORT call_out( "setup_gopher", 2, ({ GOPHER_PORT, GOPHER_PATH }) ); #endif } /* create() */ void do_connect( int type, string name, string host, string port, string func, string search_text ) { if( !in_progress[ host ] ) { in_progress[ host ] = ({ ({ name, port, previous_object(), func, search_text, "", type }) }); resolve( host, "finish_lookup" ); } else in_progress[ host ] += ({ ({ name, port, previous_object(), func, search_text, "", type }) }); } /* do_connect() */ void do_finish_lookup( string host, string number ) { int new_fd, ret; int i; /* We failed to lookup our host... :( */ if( !number ) { if( !in_progress[ host ] ) { for( i = 0; i < sizeof( in_progress[ host ] ); i++ ) call_other( in_progress[ host ][ i ][ 2 ], in_progress[ host ][ i ][ 3 ], 0 ); } return; } /* Ok, now we wanna connect up... */ for( i = 0; i < sizeof( in_progress[ host ] ); i++ ) { new_fd = socket_create( STREAM, "out_read_callback", "out_close_callback" ); if( new_fd < 0 ) { call_other( in_progress[ host ][ i ][ 2 ], in_progress[ host ][ i ][ 3 ], 0 ); return; } ret = socket_connect( new_fd, number + " " + in_progress[ host ][ i ][ 1 ], "out_read_callback", "out_write_callback" ); TP( sprintf( "Connecting to %s\n", number + " " + in_progress[ host ][ i ][ 1 ] ) ); if( ret < 0 ) { TP( "Failure to connect.\n" ); TP( "" + catch( call_other( in_progress[ host ][ i ][ 2 ], in_progress[ host ][ i ][ 3 ], 0, in_progress[ host ][ i ][ 0..4 ] ) ) ); map_delete( in_progress, host ); return; } remove_call_out( "do_closes" ); call_out( "do_closes", 360 ); closes += ({ new_fd }); in_progress[ new_fd ] = in_progress[ host ][ i ]; /* if (in_progress[host][i][4]) socket_write(new_fd, sprintf("%s\t%s\n", in_progress[host][i][0], in_progress[host][i][4])); else socket_write(new_fd, sprintf("%s\n", in_progress[host][i][0])); */ } map_delete( in_progress, host ); } /* do_finish_lookup() */ void finish_lookup( string name, string addr ) { TP( "" + catch( do_finish_lookup( name, addr ) ) ); } /* finish_lookup() */ void out_write_callback( int fd ) { string str; if( in_progress[ fd ][ 4 ] ) socket_write( fd, ( str = sprintf( "%s\t%s\n", in_progress[ fd ][ 0 ], in_progress[ fd ][ 4 ] ) ) ); else socket_write( fd, ( str = sprintf( "%s\n", in_progress[ fd ][ 0 ] ) ) ); TP( "Write call back was called. :" + str ); } /* out_write_callback() */ void out_read_callback( int fd, string mess ) { if( !in_progress[ fd ] ) { /* Hmmmmmm. */ TP( "Hmmm.\n" ); socket_close( fd ); return; } in_progress[ fd ][ 5 ] += replace_string( mess, "\r", "" ); } /* out_read_callback() */ void out_close_callback( int fd ) { string *bits, *bb; int i; mixed * ret; TP( "Socket is closing...\n" ); socket_close( fd ); if( !in_progress[ fd ] ) return; if( in_progress[ fd ][ 0 ] == "" ) in_progress[ fd ][ 0 ] = "1"; switch( in_progress[ fd ][ 6 ] ) { case MENU: case SEARCH: bits = explode( in_progress[ fd ][ 5 ], "\n" ); ret = ({ }); for( i = 0; i < sizeof( bits ); i++ ) { bb = explode( bits[ i ][ 1..10000 ], "\t" ); reset_eval_cost(); switch( bits[ i ][ 0 ] ) { case '1': /* Menu... */ ret += ({ ({ MENU, bb }) }); break; case '0': ret += ({ ({ FILE, bb }) }); break; case '7': ret += ({ ({ SEARCH, bb }) }); break; } } TP( "" + catch( call_other( in_progress[ fd ][ 2 ], in_progress[ fd ][ 3 ], ret, in_progress[ fd ][ 0..4 ] ) ) ); break; case FILE: in_progress[ fd ][ 5 ] = in_progress[ fd ][ 5 ][ 0..strlen( in_progress[ fd ][ 5 ] ) - 3 ]; TP( "" + catch( call_other( in_progress[ fd ][ 2 ], in_progress[ fd ][ 3 ], in_progress[ fd ][ 5 ], in_progress[ fd ][ 0..4 ] ) ) ); break; } map_delete( in_progress, fd ); closes = closes - ({ fd }); } /* out_close_callback() */ void do_closes() { int i, *tmp; tmp = closes + ({ }); for( i = 0; i < sizeof( tmp ); i++ ) out_close_callback( tmp[ i ] ); } /* do_closes() */ /* From here on down is the gopher server code. */ #ifdef GOPHER_PORT void setup_gopher( mixed port, string path ) { if( pointerp( port ) ) { path = port[ 1 ]; port = port[ 0 ]; } our_path = path; our_socket = socket_create( STREAM, "in_read_callback", "in_close_callback" ); if( our_socket < 0 ) { TP( "Failed to create socket.\n" ); return; } if( socket_bind( our_socket, port ) < 0 ) { TP( "Failed to bind socket.\n" ); socket_close( our_socket ); call_out( "setup_gopher", 120, ({ port, path }) ); return; } if( socket_listen( our_socket, "in_listen_callback" ) < 0 ) { TP( "Failed to listen to socket.\n" ); return; } } /* setup_gopher() */ void in_listen_callback( int fd ) { int new_fd; if( (new_fd = socket_accept( fd, "in_read_callback", "in_write_callback" )) < 0 ) { return; } call_out( "in_close_callback", 120, new_fd ); } /* in_listen_callback() */ string create_menu( string path ) { string *bing, ret, fn, rest, tmp, new_p, name; int i, len, type; bing = get_dir( our_path + path ); ret = ""; for( i = 0; i < sizeof( bing ); i++ ) if( file_size( our_path + path + bing[ i ] ) == -2 ) { if( bing[ i ][ 0 ] != '.' ) ret += sprintf( "%c%s\t%s\t%s\t%d\n", '1', replace_string( bing[ i ], "_", " " ), "1" + path + bing[ i ] + "/", MACHINE_NAME, GOPHER_PORT ); } else { /* Allow c files to be . files. */ if( sscanf( bing[ i ], "%s.c", tmp ) ) { /* If its a file, we do terribly silly things. */ fn = replace_string( our_path + path + tmp, "//", "/" ); TP( fn + "\n" ); if( fn->query_invisible() ) continue; type = fn->query_type(); if( !(rest = fn->query_rest()) ) rest = MACHINE_NAME + "\t" + GOPHER_PORT; if( !(new_p = fn->query_path()) ) { new_p = sprintf( "%c:exec:%s", type, path + bing[ i ] ); if( !(name = fn->query_arg()) ) new_p += ":"; else new_p += ":" + name; } if( !(name = fn->query_name()) ) name = replace_string( tmp, "_", " " ); ret += sprintf( "%c%s\t%s\t%s\n", type, name, new_p, rest ); } else if( bing[ i ][ 0 ] != '.' ) ret += sprintf( "%c%s\t%s\t%s\t%d\n", '0', replace_string( bing[ i ], "_", " " ), "0" + path + bing[ i ], MACHINE_NAME, GOPHER_PORT ); } return ret; } /* create_menu() */ int check_path( string str ) { if( sizeof( explode( str, ".." ) ) > 1 ) return 0; return 1; } /* check_path() */ void in_read_callback( int fd, string str ) { int type; string path, *bits, arg, s1, s2; if( !in_progress[ fd ] ) in_progress[ fd ] = str; else in_progress[ fd ] += str; if( sscanf( str, "%s\n%s", s1, s2 ) != 2 ) { /* We have to queue the damn thing... */ return; } str = in_progress[ fd ]; map_delete( in_progress, fd ); TP( sprintf( "Got %O as a message.\n", str ) ); str = replace_string( str, "\n", "" ); str = replace_string( str, "\r", "" ); if( str == "" || str == "1" ) { /* Root dir... */ if( tack_on_mess ) { socket_write( fd, create_menu( "/" ) + tack_on_mess + ".\n" ); tack_on_mess = 0; } else socket_write( fd, create_menu( "/" ) + ".\n" ); /* I hope it does flushing... */ socket_close( fd ); return; } if( sscanf( str, "%d:exec:%s", type, path ) == 2 ) { /* For use with the search facility */ bits = explode( path, "\t" ); sscanf( bits[ 0 ], "%s:%s", bits[ 0 ], arg ); bits[ 0 ] = our_path + bits[ 0 ]; if( check_path( bits[ 0 ] ) ) { /* Not allowed to have ..'s... */ bits[ 0 ] = replace_string( bits[ 0 ], "//", "/" ); if( type != 0 && tack_on_mess ) { socket_write( fd, bits[ 0 ]->do_gopher( bits, arg, fd ) + tack_on_mess + ".\n" ); tack_on_mess = 0; } else socket_write( fd, bits[ 0 ]->do_gopher( bits, arg, fd ) + ".\n" ); } else { TP( "Path error.\n" ); switch( type ) { case 1: case 7: if( tack_on_mess ) { socket_write( fd, "0Big nasty error\t0/error.error\t" + "error.host\t70\n" + "1Root menu\t\t" + MACHINE_NAME + "\t" + GOPHER_PORT + "\n" + tack_on_mess + ".\n" ); tack_on_mess = 0; } else socket_write( fd, "0Big nasty error\t0/error.error\t" + "error.host\t70\n" + "1Root menu\t\t" + MACHINE_NAME + "\t" + GOPHER_PORT + "\n.\n" ); break; case 0: socket_write( fd, "You have just encountered an error, " + "lucky you.\n.\n" ); break; } } socket_close( fd ); return; } sscanf( str, "%d/%s", type, path ); if( !path || !check_path( str ) ) { TP( "New path error.\n" ); switch( type ) { case 1: case 7: if( tack_on_mess ) { socket_write( fd, "0Big nasty error\t0/error.error\terror.host\t70\n" + "1Root menu\t\t" + MACHINE_NAME + "\t" + GOPHER_PORT + "\n" + tack_on_mess + ".\n" ); tack_on_mess = 0; } else socket_write( fd, "0Big nasty error\t0/error.error\terror.host\t70\n" + "1Root menu\t\t" + MACHINE_NAME + "\t" + GOPHER_PORT + "\n.\n" ); break; case 0: socket_write( fd, "You have just encountered an error, " + "lucky you.\n.\n" ); break; } socket_close( fd ); return; } path = "/" + path; switch( type ) { case 0: /* Grab the file and give it to them. */ str = read_file( our_path + path ); if( !str ) { str = "File requested was not found! Oh no! Find a hose clamp.\n"; } else if( str[ 0 ] == '.' ) { NROFF_HAND->create_nroff( our_path + path, "/tmp/gopher" ); str = NROFF_HAND->cat_file( "/tmp/gopher" ); rm( "/tmp/gopher" ); } socket_write( fd, str + "\n.\n" ); break; case 1: if( tack_on_mess ) { socket_write( fd, create_menu( path ) + tack_on_mess + ".\n" ); tack_on_mess = 0; } else socket_write( fd, create_menu( path ) + ".\n" ); break; } socket_close( fd ); } /* in_read_callback() */ void in_close_callback( int fd ) { socket_close( fd ); } /* in_close_callback() */ #endif /* GOPHER_PORT */ void set_tack_on_mess( string mess ) { tack_on_mess = mess; } string query_tack_on_mess() { return tack_on_mess; }