/************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * In order to use any part of this Merc Diku Mud, you must comply with * * both the original Diku license in 'license.doc' as well the Merc * * license in 'license.txt'. In particular, you may not remove either of * * these copyright notices. * * * * Much time and thought has gone into this software and you are * * benefiting. We hope that you share your changes too. What goes * * around, comes around. * *************************************************************************** * ROM 2.4 is copyright 1993-1998 Russ Taylor * * ROM has been brought to you by the ROM consortium * * Russ Taylor (rtaylor@hypercube.org) * * Gabrielle Taylor (gtaylor@hypercube.org) * * Brian Moore (zump@rom.org) * * By using this code, you have agreed to follow the terms of the * * ROM license, in the file Rom24/doc/rom.license * *************************************************************************** * The Dawn of Time v1.69q (c)1997-2002 Michael Garratt * * >> A number of people have contributed to the Dawn codebase, with the * * majority of code written by Michael Garratt - www.dawnoftime.org * * >> To use this source code, you must fully comply with the dawn license * * in licenses.txt... In particular, you may not remove this copyright * * notice. * *************************************************************************** * 1stMud ROM Derivative (c) 2001-2004 by Markanth * * http://www.firstmud.com/ <markanth@firstmud.com> * * By using this code you have agreed to follow the term of * * the 1stMud license in ../doc/1stMud/LICENSE * ***************************************************************************/ // About: The code within this module sends to firstmud.com statistical // information. Using this information we are able to // get an idea of the number of 1stmud based muds running and // hopefully the number of players. // // The statistical information submitted to firstmud.com is a // summary of mudstats, combined with a few other things specific to // your mud environment (such as the name of the mud). Only the info // relavent to possible players is posted publicly, the rest is for // the developers of 1stmud. The SENDSTAT_SECURE_LEVEL define can be // used to limit some of the info as you see fit, but please leave the // unique_id, name, and version if possible. // // By default the mud after about 10 minutes of running will // send the stats to http://firstmud.com/scripts/post/post.php // This does not lag the mud in anyway, (unless your dns resolver // is broken - use sockets to determine this), if the dns resolver // is broken there may be a one off small delay (ordinarily less // than 5 seconds) while the mud resolves the ip address of // firstmud.com in order to know where to send the // stats to. // #include "merc.h" #include "recycle.h" #include "tables.h" #include "olc.h" #ifndef DISABLE_SENDSTAT //#define SENDSTAT_LOG_PROGRESS #define SENDSTAT_SUBMIT_DOMAIN "1stmud.dlmud.com" #define SENDSTAT_SUBMIT_URL "/scripts/post.php" /* Determines the level of information sent to create a mud listing. 1 - minimal 2 - average 3 - all */ #define SENDSTAT_SECURE_LEVEL 2 void sendstat_logf(const char *fmt, ...) { char buf[MSL]; va_list args; #ifndef SENDSTAT_LOG_PROGRESS return; #endif if (NullStr(fmt)) return; va_start(args, fmt); vsnprintf(buf, MSL, fmt, args); va_end(args); logf("sendstat: %s", buf); } const char *url_encode_table[] = { "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", "%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", "%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", "+", "!", "%22", "%23", "$", "%25", "%26", "%27", "(", ")", "*", "%2B", ",", "-", ".", "%2F", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "%3A", "%3B", "%3C", "%3D", "%3E", "%3F", "%40", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "%5B", "%5C", "%5D", "%5E", "_", "%60", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "%7B", "%7C", "%7D", "%7E", "%7F", "%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", "%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", "%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", "%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF", "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", "%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF", "%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7", "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", "%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF" }; char *url_encode_post_data(const char *postdata) { static char *result; unsigned char *s; const char *t; char *d; if (result) { free_mem(result); } alloc_mem(result, char, strlen(postdata) * 3 + 1); d = result; for (s = (unsigned char *) postdata; *s; s++) { t = url_encode_table[*s]; while (*t) { *d++ = *t++; } } *d = '\0'; return result; } char *sendstat_generate_statistics_text() { static char result[45000]; char stats[45000]; char *encoded; int len; stats[0] = NUL; /* required to post data on site */ strcatf(stats, "&unique_id=%d", mud_info.unique_id); /* required to post data on site */ strcatf(stats, "&name=%s", url_encode_post_data(mud_info.name)); /* level 1 security: minimal */ #if SENDSTAT_SECURE_LEVEL >= 1 strcatf(stats, "&hostname=%s", url_encode_post_data(HOSTNAME)); strcatf(stats, "&version=%s", url_encode_post_data(MUDVERSION)); strcatf(stats, "&mainport=%d", mainport); #ifndef DISABLE_WEBSRV strcatf(stats, "&webport=%d", webport); #endif strcatf(stats, "&areas=%d", top_area); strcatf(stats, "&rooms=%d", top_room_index); strcatf(stats, "&shops=%d", top_shop); strcatf(stats, "&explorable_rooms=%d", top_explored); strcatf(stats, "&resets=%d", top_reset); strcatf(stats, "&exits=%d", top_exit); strcatf(stats, "&extra_descriptions=%d", top_ed); strcatf(stats, "&affects=%d", top_affect); strcatf(stats, "&mobiles=%d", top_char_index); strcatf(stats, "&mobile_count=%d", mobile_count); strcatf(stats, "&objects=%d", top_obj_index); strcatf(stats, "&object_count=%d", top_obj); strcatf(stats, "&mob_programs=%d", top_mprog); strcatf(stats, "&obj_programs=%d", top_oprog); strcatf(stats, "&room_programs=%d", top_rprog); strcatf(stats, "&helps=%d", top_help); strcatf(stats, "&max_online=%d", mud_info.stats.online); strcatf(stats, "&logins=%d", mud_info.stats.logins); strcatf(stats, "&quests=%d", mud_info.stats.quests); strcatf(stats, "&qcomplete=%d", mud_info.stats.qcomplete); strcatf(stats, "&levels=%d", mud_info.stats.levels); strcatf(stats, "&newbies=%d", mud_info.stats.newbies); strcatf(stats, "&deletions=%d", mud_info.stats.deletions); strcatf(stats, "&mobdeaths=%d", mud_info.stats.mobdeaths); strcatf(stats, "&auctions=%d", mud_info.stats.auctions); strcatf(stats, "&aucsold=%d", mud_info.stats.aucsold); strcatf(stats, "&pdied=%d", mud_info.stats.pdied); strcatf(stats, "&pkill=%d", mud_info.stats.pkill); strcatf(stats, "¬es=%d", mud_info.stats.notes); strcatf(stats, "&remorts=%d", mud_info.stats.remorts); strcatf(stats, "&wars=%d", mud_info.stats.wars); strcatf(stats, "&gquests=%d", mud_info.stats.gquests); strcatf(stats, "&connections=%d", mud_info.stats.connections); strcatf(stats, "&longest_uptime=%s", timestr(mud_info.longest_uptime, false)); strcatf(stats, "&web_requests=%d", mud_info.stats.web_requests); strcatf(stats, "&chan_msgs=%d", mud_info.stats.chan_msgs); strcatf(stats, "&hero_level=%d", LEVEL_HERO); strcatf(stats, "&immortal_level=%d", LEVEL_IMMORTAL); strcatf(stats, "&max_level=%d", MAX_LEVEL); strcatf(stats, "¤t_time=%s", url_encode_post_data(str_time(-1, -1, NULL))); strcatf(stats, "&timezone=%s", url_encode_post_data(str_time(-1, -1, "%z %Z"))); strcatf(stats, "&races=%d", top_race); strcatf(stats, "&classes=%d", top_class); strcatf(stats, "&skills=%d", top_skill); strcatf(stats, "&groups=%d", top_group); strcatf(stats, "&socials=%d", top_social); strcatf(stats, "&clans=%d", top_clan); strcatf(stats, "&commands=%d", top_cmd); strcatf(stats, "&deities=%d", top_deity); strcatf(stats, "&songs=%d", top_song); strcatf(stats, "&channels=%d", top_channel); strcatf(stats, "&pfiles=%d", pfiles.count); strcatf(stats, "&mudflags=%s", url_encode_post_data(flag_string(mud_flags, mud_info.mud_flags))); strcatf(stats, "&boot_time=%s", url_encode_post_data(str_time(boot_time, -1, NULL))); strcatf(stats, "¤t_time=%s", url_encode_post_data(str_time(-1, -1, NULL))); #ifndef DISABLE_MCCP strcatf(stats, "&MCCP=%s", url_encode_post_data("yes")); #endif strcatf(stats, "&bans=%d", top_ban); strcatf(stats, "&disabled_cmds=%d", top_disabled); strcatf(stats, "&clan_members=%d", top_mbr); strcatf(stats, "&msp_sounds=%d", top_msp); /* level 2 security: average */ #elif SENDSTAT_SECURE_LEVEL >= 2 #ifdef __cplusplus strcatf(stats, "&Cplus=%s", url_encode_post_data("yes")); #endif strcatf(stats, "&default_port=%d", mud_info.default_port); strcatf(stats, "&min_save_lvl=%d", mud_info.min_save_lvl); strcatf(stats, "&group_lvl_range=%d", mud_info.group_lvl_limit); strcatf(stats, "&pcdam_mod=%d", mud_info.pcdam); strcatf(stats, "&mobdam_mod=%d", mud_info.mobdam); strcatf(stats, "&creation_point_mod=%d", mud_info.max_points); strcatf(stats, "&str_count=%d", nAllocString); strcatf(stats, "&str_size=%d", sAllocString); strcatf(stats, "&share_value=%d", mud_info.share_value); #ifdef __DATE__ strcatf(stats, "&compiled_date=%s", url_encode_post_data(__DATE__)); #endif #ifdef __TIME__ strcatf(stats, "&compiled_time=%s", url_encode_post_data(__TIME__)); #endif #ifdef __CYGWIN__ strcatf(stats, "&compiled_platform=%s", url_encode_post_data("cygwin")); #elif defined WIN32 strcatf(stats, "&compiled_platform=%s", url_encode_post_data("Win32")); #elif defined unix #ifdef linux strcatf(stats, "&compiled_platform=%s", url_encode_post_data("linux")); #elif defined __OpenBSD__ strcatf(stats, "&compiled_platform=%s", url_encode_post_data("OpenBSD")); #elif defined __FreeBSD__ strcatf(stats, "&compiled_platform=%s", url_encode_post_data("FreeBSD")); #elif defined __NetBSD__ strcatf(stats, "&compiled_platform=%s", url_encode_post_data("NetBSD")); #elif defined BSD strcatf(stats, "&compiled_platform=%s", url_encode_post_data("BSD")); #else strcatf(stats, "&compiled_platform=%s", url_encode_post_data("unix")); #endif #else strcatf(stats, "&compiled_platform=%s", url_encode_post_data("unknown")); #endif #ifdef __VERSION__ strcatf(stats, "&compiler_version=%s", url_encode_post_data(__VERSION__)); #endif /* level 3 security: all */ #elif SENDSTAT_SECURE_LEVEL >= 3 strcatf(stats, "&bind_ip=%s", url_encode_post_data(mud_info.bind_ip_address)); strcatf(stats, "&max_stats=%d", MAX_STATS); strcatf(stats, "&pulsepersec=%d", mud_info.pulsepersec); strcatf(stats, "&top_vnum_room=%ld", top_vnum_room); strcatf(stats, "&top_vnum_obj=%ld", top_vnum_obj); strcatf(stats, "&top_vnum_mob=%ld", top_vnum_mob); strcatf(stats, "&max_key_hash=%d", MAX_KEY_HASH); strcatf(stats, "&max_string_length=%d", MAX_STRING_LENGTH); strcatf(stats, "&max_input_length=%d", MAX_INPUT_LENGTH); /* up to you! */ strcatf(stats, "&uname=%s", url_encode_post_data(UNAME)); strcatf(stats, "&curr_directory=%s", url_encode_post_data(CWDIR)); strcatf(stats, "&exe_file=%s", url_encode_post_data(EXE_FILE)); strcatf(stats, "&platform_info=%s", url_encode_post_data(get_platform_info())); #endif encoded = &stats[1]; len = strlen(encoded); sprintf(result, "POST " SENDSTAT_SUBMIT_URL " HTTP/1.1\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "Host: " SENDSTAT_SUBMIT_DOMAIN "\r\n" "User-Agent: Mozilla/4.0 (compatible; " MUDNAME "SendStat/1.0;)\r\n" "Content-Length: %d\r\n" "Cache-Control: no-cache\r\n" "\r\n%s", len, encoded); return result; } char *sendstat_stattext_to_post; typedef enum { SENDSTATSTAGE_WAIT, SENDSTATSTAGE_DOMAIN_RESOLVED, SENDSTATSTAGE_CONNECT_IN_PROGRESS, SENDSTATSTAGE_GENERATE_STATS, SENDSTATSTAGE_POSTING, SENDSTATSTAGE_CLOSE_CONNECT, SENDSTATSTAGE_COMPLETED, SENDSTATSTAGE_ABORTED } sendstat_stages; sendstat_stages sendstat_stage = SENDSTATSTAGE_WAIT; time_t sendstat_connect_timeout = 0; struct in_addr sendstat_address; static SOCKET sendstat_socket; void sendstat_resolve_domain() { if (!inet_aton(SENDSTAT_SUBMIT_DOMAIN, &sendstat_address)) { if (sendstat_address.s_addr == 0) { struct hostent *hostp = gethostbyname(SENDSTAT_SUBMIT_DOMAIN); if (!hostp) { sendstat_logf("failed to resolve '%s.'", SENDSTAT_SUBMIT_DOMAIN); sendstat_stage = SENDSTATSTAGE_ABORTED; return; } memcpy(&sendstat_address, hostp->h_addr, hostp->h_length); } } sendstat_logf("resolved '%s' as %s", SENDSTAT_SUBMIT_DOMAIN, inet_ntoa(sendstat_address)); sendstat_stage = SENDSTATSTAGE_DOMAIN_RESOLVED; } struct sockaddr_in sockaddress; void sendstat_initiate_connection() { SOCKET nRet; sendstat_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sendstat_socket == INVALID_SOCKET) { sendstat_logf("Error creating connection socket"); return; } sockaddress.sin_family = AF_INET; sockaddress.sin_addr.s_addr = sendstat_address.s_addr; sockaddress.sin_port = htons(80); if (!socket_cntl(sendstat_socket)) { sendstat_logf("error setting new socket to nonblocking"); sendstat_stage = SENDSTATSTAGE_ABORTED; return; } nRet = connect(sendstat_socket, (SOCKADDR *) & sockaddress, sizeof(sockaddress)); if (nRet == 0) { // successful connection, jump straight to generating the stats to post sendstat_stage = SENDSTATSTAGE_GENERATE_STATS; return; } #ifdef WIN32 if (WSAGetLastError() != WSAEWOULDBLOCK) { sendstat_logf("sendstat_initiate_connection(): connect() error %d", WSAGetLastError()); sendstat_stage = SENDSTATSTAGE_ABORTED; return; } #else if (nRet < 0) { if (errno != EINPROGRESS && errno != EALREADY) { sendstat_logf ("sendstat_initiate_connection(): connect() error %d", errno); sendstat_stage = SENDSTATSTAGE_ABORTED; return; } } #endif sendstat_connect_timeout = current_time + 200; sendstat_logf("connection initiation successful"); sendstat_stage = SENDSTATSTAGE_CONNECT_IN_PROGRESS; return; } void sendstat_process_connect() { SOCKET nRet; if (sendstat_connect_timeout < current_time) { sendstat_logf ("sendstat_process_connect(): pending connection timed out."); sendstat_stage = SENDSTATSTAGE_ABORTED; return; } sendstat_logf("processing connection %s:80", inet_ntoa(sendstat_address)); #ifdef WIN32 { struct timeval select_timeout; fd_set fdWrite; fd_set fdExcept; select_timeout.tv_sec = 0; select_timeout.tv_usec = 0; FD_ZERO(&fdWrite); FD_SET(sendstat_socket, &fdWrite); FD_ZERO(&fdExcept); FD_SET(sendstat_socket, &fdExcept); nRet = select(sendstat_socket + 1, NULL, &fdWrite, &fdExcept, &select_timeout); if (nRet < 1) { if (nRet) { sendstat_logf ("sendstat_process_connect(): select() returned error"); sendstat_stage = SENDSTATSTAGE_ABORTED; } return; } if (FD_ISSET(sendstat_socket, &fdWrite)) { sendstat_logf ("sendstat_process_connect(): connection established."); sendstat_stage = SENDSTATSTAGE_GENERATE_STATS; return; } if (FD_ISSET(sendstat_socket, &fdExcept)) { sendstat_logf("sendstat_process_connect(): connection failed."); } else { sendstat_logf ("sendstat_process_connect(): don't know how we got here!"); } sendstat_stage = SENDSTATSTAGE_ABORTED; return; } #else nRet = connect(sendstat_socket, (SOCKADDR *) & sockaddress, sizeof(sockaddress)); if (nRet == 0) { sendstat_stage = SENDSTATSTAGE_GENERATE_STATS; return; } if (nRet < 0) { if (errno != EINPROGRESS && errno != EALREADY) { sendstat_logf("sendstat_process_connect(): connect() error %d", errno); sendstat_stage = SENDSTATSTAGE_ABORTED; return; } } #endif return; } void sendstat_generate_stats() { sendstat_stattext_to_post = sendstat_generate_statistics_text(); if (NullStr(sendstat_stattext_to_post)) { sendstat_logf ("sendstat_generate_stats(): An error occured generating statistics."); sendstat_stage = SENDSTATSTAGE_ABORTED; } else sendstat_stage = SENDSTATSTAGE_POSTING; } void sendstat_post() { char *msg = sendstat_stattext_to_post; int written; int msglen = strlen(msg); written = write_to_socket(sendstat_socket, msg, msglen); if (written < 0) { sendstat_logf ("sendstat_post(): An error occured posting statistics."); sendstat_stage = SENDSTATSTAGE_ABORTED; return; } if (written < msglen) { sendstat_logf ("Incomplete write, sent %d bytes of %d, write rest later", written, msglen); sendstat_stattext_to_post += written; return; } sendstat_logf("Submitted %d bytes", written); sendstat_stage = SENDSTATSTAGE_CLOSE_CONNECT; } #endif time_t last_sendstat_post; Do_Fun(do_sendstat) { #ifdef DISABLE_SENDSTAT chprintlnf(ch, "Posting {n statistics to http://" SENDSTAT_SUBMIT_DOMAIN "/muds/%s%d.php is disabled.", mud_info.name, mud_info.unique_id); #else while (sendstat_stage != SENDSTATSTAGE_ABORTED && sendstat_stage != SENDSTATSTAGE_COMPLETED) { sendstat_resolve_domain(); sendstat_initiate_connection(); sendstat_process_connect(); sendstat_generate_stats(); sendstat_post(); closesocket(sendstat_socket); last_sendstat_post = current_time; sendstat_stage = SENDSTATSTAGE_COMPLETED; } if (sendstat_stage == SENDSTATSTAGE_COMPLETED) chprintln(ch, "{n statistics sent to http://" SENDSTAT_SUBMIT_DOMAIN SENDSTAT_SUBMIT_URL "."); else chprintln(ch, "There was an error posting {n statistics on http://" SENDSTAT_SUBMIT_DOMAIN SENDSTAT_SUBMIT_URL "."); #endif } void sendstat_update(void) { #ifndef DISABLE_SENDSTAT static time_t wait_until = 0; if ((int) sendstat_stage < (int) SENDSTATSTAGE_COMPLETED) { if (!wait_until || sendstat_stage != SENDSTATSTAGE_WAIT) { sendstat_logf("sendstat_update(%d)", (int) sendstat_stage); } } switch (sendstat_stage) { case SENDSTATSTAGE_WAIT: if (wait_until == 0) { wait_until = current_time + MINUTE * 30; } else if (wait_until < current_time) { wait_until = 0; sendstat_logf("moving on to resolving stage."); sendstat_resolve_domain(); } break; case SENDSTATSTAGE_DOMAIN_RESOLVED: sendstat_logf("initiating connection to '%s:80'", inet_ntoa(sendstat_address)); sendstat_initiate_connection(); break; case SENDSTATSTAGE_CONNECT_IN_PROGRESS: sendstat_logf("processing connection."); sendstat_process_connect(); break; case SENDSTATSTAGE_GENERATE_STATS: sendstat_logf("generating statistics."); sendstat_generate_stats(); break; case SENDSTATSTAGE_POSTING: sendstat_logf("posting statistics."); sendstat_post(); break; case SENDSTATSTAGE_CLOSE_CONNECT: sendstat_logf("closing socket."); closesocket(sendstat_socket); sendstat_stage = SENDSTATSTAGE_COMPLETED; break; case SENDSTATSTAGE_COMPLETED: last_sendstat_post = current_time; case SENDSTATSTAGE_ABORTED: { static time_t redo_in = 0; if (redo_in) { if (redo_in < current_time) { sendstat_logf("restarting sendstats"); sendstat_stage = SENDSTATSTAGE_WAIT; redo_in = 0; } } else { redo_in = current_time + (24 * HOUR); } } default: break; }; #endif }