X-Git-Url: https://git.cworth.org/git?p=ttt;a=blobdiff_plain;f=src%2Fttt-client.c;h=1dd755f83651e3d7c6f80f176e0befa05f8af8dc;hp=24738649e2f52990dd3631e48e1eee9183c28d40;hb=f2187ebd49f78b84bd2dca6172abc81e54dda199;hpb=65526318e1c88d56bd1a78d072c4d38252dbd864 diff --git a/src/ttt-client.c b/src/ttt-client.c index 2473864..1dd755f 100644 --- a/src/ttt-client.c +++ b/src/ttt-client.c @@ -21,6 +21,8 @@ #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" @@ -30,14 +32,14 @@ struct _ttt_client { pthread_mutex_t mutex; pthread_t thread; - + ttt_server_t *server; int socket; yyscan_t scanner; - + char **request_strings; int num_request_strings; - + char *name; ttt_bool_t registered; int num_wins; @@ -57,6 +59,21 @@ _ttt_client_execute_who (ttt_client_t *client, char **args, int num_args); +static ttt_error_t +_ttt_client_execute_message (ttt_client_t *client, + char **args, + int num_args); + +static ttt_error_t +_ttt_client_execute_help (ttt_client_t *client, + char **args, + int num_args); + +static ttt_error_t +_ttt_client_execute_version (ttt_client_t *client, + char **args, + int num_args); + static ttt_error_t _ttt_client_execute_quit (ttt_client_t *client, char **args, @@ -64,15 +81,33 @@ _ttt_client_execute_quit (ttt_client_t *client, typedef struct _ttt_command_description { const char *command; - int args_required; + 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, _ttt_client_execute_helo}, - {"WHO", 0, _ttt_client_execute_who }, - {"QUIT", 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."}, + + {"MESSAGE", 1, 1, _ttt_client_execute_message, + "MESSAGE ", "Send a message to everyone."}, + + {"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])) static ttt_error_t @@ -82,6 +117,7 @@ _ttt_client_execute_helo (ttt_client_t *client, { ttt_error_t error; char *response; + char *notice; assert (num_args == 1); @@ -90,16 +126,19 @@ _ttt_client_execute_helo (ttt_client_t *client, error = ttt_server_register_client (client->server, client); if (error) return error; - client->registered = TRUE; - xasprintf (&response, "HELO %s %s %s\n", + xasprintf (&response, "HELO %s %s %s\r\n", client->name, 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); + ttt_server_broadcast (client->server, notice); + + free (notice); free (response); return TTT_ERROR_NONE; @@ -110,12 +149,14 @@ _ttt_client_execute_who (ttt_client_t *client, char **args, int num_args) { - const char *response; + char *response; assert (num_args == 0); - response = ttt_server_who (client->server); + if (!client->registered) + return TTT_ERROR_NO_NAME_SET; + response = xstrdup (ttt_server_who (client->server)); ttt_client_send (client, response); free (response); @@ -124,12 +165,122 @@ _ttt_client_execute_who (ttt_client_t *client, } static ttt_error_t -_ttt_client_execute_quit (ttt_client_t *client, - char **args, - int num_args) +_ttt_client_execute_message (ttt_client_t *client, + char **args, + int num_args) { + char *response; + char *notice; + + assert (num_args == 1); + + if (!client->registered) + return TTT_ERROR_NO_NAME_SET; + + xasprintf (&response, "MESSAGE\r\n"); + ttt_client_send (client, response); + + xasprintf (¬ice, "NOTICE MESSAGE %s \"%s\"\r\n", + client->name, + args[0]); + ttt_server_broadcast (client->server, notice); + + free (notice); + free (response); + + return TTT_ERROR_NONE; +} + +static ttt_error_t +_ttt_client_execute_help (ttt_client_t *client, + 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; + + ttt_client_send (client, response); + + free (response); + return TTT_ERROR_NONE; +} + +static ttt_error_t +_ttt_client_execute_version (ttt_client_t *client, + char **args, + int num_args) +{ + char *response; + + assert (num_args == 1); + + /* XXX: Argument is being ignored. + This is not completely implemented. */ + + xasprintf (&response, "VERSION 1\r\n"); + ttt_client_send (client, response); + + free (response); + return TTT_ERROR_NONE; +} + +static ttt_error_t +_ttt_client_execute_quit (ttt_client_t *client, + char **args, + int num_args) +{ + char *notice; + assert (num_args == 0); + if (!client->registered) + return TTT_ERROR_QUIT_REQUESTED; + + xasprintf (¬ice, "NOTICE QUIT %s\r\n", + client->name); + ttt_server_broadcast (client->server, notice); + + free (notice); + return TTT_ERROR_QUIT_REQUESTED; } @@ -149,7 +300,7 @@ _ttt_client_init (ttt_client_t *client, client->socket = socket; file = xfdopen (socket, "r"); - yylex_init(&client->scanner); + yylex_init (&client->scanner); yyset_in (file, client->scanner); client->request_strings = NULL; @@ -212,25 +363,28 @@ static ttt_status_t _read_request (ttt_client_t *client) { ttt_token_t token; + ttt_token_type_t token_type; _free_request (client); while (1) { - token = yylex (client->scanner); + token_type = yylex (client->scanner, &token); /* Yes, EOF in two different enums is pretty ugly. */ - if (token == TTT_TOKEN_EOF) + if (token_type == TTT_TOKEN_TYPE_EOF) return TTT_STATUS_EOF; - if (token == TTT_TOKEN_NEWLINE) { + if (token_type == TTT_TOKEN_TYPE_NEWLINE) { if (client->num_request_strings) return TTT_STATUS_SUCCESS; else continue; } - assert (token == TTT_TOKEN_STRING); - - _append_to_request (client, yyget_text (client->scanner)); + assert (token_type == TTT_TOKEN_TYPE_STRING); + + _append_to_request (client, token.u.string); + + free (token.u.string); } } @@ -243,17 +397,19 @@ _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 (num_args != desc->args_required) + 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); } + } return TTT_ERROR_COMMAND; } @@ -301,7 +457,7 @@ ttt_client_new (void *closure, int client_socket) err = pthread_create (&client->thread, NULL, _handle_requests_thread, client); if (err != 0) { - fprintf (stderr, "Error: pthread_create failed: %s. Aborting.\n", + fprintf (stderr, "Error: pthread_create failed: %s. Aborting.\r\n", strerror (err)); exit (1); }