BF2MC-Matchmaker
gpsp/client.cpp
1 #include <unistd.h>
2 #include <iostream>
3 #include <iomanip>
4 #include <algorithm>
5 
6 #include <settings.h>
7 #include <logger.h>
8 #include <server.h>
9 #include <globals.h>
10 #include <util.h>
11 #include <database.h>
12 
13 #include <gpsp/client.h>
14 
15 typedef void (GPSP::Client::*RequestActionFunc)(const GameSpy::Parameter&) const;
16 
17 static std::map<std::string, RequestActionFunc> mRequestActions =
18 {
19  { "nicks", &GPSP::Client::requestNicks }, // Done
20  { "valid", &GPSP::Client::requestValid }, // Done
21  { "newuser", &GPSP::Client::requestNewUser }, // Done
22  { "search", &GPSP::Client::requestSearch }, // Done
23 };
24 
25 GPSP::Client::Client(int socket, struct sockaddr_in address)
26 {
27  this->_socket = socket;
28  this->_address = address;
29  this->UpdateLastRecievedTime();
30 }
31 
33 {
34  this->Disconnect();
35 }
36 
38 {
39  while(true)
40  {
41  std::string request;
42  std::vector<char> buffer(4096, 0);
43 
44  int recv_size = read(this->_socket, &(buffer[0]), 4096);
45 
46  // If error or no data is recieved we end the connection
47  if(recv_size <= 0)
48  {
49  break;
50  }
51 
52  // Resize buffer
53  buffer.resize(recv_size);
54 
55  this->UpdateLastRecievedTime();
56 
57  request = Util::Buffer::ToString(buffer);
58 
59  this->_LogTransaction("-->", request);
60 
61  this->onRequest(request);
62  }
63 
64  this->Disconnect();
65 }
66 
68 {
69  this->Close();
70  g_gpsp_server->onClientDisconnect(*this);
71 }
72 
73 // Events
74 
75 void GPSP::Client::onRequest(const std::string& request)
76 {
77  GameSpy::Parameter parameter = GameSpy::Request2Parameter(request);
78 
79  // Find function name
80  std::string action = parameter[0];
81 
82  auto it = mRequestActions.find(action);
83  if (it != mRequestActions.end())
84  {
85  // Get Function address
86  RequestActionFunc func = it->second;
87 
88  // Execute action function with class object.
89  (this->*(func))(parameter);
90  }
91  else
92  {
93  Logger::warning("action \"" + action + "\" not implemented!", Server::Type::GPSP);
94 
95  this->Disconnect();
96  }
97 }
98 
99 /*
100  Request: \nicks\\email\help0001@gmail.com\pass\12345\namespaceid\13\gamename\bfield1942ps2\final\
101  Response: \nr\0\nick\IamLupo@6507BAD7\uniquenick\IamLupo\ndone\\final\
102 
103  Request:
104  email: (string)
105  pass: (string)
106  namespaceid: (?integer?)
107  gamename: (string)
108 
109  Return:
110  nr: (integer)
111  nick: (string)
112  uniquenick: (string)
113 */
114 
115 void GPSP::Client::requestNicks(const GameSpy::Parameter& parameter) const
116 {
117  if(parameter.size() != 11 ||
118  !Util::isAscii(parameter[3]) ||
119  !Util::isAscii(parameter[5]))
120  {
121  return;
122  }
123 
124  std::string email = parameter[3];
125  std::string password = parameter[5];
126 
127  // Filter email
128  if(email.size() > 50)
129  {
130  email = email.substr(0, 49);
131  }
132 
133  Battlefield::Players players;
134 
135  // Get players from database by email
136  g_database->queryPlayersByEmail(players, email);
137 
138  Logger::info(this->GetAddress() + " --> Nicks ", Server::Type::GPSP);
139 
140  // Check password
141  bool correct_password = false;
142  std::string md5password = Util::MD5hash(password);
143  for(Battlefield::Player player : players)
144  {
145  if(player.GetPassword() == md5password)
146  {
147  correct_password = true;
148  }
149  }
150 
151  // If no database user exist or password isn't correct
152  if(players.size() == 0 || !correct_password)
153  {
154  // Convert parameter to string response
155  std::string response = GameSpy::Parameter2Response({"nr", "260", "ndone", "", "final"});
156 
157  // Send
158  this->Send(response);
159 
160  // Log
161  this->_LogTransaction("<--", response);
162 
163  return;
164  }
165 
166  GameSpy::Parameter response_parameter = { "nr", "0" };
167 
168  for(Battlefield::Player player : players)
169  {
170  std::string uniquenick = player.GetUniquenick();
171  std::string nick = player.GetNick();
172 
173  // Update unique nick if is in clan
174  Battlefield::Clan clan;
175  g_database->queryClanByProfileId(clan, player);
176  if(clan.GetClanId() != -1)
177  {
178  uniquenick = clan.GetTag() + " " + uniquenick;
179  }
180 
181  response_parameter.push_back("nick");
182  response_parameter.push_back(nick);
183 
184  response_parameter.push_back("uniquenick");
185  response_parameter.push_back(uniquenick);
186 
187  Logger::info(this->GetAddress() + " <-- " + uniquenick, Server::Type::GPSP);
188  }
189 
190  response_parameter.push_back("ndone");
191  response_parameter.push_back("");
192  response_parameter.push_back("final");
193 
194  // Convert parameter to string response
195  std::string response = GameSpy::Parameter2Response(response_parameter);
196 
197  // Send
198  this->Send(response);
199 
200  // Log
201  this->_LogTransaction("<--", response);
202 }
203 
204 /*
205  Request: \valid\\email\help0001@gmail.com\gamename\bfield1942ps2\final\
206  Response: \vr\1\final\
207 
208  Request:
209  email: (string)
210  gamename: (string)
211 
212  Response:
213  vr: (integer):
214  1 = good
215  0 = bad
216 
217 */
218 
219 void GPSP::Client::requestValid(const GameSpy::Parameter& parameter) const
220 {
221  if(parameter.size() != 7 ||
222  !Util::isAscii(parameter[3]))
223  {
224  return;
225  }
226 
227  std::string email = Util::tolower(parameter[3]);
228 
229  Logger::info(this->GetAddress() + " --> Valid: " + email, Server::Type::GPSP);
230 
231  Battlefield::Players players;
232  std::string response;
233 
234  // Get players from database by email
235  g_database->queryPlayersByEmail(players, email);
236 
237  if(players.size() >= 1)
238  {
239  response = GameSpy::Parameter2Response({
240  "vr", "1", "final"
241  });
242  }
243  else
244  {
245  response = GameSpy::Parameter2Response({
246  "vr", "0", "final"
247  });
248  }
249 
250  this->Send(response);
251 
252  this->_LogTransaction("<--", response);
253 }
254 
255 /*
256  Request: \newuser\\nick\IamLupo@6507BAD7\email\help000@gmail.com\pass\12345\productID
257  \10307\namespaceid\13\uniquenick\IamLupo\gamename\bfield1942ps2\final\
258  Response: \nur\0\pid\10037049\final\
259 
260  Request:
261  nick: (string)
262  email: (string)
263  pass: (string)
264  productID: (integer)
265  namespaceid: (integer)
266  uniquenick: (string)
267  gamename: (string)
268 
269  Response:
270  nur: (integer)
271  0 = good
272  516 = bad username
273  pid: (integer)
274 */
275 
276 void GPSP::Client::requestNewUser(const GameSpy::Parameter& parameter) const
277 {
278  if(parameter.size() != 17 ||
279  !Util::isAscii(parameter[3]) ||
280  !Util::isAscii(parameter[5]) ||
281  !Util::isAscii(parameter[7]) ||
282  !Util::isAscii(parameter[13]))
283  {
284  return;
285  }
286 
287  // Fix: Else account with capital email address is stored in database.
288  // If this happends then he cant login anymore because magicly the ps2 desides that it cant send capital email address xD
289  std::string nick = parameter[3];
290  std::string email = Util::tolower(parameter[5]);
291  std::string password = parameter[7];
292  std::string uniquenick = parameter[13];
293 
294  // Filter email
295  if(email.size() > 50)
296  {
297  email = email.substr(0, 49);
298  }
299 
300  Logger::info(this->GetAddress() + " --> NewUser: " + nick + ", " + uniquenick + ", " + email, Server::Type::GPSP);
301 
302  // Remove clan name out of uniquenick
303  Battlefield::Player player;
304  player.SetUniquenickWithoutClanTag(uniquenick);
305  uniquenick = player.GetUniquenick();
306 
307  // Check nick and uniquenick
308  if(nick.size() < 3 or uniquenick.size() < 3)
309  {
310  std::string response = GameSpy::Parameter2Response({
311  "nur", "516",
312  "pid", "-1",
313  "final"
314  });
315 
316  this->Send(response);
317 
318  this->_LogTransaction("<--", response);
319 
320  return;
321  }
322 
323  // Get players by email
324  Battlefield::Players players;
325  g_database->queryPlayersByEmailOrUniquenick(players, email, uniquenick);
326 
327  // Check Player exist with same email or uniquenick already
328  for(Battlefield::Player player : players)
329  {
330  // Check uniquenick
331  if(player.GetUniquenick() == uniquenick)
332  {
333  std::string response = GameSpy::Parameter2Response({
334  "nur", "516",
335  "pid", std::to_string(player.GetProfileId()),
336  "final"
337  });
338 
339  this->Send(response);
340 
341  this->_LogTransaction("<--", response);
342 
343  return;
344  }
345  }
346 
347  // Create new dbuser
348  Battlefield::Player new_player;
349 
350  new_player.SetNick(nick);
351  new_player.SetEmail(email);
352  new_player.SetUniquenick(uniquenick);
353  new_player.SetPassword(password);
354 
355  // Check if player exist with same email
356  if(players.size() >= 1)
357  {
358  // Copy there userid
359  new_player.SetUserId(players[0].GetUserId());
360  }
361  else
362  {
363  // New userid
364  g_database->queryPlayerNewUserID(new_player);
365  }
366 
367  // Insert player in database
368  g_database->insertPlayer(new_player);
369 
370  // Insert player status
371  g_database->insertPlayerStats(new_player);
372 
373  std::string response = GameSpy::Parameter2Response({
374  "nur", "0",
375  "pid", std::to_string(new_player.GetProfileId()),
376  "final"
377  });
378 
379  this->Send(response);
380 
381  this->_LogTransaction("<--", response);
382 }
383 
384 /*
385  Request:
386  \search\\sesskey\1\profileid\10036819\namespaceid\13\uniquenick\IamLupo\gamename\bfield1942ps2\final\
387  Response:
388  \bsr\10036819\nick\IamLupo@6507BAD7\firstname\\lastname\\email\[hidden]\uniquenick\IamLupo\namespaceid\13\bsrdone\\final\
389 
390  \bsrdone\\final\
391 */
392 
393 void GPSP::Client::requestSearch(const GameSpy::Parameter& parameter) const
394 {
395  if(parameter.size() != 13 ||
396  !Util::isAscii(parameter[9]))
397  {
398  return;
399  }
400 
401  std::string uniquenick = parameter[9];
402 
403  Logger::info(this->GetAddress() + " --> Search: " + uniquenick, Server::Type::GPSP);
404 
405  Battlefield::Player player;
406  player.SetUniquenick(uniquenick);
407 
408  // Get player information
409  g_database->queryPlayerByUniquenick(player);
410 
411  std::string response;
412  GameSpy::Parameter response_parameter;
413 
414  // User found
415  if(player.GetProfileId() != -1)
416  {
417  response_parameter = {
418  "bsr", std::to_string(player.GetProfileId()),
419  "nick", player.GetNick(),
420  "firstname", "",
421  "lastname", "",
422  "email", "[hidden]",
423  "uniquenick", player.GetUniquenick(),
424  "namespaceid", "13",
425  "bsrdone", "",
426  "final"
427  };
428  }
429 
430  response_parameter.push_back("bsrdone");
431  response_parameter.push_back("");
432  response_parameter.push_back("final");
433 
434  response = GameSpy::Parameter2Response(response_parameter);
435 
436  this->Send(response);
437 
438  this->_LogTransaction("<--", response);
439 }
440 
441 // Private functions
442 
443 void GPSP::Client::_LogTransaction(const std::string& direction, const std::string& response) const
444 {
445  std::shared_lock<std::shared_mutex> guard(g_settings_mutex); // settings lock (read)
446 
447  if ((g_logger_mode & Logger::Mode::Development) == 0)
448  {
449  return;
450  }
451 
452  bool show_console = (g_settings["gpsp"]["show_requests"].asBool() && direction == "-->") ||
453  (g_settings["gpsp"]["show_responses"].asBool() && direction == "<--");
454 
455  Logger::info(this->GetAddress() + " " + direction + " " + response,
456  Server::Type::GPSP, show_console);
457 }
458 
Represents a clan in the Battlefield game.
Definition: clan.h:54
Represents a player with extended statistics.
Definition: player.h:38
bool insertPlayerStats(const Battlefield::Player &player)
Inserts the statistics of a player into the database.
bool queryPlayersByEmail(Battlefield::Players &players, const std::string &email)
Queries players by their email addresses.
bool queryPlayerByUniquenick(Battlefield::Player &player)
Queries a player by their unique nickname.
bool queryPlayersByEmailOrUniquenick(Battlefield::Players &players, const std::string &email, const std::string &uniquenick)
Queries players by their email addresses or unique nicknames.
bool queryClanByProfileId(Battlefield::Clan &clan, const Battlefield::Player &player)
Queries a clan by a player's profile ID.
bool insertPlayer(Battlefield::Player &player)
Inserts a player into the database.
bool queryPlayerNewUserID(Battlefield::Player &player)
Queries a new user ID for a player.
Client class for GPSP protocol.
Definition: gpsp/client.h:13
void _LogTransaction(const std::string &direction, const std::string &response) const
Log a transaction with direction and response.
void requestNicks(const GameSpy::Parameter &parameter) const
Request nicknames.
void Disconnect()
Disconnect the client.
Definition: gpsp/client.cpp:67
Client(int socket, struct sockaddr_in address)
Construct a new Client object.
Definition: gpsp/client.cpp:25
void requestValid(const GameSpy::Parameter &parameter) const
Request validation.
void requestNewUser(const GameSpy::Parameter &parameter) const
Request a new user.
void onRequest(const std::string &msg)
Handle incoming requests.
Definition: gpsp/client.cpp:75
void Listen()
Listen for incoming messages.
Definition: gpsp/client.cpp:37
void requestSearch(const GameSpy::Parameter &parameter) const
Search for users.
~Client()
Destroy the Client object.
Definition: gpsp/client.cpp:32
void UpdateLastRecievedTime()
Updates the last received time to the current system time.
Definition: socket.cpp:112
struct sockaddr_in _address
Definition: socket.h:18
int _socket
Definition: socket.h:17
void onClientDisconnect(const Net::Socket &client)
Called when a client disconnects from the server.
Definition: server.cpp:336