/********************************************************************/ /* */ /* L I QQ U U I DD W W A RR 555 */ /* L I Q Q U U I D D W W A A R R 5 */ /* L I Q Q U U I D D W W W AAA RR 55 */ /* L I Q Q U U I D D WW WW A A R R 5 */ /* LLL I Q Q U I DD W W A A R R 55 */ /* */ /* b */ /* bb y y */ /* b b yyy */ /* bb y */ /* yy */ /* */ /* U U FFF O O TTT */ /* U U F O O O O T */ /* U U TIRET FF O O O O T */ /* U U F O O O O T */ /* U F O O T */ /* */ /********************************************************************/ /*****************************************************************************/ /* Liquid War is a multiplayer wargame */ /* Copyright (C) 1998-2007 Christian Mauduit */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation; either version 2 of the License, or */ /* (at your option) any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* */ /* Liquid War homepage : http://www.ufoot.org/liquidwar/v5 */ /* Contact author : ufoot@ufoot.org */ /*****************************************************************************/ /********************************************************************/ /* name : netmess.c */ /* content : functions to create and parse network messages */ /* last update : April 14th 2001 */ /********************************************************************/ /*==================================================================*/ /* includes */ /*==================================================================*/ #include #include #include #include "netmess.h" /*==================================================================*/ /* static functions declaration */ /*==================================================================*/ static int eat_space (char **message); static int read_separator (char **message); static char *read_word (char **message); static int get_code (char **message); static int get_argc (char *message); static char **get_argv (char *message, int argc); /*==================================================================*/ /* functions */ /*==================================================================*/ /*------------------------------------------------------------------*/ /* * strcasecmp does not exists on win32, so we make it default to strcmp. * It's not a big problem since case insensitive tests are usefull in * developpement and network stuff is easier to test under UNIX anyway... */ #ifndef strcasecmp #define strcasecmp(A,B) strcmp((A),(B)) #endif /*------------------------------------------------------------------*/ /* * Skip spaces, tabs and the likes. */ static int eat_space (char **message) { int result = 0; while (isspace (**message)) { result++; ++(*message); } return result; } /*------------------------------------------------------------------*/ /* * Reads a separator token. The separator is ',' */ static int read_separator (char **message) { int result = 0; eat_space (message); if ((**message) == ',') { result++; ++(*message); } eat_space (message); return result; } /*------------------------------------------------------------------*/ /* * Reads a word, a number, basically what's between separators. * The returned pointer must be disallocated with a "free". */ static char * read_word (char **message) { char *begin = NULL; char *result = NULL; int len; char quote_char; eat_space (message); quote_char = **message; if (quote_char == '\"' || quote_char == '\'') { /* * We handle strings quoted with ' or ". * No escape character is handled yet... */ ++(*message); begin = *message; while ((**message) != quote_char && (**message) != '\0') { ++(*message); } len = (*message) - begin; ++(*message); } else { begin = *message; while ((!isspace (**message)) && (**message) != ',' && (**message) != '\0') { ++(*message); } len = (*message) - begin; } if (len >= 0) { result = (char *) malloc (len + 1); if (result != NULL) { if (len > 0) { strncpy (result, begin, len); } result[len] = 0; lw_netmess_cleanup_arg (result); } } eat_space (message); return result; } /*------------------------------------------------------------------*/ /* * Returns the code of the command. */ static int get_code (char **message) { char *command; int result = LW_NETMESS_CODE_UNKNOWN; if (read_separator (message) == 0) { if ((command = read_word (message)) != NULL) { if (strcasecmp (command, LW_NETMESS_TEXT_OK) == 0) { result = LW_NETMESS_CODE_OK; } else if (strcasecmp (command, LW_NETMESS_TEXT_ERR) == 0) { result = LW_NETMESS_CODE_ERR; } else if (strcasecmp (command, LW_NETMESS_TEXT_PING) == 0) { result = LW_NETMESS_CODE_PING; } else if (strcasecmp (command, LW_NETMESS_TEXT_ECHO) == 0) { result = LW_NETMESS_CODE_ECHO; } else if (strcasecmp (command, LW_NETMESS_TEXT_PROGRAM) == 0) { result = LW_NETMESS_CODE_PROGRAM; } else if (strcasecmp (command, LW_NETMESS_TEXT_VERSION) == 0) { result = LW_NETMESS_CODE_VERSION; } else if (strcasecmp (command, LW_NETMESS_TEXT_PASSWORD) == 0) { result = LW_NETMESS_CODE_PASSWORD; } else if (strcasecmp (command, LW_NETMESS_TEXT_FREE) == 0) { result = LW_NETMESS_CODE_FREE; } else if (strcasecmp (command, LW_NETMESS_TEXT_TEAM) == 0) { result = LW_NETMESS_CODE_TEAM; } else if (strcasecmp (command, LW_NETMESS_TEXT_READY) == 0) { result = LW_NETMESS_CODE_READY; } else if (strcasecmp (command, LW_NETMESS_TEXT_START) == 0) { result = LW_NETMESS_CODE_START; } else if (strcasecmp (command, LW_NETMESS_TEXT_WAITING) == 0) { result = LW_NETMESS_CODE_WAITING; } else if (strcasecmp (command, LW_NETMESS_TEXT_NEXT) == 0) { result = LW_NETMESS_CODE_NEXT; } else if (strcasecmp (command, LW_NETMESS_TEXT_TEAMSTARTINFO) == 0) { result = LW_NETMESS_CODE_TEAMSTARTINFO; } else if (strcasecmp (command, LW_NETMESS_TEXT_WHO) == 0) { result = LW_NETMESS_CODE_WHO; } else if (strcasecmp (command, LW_NETMESS_TEXT_LOCAL) == 0) { result = LW_NETMESS_CODE_LOCAL; } else if (strcasecmp (command, LW_NETMESS_TEXT_NETWORK) == 0) { result = LW_NETMESS_CODE_NETWORK; } else if (strcasecmp (command, LW_NETMESS_TEXT_NOBODY) == 0) { result = LW_NETMESS_CODE_NOBODY; } else if (strcasecmp (command, LW_NETMESS_TEXT_SENDMAP) == 0) { result = LW_NETMESS_CODE_SENDMAP; } else if (strcasecmp (command, LW_NETMESS_TEXT_RECVMAP) == 0) { result = LW_NETMESS_CODE_RECVMAP; } else if (strcasecmp (command, LW_NETMESS_TEXT_SENDCONFIG) == 0) { result = LW_NETMESS_CODE_SENDCONFIG; } else if (strcasecmp (command, LW_NETMESS_TEXT_RECVCONFIG) == 0) { result = LW_NETMESS_CODE_RECVCONFIG; } else if (strcasecmp (command, LW_NETMESS_TEXT_CHATTALK) == 0) { result = LW_NETMESS_CODE_CHATTALK; } else if (strcasecmp (command, LW_NETMESS_TEXT_CHATLISTEN) == 0) { result = LW_NETMESS_CODE_CHATLISTEN; } else if (strcasecmp (command, LW_NETMESS_TEXT_QUIT) == 0) { result = LW_NETMESS_CODE_QUIT; } free (command); } } return result; } /*------------------------------------------------------------------*/ /* * Gets the number of arguments in the message. * It's a little different from the C convention used in main(), * since 0 means "0 args" (whereas in main() 0 args corresponds to argc==1). */ static int get_argc (char *message) { int result = 0; char *word = NULL; int null_arg = 0; if (read_separator (&message) == 0) { if (message[0] == '\0') { null_arg = 1; } word = read_word (&message); if (word != NULL) { free (word); result = 1; while (read_separator (&message) == 1) { word = read_word (&message); if (word != NULL) { free (word); result++; } } } } if (result == 1 && null_arg) { result = 0; } return result; } /*------------------------------------------------------------------*/ /* * Parses all the arguments of the message. */ static char ** get_argv (char *message, int argc) { char **result = NULL; char *word; int i = 0; if (argc > 0 && read_separator (&message) == 0) { result = (char **) malloc (argc * sizeof (char *)); if (result != NULL) { word = read_word (&message); if (word != NULL) { result[i] = word; i = 1; while (i < argc && read_separator (&message) == 1) { word = read_word (&message); if (word != NULL) { result[i] = word; i++; } } } } if (i != argc) { free (result); result = NULL; } } return result; } /*------------------------------------------------------------------*/ /* * Parses and interprets a network message. * The syntaxe must be "COMMAND arg1,arg2,arg3,...". * This function should never fail, unless there are some problems * in memory allocation. Parsing an invalid message should simply * return a LW_NETMESS_CODE_UNKNOWN type with no args at all. * It's important to free the obtained pointer with lw_netmess_free. */ LW_NETMESS * lw_netmess_read (char *message) { LW_NETMESS *result = NULL; result = (LW_NETMESS *) malloc (sizeof (LW_NETMESS)); if (result != NULL) { result->code = get_code (&message); result->argc = get_argc (message); result->argv = get_argv (message, result->argc); } return result; } /*------------------------------------------------------------------*/ /* * To be called after each successfull call to lw_netmess_read * This function calls free() internally. */ void lw_netmess_free (LW_NETMESS * ptr) { int i; if (ptr != NULL) { if (ptr->argv != NULL) { for (i = 0; i < ptr->argc; ++i) { free (ptr->argv[i]); } if (ptr->argc > 0) { free (ptr->argv); } } } } /*------------------------------------------------------------------*/ /* * Cleanups strings so that they can be used as arguments in * network messages */ void lw_netmess_cleanup_arg (char *string) { char *pos; unsigned char c; pos = string; while ((c = *pos) != '\0') { if (c < 32 || c > 127 || strchr ("'\",", c)) { (*pos) = ' '; } pos++; } }