/** * MUD based dictionary, conforms to the DICT new protocol found in RFC 2229. * See ftp://ftp.isi.edu/in-notes/rfc2229.txt for more information. * @author Taffyd@Discworld */ #include <network.h> #define DISCONNECTED 0 #define WAITING_FOR_CONNECT 1 #define READY 2 #define RECEIVING 3 #define SENDING_DEFINE 4 #define SENDING_MATCH 5 #define WAITING_FOR_DNS 6 #define INACTIVE 0 #define DEFINE 1 #define MATCH 2 #define SEARCH_MODE_ALL "*" #define DEFAULT_SEARCH_MODE "web1913" // #define DEBUGGER "taffyd" #define DEBUGGER "" /** * This class handles connection details for each person using * the dictionary. * @member fd the file descriptor of the socket currently being * used by the dictionary. * member dns_key the resolve() key used when doing a nslookup. */ // string *dictionaries; // string *capabilities; class connection_data { int fd; int dns_key; string current_word; string message; mixed definitions; mixed *working; int status; int action; int count; string search_mode; } inherit CLIENT; inherit "/std/object"; mapping _connections = ([ ]); void create() { client::create(); SetSocketType(STREAM); object::create(); } /* create() */ string calculate_short() { if ( !mapp( _connections ) ) return "dictionary"; if ( _connections[ this_player() ] ) { return "open dictionary"; } return "closed dictionary"; } /* calculate_short() */ void setup() { set_name("dictionary"); set_short( (: calculate_short() :) ); set_long("This is a fine dictionary with faded gold embossing. You could " "probably do all manner of things with it such as defining " "and spelling words.\n"); set_weight( 0 ); _connections = ([ ]); } /* setup() */ void close_socket( int fd, object who ) { if ( _connections[ who ] ) { eventWrite(fd, "QUIT\r\n" ); eventAbortCallback( fd ); eventSocketClose( fd ); map_delete( _connections, who ); if (( environment() == who ) || ( environment( who ) == environment( this_object() ) ) ) { if(query_verb() == "close") { tell_object( who, "You close the dictionary.\n" ); } else { tell_object(who, "The dictionary closes.\n"); } } } } /* close_socket() */ void eventRead(int fd, string message) { class connection_data dict; mapping destination; object who; string *bits, *lines, line, word, extra, dictionary_name, text; mixed *bing; int i; /* Check to see if the message is valid. */ if ( !stringp( message ) ) return; /* Find the user for this connection */ destination = filter( _connections, (: $(fd) == $2->fd :) ); if ( !sizeof( destination ) ) return; who = keys( destination )[ 0 ]; dict = destination[ who ]; lines = explode( message, "\r\n" ); foreach ( line in lines ) { if ( !sizeof( line ) ) line = "\n"; if ( line == "." ) { dict->definitions += ({ dict->working }); dict->count = -1; continue; } bits = explode( line, " " ); if ( !sizeof( bits ) ) continue; tell_creator(DEBUGGER, sprintf("Event %s received in status %d\n[%s]\n", bits[0], dict->status, line)); switch ( bits[ 0 ] ) { case "150": dict->count++; dict->working = ({ }); dict->status = RECEIVING; word = 0; extra = 0; dictionary_name = 0; break; case "151": if ( dict->status == RECEIVING && ( sscanf( line, "151 \"%s\" %s \"%s\"", word, extra, dictionary_name ) == 3 ) ) dict->working += ({ ({ word, dictionary_name, "" }) }); else tell_creator( DEBUGGER, "Invalid 151 error code, %s, %s, %s.\n", word, extra, dictionary_name ); break; case "152": dict->count++; dict->working = ({ }); dict->status = RECEIVING; word = 0; extra = 0; dictionary_name = 0; break; case "220": dict->status = READY; tell_object( who, "The dictionary's pages shuffle about. It is now " "ready for a query.\n" ); // dict->capabilities = explode( bits[ <2 ][1..<2], "." ); break; case "250": text = ""; switch(dict->action) { case DEFINE: for ( i = 0; i < sizeof( dict->definitions ); i++ ) { foreach( bing in dict->definitions[ i ] ) { bing[ 2 ] = replace( bing[ 2 ], ({ " ", " ", "\t", " " }) ); text += who->fix_string( bing[ 2 ], who->query_cols() - 5, 0 ); } text += "\n"; } tell_object( who, sprintf( "$P$Definition of %s$P$\n" "%s\n", dict->current_word, text ) ); dict->status = READY; tell_object( who, "The dictionary's pages shuffle about. It is now " "ready for a query.\n" ); break; case MATCH: text = ""; for(i=0; i<sizeof(dict->definitions); i++) { foreach(line in dict->definitions[i]) { if(sscanf(line, "%s \"%s\"", extra, word) == 2) text += word + "\n"; } } tell_object(who, sprintf("$P$Spellings for %s$P$" "\n%-#*s", dict->current_word, who->query_cols(), text) + "\n"); } dict->definitions = ({ }); return; break; case "530": tell_object( who, sprintf( "The dictionary reports: %s\n" "Please contact a creator.\n", line ) ); close_socket( fd, who ); return; break; case "552": tell_object( who, "No definitions for " + dict->current_word + ".\n" ); dict->status = READY; tell_object( who, "The dictionary's pages shuffle about. It is now " "ready for a query.\n" ); return; break; default: if ( dict->status == RECEIVING ) { switch(dict->action) { case DEFINE: dict->working[<1][<1] += line; break; case MATCH: dict->working += ({ line }); } } else tell_creator(DEBUGGER, "Line not added, status " + dict->status + "\n"); break; } } } /* eventRead() */ void got_ip_address( string address, string ip_address, int key ) { object player; class connection_data dict; int found_key; int new_fd; found_key = 0; foreach( player, dict in _connections ) { if ( !player ) { continue; } if ( dict->dns_key == key ) { found_key = 1; break; } } if ( !found_key ) { map_delete( _connections, player ); return; } if ( !address || !ip_address ) { tell_object( player, the_short() + " quivers and starts to jump " "around. You get the feeling that the dictionary cannot perform " "a DNS lookup just right now.\n"); map_delete( _connections, player ); return; } tell_creator( "taffyd", "Dictionary connection details: %s, %s.\n", address, ip_address ); new_fd = eventCreateSocket( ip_address, 2628 ); if ( new_fd < 0 ) { tell_object( player, the_short() + " quivers and starts to jump " "around. You get the feeling that the dictionary cannot connect " "to a dictionary server right now.\n"); map_delete( _connections, player ); return; } dict->fd = new_fd; dict->status = WAITING_FOR_CONNECT; _connections[ player ] = dict; call_out( "close_socket", 120, new_fd, this_player() ); } /* got_ip_address() */ /* new_fd = eventCreateSocket("208.48.44.94", 2628); if (new_fd < 0) { add_failed_mess("$D quivers and starts to jump around. You " "get the feeling that it's not going to open.\n"); return 0; } call_out( "close_socket", 120, new_fd, this_player() ); */ int do_open() { class connection_data dict; int key; if ( _connections[ this_player() ] ) { add_failed_mess( "$D is already open!\n" ); return 0; } key = resolve( "dict.org", "got_ip_address" ); dict = new(class connection_data, fd : 0, status : WAITING_FOR_DNS, count : -1, definitions : ({ }), dns_key : key ); _connections[ this_player() ] = dict; tell_object( this_player(), "The dictionary is currently " "searching for a valid dictionary server.\n" ); add_succeeded_mess( "$N $V the dictionary.\n" ); return 1; } /* do_open() */ int do_close() { class connection_data dict; if ( undefinedp( _connections[ this_player() ] ) ) { add_failed_mess( "You have not opened the dictionary!\n" ); return 0; } dict = _connections[ this_player() ]; close_socket( dict->fd, this_player() ); add_succeeded_mess( "" ); return 1; } /* do_close() */ int do_define(mixed *args) { class connection_data dict; if ( !( dict = _connections[ this_player() ] ) ) { add_failed_mess( "The dictionary is closed!\n" ); return 0; } if ( dict->status != READY ) { add_failed_mess( "The dictionary is currently busy.\n" ); return -1; } dict->current_word = args[0]; if (sizeof(args) > 1) dict->search_mode = SEARCH_MODE_ALL; else dict->search_mode = DEFAULT_SEARCH_MODE; dict->status = SENDING_DEFINE; dict->action = DEFINE; tell_creator(DEBUGGER, sprintf("DEFINE %s %s\r\n", dict->search_mode, dict->current_word)); eventWrite(dict->fd, sprintf("DEFINE %s %s\r\n", dict->search_mode, dict->current_word)); add_succeeded_mess( ({ "Searching...\n", "" }) ); return 1; } /* do_define() */ int do_spell(mixed *args) { class connection_data dict; if ( !( dict = _connections[ this_player() ] ) ) { add_failed_mess( "The dictionary is closed!\n" ); return 0; } if ( dict->status != READY ) { add_failed_mess( "The dictionary is currently busy.\n" ); return -1; } dict->current_word = args[0]; if(sizeof(args) > 1) dict->search_mode = SEARCH_MODE_ALL; else dict->search_mode = DEFAULT_SEARCH_MODE; dict->status = SENDING_MATCH; dict->action = MATCH; tell_creator(DEBUGGER, sprintf("MATCH %s . %s\r\n", dict->search_mode, dict->current_word)); eventWrite(dict->fd, sprintf("MATCH %s . %s\r\n", dict->search_mode, dict->current_word)); add_succeeded_mess( ({ "Searching...\n", "" }) ); return 1; } void init() { add_command( "open", "<direct:object>", (: do_open() :) ); add_command( "close", "<direct:object>", (: do_close() :) ); add_command( "define", "<string> [all]", (: do_define($4) :) ); add_command( "spell", "<string> [all]", (: do_spell($4) :) ); } /* init() */ void dest_me() { class connection_data dict; object ob; foreach ( ob, dict in _connections ) { close_socket( dict->fd, ob ); } object::dest_me(); client::dest_me(); } /* dest_me() */