Crossfire Client, Branch
R11627
|
00001 const char * const rcsid_common_metaserver_c = 00002 "$Id: metaserver.c 11626 2009-04-04 12:41:46Z lalo $"; 00003 /* 00004 Crossfire client, a client program for the crossfire program. 00005 00006 Copyright (C) 2001 Mark Wedel & Crossfire Development Team 00007 00008 This program is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 2 of the License, or 00011 (at your option) any later version. 00012 00013 This program is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with this program; if not, write to the Free Software 00020 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00021 00022 The author can be reached via e-mail to crossfire-devel@real-time.com 00023 */ 00024 00025 /* This file deals with contact the metaserver, getting a list of hosts, 00026 * displaying/returning them to calling function, and then connecting 00027 * to the server when requested. 00028 */ 00029 00030 #ifndef WIN32 00031 #include <netdb.h> 00032 #include <sys/types.h> 00033 #include <sys/socket.h> 00034 #include <netinet/in.h> 00035 #include <arpa/inet.h> 00036 #endif /* WIN32 */ 00037 00038 #include <ctype.h> 00039 #include <stdio.h> 00040 #include <stdlib.h> 00041 00042 #include <client.h> 00043 #include <cconfig.h> 00044 #include <external.h> 00045 00046 #include <metaserver.h> 00047 00048 #ifdef HAVE_CURL_CURL_H 00049 #include <curl/curl.h> 00050 #include <curl/types.h> 00051 #include <curl/easy.h> 00052 #endif 00053 00054 00055 Meta_Info *meta_servers = NULL; 00056 00057 int meta_numservers = 0; 00058 00059 /* This checks the servers sc_version and cs_version to see 00060 * if they are compatible. 00061 * @parm entry 00062 * entry number in the metaservers array to check. 00063 * @return 00064 * 1 if this entry is compatible, 0 if it is not. Note that this can 00065 * only meaningfully check metaserver2 data - metaserver1 doesn't 00066 * include protocol version number, so treats all of those as 00067 * OK. 00068 */ 00069 int check_server_version(int entry) 00070 { 00071 00072 /* No version information - nothing to do. */ 00073 if (!meta_servers[entry].sc_version || !meta_servers[entry].cs_version) 00074 return 1; 00075 00076 if (meta_servers[entry].sc_version != VERSION_SC) { 00077 /* 1027->1028 removed a bunch of old commands, so a 1028 00078 * version client can still play on a 1027 server, so 00079 * special hard code that. 00080 * 00081 * Likewise, 1028->1029 just changed how weapon_speed 00082 * should be interperted on the client - the client 00083 * does the right thing, so not problem with a 1029 00084 * client playing on 1028 or 1027 server. 00085 * 00086 * A 1028 client could in practice play on a 1029 00087 * server, since at the protocol level, data is the same - 00088 * the client would just have screwed up weapon_sp values. 00089 */ 00090 if ((VERSION_SC == 1028 || VERSION_SC==1029) && 00091 (meta_servers[entry].sc_version==1027 || 00092 meta_servers[entry].sc_version==1028)) 00093 return 1; 00094 } 00095 if (meta_servers[entry].cs_version != VERSION_CS) return 0; 00096 00097 return 1; 00098 } 00099 00100 /***************************************************************************** 00101 * Start of cache related functions. 00102 *****************************************************************************/ 00103 int cached_servers_num = 0; 00104 char *cached_servers_name[CACHED_SERVERS_MAX]; 00105 char *cached_servers_ip[CACHED_SERVERS_MAX]; 00106 static int cached_servers_loaded = 0; 00107 const char *cached_server_file = NULL; 00108 00109 00110 static void metaserver_load_cache(void) { 00111 FILE *cache; 00112 char buf[ MS_LARGE_BUF ]; 00113 int name; 00114 00115 if (cached_servers_loaded || !cached_server_file) 00116 return; 00117 00118 /* If failure, we don't want to load again */ 00119 cached_servers_loaded = 1; 00120 cached_servers_num = 0; 00121 00122 cache = fopen(cached_server_file, "r"); 00123 if (!cache) 00124 return; 00125 00126 name = 0; 00127 while (fgets(buf, MS_LARGE_BUF, cache) != NULL && cached_servers_num < CACHED_SERVERS_MAX) { 00128 buf[strlen(buf)-1] = 0; 00129 if (!name) { 00130 name = 1; 00131 cached_servers_name[cached_servers_num] = strdup(buf); 00132 } else { 00133 name = 0; 00134 cached_servers_ip[cached_servers_num++] = strdup(buf); 00135 } 00136 } 00137 fclose(cache); 00138 if (name) { 00139 /* Missing IP? */ 00140 cached_servers_num--; 00141 } 00142 } 00143 00144 static void metaserver_save_cache(void) { 00145 FILE *cache; 00146 int server; 00147 00148 if (!cached_server_file) 00149 return; 00150 00151 cache = fopen(cached_server_file, "w"); 00152 if (!cache) 00153 return; 00154 00155 for (server = 0; server < cached_servers_num; server++) { 00156 fprintf(cache, "%s\n", cached_servers_name[server]); 00157 fprintf(cache, "%s\n", cached_servers_ip[server]); 00158 } 00159 fclose(cache); 00160 } 00161 00162 /***************************************************************************** 00163 * End of cache related functions. 00164 *****************************************************************************/ 00165 00166 /****************************************************************************** 00167 * Metaserver2 support starts here. 00168 * 00169 ******************************************************************************/ 00170 00171 pthread_mutex_t ms2_info_mutex; 00172 00173 /* we use threads so that the GUI keeps responding while we wait for 00174 * data. But we need to note if the thread is running or not, 00175 * so we store it here. This, like the other metaserver2 data, 00176 * should be protected by using the ms2_info_mutext. 00177 */ 00178 static int ms2_is_running=0; 00179 00180 /* list of metaserver URL to get information from - this should generally 00181 * correspond to the value in the metaserver2 server file, but instead 00182 * of meta_update.php, use meta_client.php. 00183 * 00184 * These could perhaps be in some other file (config.h or the like), but 00185 * it seems unlikely that these will change very often, and certainly not 00186 * at a level where we would expect users to go about changing the values. 00187 */ 00188 static char *metaservers[] = {"http://crossfire.real-time.com/metaserver2/meta_client.php"}; 00189 00214 size_t metaserver2_writer(void *ptr, size_t size, size_t nmemb, void *data) 00215 { 00216 #ifdef HAVE_CURL_CURL_H 00217 size_t realsize = size * nmemb; 00218 char *cp, *newline, *eq, inbuf[CURL_MAX_WRITE_SIZE*2+1], *leftover; 00219 00220 leftover = (char*) data; 00221 00222 if (realsize > CURL_MAX_WRITE_SIZE) { 00223 LOG(LOG_CRITICAL, "common::metaserver2_writer", "Function called with more data than allowed!"); 00224 } 00225 00226 /* This memcpy here is to just give us a null terminated character 00227 * array - easier to do with than having to check lengths as well as other 00228 * values. Also, it makes it easier to deal with unprocessed data from 00229 * the last call. 00230 */ 00231 memcpy(inbuf, leftover, strlen(leftover)); 00232 memcpy(inbuf+strlen(leftover), ptr, realsize); 00233 inbuf[realsize] = 0; 00234 00235 /* Processing this block of data shouldn't take very long, even on 00236 * slow machines, so putting the lock here, instead of each time 00237 * we update a variable is cleaner 00238 */ 00239 pthread_mutex_lock(&ms2_info_mutex); 00240 00241 for (cp = inbuf; cp != NULL && *cp!=0; cp=newline) { 00242 newline=strchr(cp, '\n'); 00243 if (newline) { 00244 *newline = 0; 00245 newline++; 00246 } else { 00247 /* If we didn't get a newline, then this is the 00248 * end of the block of data for this call - store 00249 * away the extra for the next call. 00250 */ 00251 strncpy(leftover, cp, CURL_MAX_WRITE_SIZE-1); 00252 leftover[CURL_MAX_WRITE_SIZE-1] = 0; 00253 break; 00254 } 00255 00256 eq = strchr(cp,'='); 00257 if (eq) { 00258 *eq = 0; 00259 eq++; 00260 } 00261 00262 if (!strcmp(cp, "START_SERVER_DATA")) { 00263 /* Clear out all data - MS2 doesn't necessarily use all the 00264 * fields, so blank out any that we are not using. 00265 */ 00266 memset(&meta_servers[meta_numservers], 0, sizeof(Meta_Info)); 00267 } 00268 else if (!strcmp(cp, "END_SERVER_DATA")) { 00269 int i; 00270 00271 /* we can get data from both metaserver1 & 2 - no reason to keep 00272 * both. So check for duplicates, and consider metaserver2 00273 * data 'better'. 00274 */ 00275 for (i=0; i<meta_numservers; i++) { 00276 if (!strcasecmp(meta_servers[i].hostname, meta_servers[meta_numservers].hostname)) { 00277 memcpy(&meta_servers[i], &meta_servers[meta_numservers], sizeof(Meta_Info)); 00278 break; 00279 } 00280 } 00281 if (i>=meta_numservers) { 00282 meta_numservers++; 00283 } 00284 } else { 00285 /* If we get here, these should be variable=value pairs. 00286 * if we don't have a value, can't do anything, and 00287 * report an error. This would normally be incorrect 00288 * data from the server. 00289 */ 00290 if (!eq) { 00291 LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s",cp); 00292 continue; 00293 } 00294 if (!strcmp(cp,"hostname")) { 00295 strncpy(meta_servers[meta_numservers].hostname, eq, sizeof(meta_servers[meta_numservers].hostname)); 00296 } 00297 else if (!strcmp(cp,"port")) { 00298 meta_servers[meta_numservers].port = atoi(eq); 00299 } 00300 else if (!strcmp(cp,"html_comment")) { 00301 strncpy(meta_servers[meta_numservers].html_comment, eq, sizeof(meta_servers[meta_numservers].html_comment)); 00302 } 00303 else if (!strcmp(cp,"text_comment")) { 00304 strncpy(meta_servers[meta_numservers].text_comment, eq, sizeof(meta_servers[meta_numservers].text_comment)); 00305 } 00306 else if (!strcmp(cp,"archbase")) { 00307 strncpy(meta_servers[meta_numservers].archbase, eq, sizeof(meta_servers[meta_numservers].archbase)); 00308 } 00309 else if (!strcmp(cp,"mapbase")) { 00310 strncpy(meta_servers[meta_numservers].mapbase, eq, sizeof(meta_servers[meta_numservers].mapbase)); 00311 } 00312 else if (!strcmp(cp,"codebase")) { 00313 strncpy(meta_servers[meta_numservers].codebase, eq, sizeof(meta_servers[meta_numservers].codebase)); 00314 } 00315 else if (!strcmp(cp,"flags")) { 00316 strncpy(meta_servers[meta_numservers].flags, eq, sizeof(meta_servers[meta_numservers].flags)); 00317 } 00318 else if (!strcmp(cp,"version")) { 00319 strncpy(meta_servers[meta_numservers].version, eq, sizeof(meta_servers[meta_numservers].version)); 00320 } 00321 else if (!strcmp(cp,"num_players")) { 00322 meta_servers[meta_numservers].num_players = atoi(eq); 00323 } 00324 else if (!strcmp(cp,"in_bytes")) { 00325 meta_servers[meta_numservers].in_bytes = atoi(eq); 00326 } 00327 else if (!strcmp(cp,"out_bytes")) { 00328 meta_servers[meta_numservers].out_bytes = atoi(eq); 00329 } 00330 else if (!strcmp(cp,"uptime")) { 00331 meta_servers[meta_numservers].uptime = atoi(eq); 00332 } 00333 else if (!strcmp(cp,"sc_version")) { 00334 meta_servers[meta_numservers].sc_version = atoi(eq); 00335 } 00336 else if (!strcmp(cp,"cs_version")) { 00337 meta_servers[meta_numservers].cs_version = atoi(eq); 00338 } 00339 else if (!strcmp(cp,"last_update")) { 00340 /* MS2 reports update time as when it last got an update, 00341 * where as we want actual elapsed time since last update. 00342 * So do the conversion. Second check is because of clock 00343 * skew - my clock may be fast, and we don't want negative times. 00344 */ 00345 meta_servers[meta_numservers].idle_time = time(NULL) - atoi(eq); 00346 if (meta_servers[meta_numservers].idle_time < 0) 00347 meta_servers[meta_numservers].idle_time = 0; 00348 } 00349 else { 00350 LOG(LOG_ERROR, "common::metaserver2_writer", "Unknown line: %s=%s",cp,eq); 00351 } 00352 } 00353 } 00354 pthread_mutex_unlock(&ms2_info_mutex); 00355 return realsize; 00356 #else 00357 return 0; 00358 #endif 00359 } 00360 00369 static int get_metaserver2_data(char *metaserver2) { 00370 #ifdef HAVE_CURL_CURL_H 00371 CURL *curl; 00372 CURLcode res; 00373 char leftover[CURL_MAX_WRITE_SIZE]; 00374 00375 curl = curl_easy_init(); 00376 if (!curl) return 0; 00377 leftover[0] =0; 00378 curl_easy_setopt(curl, CURLOPT_URL, metaserver2); 00379 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, metaserver2_writer); 00380 curl_easy_setopt(curl, CURLOPT_WRITEDATA, leftover); 00381 res = curl_easy_perform(curl); 00382 curl_easy_cleanup(curl); 00383 00384 if (res) return 0; 00385 else return 1; 00386 #else 00387 return 1; 00388 #endif 00389 } 00390 00397 void *metaserver2_thread(void *junk) 00398 { 00399 int metaserver_choice; 00400 00401 do { 00402 metaserver_choice = random() % (sizeof(metaservers) / sizeof(char*)); 00403 } while (!get_metaserver2_data(metaservers[metaserver_choice])); 00404 00405 pthread_mutex_lock(&ms2_info_mutex); 00406 qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort); 00407 ms2_is_running=0; 00408 pthread_mutex_unlock(&ms2_info_mutex); 00409 pthread_exit(NULL); 00410 // never reached, just to make the compiler happy. 00411 return NULL; 00412 } 00413 00414 00423 int metaserver2_get_info(void) { 00424 pthread_t thread_id; 00425 int ret; 00426 00427 if (!metaserver2_on) { 00428 return 0; 00429 } 00430 #ifndef HAVE_CURL_CURL_H 00431 return 0; 00432 #endif 00433 00434 metaserver_load_cache(); 00435 00436 pthread_mutex_lock(&ms2_info_mutex); 00437 if (!meta_servers) 00438 meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info)); 00439 00440 ms2_is_running=1; 00441 pthread_mutex_unlock(&ms2_info_mutex); 00442 00443 ret=pthread_create(&thread_id, NULL, metaserver2_thread, NULL); 00444 if (ret) { 00445 LOG(LOG_ERROR, "common::metaserver2_get_info", "Thread creation failed."); 00446 pthread_mutex_lock(&ms2_info_mutex); 00447 ms2_is_running=0; 00448 pthread_mutex_unlock(&ms2_info_mutex); 00449 } 00450 00451 return 0; 00452 } 00453 00454 00455 00459 void init_metaserver(void) 00460 { 00461 pthread_mutex_init(&ms2_info_mutex, NULL); 00462 #ifdef HAVE_CURL_CURL_H 00463 curl_global_init(CURL_GLOBAL_ALL); 00464 #endif 00465 } 00466 00467 /****************************************************************************** 00468 * End of Metasever2 functions. 00469 ******************************************************************************/ 00470 00471 /****************************************************************************** 00472 * Start of metaserver1 logic 00473 * 00474 * Note that this shares the same mutex as metaserver2, since it is updating 00475 * most of the same structures. 00476 *******************************************************************************/ 00477 00478 static int ms1_is_running=0; 00479 00480 00481 #ifdef WIN32 00482 /* Need script.h for script_killall */ 00483 #include <script.h> 00484 00485 /* This gets input from a socket, and returns it one line at a time. 00486 */ 00487 /* This is a Windows-specific function, since you can't use fgets under Win32 */ 00488 char *get_line_from_sock(char *s, size_t n, int fd) { 00489 static long charsleft = 0; 00490 static char inbuf[MS_LARGE_BUF*4]; 00491 char *cp; 00492 int ct; 00493 00494 if (!s) 00495 return s; 00496 if (n != MS_LARGE_BUF*4-1) { 00497 LOG(LOG_CRITICAL, "common::get_line_from_sock", "Serious program logic error in get_line_from_sock()."); 00498 exit(-1); 00499 } 00500 00501 if (charsleft > MS_LARGE_BUF*4-3 && strchr(inbuf, '\n') == NULL) { 00502 draw_info("Metaserver returned an overly long line.", NDI_BLACK); 00503 return NULL; 00504 } 00505 00506 /* If there is no line in the buffer */ 00507 while (charsleft == 0 || (cp = strchr(inbuf, '\n')) == NULL) { 00508 FD_SET fdset; 00509 TIMEVAL tv = {3, 0}; /* 3 second timeout on reads */ 00510 int nlen; 00511 FD_ZERO(&fdset); 00512 FD_SET(fd, &fdset); 00513 if (select(0, &fdset, NULL, NULL, &tv) == 0) { 00514 draw_info("Metaserver timed out.", NDI_BLACK); 00515 return NULL; 00516 } 00517 00518 nlen = recv(fd, inbuf+charsleft-1, MS_LARGE_BUF*4-1-charsleft, 0); 00519 if (nlen == SOCKET_ERROR || nlen <= 0) /* Probably EOF */ 00520 return NULL; 00521 00522 charsleft += nlen; 00523 } 00524 00525 /* OK, inbuf contains a null terminated string with at least one \n 00526 * Copy the string up to the \n to s, and then move the rest of the 00527 * inbuf string to the beginning of the buffer. And finally, set 00528 * charsleft to the number of characters left in inbuf, or 0. 00529 * Oh, and cp contains the location of the \n. 00530 */ 00531 00532 memcpy(s, inbuf, cp-inbuf+1); /* Extract the line, including the \n. */ 00533 s[cp-inbuf+1] = 0; /* null terminate it */ 00534 00535 /* Copy cp to inbuf up to the \0, (skipping the \n) */ 00536 ct = 0; 00537 while (cp[++ct] != 0) { 00538 inbuf[ct-1] = cp[ct]; 00539 } 00540 inbuf[ct-1] = 0; 00541 charsleft = ct; /* And keep track of how many characters are left. */ 00542 00543 return s; 00544 } 00545 00546 #endif /* Win32 */ 00547 00548 void *metaserver1_thread(void *junk) 00549 { 00550 struct protoent *protox; 00551 int fd; 00552 struct sockaddr_in insock; 00553 #ifndef WIN32 00554 FILE *fp; 00555 #endif 00556 char inbuf[MS_LARGE_BUF*4]; 00557 Meta_Info *current; 00558 00559 protox = getprotobyname("tcp"); 00560 if (protox == NULL) { 00561 LOG(LOG_WARNING, "common::metaserver_get_info", "Error getting protobyname (tcp)"); 00562 pthread_mutex_lock(&ms2_info_mutex); 00563 ms1_is_running=0; 00564 pthread_mutex_unlock(&ms2_info_mutex); 00565 pthread_exit(NULL); 00566 } 00567 00568 fd = socket(PF_INET, SOCK_STREAM, protox->p_proto); 00569 if (fd == -1) { 00570 perror("get_metaserver_info: Error on socket command.\n"); 00571 pthread_mutex_lock(&ms2_info_mutex); 00572 ms1_is_running=0; 00573 pthread_mutex_unlock(&ms2_info_mutex); 00574 pthread_exit(NULL); 00575 } 00576 insock.sin_family = AF_INET; 00577 insock.sin_port = htons((unsigned short)meta_port); 00578 if (isdigit(*meta_server)) 00579 insock.sin_addr.s_addr = inet_addr(meta_server); 00580 else { 00581 struct hostent *hostbn = gethostbyname(meta_server); 00582 if (hostbn == NULL) { 00583 LOG(LOG_WARNING, "common::metaserver_get_info", "Unknown metaserver hostname: %s", meta_server); 00584 pthread_mutex_lock(&ms2_info_mutex); 00585 ms1_is_running=0; 00586 pthread_mutex_unlock(&ms2_info_mutex); 00587 pthread_exit(NULL); 00588 } 00589 memcpy(&insock.sin_addr, hostbn->h_addr, hostbn->h_length); 00590 } 00591 if (connect(fd, (struct sockaddr *)&insock, sizeof(insock)) == -1) { 00592 perror("Can't connect to metaserver"); 00593 draw_info("\nCan't connect to metaserver.", NDI_BLACK); 00594 pthread_mutex_lock(&ms2_info_mutex); 00595 ms1_is_running=0; 00596 pthread_mutex_unlock(&ms2_info_mutex); 00597 pthread_exit(NULL); 00598 } 00599 00600 #ifndef WIN32 /* Windows doesn't support this */ 00601 /* Turn this into a file handle - this will break it on newlines 00602 * for us, which makes our processing much easier - it basically 00603 * means one line/server 00604 */ 00605 if ((fp = fdopen(fd, "r")) == NULL) { 00606 perror("fdopen failed."); 00607 pthread_mutex_lock(&ms2_info_mutex); 00608 ms1_is_running=0; 00609 pthread_mutex_unlock(&ms2_info_mutex); 00610 pthread_exit(NULL); 00611 } 00612 #endif 00613 00614 pthread_mutex_lock(&ms2_info_mutex); 00615 if (!meta_servers) 00616 meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info)); 00617 00618 00619 /* The loop goes through and unpacks the data from the metaserver 00620 * into its individual components. We do a little extra work and 00621 * put the |'s back in the string after we are done with that section - 00622 * this is so if there is a corrupt entry, it gets displayed as 00623 * originally received from the server. 00624 */ 00625 #ifndef WIN32 /* Windows doesn't support this */ 00626 while (fgets(inbuf, MS_LARGE_BUF*4-1, fp) != NULL) { 00627 #else 00628 while (get_line_from_sock(inbuf, MS_LARGE_BUF*4-1, fd) != NULL) { 00629 #endif 00630 char *cp, *cp1; 00631 00632 cp = strchr(inbuf, '|'); 00633 if (cp == NULL) { 00634 LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf); 00635 break; 00636 } 00637 *cp = 0; 00638 00639 current = &meta_servers[meta_numservers]; 00640 00641 strncpy(current->ip_addr, inbuf, sizeof(current->ip_addr)-1); 00642 current->ip_addr[sizeof(current->ip_addr)-1] = '\0'; 00643 *cp++ = '|'; 00644 00645 current->idle_time = atoi(cp); 00646 00647 cp1 = strchr(cp, '|'); 00648 if (cp1 == NULL) { 00649 LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf); 00650 break; 00651 } 00652 *cp1 = 0; 00653 00654 cp = strchr(cp1+1, '|'); 00655 if (cp == NULL) { 00656 LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf); 00657 break; 00658 } 00659 *cp = 0; 00660 /* cp1 points at start of comment, cp points at end */ 00661 strncpy(current->hostname, cp1+1, sizeof(current->hostname)-1); 00662 current->hostname[sizeof(current->hostname)-1] = '\0'; 00663 00664 *cp1++ = '|'; 00665 *cp++ = '|'; /* cp now points to num players */ 00666 00667 current->num_players = atoi(cp); 00668 00669 cp1 = strchr(cp, '|'); 00670 if (cp1 == NULL) { 00671 LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf); 00672 break; 00673 } 00674 *cp1 = 0; 00675 00676 cp = strchr(cp1+1, '|'); 00677 if (cp == NULL) { 00678 LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf); 00679 break; 00680 } 00681 *cp = 0; 00682 /* cp1 is start of version, cp is end */ 00683 strncpy(current->version, cp1+1, sizeof(current->version)-1); 00684 current->version[sizeof(current->version)-1] = '\0'; 00685 00686 *cp1++ = '|'; 00687 *cp++ = '|'; /* cp now points to comment */ 00688 00689 cp1 = strchr(cp, '\n'); 00690 if (cp1 == NULL) { 00691 LOG(LOG_WARNING, "common::metaserver_get_info", "Corrupt line from server: %s", inbuf); 00692 break; 00693 } 00694 *cp1 = 0; 00695 /* There is extra info included, like the bytes to/from the server 00696 * that we dont' care about, so strip them off so they don't show up in 00697 * the comment. 00698 */ 00699 cp1 = strchr(cp, '|'); 00700 if (cp1 != NULL) 00701 *cp1 = 0; 00702 00703 strncpy(current->text_comment, cp, sizeof(current->text_comment)-1); 00704 current->text_comment[sizeof(current->text_comment)-1] = '\0'; 00705 00706 meta_numservers++; 00707 /* has to be 1 less than array size, since array starts counting 00708 * at 0. 00709 */ 00710 if (meta_numservers >= MAX_METASERVER-1) { 00711 LOG(LOG_WARNING, "common:metaserver_get_info", "Have reached maximum metaserver count\n"); 00712 break; 00713 } 00714 } 00715 #ifdef WIN32 00716 closesocket(fd); 00717 #else 00718 fclose(fp); 00719 #endif 00720 qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort); 00721 ms1_is_running=0; 00722 pthread_mutex_unlock(&ms2_info_mutex); 00723 pthread_exit(NULL); 00724 // never reached, just to make the compiler happy. 00725 return NULL; 00726 } 00727 00728 00729 int metaserver1_get_info(void) { 00730 pthread_t thread_id; 00731 int ret; 00732 00733 if (!metaserver_on) { 00734 return 0; 00735 } 00736 metaserver_load_cache(); 00737 00738 pthread_mutex_lock(&ms2_info_mutex); 00739 if (!meta_servers) 00740 meta_servers = calloc(MAX_METASERVER, sizeof(Meta_Info)); 00741 00742 ms1_is_running=1; 00743 pthread_mutex_unlock(&ms2_info_mutex); 00744 00745 ret=pthread_create(&thread_id, NULL, metaserver1_thread, NULL); 00746 if (ret) { 00747 LOG(LOG_ERROR, "common::metaserver1_get_info", "Thread creation failed."); 00748 pthread_mutex_lock(&ms2_info_mutex); 00749 ms1_is_running=0; 00750 pthread_mutex_unlock(&ms2_info_mutex); 00751 } 00752 00753 return 0; 00754 } 00755 /****************************************************************************** 00756 * End of metaserver1 logic 00757 ******************************************************************************/ 00758 00759 /****************************************************************************** 00760 * This is start of common logic - the above sections are actually getting 00761 * the data. The code below here is just displaying the data we got 00762 */ 00763 00774 int metaserver_check_status(void) { 00775 int status; 00776 00777 pthread_mutex_lock(&ms2_info_mutex); 00778 status = ms2_is_running | ms1_is_running; 00779 pthread_mutex_unlock(&ms2_info_mutex); 00780 00781 return status; 00782 } 00783 00784 /* This contacts the metaserver and gets the list of servers. returns 0 00785 * on success, 1 on failure. Errors will get dumped to stderr, 00786 * so most errors should be reasonably clear. 00787 * metaserver and meta_port are the server name and port number 00788 * to connect to. 00789 */ 00790 00791 int metaserver_get_info(char *metaserver, int meta_port) { 00792 00793 meta_numservers = 0; 00794 00795 metaserver2_get_info(); 00796 00797 if (metaserver_on) { 00798 metaserver1_get_info(); 00799 } 00800 00801 return 0; 00802 } 00803 00804 /* show the metaservers to the player. we use the draw_info to do 00805 * that, and also let the player know they can enter their own host name. 00806 */ 00807 void metaserver_show(int show_selection) { 00808 int i; 00809 char buf[256]; 00810 00811 if (cached_servers_num) { 00812 draw_info("\nLast servers you connected to:\n", NDI_BLACK); 00813 for (i = 0; i < cached_servers_num; i++) { 00814 snprintf(buf, sizeof(buf), "%2d) %-20.20s %-20.20s", i+1, cached_servers_name[i], cached_servers_ip[i]); 00815 draw_info(buf, NDI_BLACK); 00816 } 00817 draw_info(" ", NDI_BLACK); 00818 } 00819 00820 while(metaserver_check_status()) { 00821 usleep(100); 00822 } 00823 00824 draw_info(" #) Server # version idle", NDI_BLACK); 00825 draw_info(" Name players seconds", NDI_BLACK); 00826 pthread_mutex_lock(&ms2_info_mutex); 00827 00828 /* Re-sort the data - may get different data from ms1 and ms2, so 00829 * order of this is somewhat random. 00830 */ 00831 qsort(meta_servers, meta_numservers, sizeof(Meta_Info), (int (*)(const void *, const void *))meta_sort); 00832 for (i = 0; i < meta_numservers; i++) { 00833 if (check_server_version(i)) { 00834 snprintf(buf, sizeof(buf), "%2d) %-15.15s %2d %-12.12s %2d", 00835 i+1+cached_servers_num, meta_servers[i].hostname, 00836 meta_servers[i].num_players, meta_servers[i].version, 00837 meta_servers[i].idle_time); 00838 draw_info(buf, NDI_BLACK); 00839 } 00840 } 00841 if (show_selection) { 00842 /* Show default/current server */ 00843 if (server) { 00844 snprintf(buf, sizeof(buf), "%2d) %s (default)", meta_numservers+1+cached_servers_num, server); 00845 draw_info(buf, NDI_BLACK); 00846 } 00847 00848 draw_info("Choose one of the entries above", NDI_BLACK); 00849 draw_info("or type in a hostname/ip address", NDI_BLACK); 00850 draw_info("Hit enter to re-update this list", NDI_BLACK); 00851 draw_info("Enter 0 to exit the program.", NDI_BLACK); 00852 } 00853 pthread_mutex_unlock(&ms2_info_mutex); 00854 } 00855 00856 /* String contains the selection that the player made for the metaserver. 00857 * this may not be a a selection, but could be a host name or ip address. 00858 * this returns 0 on sucessful selection, 1 if failure (invalid selection 00859 * or the like. 00860 */ 00861 int metaserver_select(char *sel) { 00862 int num = atoi(sel); 00863 int port=0; 00864 char buf[MAX_BUF], buf2[MAX_BUF]; 00865 char *server_name = NULL, *server_ip; 00866 00867 /* User hit return */ 00868 if (sel[0] == 0) { 00869 metaserver_get_info(meta_server, meta_port); 00870 metaserver_show(TRUE); 00871 return 1; 00872 } 00873 00874 /* Special case - player really entered a 0, so exit the 00875 * program. 00876 */ 00877 if (num == 0 && sel[0] == '0') { 00878 #ifdef WIN32 00879 script_killall(); 00880 #endif 00881 exit(0); 00882 } 00883 00884 pthread_mutex_lock(&ms2_info_mutex); 00885 00886 /* if the entry is not a number (selection from the list), 00887 * or is a selection but also has a dot (suggesting 00888 * a.b.c.d selection), just try to connect with given name. 00889 */ 00890 if (num == 0 || strchr(sel, '.') != NULL) { 00891 server_name = sel; 00892 server_ip = sel; 00893 } else { 00894 if (num <= 0 || num > meta_numservers+cached_servers_num+1) { 00895 draw_info("Invalid selection. Try again", NDI_BLACK); 00896 return 1; 00897 } 00898 00899 if (num == meta_numservers+cached_servers_num+1) { 00900 server_name = server; 00901 server_ip = server; 00902 } else if (num > cached_servers_num) { 00903 server_name = meta_servers[num-cached_servers_num-1 ].hostname; 00904 server_ip = meta_servers[num-cached_servers_num-1 ].ip_addr; 00905 port = meta_servers[num-cached_servers_num-1 ].port; 00906 } else { 00907 server_name = cached_servers_name[num-1]; 00908 server_ip = cached_servers_ip[num-1]; 00909 } 00910 } 00911 pthread_mutex_unlock(&ms2_info_mutex); 00912 if (!server_name) { 00913 draw_info("Bad selection. Try again", NDI_BLACK); 00914 return 1; 00915 } 00916 00917 /* check for :port suffix, and use it */ 00918 if (!port) { 00919 if ((sel = strrchr(server_name, ':')) != NULL && (port = atoi(sel+1)) > 0) { 00920 snprintf(buf2, sizeof(buf2), "%s", server_name); 00921 buf2[sel-server_name] = '\0'; 00922 server_name = buf2; 00923 } 00924 else { 00925 port = use_config[CONFIG_PORT]; 00926 } 00927 } 00928 00929 snprintf(buf, sizeof(buf), "Trying to connect to %s:%d", server_name, port); 00930 draw_info(buf, NDI_BLACK); 00931 #ifdef MULTKEYS 00932 csocket.fd = init_connection(server_name, port); 00933 #else 00934 csocket.fd = init_connection(server_ip, port); 00935 #endif 00936 if (csocket.fd == -1) { 00937 draw_info("Unable to connect to server.", NDI_BLACK); 00938 return 1; 00939 } 00940 00941 /* Add server to cache */ 00942 if ((num <= meta_numservers) && (num != meta_numservers + cached_servers_num + 1)) { 00943 int index; 00944 for (index = 0; index < cached_servers_num; index++) { 00945 if (strcmp(server_name, cached_servers_name[index]) == 0) 00946 break; 00947 } 00948 /* If server is first in cache, no need to re-add id */ 00949 if (index != 0 || !cached_servers_num) { 00950 char *name; 00951 char *ip; 00952 int copy; 00953 00954 if (index == cached_servers_num) { 00955 name = strdup(server_name); 00956 ip = strdup(server_ip); 00957 cached_servers_num++; 00958 if (cached_servers_num > CACHED_SERVERS_MAX) { 00959 cached_servers_num--; 00960 free(cached_servers_name[cached_servers_num-1]); 00961 free(cached_servers_ip[cached_servers_num-1]); 00962 } 00963 } else { 00964 name = cached_servers_name[index]; 00965 ip = cached_servers_ip[index]; 00966 } 00967 for (copy = MIN(index, CACHED_SERVERS_MAX-1); copy > 0; copy--) { 00968 cached_servers_name[copy] = cached_servers_name[copy-1]; 00969 cached_servers_ip[copy] = cached_servers_ip[copy-1]; 00970 } 00971 cached_servers_name[0] = name; 00972 cached_servers_ip[0] = ip; 00973 metaserver_save_cache(); 00974 } 00975 } 00976 00977 return 0; 00978 } 00979 00980 #ifdef MS_STANDALONE 00981 /* This is here just to verify that the code seems to be working 00982 * properly 00983 * To use this code, compile as: 00984 * gcc -o metaserver -I. -DMS_STANDALONE metaserver.c 00985 */ 00986 00987 int main(int argc, char *argv[]) 00988 { 00989 int i; 00990 00991 metaserver_get_info(META_SERVER, META_PORT); 00992 for (i = 0; i < meta_numservers; i++) { 00993 printf("%s:%d:%s:%d:%s:%s\n", 00994 meta_servers[i].ip_addr, 00995 meta_servers[i].idle_time, 00996 meta_servers[i].hostname, 00997 meta_servers[i].num_players, 00998 meta_servers[i].version, 00999 meta_servers[i].text_comment); 01000 } 01001 } 01002 01003 #endif 01004 01005 #ifdef MS2_STANDALONE 01006 /* This is here just to verify that the code seems to be working 01007 * properly 01008 * To use this code, compile as: 01009 * gcc -o metaserver -I. -DMS2_STANDALONE metaserver.c misc.o -lcurl -lpthread 01010 */ 01011 01012 /* Following lines are to cover external symbols not 01013 * defined - trying to bring in the files the are defined 01014 * in just causes more dependencies, etc. 01015 */ 01016 void draw_info(const char *str, int color) { } 01017 int init_connection(char *host, int port) {} 01018 01019 int metaserver_on=1, meta_port=0; 01020 char *server=NULL, *meta_server; 01021 sint16 use_config[CONFIG_NUMS]; 01022 ClientSocket csocket; 01023 01024 int main(int argc, char *argv[]) 01025 { 01026 int i; 01027 01028 init_metaserver(); 01029 metaserver2_get_info(); 01030 fprintf(stderr,"Collecting data."); 01031 while (metaserver2_check_status()) { 01032 fprintf(stderr,"."); 01033 sleep(1); 01034 } 01035 fprintf(stderr,"\n"); 01036 for (i = 0; i < meta_numservers; i++) { 01037 printf("%s:%d:%s:%d:%s:%s\n", 01038 meta_servers[i].ip_addr, 01039 meta_servers[i].idle_time, 01040 meta_servers[i].hostname, 01041 meta_servers[i].num_players, 01042 meta_servers[i].version, 01043 meta_servers[i].text_comment); 01044 } 01045 } 01046 01047 #endif