1 /*
2  *  network_metaserver.cpp - Metaserver client
3 
4 	Copyright (C) 2004 and beyond by Woody Zenfell, III
5 	and the "Aleph One" developers.
6 
7 	This program is free software; you can redistribute it and/or modify
8 	it under the terms of the GNU General Public License as published by
9 	the Free Software Foundation; either version 3 of the License, or
10 	(at your option) any later version.
11 
12 	This program is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU General Public License for more details.
16 
17 	This license is contained in the file "COPYING",
18 	which is included with this source code; it is available online at
19 	http://www.gnu.org/licenses/gpl.html
20 
21  April 15, 2004 (Woody Zenfell):
22 	Created.
23  */
24 
25 #if !defined(DISABLE_NETWORKING)
26 
27 #include "cseries.h"
28 
29 #include "network_metaserver.h"
30 
31 #include "metaserver_messages.h"
32 
33 #include "Message.h"
34 #include "MessageHandler.h"
35 #include "MessageDispatcher.h"
36 #include "MessageInflater.h"
37 #include "CommunicationsChannel.h"
38 #include "preferences.h"
39 #include "alephversion.h"
40 #include "HTTP.h"
41 
42 #include <string>
43 #include <iostream>
44 #include <iterator> // ostream_iterator
45 #include <algorithm>
46 #include "Logging.h"
47 
48 #include <boost/algorithm/string/predicate.hpp>
49 
50 
51 using namespace std;
52 
53 
54 set<MetaserverClient*> MetaserverClient::s_instances;
55 set<string> MetaserverClient::s_ignoreNames;
56 
57 
58 static const int kKeyLength = 16;
59 
60 static std::string remove_formatting(const std::string &s);
61 
62 void
handleUnexpectedMessage(Message * inMessage,CommunicationsChannel * inChannel)63 MetaserverClient::handleUnexpectedMessage(Message* inMessage, CommunicationsChannel* inChannel)
64 {
65 	logAnomaly("Metaserver received message ID %i", inMessage->type());
66 	if(inMessage->type() == UninflatedMessage::kTypeID)
67 	{
68 		UninflatedMessage* theMessage = dynamic_cast<UninflatedMessage*>(inMessage);
69 		if(theMessage != NULL)
70 			logAnomaly("-- internal ID %i, length %i", theMessage->inflatedType(), theMessage->length());
71 	}
72 }
73 
74 
75 void
handleBroadcastMessage(BroadcastMessage * inMessage,CommunicationsChannel * inChannel)76 MetaserverClient::handleBroadcastMessage(BroadcastMessage* inMessage, CommunicationsChannel* inChannel)
77 {
78 	if(m_notificationAdapter)
79 		m_notificationAdapter->receivedBroadcastMessage(inMessage->message());
80 }
81 
82 
83 void
handleChatMessage(ChatMessage * message,CommunicationsChannel * inChannel)84 MetaserverClient::handleChatMessage(ChatMessage* message, CommunicationsChannel* inChannel)
85 {
86 	if(message->internalType() == 0 && m_notificationAdapter)
87 	{
88 		std::string realSenderName = remove_formatting(message->senderName());
89 		if (realSenderName[0] == '\260') realSenderName.erase(realSenderName.begin());
90 		if (network_preferences->mute_metaserver_guests && boost::algorithm::starts_with(realSenderName, "Guest "))
91 			return;
92 		if (s_ignoreNames.find(realSenderName) != s_ignoreNames.end())
93 			return;
94 
95 		if (message->directed())
96 			m_notificationAdapter->receivedPrivateMessage(message->senderName(), message->senderID(), message->message());
97 		else
98 			m_notificationAdapter->receivedChatMessage(message->senderName(), message->senderID(), message->message());
99 	}
100 }
101 
handlePrivateMessage(PrivateMessage * message,CommunicationsChannel * inChannel)102 void MetaserverClient::handlePrivateMessage(PrivateMessage* message, CommunicationsChannel* inChannel)
103 {
104 	if (message->internalType() == 0 && m_notificationAdapter)
105 	{
106 		std::string realSenderName = remove_formatting(message->senderName());
107 		if (realSenderName[0] == '\260') realSenderName.erase(realSenderName.begin());
108 		if (network_preferences->mute_metaserver_guests && boost::algorithm::starts_with(realSenderName, "Guest "))
109 			return;
110 
111 		if (s_ignoreNames.find(realSenderName) != s_ignoreNames.end())
112 			return;
113 
114 		if (message->directed())
115 			m_notificationAdapter->receivedPrivateMessage(message->senderName(), message->senderID(), message->message());
116 		else
117 			m_notificationAdapter->receivedChatMessage(message->senderName(), message->senderID(), message->message());
118 	}
119 }
120 
121 
122 void
handleKeepAliveMessage(Message * inMessage,CommunicationsChannel * inChannel)123 MetaserverClient::handleKeepAliveMessage(Message* inMessage, CommunicationsChannel* inChannel)
124 {
125 	inChannel->enqueueOutgoingMessage(KeepAliveMessage());
126 }
127 
128 
129 void
handlePlayerListMessage(PlayerListMessage * inMessage,CommunicationsChannel * inChannel)130 MetaserverClient::handlePlayerListMessage(PlayerListMessage* inMessage, CommunicationsChannel* inChannel)
131 {
132 	std::vector<MetaserverPlayerInfo> players = inMessage->players();
133 	if (m_notificationAdapter)
134 	{
135 		// metaserver says invisible people leave twice
136 		std::vector<MetaserverPlayerInfo>::iterator it = players.begin();
137 		while (it != players.end())
138 		{
139 			if (it->verb() == PlayersInRoom::kDelete && !find_player(it->playerID()))
140 			{
141 				it = players.erase(it);
142 			}
143 			else
144 			{
145 				++it;
146 			}
147 		}
148 	}
149 
150 	m_playersInRoom.processUpdates(inMessage->players());
151 
152 	if(m_notificationAdapter) {
153 		m_notificationAdapter->playersInRoomChanged(players);
154 	}
155 }
156 
157 
158 void
handleRoomListMessage(RoomListMessage * inMessage,CommunicationsChannel * inChannel)159 MetaserverClient::handleRoomListMessage(RoomListMessage* inMessage, CommunicationsChannel* inChannel)
160 {
161 	m_rooms = inMessage->rooms();
162 }
163 
164 void
handleGameListMessage(GameListMessage * inMessage,CommunicationsChannel * inChannel)165 MetaserverClient::handleGameListMessage(GameListMessage* inMessage, CommunicationsChannel* inChannel)
166 {
167 	vector<GameListMessage::GameListEntry> entries = inMessage->entries();
168 	for (vector<GameListMessage::GameListEntry>::iterator it = entries.begin(); it != entries.end(); ++it)
169 	{
170 		const MetaserverPlayerInfo *player = m_playersInRoom.find(it->m_hostPlayerID);
171 		if (player)
172 		{
173 			it->m_hostPlayerName = player->name();
174 		}
175 	}
176 
177 	m_gamesInRoom.processUpdates(entries);
178 
179 	if(m_notificationAdapter) {
180 		m_notificationAdapter->gamesInRoomChanged(inMessage->entries());
181 	}
182 }
183 
184 
MetaserverClient()185 MetaserverClient::MetaserverClient()
186 	: m_channel(new CommunicationsChannel())
187 	, m_inflater(new MessageInflater())
188 	, m_dispatcher(new MessageDispatcher())
189 	, m_loginDispatcher(new MessageDispatcher())
190 	, m_notificationAdapter(NULL)
191 	, m_notifiedOfDisconnected(false)
192 	, m_gameAnnounced(false)
193 {
194 	m_unexpectedMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handleUnexpectedMessage));
195 	m_broadcastMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handleBroadcastMessage));
196 	m_chatMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handleChatMessage));
197 	m_privateMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handlePrivateMessage));
198 	m_keepAliveMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handleKeepAliveMessage));
199 	m_playerListMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handlePlayerListMessage));
200 	m_roomListMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handleRoomListMessage));
201 	m_gameListMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handleGameListMessage));
202 	m_setPlayerDataMessageHandler.reset(newMessageHandlerMethod(this, &MetaserverClient::handleSetPlayerDataMessage));
203 
204 	m_inflater->learnPrototype(SaltMessage());
205 	m_inflater->learnPrototype(AcceptMessage());
206 	m_inflater->learnPrototype(RoomListMessage());
207 	m_inflater->learnPrototype(IDAndLimitMessage());
208 	m_inflater->learnPrototype(DenialMessage());
209 	m_inflater->learnPrototype(BroadcastMessage());
210 	m_inflater->learnPrototype(ChatMessage());
211 	m_inflater->learnPrototype(PrivateMessage());
212 	m_inflater->learnPrototype(KeepAliveMessage());
213 	m_inflater->learnPrototype(PlayerListMessage());
214 	m_inflater->learnPrototype(LoginSuccessfulMessage());
215 	m_inflater->learnPrototype(SetPlayerDataMessage());
216 	m_inflater->learnPrototype(GameListMessage());
217 
218 	m_channel->setMessageInflater(m_inflater.get());
219 
220 	m_dispatcher->setDefaultHandler(m_unexpectedMessageHandler.get());
221 	m_dispatcher->setHandlerForType(m_broadcastMessageHandler.get(), BroadcastMessage::kType);
222 	m_dispatcher->setHandlerForType(m_chatMessageHandler.get(), ChatMessage::kType);
223 	m_dispatcher->setHandlerForType(m_privateMessageHandler.get(), PrivateMessage::kType);
224 	m_dispatcher->setHandlerForType(m_keepAliveMessageHandler.get(), KeepAliveMessage::kType);
225 	m_dispatcher->setHandlerForType(m_playerListMessageHandler.get(), PlayerListMessage::kType);
226 	m_dispatcher->setHandlerForType(m_roomListMessageHandler.get(), RoomListMessage::kType);
227 	m_dispatcher->setHandlerForType(m_gameListMessageHandler.get(), GameListMessage::kType);
228 	m_dispatcher->setHandlerForType(m_setPlayerDataMessageHandler.get(), SetPlayerDataMessage::kType);
229 
230 	m_loginDispatcher->setDefaultHandler(m_unexpectedMessageHandler.get());
231 	m_loginDispatcher->setHandlerForType(m_setPlayerDataMessageHandler.get(), SetPlayerDataMessage::kType);
232 
233 	s_instances.insert(this);
234 	s_ignoreNames.insert("Bacon");
235 
236 }
237 
238 void
connect(const std::string & serverName,uint16 port,const std::string & userName,const std::string & userPassword)239 MetaserverClient::connect(const std::string& serverName, uint16 port, const std::string& userName, const std::string& userPassword)
240 {
241 	try
242 	{
243 		m_channel->setMessageHandler(m_loginDispatcher.get());
244 
245 		if (m_channel->isConnected()) m_channel->disconnect();
246 
247 		m_playersInRoom.clear();
248 		m_gamesInRoom.clear();
249 
250 		m_channel->connect(serverName.c_str(), port);
251 
252 		LoginAndPlayerInfoMessage theLoginMessage(userName, m_playerName, m_teamName);
253 		m_channel->enqueueOutgoingMessage(theLoginMessage);
254 
255 		std::unique_ptr<Message> theSaltOrAcceptMessage(m_channel->receiveMessage());
256 		if (theSaltOrAcceptMessage.get() == 0)
257 			throw ServerConnectException("Server Disconnected");
258 
259 		if (dynamic_cast<SaltMessage*>(theSaltOrAcceptMessage.get()) != 0)
260 		{
261 			SaltMessage* theSaltMessage = dynamic_cast<SaltMessage*>(theSaltOrAcceptMessage.get());
262 
263 			char theKey[kKeyLength];
264 			char thePasswordCopy[kKeyLength];
265 			memset(thePasswordCopy, 0x23, sizeof(thePasswordCopy));
266 			strncpy(reinterpret_cast<char*>(thePasswordCopy), userPassword.c_str(), kKeyLength);
267 
268 			if (theSaltMessage->encryptionType() == SaltMessage::kPlaintextEncryption)
269 			{
270 				strncpy(theKey, thePasswordCopy, kKeyLength);
271 			}
272 			else if (theSaltMessage->encryptionType() == SaltMessage::kBraindeadSimpleEncryption)
273 			{
274 				for(size_t i = 0; i < sizeof(theKey); i++)
275 				{
276 					theKey[i] = thePasswordCopy[i] ^ (theSaltMessage->salt()[i]);
277 				}
278 
279 				for(size_t i = 1; i < sizeof(theKey); i++)
280 				{
281 					theKey[i] = theKey[i]^theKey[i - 1];
282 				}
283 
284 				for(size_t i = 1; i < sizeof(theKey); i++) {
285 					short value;
286 
287 					value = ~( theKey[i]*theKey[i-1]);
288 					theKey[i] = (unsigned char) value;
289 				}
290 			}
291 			else if (theSaltMessage->encryptionType() == SaltMessage::kHTTPSEncryption)
292 			{
293 				HTTPClient conn;
294 				HTTPClient::parameter_map params;
295 				params["username"] = userName;
296 				params["password"] = userPassword;
297 				params["salt"] = std::string(reinterpret_cast<const char *>(theSaltMessage->salt()));
298 
299 				if (conn.Post(A1_METASERVER_LOGIN_URL, params))
300 				{
301 					strncpy(theKey, conn.Response().c_str(), sizeof(theKey));
302 				}
303 				else
304 				{
305 					throw ServerConnectException("HTTPS Connection Failed");
306 				}
307 			}
308 			else
309 			{
310 				throw ServerConnectException("Unsupported Encryption");
311 			}
312 
313 			BigChunkOfDataMessage theKeyMessage(kCLIENT_KEY, (uint8 *) theKey, sizeof(theKey));
314 			m_channel->enqueueOutgoingMessage(theKeyMessage);
315 
316 			std::unique_ptr<Message> theResponseMessage(m_channel->receiveMessage());
317 			if (!theResponseMessage.get())
318 			{
319 				throw ServerConnectException("Server Disconnected");
320 			}
321 			else if (dynamic_cast<DenialMessage*>(theResponseMessage.get()))
322 			{
323 				DenialMessage *dm = dynamic_cast<DenialMessage*>(theResponseMessage.get());
324 				throw LoginDeniedException(dm->code(), dm->message());
325 			}
326 			else if (!dynamic_cast<AcceptMessage*>(theResponseMessage.get()))
327 			{
328 				throw CommunicationsChannel::FailedToReceiveSpecificMessageException();
329 			}
330 		}
331 		else if (dynamic_cast<AcceptMessage*>(theSaltOrAcceptMessage.get()) == 0)
332 		{
333 			if (dynamic_cast<DenialMessage*>(theSaltOrAcceptMessage.get()))
334 			{
335 				DenialMessage *dm = dynamic_cast<DenialMessage*>(theSaltOrAcceptMessage.get());
336 				throw LoginDeniedException(dm->code(), dm->message());
337 			}
338 			else
339 			{
340 				throw CommunicationsChannel::FailedToReceiveSpecificMessageException();
341 			}
342 		}
343 
344 		m_channel->enqueueOutgoingMessage(LocalizationMessage());
345 
346 		std::unique_ptr<LoginSuccessfulMessage> theLoginSuccessfulMessage(m_channel->receiveSpecificMessageOrThrow<LoginSuccessfulMessage>());
347 
348 //	std::unique_ptr<SetPlayerDataMessage> theSetPlayerDataMessage(m_channel->receiveSpecificMessageOrThrow<SetPlayerDataMessage>());
349 
350 		std::unique_ptr<RoomListMessage> theRoomListMessage(m_channel->receiveSpecificMessageOrThrow<RoomListMessage>());
351 		m_dispatcher.get()->handle(theRoomListMessage.get(), m_channel.get());
352 
353 		m_channel->disconnect();
354 
355 		///// ROOM CONNECTION
356 
357 		if (!m_rooms.size())
358 		{
359 			throw ServerConnectException("No Rooms Available");
360 		}
361 
362 		IPaddress roomServerAddress = m_rooms[0].roomServerAddress();
363 
364 		for (int i = 0; i < m_rooms.size(); i++)
365 		{
366 			if (m_rooms[i].roomName() == "Arrival")
367 			{
368 				roomServerAddress = m_rooms[i].roomServerAddress();
369 				break;
370 			}
371 		}
372 
373 //	roomServerAddress.host = 0xC0A80108;
374 //	roomServerAddress.host = 0x0801A8C0;
375 
376 		m_channel->connect(roomServerAddress);
377 
378 		m_channel->enqueueOutgoingMessage(RoomLoginMessage(userName, theLoginSuccessfulMessage->token()));
379 
380 		m_channel->enqueueOutgoingMessage(NameAndTeamMessage(m_playerName, m_teamName));
381 
382 		std::unique_ptr<IDAndLimitMessage> theIDAndLimitMessage(m_channel->receiveSpecificMessageOrThrow<IDAndLimitMessage>());
383 		m_playerID = theIDAndLimitMessage->playerID();
384 
385 		std::unique_ptr<DenialMessage> theRoomAcceptMessage(m_channel->receiveSpecificMessageOrThrow<DenialMessage>());
386 
387 		m_channel->setMessageHandler(m_dispatcher.get());
388 	}
389 	catch (const CommunicationsChannel::FailedToReceiveSpecificMessageException&)
390 	{
391 		// translate for caller
392 		throw ServerConnectException("Unexpected Response");
393 	}
394 }
395 
396 
397 
398 bool
isConnected() const399 MetaserverClient::isConnected() const
400 {
401 	return m_channel->isConnected();
402 }
403 
404 
405 
406 void
disconnect()407 MetaserverClient::disconnect()
408 {
409 	// don't throw, we're called in ~MetaserverClient
410 	try {
411 		m_channel->enqueueOutgoingMessage(LogoutMessage());
412 		m_channel->pump();
413 	}
414 	catch(...) { }
415 	m_channel->disconnect();
416 }
417 
418 
419 
420 void
pump()421 MetaserverClient::pump()
422 {
423 	m_channel->pump();
424 	m_channel->dispatchIncomingMessages();
425 	if (!m_channel->isConnected() && !m_notifiedOfDisconnected && m_notificationAdapter)
426 	{
427 		m_notifiedOfDisconnected = true;
428 		m_notificationAdapter->roomDisconnected();
429 	}
430 }
431 
432 
433 
434 
435 void
pumpAll()436 MetaserverClient::pumpAll()
437 {
438 	for_each(s_instances.begin(), s_instances.end(), mem_fun(&MetaserverClient::pump));
439 }
440 
441 
442 
443 
444 void
sendChatMessage(const std::string & message)445 MetaserverClient::sendChatMessage(const std::string& message)
446 {
447 	if (message == ".available" || message == ".avail") {
448 		if(m_notificationAdapter) {
449 			string players = "Available Players: ";
450 			bool found_players = false;
451 			for (size_t i = 0; i < playersInRoom().size(); i++) {
452 				if (!playersInRoom()[i].away()) {
453 					if (found_players)
454 						players += ", ";
455 					found_players = true;
456 					players += "\"" + playersInRoom()[i].name() + "\"";
457 				}
458 			}
459 			if (!found_players)
460 				players += "none";
461 			m_notificationAdapter->receivedLocalMessage(players);
462 		}
463 	} else if (message == ".who") {
464 		if(m_notificationAdapter) {
465 			string players = "Players: ";
466 			if (playersInRoom().size()) {
467 				players += "\"" + playersInRoom()[0].name() + "\"";
468 				for (size_t i = 1; i < playersInRoom().size(); i++) {
469 					players += ", \"" + playersInRoom()[i].name() + "\"";
470 				}
471 			} else {
472 				players += "none";
473 			}
474 			m_notificationAdapter->receivedLocalMessage(players);
475 		}
476 	} else if (message.compare(0, strlen(".pork"), ".pork") == 0) {
477 		if (m_notificationAdapter) {
478 			m_notificationAdapter->receivedLocalMessage("NO BACON FOR YOU");
479 		}
480 	} else if (message == ".ignore") {
481 		if (m_notificationAdapter) {
482 			// list the players on the ignore list
483 			string players = "Ignoring: ";
484 			for (set<string>::iterator it = s_ignoreNames.begin(); it != s_ignoreNames.end(); it++)
485 			{
486 				if (it == s_ignoreNames.begin())
487 				{
488 					players += "\"" + *it + "\"";
489 				}
490 				else
491 				{
492 					players += ", \"" + *it +"\"";
493 				}
494 			}
495 
496 			m_notificationAdapter->receivedLocalMessage(players);
497 		}
498 	} else if (message.compare(0, strlen(".ignore "), ".ignore ") == 0) {
499 		// everything after the space is the name to ignore
500 		string name = message.substr(strlen(".ignore "));
501 		ignore(name);
502 	} else if (message == ".games") {
503 		if (m_notificationAdapter) {
504 			m_notificationAdapter->receivedLocalMessage("Games:");
505 			vector<GameListMessage::GameListEntry> games = gamesInRoom();
506 			vector<MetaserverPlayerInfo> players = playersInRoom();
507 			for (vector<GameListMessage::GameListEntry>::iterator it = games.begin(); it != games.end(); ++it)
508 			{
509 				// look up the player name
510  				string player_name;
511 				for (vector<MetaserverPlayerInfo>::iterator player_it = players.begin(); player_it != players.end(); ++player_it)
512  				{
513  					if (player_it->id() == it->m_hostPlayerID)
514  					{
515 						player_name = player_it->name();
516 						break;
517  					}
518  				}
519 
520  				if (player_name.size() > 0) {
521  					m_notificationAdapter->receivedLocalMessage(it->format_for_chat(player_name));
522 				}
523 			}
524 		}
525 	} else {
526 		m_channel->enqueueOutgoingMessage(ChatMessage(m_playerID, m_playerName, message));
527 	}
528 }
529 
530 void
sendPrivateMessage(MetaserverPlayerInfo::IDType id,const std::string & message)531 MetaserverClient::sendPrivateMessage(MetaserverPlayerInfo::IDType id, const std::string& message)
532 {
533 	m_channel->enqueueOutgoingMessage(PrivateMessage(m_playerID, m_playerName, id, message));
534 }
535 
536 void
announceGame(uint16 gamePort,const GameDescription & description)537 MetaserverClient::announceGame(uint16 gamePort, const GameDescription& description)
538 {
539 	m_channel->enqueueOutgoingMessage(CreateGameMessage(gamePort, description));
540 	m_gameDescription = description;
541 	m_gamePort = gamePort;
542 	m_gameAnnounced = true;
543 }
544 
545 void
announcePlayersInGame(uint8 players)546 MetaserverClient::announcePlayersInGame(uint8 players)
547 {
548 	m_gameDescription.m_numPlayers = players;
549 	m_channel->enqueueOutgoingMessage(CreateGameMessage(m_gamePort, m_gameDescription));
550 }
551 
552 void
announceGameStarted(int32 gameTimeInSeconds)553 MetaserverClient::announceGameStarted(int32 gameTimeInSeconds)
554 {
555 	m_gameDescription.m_closed = true;
556 	m_gameDescription.m_running = true;
557 	m_channel->enqueueOutgoingMessage(CreateGameMessage(m_gamePort, m_gameDescription));
558 	m_channel->enqueueOutgoingMessage(StartGameMessage(gameTimeInSeconds == INT32_MAX ? INT32_MIN : gameTimeInSeconds));
559 }
560 
561 
562 
563 void
announceGameReset()564 MetaserverClient::announceGameReset()
565 {
566 	m_channel->enqueueOutgoingMessage(ResetGameMessage());
567 }
568 
569 
570 
571 void
announceGameDeleted()572 MetaserverClient::announceGameDeleted()
573 {
574 	if (m_gameAnnounced)
575 	{
576 		m_channel->enqueueOutgoingMessage(RemoveGameMessage());
577 		m_gameAnnounced = false;
578 	}
579 }
580 
style_code(char c)581 static 	inline bool style_code(char c)
582 {
583 	switch(tolower(c)) {
584 	case 'p':
585 	case 'b':
586 	case 'i':
587 	case 'l':
588 	case 'r':
589 	case 'c':
590 	case 's':
591 		return true;
592 	default:
593 		return false;
594 	}
595 }
596 
remove_formatting(const std::string & s)597 static std::string remove_formatting(const std::string &s)
598 {
599 	string temp;
600 	int i = 0;
601 	while (i < s.size()) {
602 		if (s[i] == '|' && style_code(s[i + 1]))
603 			i += 2;
604 		else
605 			temp += s[i++];
606 	}
607 
608 	return temp;
609 }
610 
ignore(const std::string & name)611 void MetaserverClient::ignore(const std::string& name)
612 {
613 	std::string cleaned_name = remove_formatting(name);
614 	if (s_ignoreNames.find(cleaned_name) != s_ignoreNames.end())
615 	{
616 		s_ignoreNames.erase(cleaned_name);
617 		if (m_notificationAdapter) {
618 			m_notificationAdapter->receivedLocalMessage("Removing \"" + cleaned_name + "\" from the ignore list");
619 		}
620 	} else {
621 		s_ignoreNames.insert(cleaned_name);
622 		if (m_notificationAdapter) {
623 			m_notificationAdapter->receivedLocalMessage("Adding \"" + cleaned_name + "\" to the ignore list");
624 		}
625 	}
626 }
627 
ignore(MetaserverPlayerInfo::IDType id)628 void MetaserverClient::ignore(MetaserverPlayerInfo::IDType id)
629 {
630 	// find the guy's name, remove the \266 if it's there
631 	const MetaserverPlayerInfo *player = m_playersInRoom.find(id);
632 	if (player)
633 	{
634 		std::string name = player->name();
635 		if (name[0] == '\260') name.erase(name.begin());
636 
637 		ignore(name);
638 	}
639 }
640 
is_ignored(MetaserverPlayerInfo::IDType id)641 bool MetaserverClient::is_ignored(MetaserverPlayerInfo::IDType id)
642 {
643 	std::string name;
644 	const MetaserverPlayerInfo *player = m_playersInRoom.find(id);
645 	if (player)
646 	{
647 		std::string name = player->name();
648 		if (name[0] == '\260') name.erase(name.begin());
649 
650 		return (s_ignoreNames.find(remove_formatting(name)) != s_ignoreNames.end());
651 	}
652 	else
653 	{
654 		// uh, what do we do?
655 		return false;
656 	}
657 }
658 
659 
660 void
syncGames()661 MetaserverClient::syncGames()
662 {
663 	m_channel->enqueueOutgoingMessage(SyncGamesMessage());
664 }
665 
666 
setAway(bool away,const std::string & away_message)667 void MetaserverClient::setAway(bool away, const std::string& away_message)
668 {
669 	m_channel->enqueueOutgoingMessage(NameAndTeamMessage(m_playerName, m_teamName, away, away_message));
670 }
671 
setMode(uint16 mode,const std::string & session_id)672 void MetaserverClient::setMode(uint16 mode, const std::string& session_id)
673 {
674 	m_channel->enqueueOutgoingMessage(SetPlayerModeMessage(mode, session_id));
675 }
676 
677 void
setPlayerName(const std::string & name)678 MetaserverClient::setPlayerName(const std::string& name)
679 {
680 	m_playerName = name;
681 }
682 
683 
684 
685 void
setPlayerTeamName(const std::string & name)686 MetaserverClient::setPlayerTeamName(const std::string& name)
687 {
688 	m_teamName = name;
689 }
690 
691 
692 
~MetaserverClient()693 MetaserverClient::~MetaserverClient()
694 {
695 	disconnect();
696 	s_instances.erase(this);
697 }
698 
699 #endif // !defined(DISABLE_NETWORKING)
700