/********************************************************************/ /* */ /* 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 : wwwcli.c */ /* content : client code used to contact www.ufoot.org */ /********************************************************************/ /*==================================================================*/ /* includes */ /*==================================================================*/ #include #include #include #include "wwwcli.h" #include "startup.h" #include "log.h" #include "httputil.h" #include "init.h" #include "server.h" #include "macro.h" #include "ping.h" /*==================================================================*/ /* constants */ /*==================================================================*/ #define LW_WWWCLI_HTTP_CONTENT_SIZE 100000 #define LW_WWWCLI_HTTP_URL_SIZE (URL_SIZE + 200) #define LW_WWWCLI_URL_LIST "list.php3" #define LW_WWWCLI_TEMP_BUFFER_SIZE 100 /* * If a line is shorter than this, we consider it as an empty * line, this avoids errors with useless stupid malformatted lines */ #define LW_WWWCLI_MIN_LINE_SIZE 20 /*==================================================================*/ /* static functions */ /*==================================================================*/ static int parse_line (LW_WWWCLI_DATA * result, char **str); static void skip_space (char **str); static int read_string (char *result, char **str, int size); static int read_int (int *result, char **str); static int read_separator (char **str); static int read_eol (char **str); static int read_eol_n (char **str, int n); static void next_line (char **str); /*==================================================================*/ /* global functions */ /*==================================================================*/ /*------------------------------------------------------------------*/ /* * Main function of the server thread which tries to connect * on the metaserver */ int lw_wwwcli_get_games (LW_WWWCLI_DATA * buffer, int *size) { int result = 0; char buffer_content[LW_WWWCLI_HTTP_CONTENT_SIZE]; char buffer_url[LW_WWWCLI_HTTP_URL_SIZE]; char *pos, *pos_tmp; int i = 0; log_print_str ("Getting server list from \""); log_print_str (STARTUP_METASERVER); log_print_str ("\" "); memset (&buffer_url, 0, sizeof (buffer_url)); LW_MACRO_SPRINTF3 (buffer_url, "%s%s?protocol=%s&game=" LW_PROGRAM "&version=" LW_VERSION, STARTUP_METASERVER, LW_WWWCLI_URL_LIST, LW_SERVER_METASERVER_PROTOCOL); if (lw_httputil_get_page (buffer_content, buffer_url, sizeof (buffer_content))) { pos = buffer_content; while (read_eol (&pos)); while (i < (*size) && strlen (pos) > 0) { pos_tmp = pos; if (read_eol_n (&pos_tmp, LW_WWWCLI_MIN_LINE_SIZE)) { /* * This was just an empty or too short line... */ pos = pos_tmp; } else { /* * We parse the line only if there's something on it... */ if (parse_line (buffer + i, &pos)) { if (buffer[i].busy_players < buffer[i].max_players) { /* * we increment i only if there's some room left on the * server. If it's full of people playing on it, it's * of absolutely no use to present it as a working * server... And not incrementing i will make it be * overwritten by the next server. */ ++i; log_print_str ("+"); } else { log_print_str ("="); } } else { log_print_str ("-"); } } } result = 1; } if (result == 1) { (*size) = i; } else { (*size) = 0; } display_success (result); if (!result) { log_print_str ("Error: "); log_println_str (buffer_content); } return result; } /*------------------------------------------------------------------*/ /* * Parses a line returned by the meta-server */ static int parse_line (LW_WWWCLI_DATA * result, char **str) { int ok = 0; char temp[LW_WWWCLI_TEMP_BUFFER_SIZE]; memset (result, 0, sizeof (LW_WWWCLI_DATA)); if (read_string (result->ip, str, sizeof (result->ip)) && read_separator (str) && read_int (&(result->port), str) && read_separator (str) && read_string (temp, str, sizeof (temp)) && read_separator (str) && read_string (temp, str, sizeof (temp)) && read_separator (str) && read_int (&(result->uptime), str) && read_separator (str) && read_int (&(result->busy_players), str) && read_separator (str) && read_int (&(result->max_players), str) && read_separator (str) && read_int (&(result->password), str) && read_separator (str) && read_string (result->comment, str, sizeof (result->comment))) { LW_MACRO_SPRINTF3 (result->label, "%s (%d/%d)", result->ip, result->busy_players, result->max_players); result->ping_delay = lw_ping_try_server (result->ip, result->port); ok = 1; } next_line (str); return ok; } /*------------------------------------------------------------------*/ /* * Skip spaces, tabs and the likes. */ static void skip_space (char **str) { while ((isspace (**str) || (**str) == '\r') && (**str) != '\n') { ++(*str); } } /*------------------------------------------------------------------*/ /* * Reads a string between ''. Double '' are interpreted as a single ' * which is the standard SQL convention */ static int read_string (char *result, char **str, int size) { int i = 0; int done = 0; char c; skip_space (str); c = **str; if (c == '\'') { ++(*str); while ((c = **str) != '\0' && c != '\n' && !done) { ++(*str); if (c == '\'') { if ((**str) == '\'') { ++(*str); if (i < size - 1) { result[i++] = '\''; } } else { done = 1; } } else { if (i < size - 1) { result[i++] = c; } } } } result[i++] = '\0'; skip_space (str); return done; } /*------------------------------------------------------------------*/ /* * Reads an integer */ static int read_int (int *result, char **str) { int ok = 0; char c; (*result) = 0; skip_space (str); while (isdigit (c = (**str))) { ++(*str); (*result) *= 10; (*result) += c - '0'; ok = 1; } skip_space (str); return ok; } /*------------------------------------------------------------------*/ /* * Reads a field separator */ static int read_separator (char **str) { int ok = 0; char c; skip_space (str); c = **str; if (c == ',') { ++(*str); ok = 1; } skip_space (str); return ok; } /*------------------------------------------------------------------*/ /* * Reads a new-line character */ static int read_eol (char **str) { int ok = 0; char c; skip_space (str); c = **str; if (c == '\n') { ++(*str); ok = 1; } skip_space (str); return ok; } /*------------------------------------------------------------------*/ /* * Reads "less than n chars", then a "\n" */ static int read_eol_n (char **str, int n) { int ok = 0; char *tmp; skip_space (str); tmp = strchr (*str, '\n'); if (tmp != NULL) { if (tmp - (*str) < n) { ok = 1; (*str) = tmp + 1; skip_space (str); } } return ok; } /*------------------------------------------------------------------*/ /* * Reads an end of line */ static void next_line (char **str) { char *pos; int len; skip_space (str); pos = strchr (*str, '\n'); if (pos) { (*str) = pos + 1; } else { /* * No '\n' found, we decided to go to the * very end of the string */ len = strlen (*str); (*str) += len; } skip_space (str); }