2012-03-12 12:55:53 +00:00
# include <glib.h>
# include <gio/gio.h>
2012-03-12 23:42:00 +00:00
# include <string.h>
2012-03-14 19:03:54 +00:00
# include <stdarg.h>
2012-03-12 12:55:53 +00:00
2012-03-22 18:49:59 +00:00
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif /* HAVE_CONFIG_H */
2012-03-12 12:55:53 +00:00
# include "main.h"
2012-03-12 22:52:43 +00:00
# include "networking.h"
2012-03-13 01:22:22 +00:00
# include "interpreter.h"
2012-03-21 15:07:02 +00:00
# include "players.h"
2012-03-22 17:34:39 +00:00
# include "db.h"
2012-03-12 12:55:53 +00:00
struct AcceptData {
GMainContext * context ;
GSocketListener * listener ;
} ;
2012-03-12 22:52:43 +00:00
GSList * clients ;
2012-03-22 17:34:39 +00:00
void wmud_client_interpret_newplayer_email ( wmudClient * client ) ;
void wmud_client_interpret_newplayer_mailconfirm ( wmudClient * client_data ) ;
2012-03-21 15:07:02 +00:00
void
2012-03-22 17:34:39 +00:00
wmud_client_close ( wmudClient * client , gboolean send_goodbye )
2012-03-21 15:07:02 +00:00
{
GError * err = NULL ;
if ( send_goodbye )
{
2012-03-22 17:34:39 +00:00
wmud_client_send ( client , " \r \n Have a nice real-world day! \r \n \r \n " ) ;
2012-03-21 15:07:02 +00:00
}
2012-03-22 17:45:45 +00:00
g_log ( G_LOG_DOMAIN , G_LOG_LEVEL_INFO , " Connection closed. " ) ;
2012-03-21 15:07:02 +00:00
/* TODO: Error checking */
g_socket_close ( client - > socket , & err ) ;
clients = g_slist_remove ( clients , client ) ;
2012-03-22 17:34:39 +00:00
wmud_player_free ( & ( client - > player ) ) ;
2012-03-21 15:07:02 +00:00
if ( client - > buffer )
g_string_free ( client - > buffer , TRUE ) ;
g_free ( client ) ;
}
2012-03-12 12:55:53 +00:00
gboolean
2012-03-12 22:52:43 +00:00
client_callback ( GSocket * client , GIOCondition condition , wmudClient * client_data )
2012-03-12 12:55:53 +00:00
{
GError * err = NULL ;
if ( condition & G_IO_HUP )
{
2012-03-22 17:34:39 +00:00
wmud_client_close ( client_data , FALSE ) ;
2012-03-12 12:55:53 +00:00
return FALSE ;
}
else if ( ( condition & G_IO_IN ) | | ( condition & G_IO_PRI ) )
{
gssize len ;
2012-03-12 23:42:00 +00:00
gchar * buf2 ;
2012-03-12 12:55:53 +00:00
gchar * buf = g_malloc0 ( sizeof ( gchar ) * ( MAX_RECV_LEN + 1 ) ) ;
/* TODO: Error checking */
if ( ( len = g_socket_receive ( client , buf , MAX_RECV_LEN , NULL , & err ) ) = = 0 )
{
g_free ( buf ) ;
2012-03-22 17:34:39 +00:00
wmud_client_close ( client_data , FALSE ) ;
2012-03-12 12:55:53 +00:00
return FALSE ;
}
2012-03-12 23:42:00 +00:00
buf2 = buf ;
while ( TRUE )
{
char * r = strchr ( ( char * ) buf2 , ' \r ' ) ,
* n = strchr ( ( char * ) buf2 , ' \n ' ) ;
if ( r | | n )
{
if ( ( r < n ) & & r )
{
if ( client_data - > buffer - > len > 0 )
g_string_append_len ( client_data - > buffer , buf2 , ( r - buf2 ) ) ;
else
g_string_overwrite_len ( client_data - > buffer , 0 , buf2 , ( r - buf2 ) ) ;
buf2 = r ;
}
else if ( n )
{
if ( client_data - > buffer - > len > 0 )
g_string_append_len ( client_data - > buffer , buf2 , ( n - buf2 ) ) ;
else
g_string_overwrite_len ( client_data - > buffer , 0 , buf2 , ( n - buf2 ) ) ;
buf2 = n ;
}
2012-03-21 15:07:02 +00:00
switch ( client_data - > state )
{
case WMUD_CLIENT_STATE_FRESH :
2012-03-22 17:34:39 +00:00
if ( * ( client_data - > buffer - > str ) )
wmud_client_start_login ( client_data ) ;
2012-03-21 15:07:02 +00:00
break ;
case WMUD_CLIENT_STATE_PASSWAIT :
2012-03-22 17:34:39 +00:00
if ( * ( client_data - > buffer - > str ) )
wmud_player_auth ( client_data ) ;
2012-03-21 15:07:02 +00:00
break ;
case WMUD_CLIENT_STATE_MENU :
//wmud_client_interpret_menu_command(client_data);
break ;
case WMUD_CLIENT_STATE_INGAME :
wmud_interpret_game_command ( client_data ) ;
break ;
case WMUD_CLIENT_STATE_QUITWAIT :
//wmud_interpret_quit_answer(client_data);
break ;
2012-03-22 17:34:39 +00:00
case WMUD_CLIENT_STATE_NEWCHAR :
wmud_client_interpret_newplayer_answer ( client_data ) ;
break ;
case WMUD_CLIENT_STATE_REGISTERING :
wmud_client_interpret_newplayer_email ( client_data ) ;
break ;
case WMUD_CLIENT_STATE_REGEMAIL_CONFIRM :
wmud_client_interpret_newplayer_mailconfirm ( client_data ) ;
break ;
2012-03-21 15:07:02 +00:00
}
2012-03-12 23:42:00 +00:00
g_string_erase ( client_data - > buffer , 0 , - 1 ) ;
for ( ; ( ( * buf2 = = ' \r ' ) | | ( * buf2 = = ' \n ' ) ) & & * buf2 ; buf2 + + ) ;
if ( ! * buf2 )
break ;
}
else
{
if ( client_data - > buffer - > len > 0 )
g_string_append ( client_data - > buffer , buf2 ) ;
else
g_string_overwrite ( client_data - > buffer , 0 , buf2 ) ;
break ;
}
}
2012-03-12 12:55:53 +00:00
g_free ( buf ) ;
}
return TRUE ;
}
2012-03-21 15:07:02 +00:00
/* game_source_callback()
*
* This function is called whenever a new connection is available on the game socket
*/
2012-03-12 12:55:53 +00:00
gboolean
game_source_callback ( GSocket * socket , GIOCondition condition , struct AcceptData * accept_data )
{
GSocket * client_socket ;
GSource * client_source ;
GError * err = NULL ;
2012-03-12 22:52:43 +00:00
wmudClient * client_data ;
2012-03-12 12:55:53 +00:00
/* TODO: Error checking */
client_socket = g_socket_listener_accept_socket ( accept_data - > listener , NULL , NULL , & err ) ;
2012-03-12 22:52:43 +00:00
client_data = g_new0 ( wmudClient , 1 ) ;
client_data - > socket = client_socket ;
2012-03-12 23:42:00 +00:00
client_data - > buffer = g_string_new ( " " ) ;
2012-03-21 15:07:02 +00:00
client_data - > state = WMUD_CLIENT_STATE_FRESH ;
2012-03-12 22:52:43 +00:00
clients = g_slist_prepend ( clients , client_data ) ;
2012-03-12 12:55:53 +00:00
client_source = g_socket_create_source ( client_socket , G_IO_IN | G_IO_OUT | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL , NULL ) ;
2012-03-12 22:52:43 +00:00
g_source_set_callback ( client_source , ( GSourceFunc ) client_callback , client_data , NULL ) ;
2012-03-12 12:55:53 +00:00
g_source_attach ( client_source , accept_data - > context ) ;
2012-03-22 17:34:39 +00:00
g_log ( G_LOG_DOMAIN , G_LOG_LEVEL_INFO , " New connection. " ) ;
wmud_client_send ( client_data , " By what name shall we call you? " ) ;
2012-03-12 12:55:53 +00:00
return TRUE ;
}
gboolean
2012-03-22 17:34:39 +00:00
wmud_networking_init ( guint port_number , GError * * err )
2012-03-12 12:55:53 +00:00
{
struct AcceptData * accept_data ;
GSocketListener * game_listener ;
gboolean need_ipv4_socket = TRUE ;
GSocket * game_socket6 ,
* game_socket4 ;
2012-03-22 17:34:39 +00:00
GError * in_err = NULL ;
2012-03-12 12:55:53 +00:00
GSource * game_net_source4 = NULL ,
* game_net_source6 = NULL ;
2012-03-12 22:52:43 +00:00
clients = NULL ;
2012-03-12 12:55:53 +00:00
game_listener = g_socket_listener_new ( ) ;
/* The following snippet is borrowed from GLib 2.30's gsocketlistener.c
* code , to create the necessary sockets to listen on both IPv4 and
* IPv6 address */
2012-03-22 17:34:39 +00:00
if ( ( game_socket6 = g_socket_new ( G_SOCKET_FAMILY_IPV6 , G_SOCKET_TYPE_STREAM , G_SOCKET_PROTOCOL_DEFAULT , & in_err ) ) ! = NULL )
2012-03-12 12:55:53 +00:00
{
GInetAddress * inet_address ;
GSocketAddress * address ;
gboolean result ;
inet_address = g_inet_address_new_any ( G_SOCKET_FAMILY_IPV6 ) ;
address = g_inet_socket_address_new ( inet_address , port_number ) ;
g_object_unref ( inet_address ) ;
g_socket_set_listen_backlog ( game_socket6 , 10 ) ;
2012-03-22 17:34:39 +00:00
result = g_socket_bind ( game_socket6 , address , TRUE , & in_err )
& & g_socket_listen ( game_socket6 , & in_err ) ;
2012-03-12 12:55:53 +00:00
g_object_unref ( address ) ;
if ( ! result )
{
g_object_unref ( game_socket6 ) ;
2012-03-22 17:34:39 +00:00
g_log ( G_LOG_DOMAIN , G_LOG_LEVEL_WARNING , " Unable to create listener IPv6 socket " ) ;
2012-03-12 12:55:53 +00:00
return FALSE ;
}
if ( g_socket_speaks_ipv4 ( game_socket6 ) )
need_ipv4_socket = FALSE ;
game_net_source6 = g_socket_create_source ( game_socket6 , G_IO_IN , NULL ) ;
/* TODO: error checking */
2012-03-22 17:34:39 +00:00
g_socket_listener_add_socket ( game_listener , game_socket6 , NULL , & in_err ) ;
2012-03-12 12:55:53 +00:00
}
/* TODO: else { error checking } */
if ( need_ipv4_socket )
{
2012-03-22 17:34:39 +00:00
if ( ( game_socket4 = g_socket_new ( G_SOCKET_FAMILY_IPV4 , G_SOCKET_TYPE_STREAM , G_SOCKET_PROTOCOL_DEFAULT , & in_err ) ) ! = NULL )
2012-03-12 12:55:53 +00:00
{
GInetAddress * inet_address ;
GSocketAddress * address ;
gboolean result ;
inet_address = g_inet_address_new_any ( G_SOCKET_FAMILY_IPV4 ) ;
address = g_inet_socket_address_new ( inet_address , port_number ) ;
g_object_unref ( inet_address ) ;
g_socket_set_listen_backlog ( game_socket4 , 10 ) ;
2012-03-22 17:34:39 +00:00
result = g_socket_bind ( game_socket4 , address , TRUE , & in_err )
& & g_socket_listen ( game_socket4 , & in_err ) ;
2012-03-12 12:55:53 +00:00
g_object_unref ( address ) ;
if ( ! result )
{
g_object_unref ( game_socket4 ) ;
if ( ! game_socket6 )
g_object_unref ( game_socket6 ) ;
2012-03-22 17:34:39 +00:00
g_log ( G_LOG_DOMAIN , G_LOG_LEVEL_WARNING , " Unable to create listener IPv4 socket! \n " ) ;
2012-03-12 12:55:53 +00:00
return FALSE ;
}
game_net_source4 = g_socket_create_source ( game_socket4 , G_IO_IN , NULL ) ;
/* TODO: error checking */
2012-03-22 17:34:39 +00:00
g_socket_listener_add_socket ( game_listener , game_socket4 , NULL , & in_err ) ;
2012-03-12 12:55:53 +00:00
}
/* TODO: else { error checking } */
}
else
{
if ( game_socket6 ! = NULL )
2012-03-22 17:34:39 +00:00
g_clear_error ( & in_err ) ;
2012-03-12 12:55:53 +00:00
else
return FALSE ;
}
accept_data = g_new ( struct AcceptData , 1 ) ;
accept_data - > listener = game_listener ;
accept_data - > context = game_context ;
if ( game_net_source6 )
{
g_source_set_callback ( game_net_source6 , ( GSourceFunc ) game_source_callback , ( gpointer ) accept_data , NULL ) ;
g_source_attach ( game_net_source6 , game_context ) ;
}
if ( game_net_source4 )
{
g_source_set_callback ( game_net_source4 , ( GSourceFunc ) game_source_callback , ( gpointer ) accept_data , NULL ) ;
g_source_attach ( game_net_source4 , game_context ) ;
}
return TRUE ;
}
2012-03-14 19:03:54 +00:00
void
wmud_client_send ( wmudClient * client , const gchar * fmt , . . . )
{
va_list ap ;
GString * buf = g_string_new ( " " ) ;
va_start ( ap , fmt ) ;
g_string_vprintf ( buf , fmt , ap ) ;
va_end ( ap ) ;
/* TODO: error checking */
g_socket_send ( client - > socket , buf - > str , buf - > len , NULL , NULL ) ;
g_string_free ( buf , TRUE ) ;
}
2012-03-22 09:49:19 +00:00
void
wmud_client_start_login ( wmudClient * client )
{
2012-03-22 17:34:39 +00:00
wmudPlayer * player ;
if ( ( player = wmud_player_exists ( client - > buffer - > str ) ) ! = NULL )
{
g_log ( G_LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Trying to login with playername '%s' \n " , client - > buffer - > str ) ;
if ( player - > cpassword = = NULL )
{
wmud_client_send ( client , " Your registration is not finished yet. \r \n " ) ;
wmud_client_close ( client , TRUE ) ;
}
2012-03-23 10:05:51 +00:00
else
{
client - > state = WMUD_CLIENT_STATE_PASSWAIT ;
wmud_client_send ( client , " Please provide us your password: %c%c%c " , TELNET_IAC , TELNET_WONT , TELNET_ECHO ) ;
}
2012-03-22 17:34:39 +00:00
}
else
{
client - > player = g_new0 ( wmudPlayer , 1 ) ;
client - > player - > player_name = g_strdup ( client - > buffer - > str ) ;
client - > state = WMUD_CLIENT_STATE_NEWCHAR ;
wmud_client_send ( client , " Is %s new to this game? [Y/N] " , client - > buffer - > str ) ;
}
}
void
wmud_client_interpret_newplayer_answer ( wmudClient * client )
{
if ( g_ascii_strcasecmp ( client - > buffer - > str , " n " ) = = 0 )
{
wmud_client_send ( client , " What is your player-name, then? " ) ;
client - > state = WMUD_CLIENT_STATE_FRESH ;
}
else if ( g_ascii_strcasecmp ( client - > buffer - > str , " y " ) = = 0 )
{
g_log ( G_LOG_DOMAIN , G_LOG_LEVEL_DEBUG , " Creating new player \n " ) ;
wmud_client_send ( client , " Welcome to this MUD! \r \n Please enter your e-mail address: " ) ;
client - > state = WMUD_CLIENT_STATE_REGISTERING ;
}
else
{
wmud_client_send ( client , " Sorry, but for this question I only understand 'Y' or 'N'. \r \n Is %s a new player here? [Y/N] " , client - > player - > player_name ) ;
}
}
void
wmud_client_interpret_newplayer_email ( wmudClient * client )
{
/* TODO: Error checking */
GRegex * email_regex = g_regex_new ( " ^[A-Z0-9._%+-]+@[A-Z0-9.-]+ \\ .[A-Z]{2,4}$ " , G_REGEX_CASELESS , 0 , NULL ) ;
if ( ! * ( client - > buffer - > str ) )
{
if ( client - > bademail )
{
wmud_client_close ( client , TRUE ) ;
}
}
if ( g_regex_match ( email_regex , client - > buffer - > str , 0 , NULL ) )
{
client - > player - > email = g_strdup ( client - > buffer - > str ) ;
client - > state = WMUD_CLIENT_STATE_REGEMAIL_CONFIRM ;
wmud_client_send ( client , " It seems to be a valid address to me, but could you write it again? " ) ;
}
else
{
wmud_client_send ( client , " \r \n Sorry, but this e-mail address doesn't seem to be valid to me. \r \n \r \n If you think this is a valid address, simply press enter to quit, and send an e-mail to %s from that address, so we can fix our e-mail validation code. \r \n \r \n If you just mistyped your address, type it now: " , admin_email ) ;
if ( * ( client - > buffer - > str ) )
client - > bademail = TRUE ;
}
}
void
wmud_client_interpret_newplayer_mailconfirm ( wmudClient * client )
{
GError * err = NULL ;
if ( g_ascii_strcasecmp ( client - > player - > email , client - > buffer - > str ) = = 0 )
{
if ( wmud_db_save_player ( client - > player , & err ) )
wmud_client_send ( client , " Good. We will generate the password for this player name, and send it to you \r \n via e-mail. Please come back to us, if you get that code, so you can log \r \n in. \r \n " ) ;
else
{
g_critical ( " wmud_db_save_player() error: %s " , err - > message ) ;
wmud_client_send ( client , " There was an error during the database update. Please try again later! \r \n " ) ;
}
wmud_client_close ( client , TRUE ) ;
}
else
{
g_free ( client - > player - > email ) ;
client - > player - > email = NULL ;
wmud_client_send ( client , " This is not the same as you entered before. \r \n Let's just try it again: " ) ;
client - > state = WMUD_CLIENT_STATE_REGISTERING ;
}
2012-03-22 09:49:19 +00:00
}