X-Git-Url: https://git.cworth.org/git?p=ttt;a=blobdiff_plain;f=src%2Fttt-client.c;h=e68f70ca5cbde835021b053814eec7098ec22ff4;hp=81f6e13e14268227e23a640f8288d933e7e66e15;hb=5ae1f2d980cc2bc30138c3e13d04f4e068f34911;hpb=b18c78a24034922e7b9152a03683cf930d703dd0 diff --git a/src/ttt-client.c b/src/ttt-client.c index 81f6e13..e68f70c 100644 --- a/src/ttt-client.c +++ b/src/ttt-client.c @@ -20,15 +20,15 @@ */ #include "ttt-client.h" - -#define YY_DECL int yylex (yyscan_t yyscanner, ttt_token_t *token) - #include "ttt-error.h" -#include "ttt-lex.h" #include "ttt-server.h" #include "ttt-socket.h" #include "ttt-token.h" +#define YY_DECL int yylex (yyscan_t yyscanner, ttt_token_t *token) +#include "ttt-lex.h" +YY_DECL; + struct _ttt_client { pthread_mutex_t mutex; pthread_t thread; @@ -40,14 +40,14 @@ struct _ttt_client { char **request_strings; int num_request_strings; - char *name; + char *username; ttt_bool_t registered; int num_wins; }; typedef ttt_error_t (*ttt_command_func_t) (ttt_client_t *client, - char **args, - int num_args); + char **args, + int num_args); static ttt_error_t _ttt_client_execute_helo (ttt_client_t *client, @@ -59,6 +59,11 @@ _ttt_client_execute_who (ttt_client_t *client, char **args, int num_args); +static ttt_error_t +_ttt_client_execute_statistics (ttt_client_t *client, + char **args, + int num_args); + static ttt_error_t _ttt_client_execute_message (ttt_client_t *client, char **args, @@ -66,33 +71,81 @@ _ttt_client_execute_message (ttt_client_t *client, static ttt_error_t _ttt_client_execute_help (ttt_client_t *client, - char **args, - int num_args); + char **args, + int num_args); static ttt_error_t _ttt_client_execute_version (ttt_client_t *client, - char **args, - int num_args); + char **args, + int num_args); static ttt_error_t _ttt_client_execute_quit (ttt_client_t *client, char **args, int num_args); +static ttt_error_t +_ttt_client_execute_invite (ttt_client_t *client, + char **args, + int num_args); + +static ttt_error_t +_ttt_client_execute_accept (ttt_client_t *client, + char **args, + int num_args); + +static ttt_error_t +_ttt_client_execute_retract (ttt_client_t *client, + char **args, + int num_args); + +static ttt_error_t +_ttt_client_execute_decline (ttt_client_t *client, + char **args, + int num_args); + typedef struct _ttt_command_description { const char *command; int args_min; int args_max; ttt_command_func_t execute; + const char *usage; + const char *description; } ttt_command_description_t; ttt_command_description_t command_descriptions[] = { - {"HELO", 1, 1, _ttt_client_execute_helo }, - {"WHO", 0, 0, _ttt_client_execute_who }, - {"MESSAGE", 1, 1, _ttt_client_execute_message}, - {"HELP", 0, 1, _ttt_client_execute_help }, - {"VERSION", 1, 1, _ttt_client_execute_version}, - {"QUIT", 0, 0, _ttt_client_execute_quit } + {"HELO", 1, 1, _ttt_client_execute_helo, + "HELO ", "Register."}, + + {"HELP", 0, 1, _ttt_client_execute_help, + "HELP ", "Display help for a command."}, + + {"INVITE", 1, 1, _ttt_client_execute_invite, + "INVITE ", "Invite a player to play a game."}, + + {"ACCEPT", 1, 1, _ttt_client_execute_accept, + "ACCEPT ", "Accept a game invitation."}, + + {"RETRACT", 1, 1, _ttt_client_execute_retract, + "RETRACT ", "Retract a game invitation."}, + + {"DECLINE", 1, 1, _ttt_client_execute_decline, + "DECLINE ", "Decline a game invitation."}, + + {"MESSAGE", 1, 1, _ttt_client_execute_message, + "MESSAGE ", "Send a message to everyone."}, + + {"STATISTICS", 1, 1, _ttt_client_execute_statistics, + "STATISTICS ", "Lists the statistics for the specified user."}, + + {"QUIT", 0, 0, _ttt_client_execute_quit, + "QUIT ", "Quit session."}, + + {"VERSION", 1, 1, _ttt_client_execute_version, + "VERSION ", "Negotiate protocol version."}, + + {"WHO", 0, 0, _ttt_client_execute_who, + "WHO ", "List registered users."} }; #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) @@ -108,7 +161,7 @@ _ttt_client_execute_helo (ttt_client_t *client, assert (num_args == 1); - ttt_client_set_name (client, args[0]); + ttt_client_set_username (client, args[0]); error = ttt_server_register_client (client->server, client); if (error) @@ -116,13 +169,13 @@ _ttt_client_execute_helo (ttt_client_t *client, client->registered = TRUE; xasprintf (&response, "HELO %s %s %s\r\n", - client->name, + client->username, ttt_server_get_host (client->server), ttt_server_get_port (client->server)); ttt_client_send (client, response); xasprintf (¬ice, "NOTICE USER %s\r\n", - client->name); + client->username); ttt_server_broadcast (client->server, notice); free (notice); @@ -151,10 +204,37 @@ _ttt_client_execute_who (ttt_client_t *client, return TTT_ERROR_NONE; } +static ttt_error_t +_ttt_client_execute_statistics (ttt_client_t *client, + char **args, + int num_args) +{ + char *response; + char *username; + ttt_error_t error; + + assert (num_args == 1); + + username = args[0]; + + if (!client->registered) + return TTT_ERROR_NO_NAME_SET; + + error = ttt_server_statistics (client->server, username, &response); + if (error) + return error; + + ttt_client_send (client, response); + + free (response); + + return TTT_ERROR_NONE; +} + static ttt_error_t _ttt_client_execute_message (ttt_client_t *client, - char **args, - int num_args) + char **args, + int num_args) { char *response; char *notice; @@ -164,11 +244,11 @@ _ttt_client_execute_message (ttt_client_t *client, if (!client->registered) return TTT_ERROR_NO_NAME_SET; - xasprintf(&response, "MESSAGE\r\n"); + xasprintf (&response, "MESSAGE\r\n"); ttt_client_send (client, response); - xasprintf(¬ice, "NOTICE MESSAGE %s \"%s\"\r\n", - client->name, + xasprintf (¬ice, "NOTICE MESSAGE %s \"%s\"\r\n", + client->username, args[0]); ttt_server_broadcast (client->server, notice); @@ -180,14 +260,51 @@ _ttt_client_execute_message (ttt_client_t *client, static ttt_error_t _ttt_client_execute_help (ttt_client_t *client, - char **args, - int num_args) + char **args, + int num_args) { char *response; + char *command; + ttt_command_description_t *desc; + int i; + ttt_bool_t is_command = FALSE; + + if (num_args == 0) { + xasprintf (&response, "HELP \"\r\n" + "Available Commands:\r\n"); + for (i = 0; i < ARRAY_SIZE(command_descriptions); i++) { + desc = &command_descriptions[i]; + xasprintf (&response, "%s\r\n %s - %s\r\n", + response, + desc->usage, + desc->description); + } + xasprintf (&response, "%s\"\r\n", response); + } else { + command = args[0]; + for (i = 0; i < strlen (command); i++) + command[i] = toupper (command[i]); + for (i = 0; i < ARRAY_SIZE(command_descriptions); i++) { + desc = &command_descriptions[i]; + if (strcmp (desc->command, command) == 0) { + is_command = TRUE; + xasprintf (&response, "HELP %s \"\r\n" + "%s\r\n" + "\r\n" + "Usage:\r\n" + " %s\r\n" + "\"\r\n", + desc->command, + desc->description, + desc->usage); + /* XXX: Add detailed help. */ + } + } + } + + if ((num_args == 1) && (!is_command)) + return TTT_ERROR_SYNTAX; - /* XXX: NYI */ - - xasprintf(&response, "HELP - NYI\r\n"); ttt_client_send (client, response); free (response); @@ -196,17 +313,32 @@ _ttt_client_execute_help (ttt_client_t *client, static ttt_error_t _ttt_client_execute_version (ttt_client_t *client, - char **args, - int num_args) + char **args, + int num_args) { char *response; + char *clientversion; + int version; + int i; assert (num_args == 1); - /* XXX: Argument is being ignored. - This is not completely implemented. */ + clientversion = args[0]; + + /* Verify that provided version arg is a positive integer */ + for (i = 0; i < strlen(clientversion); i++) + if (!isdigit (clientversion[i])) + return TTT_ERROR_SYNTAX; + + version = atoi (clientversion); + + if (version < 1) + return TTT_ERROR_SYNTAX; + + if (version > TTT_SERVER_PROTOCOL_VERSION) + version = TTT_SERVER_PROTOCOL_VERSION; - xasprintf (&response, "VERSION 1\r\n"); + xasprintf (&response, "VERSION %d\r\n", version); ttt_client_send (client, response); free (response); @@ -226,7 +358,7 @@ _ttt_client_execute_quit (ttt_client_t *client, return TTT_ERROR_QUIT_REQUESTED; xasprintf (¬ice, "NOTICE QUIT %s\r\n", - client->name); + client->username); ttt_server_broadcast (client->server, notice); free (notice); @@ -234,6 +366,160 @@ _ttt_client_execute_quit (ttt_client_t *client, return TTT_ERROR_QUIT_REQUESTED; } +static ttt_error_t +_ttt_client_execute_invite (ttt_client_t *client, + char **args, + int num_args) +{ + const char *username; + char *response; + char *notice; + ttt_error_t error; + + assert (num_args == 1); + + username = args[0]; + + if (!client->registered) + return TTT_ERROR_NO_NAME_SET; + + error = ttt_server_verify_username (client->server, username); + if (error) + return error; + + xasprintf (&response, "INVITE\r\n"); + ttt_client_send (client, response); + + xasprintf (¬ice, "NOTICE INVITE %s %s\r\n", + client->username, + username); + ttt_server_broadcast (client->server, notice); + + /* XXX: Store invitation in state */ + + free (notice); + free (response); + + return TTT_ERROR_NONE; +} + +static ttt_error_t +_ttt_client_execute_accept (ttt_client_t *client, + char **args, + int num_args) +{ + const char *username; + char *response; + char *notice; + ttt_error_t error; + + assert (num_args == 1); + + username = args[0]; + + if (!client->registered) + return TTT_ERROR_NO_NAME_SET; + + error = ttt_server_verify_username (client->server, username); + if (error) + return error; + + /* XXX: Verify invitation, else return ERROR NO_INVITE */ + + xasprintf (&response, "ACCEPT\r\n"); + ttt_client_send (client, response); + + xasprintf (¬ice, "NOTICE ACCEPT %s %s\r\n", + client->username, + username); + ttt_server_broadcast (client->server, notice); + + /* XXX: Start a new game */ + + free (notice); + free (response); + + return TTT_ERROR_NONE; +} + +static ttt_error_t +_ttt_client_execute_retract (ttt_client_t *client, + char **args, + int num_args) +{ + const char *username; + char *response; + char *notice; + ttt_error_t error; + + assert (num_args == 1); + + username = args[0]; + + if (!client->registered) + return TTT_ERROR_NO_NAME_SET; + + error = ttt_server_verify_username (client->server, username); + if (error) + return error; + + /* XXX: Verify invitation, else return ERROR NO_INVITE */ + + xasprintf (&response, "RETRACT\r\n"); + ttt_client_send (client, response); + + xasprintf (¬ice, "NOTICE RETRACT %s %s\r\n", + client->username, + username); + ttt_server_broadcast (client->server, notice); + + /* XXX: Remove invitiation from state */ + + free (notice); + free (response); + + return TTT_ERROR_NONE; +} + +static ttt_error_t +_ttt_client_execute_decline (ttt_client_t *client, + char **args, + int num_args) +{ + const char *username; + char *response; + char *notice; + ttt_error_t error; + + assert (num_args == 1); + + username = args[0]; + + if (!client->registered) + return TTT_ERROR_NO_NAME_SET; + + error = ttt_server_verify_username (client->server, username); + if (error) + return error; + + /* XXX: Verify invitation, else return ERROR NO_INVITE */ + + xasprintf (&response, "DECLINE\r\n"); + ttt_client_send (client, response); + + xasprintf (¬ice, "NOTICE DECLINE %s %s\r\n", + client->username, + username); + ttt_server_broadcast (client->server, notice); + + /* XXX: Remove invitation from state */ + + free (notice); + free (response); + + return TTT_ERROR_NONE; +} + static void _free_request (ttt_client_t *client); @@ -256,7 +542,7 @@ _ttt_client_init (ttt_client_t *client, client->request_strings = NULL; client->num_request_strings = 0; - client->name = NULL; + client->username = NULL; client->registered = FALSE; client->num_wins = 0; } @@ -269,8 +555,8 @@ _ttt_client_fini (ttt_client_t *client) if (client->registered) ttt_server_unregister_client (client->server, client); - free (client->name); - client->name = NULL; + free (client->username); + client->username = NULL; yylex_destroy (client->scanner); shutdown (client->socket, SHUT_RDWR); @@ -347,15 +633,17 @@ _execute_request (ttt_client_t *client) int num_args = client->num_request_strings-1; ttt_command_description_t *desc; - for (i=0; i < strlen (command); i++) + for (i = 0; i < strlen (command); i++) command[i] = toupper (command[i]); - for (i=0; i < ARRAY_SIZE(command_descriptions); i++) { + for (i = 0; i < ARRAY_SIZE(command_descriptions); i++) { desc = &command_descriptions[i]; - if (strcmp(command, desc->command) == 0) { + if (strcmp (command, desc->command) == 0) { if ((num_args < desc->args_min) || (num_args > desc->args_max)) return TTT_ERROR_SYNTAX; - return (desc->execute) (client, &client->request_strings[1], num_args); + return (desc->execute) (client, + &client->request_strings[1], + num_args); } } @@ -424,17 +712,17 @@ ttt_client_send (ttt_client_t *client, const char *message) /* Exported: See ttt-client.h for documentation. */ const char* -ttt_client_get_name (ttt_client_t *client) +ttt_client_get_username (ttt_client_t *client) { - return client->name; + return client->username; } /* Exported: See ttt-client.h for documentation. */ void -ttt_client_set_name (ttt_client_t *client, const char *name) +ttt_client_set_username (ttt_client_t *client, const char *username) { - free (client->name); - client->name = xstrdup (name); + free (client->username); + client->username = xstrdup (username); } /* Exported: See ttt-client.h for documentation. */ @@ -443,3 +731,9 @@ ttt_client_get_num_wins (ttt_client_t *client) { return client->num_wins; } + +/* This is just to keep the compiler quiet about a function declared + * static but never defined. Just an annoying bug in flex's output. */ +static int yy_init_globals (yyscan_t yyscanner) {return 0;} +void use_yy_init_globals (void); +void use_yy_init_globals (void) {yyscan_t scan; yy_init_globals(scan);}