/
LIB3/
LIB3/D/ADMIN/
LIB3/D/ADMIN/OBJ/
LIB3/D/ADMIN/ROOM/W/
LIB3/D/HOME/
LIB3/D/HOME/CITY/ARENA/
LIB3/D/HOME/CITY/ITEMS/
LIB3/D/HOME/CITY/POSTOFFI/
LIB3/DOC/
LIB3/GLOBAL/SPECIAL/
LIB3/GLOBAL/VIRTUAL/
LIB3/NET/
LIB3/NET/CONFIG/
LIB3/NET/DAEMON/CHARS/
LIB3/NET/GOPHER/
LIB3/NET/INHERIT/
LIB3/NET/OBJ/
LIB3/NET/SAVE/
LIB3/NET/VIRTUAL/
LIB3/OBJ/B_DAY/
LIB3/OBJ/HANDLERS/TERM_TYP/
LIB3/PLAYERS/B/
LIB3/PLAYERS/N/
LIB3/ROOM/
LIB3/SAVE/
LIB3/SAVE/BOARDS/
LIB3/SAVE/ENVIRON/
LIB3/SAVE/POST/
LIB3/STD/COMMANDS/SHADOWS/
LIB3/STD/CREATOR/
LIB3/STD/DOM/
LIB3/STD/EFFECTS/
LIB3/STD/EFFECTS/HEALING/
LIB3/STD/EFFECTS/OTHER/
LIB3/STD/EFFECTS/POISONS/
LIB3/STD/ENVIRON/
LIB3/STD/GUILDS/
LIB3/STD/LIQUIDS/
LIB3/STD/ROOM/
LIB3/STD/TRIGGER/SHADOW/
LIB3/W/
LIB3/W/BANNOR/
LIB3/W/NEWSTYLE/
/*
 * 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;
}