PageRenderTime 60ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/serverlist.cpp

https://github.com/mabako/mta-browser
C++ | 374 lines | 275 code | 46 blank | 53 comment | 31 complexity | e0bae915ace9b9c923a34c4db89d2c74 MD5 | raw file
Possible License(s): GPL-3.0
  1. /*
  2. GTK+ based MTA:SA Server Browser
  3. Copyright (c) 2010 mabako
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include <string>
  16. #include <sstream>
  17. #include <curl/curl.h>
  18. /* Networking stuff */
  19. #ifndef WIN32
  20. #include <sys/socket.h>
  21. #include <fcntl.h>
  22. #include <arpa/inet.h>
  23. #endif
  24. #ifdef DEBUG
  25. #include <iostream>
  26. #endif
  27. #include "serverlist.hpp"
  28. namespace browse
  29. {
  30. using namespace std;
  31. /* Small Helper for HTTP Requests */
  32. class HTTP
  33. {
  34. public:
  35. /* Does the request */
  36. HTTP( const string url, unsigned int timeout = 0 )
  37. {
  38. /* Initalize CURL */
  39. CURL* curl = curl_easy_init( );
  40. if( curl )
  41. {
  42. curl_easy_setopt( curl, CURLOPT_URL, SERVER_LIST_URL );
  43. curl_easy_setopt( curl, CURLOPT_HEADER, 0 );
  44. if( timeout > 0 )
  45. {
  46. curl_easy_setopt( curl, CURLOPT_TIMEOUT_MS, timeout );
  47. }
  48. /* Fetch our data */
  49. string buffer;
  50. curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, WriteResult );
  51. curl_easy_setopt( curl, CURLOPT_WRITEDATA, &buffer );
  52. CURLcode curlresult = curl_easy_perform( curl );
  53. /* Cleanup */
  54. curl_easy_cleanup( curl );
  55. if( curlresult == CURLE_OK )
  56. {
  57. /* Check for HTTP Status code */
  58. long httpcode = 0;
  59. curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &httpcode );
  60. switch( httpcode )
  61. {
  62. case 0: /* Will be returned if no response code is recieved */
  63. case 200:
  64. result = buffer;
  65. break;
  66. case 401:
  67. error = "GM Error 401: Unauthorized";
  68. break;
  69. case 403:
  70. error = "GM Error 403: Forbidden";
  71. break;
  72. case 404:
  73. error = "GM Error 404: Not Found";
  74. break;
  75. case 500:
  76. error = "GM Error 500: Internal Server Error";
  77. break;
  78. default:
  79. stringstream ss;
  80. ss << "GM Error " << httpcode;
  81. error = ss.str( );
  82. }
  83. return;
  84. }
  85. error = result + " " + curl_easy_strerror( curlresult );
  86. return;
  87. }
  88. error = "Could not initalize CURL";
  89. }
  90. /* Not much left to destroy */
  91. ~HTTP( )
  92. {
  93. }
  94. /* Returns true if the request was successful */
  95. bool WasSuccessful( )
  96. {
  97. return error.length( ) == 0;
  98. }
  99. /* Returns the response to the HTTP request if it was successful. */
  100. const string GetResult( )
  101. {
  102. return result;
  103. }
  104. /* Returns the CURL error message if any */
  105. const string GetError( )
  106. {
  107. return error;
  108. }
  109. private:
  110. string error;
  111. string result;
  112. /* Writer for whatever is returned by CURL */
  113. static int WriteResult( char* data, size_t size, size_t something, string* buffer )
  114. {
  115. int result = 0;
  116. if( buffer )
  117. {
  118. buffer->append( data, size * something );
  119. result = size * something;
  120. }
  121. return result;
  122. }
  123. };
  124. /* Server List Class */
  125. ServerList::ServerList( )
  126. {
  127. #ifdef WIN32
  128. /* Shoot me against a wall, I'm a WinSock */
  129. WSADATA wsa;
  130. WSAStartup(MAKEWORD(2,0),&wsa);
  131. #endif
  132. /* Create a Socket - one for all our needs */
  133. sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
  134. /* We need non-blocking sockets */
  135. #ifdef _WIN32
  136. unsigned long flag = 1;
  137. ioctlsocket( sock, FIONBIO, &flag );
  138. #else
  139. int flags = fcntl(sock, F_GETFL);
  140. flags |= O_NONBLOCK;
  141. fcntl(sock, F_SETFL, flags);
  142. #endif
  143. /* Initalize our status */
  144. totalServers = 0;
  145. filteredServers = 0;
  146. }
  147. ServerList::~ServerList( )
  148. {
  149. if( sock > 0 )
  150. #ifdef WIN32
  151. closesocket( sock );
  152. #else
  153. close( sock );
  154. #endif
  155. Clear( );
  156. #ifdef WIN32
  157. WSACleanup( );
  158. #endif
  159. }
  160. /* Clears the old server list and instead fetches it again */
  161. const string ServerList::Refresh( )
  162. {
  163. Clear( );
  164. /* Delete the old socket since we do not want its messages anymore */
  165. if( sock > 0 )
  166. #ifdef WIN32
  167. closesocket( sock );
  168. #else
  169. close( sock );
  170. #endif
  171. /* Create another socket */
  172. sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
  173. /* We need non-blocking sockets */
  174. #ifdef _WIN32
  175. unsigned long flag = 1;
  176. ioctlsocket( sock, FIONBIO, &flag );
  177. #else
  178. int flags = fcntl(sock, F_GETFL);
  179. flags |= O_NONBLOCK;
  180. fcntl(sock, F_SETFL, flags);
  181. #endif
  182. /* New HTTP request */
  183. HTTP* pHTTP = new HTTP( SERVER_LIST_URL, SERVER_LIST_MASTER_TIMEOUT );
  184. string returnValue = "";
  185. if( pHTTP )
  186. {
  187. if( pHTTP->WasSuccessful( ) )
  188. {
  189. returnValue = ParseList( pHTTP->GetResult( ) );
  190. }
  191. else
  192. {
  193. returnValue = pHTTP->GetError( );
  194. }
  195. delete pHTTP;
  196. }
  197. return returnValue;
  198. }
  199. /* Performs a single tick */
  200. list < Server* > ServerList::Pulse( )
  201. {
  202. list < Server* > newServersThisPulse;
  203. unsigned int sentQueriesThisPulse = 0;
  204. sockaddr_in address;
  205. socklen_t addressSize = sizeof( address );
  206. char buffer[ SERVER_LIST_QUERY_BUFFER ] = { 0 };
  207. while( recvfrom( sock, buffer, sizeof( buffer ), 0, ( sockaddr* ) &address, &addressSize ) > 0 )
  208. {
  209. Server* server = new Server( buffer, string( inet_ntoa( address.sin_addr ) ) );
  210. if( server->IsValid( ) )
  211. {
  212. /* add it to the list of existing servers */
  213. servers.push_back( server );
  214. #ifdef DEBUG
  215. cout << "Server " << servers.size( ) << ": " << server->GetIP( ) << ":" << server->GetPort( ) << " - " << server->GetName( ) << endl;
  216. #endif
  217. /* We only want it to appear on the GUI if it matches our filter */
  218. if( server->Matches( filter ) )
  219. {
  220. ++ filteredServers;
  221. newServersThisPulse.push_back( server );
  222. }
  223. }
  224. else
  225. {
  226. #ifdef DEBUG
  227. cout << "Dropping Server " << inet_ntoa( address.sin_addr ) << " (Invalid Server)" << endl;
  228. #endif
  229. delete server;
  230. }
  231. }
  232. for( list < ServerListItem* >::iterator iter = newServers.begin( ); iter != newServers.end( ); )
  233. {
  234. if( !(*iter)->WasQuerySent( ) )
  235. {
  236. if( sentQueriesThisPulse < MAX_QUERIES_PER_PULSE )
  237. {
  238. if( (*iter)->SendQuery( ) )
  239. {
  240. ++ sentQueriesThisPulse;
  241. newServers.erase( iter++ );
  242. }
  243. else
  244. {
  245. ++ iter;
  246. }
  247. }
  248. else
  249. {
  250. /* Let's fast-forward to the end of the list as we don't want to mess with more stuff */
  251. break;
  252. }
  253. }
  254. }
  255. return newServersThisPulse;
  256. }
  257. /* Returns all servers matching the filter */
  258. list < Server* > ServerList::Filter( string _filter )
  259. {
  260. filter = _filter;
  261. list < Server* > matchingServers;
  262. for( list < Server* >::iterator iter = servers.begin( ); iter != servers.end( ); ++ iter )
  263. if( (*iter)->Matches( filter ) )
  264. matchingServers.push_back( *iter );
  265. filteredServers = matchingServers.size( );
  266. return matchingServers;
  267. }
  268. /* Clears the whole server list */
  269. void ServerList::Clear( )
  270. {
  271. /* Clear the old server lists */
  272. for( list < ServerListItem* >::iterator iter = newServers.begin( ); iter != newServers.end( ); ++ iter )
  273. {
  274. delete *iter;
  275. }
  276. newServers.clear( );
  277. for( list < Server* >::iterator iter = servers.begin( ); iter != servers.end( ); ++ iter )
  278. {
  279. delete *iter;
  280. }
  281. servers.clear( );
  282. /* Update our status */
  283. totalServers = 0;
  284. filteredServers = 0;
  285. }
  286. /* Parses the Server List and creates a new class for each server */
  287. const string ServerList::ParseList( string list )
  288. {
  289. if( list.length( ) < 2 )
  290. return "Failed to parse List";
  291. unsigned short count = ( list[0] << 8 ) + list[1];
  292. totalServers = count;
  293. /*
  294. cout << "Server List: " << count << " Servers" << endl;
  295. */
  296. unsigned int length = list.length( );
  297. unsigned int pos = 2;
  298. while( pos < length - 6 && count -- )
  299. {
  300. /* Fetch IP/Host parts */
  301. unsigned char
  302. a = list[pos],
  303. b = list[pos+1],
  304. c = list[pos+2],
  305. d = list[pos+3];
  306. stringstream ip;
  307. ip << (unsigned int) a << "." << (unsigned int) b << "." << (unsigned int) c << "." << (unsigned int) d;
  308. /* add to our server list */
  309. newServers.push_back( new ServerListItem( ip.str( ), ( unsigned short )( ( list[pos+4] << 8 ) + list[pos+5] ), sock ) );
  310. pos += 6;
  311. }
  312. return "";
  313. }
  314. const string ServerList::GetStatus( )
  315. {
  316. stringstream ss;
  317. if( filter.length( ) == 0 )
  318. ss << "Found " << servers.size( ) << "/" << totalServers << " Servers";
  319. else if( filteredServers == 0 )
  320. return "No matching Servers found";
  321. else
  322. ss << "Found " << filteredServers << "/" << totalServers << " matching Servers";
  323. return ss.str( );
  324. }
  325. }