Merge branch 'ryzom/marauder-gameplay' into main/gingo-test
[ryzomcore.git] / nelns / login_service / connection_client.cpp
blob6ed1b48a4f3294ff52ceff679fc762d703512528
1 // NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
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 Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 // Includes
25 #include <nel/misc/types_nl.h>
27 #include <math.h>
28 #include <stdio.h>
29 #include <ctype.h>
31 #include <map>
32 #include <vector>
34 #include <nel/misc/log.h>
35 #include <nel/misc/debug.h>
36 #include <nel/misc/displayer.h>
37 #include <nel/misc/config_file.h>
39 #include <nel/net/service.h>
40 #include <nel/net/login_cookie.h>
42 #include "login_service.h"
43 #include "mysql_helper.h"
47 // Namespaces
50 using namespace std;
51 using namespace NLMISC;
52 using namespace NLNET;
56 // Variables
59 static CCallbackServer *ClientsServer = 0;
63 // Functions
66 void sendToClient(CMessage &msgout, TSockId sockId)
68 nlassert(ClientsServer != 0);
69 ClientsServer->send(msgout, sockId);
72 void string_escape(string &str)
74 string::size_type pos = 0;
75 while((pos=str.find('\'')) != string::npos)
77 str.replace(pos, 1, " ");
81 static void cbClientVerifyLoginPassword(CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
84 // S03: check the validity of the client login/password and send "VLP" message to client
87 // reason is empty if everything goes right or contains the reason of the failure
88 string reason;
89 sint32 uid = -1;
90 ucstring login;
91 string cpassword, application;
92 msgin.serial (login);
93 msgin.serial (cpassword);
94 msgin.serial (application);
96 breakable
98 CMysqlResult result;
99 MYSQL_ROW row;
100 sint32 nbrow;
101 //const CInetAddress &ia = netbase.hostAddress (from);
102 retry:
103 reason = sqlQuery("select * from user where Login='"+login.toUtf8()+"'", nbrow, row, result);
104 if(!reason.empty()) break;
106 if(nbrow == 0)
108 if(IService::getInstance ()->ConfigFile.getVar("AcceptUnknownUsers").asInt () == 1)
110 // we accept new users, add it
111 string query = "insert into user (Login, Password) values ('"+login.toUtf8()+"', '"+cpassword+"')";
112 reason = sqlQuery(query, nbrow, row, result);
113 if (!reason.empty()) break;
114 nlinfo("The user %s was inserted in the database for the application '%s'!", login.toUtf8().c_str(), application.c_str());
115 goto retry;
117 else
119 reason = toString("Login '%s' doesn't exist", login.toUtf8().c_str());
120 break;
124 if(nbrow != 1)
126 reason = toString("Too much login '%s' exists", login.toUtf8().c_str());
127 break;
130 // now the user is on the database
132 NLMISC::fromString(row[0], uid);
134 if(cpassword != row[2])
136 reason = toString("Bad password");
137 break;
140 if(row[4] != string("Offline"))
142 // 2 players are trying to play with the same id, disconnect all
143 //reason = sqlQuery(string("update user set state='Offline', ShardId=-1 where UId=")+uid);
144 //if(!reason.empty()) break;
146 // send a message to the already connected player to disconnect
147 CMessage msgout("DC");
148 msgout.serial(uid);
149 CUnifiedNetwork::getInstance()->send("WS", msgout);
151 reason = "You are already connected.";
152 break;
155 CLoginCookie c;
156 c.set((uint32)(uintptr_t)from, rand(), uid);
158 reason = sqlQuery("update user set state='Authorized', Cookie='"+c.setToString()+"' where UId=" + NLMISC::toString(uid));
159 if(!reason.empty()) break;
161 reason = sqlQuery("select * from shard where Online>0 and ClientApplication='"+application+"'", nbrow, row, result);
162 if(!reason.empty()) break;
164 // Send success message
165 CMessage msgout ("VLP");
166 msgout.serial(reason);
167 msgout.serial(nbrow);
169 // send address and name of all online shards
170 while(row != 0)
172 // serial the name of the shard
173 ucstring shardname;
174 shardname.fromUtf8(row[3]);
175 uint8 nbplayers = atoi(row[2]);
176 uint32 sid = atoi(row[0]);
177 msgout.serial (shardname, nbplayers, sid);
178 row = mysql_fetch_row(result);
180 netbase.send (msgout, from);
181 netbase.authorizeOnly ("CS", from);
183 return;
186 // Manage error
187 CMessage msgout("VLP");
188 if(reason.empty()) reason = "Unknown error";
189 msgout.serial(reason);
190 netbase.send(msgout, from);
191 // FIX: On GNU/Linux, when we disconnect now, sometime the other side doesn't receive the message sent just before.
192 // So it is the other side to disconnect
193 // netbase.disconnect (from);
196 static void cbClientChooseShard(CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
199 // S06: receive "CS" message from client
202 string reason;
204 breakable
206 CMysqlResult result;
207 MYSQL_ROW row;
208 sint32 nbrow;
209 reason = sqlQuery("select UId, Cookie, Privilege, ExtendedPrivilege from user where State='Authorized'", nbrow, row, result);
210 if(!reason.empty()) break;
212 if(nbrow == 0)
214 reason = "You are not authorized to select a shard";
215 break;
218 bool ok = false;
219 while(row != 0)
221 CLoginCookie lc;
222 lc.setFromString(row[1]);
223 if(lc.getUserAddr() == (uint32)(uintptr_t)from)
225 ok = true;
226 break;
228 row = mysql_fetch_row(result);
231 if(!ok)
233 reason = "You are not authorized to select a shard";
234 break;
237 string uid = row[0];
238 string cookie = row[1];
239 string priv = row[2];
240 string expriv = row[3];
242 // it is ok, so we find the wanted shard
243 sint32 shardid;
244 msgin.serial(shardid);
246 reason = sqlQuery("select * from shard where ShardId="+toString(shardid), nbrow, row, result);
247 if(!reason.empty()) break;
249 if(nbrow == 0)
251 reason = "This shard is not available";
252 break;
255 sint32 s = findShard (shardid);
256 if (s == -1)
258 reason = "Cannot find the shard internal id";
259 break;
261 TServiceId sid = Shards[s].SId;
263 reason = sqlQuery("update user set State='Waiting', ShardId="+toString(shardid)+" where UId="+uid);
264 if(!reason.empty()) break;
266 reason = sqlQuery("select Login from user where UId="+uid, nbrow, row, result);
267 if(!reason.empty()) break;
269 if(nbrow == 0)
271 reason = "Cannot retrieve the username";
272 break;
275 ucstring name;
276 name.fromUtf8(row[0]);
278 CLoginCookie lc;
279 lc.setFromString(cookie);
280 CMessage msgout("CS");
281 msgout.serial(lc, name, priv, expriv);
282 CUnifiedNetwork::getInstance()->send(sid, msgout);
284 return;
287 // Manage error
288 CMessage msgout("SCS");
289 msgout.serial(reason);
290 netbase.send(msgout, from);
291 // FIX: On GNU/Linux, when we disconnect now, sometime the other side doesn't receive the message sent just before.
292 // So it's the other side to disconnect
293 // netbase.disconnect (from);
296 static void cbClientConnection (TSockId from, void *arg)
298 CCallbackNetBase *cnb = ClientsServer;
299 const CInetAddress &ia = cnb->hostAddress (from);
300 nldebug("new client connection: %s", ia.asString ().c_str ());
301 Output->displayNL ("CCC: Connection from %s", ia.asString ().c_str ());
302 cnb->authorizeOnly ("VLP", from);
305 static void cbClientDisconnection (TSockId from, void *arg)
307 CCallbackNetBase *cnb = ClientsServer;
308 const CInetAddress &ia = cnb->hostAddress (from);
310 nldebug("new client disconnection: %s", ia.asString ().c_str ());
312 string reason;
314 CMysqlResult result;
315 MYSQL_ROW row;
316 sint32 nbrow;
317 reason = sqlQuery("select UId, State, Cookie from user where State!='Offline'", nbrow, row, result);
318 if(!reason.empty()) return;
320 if(nbrow == 0)
322 return;
325 while(row != 0)
327 CLoginCookie lc;
328 string str = row[2];
329 if(!str.empty())
331 lc.setFromString(str);
332 if(lc.getUserAddr() == (uint32)(uintptr_t)from)
334 // got it, if he is not in waiting state, it s not normal, remove all
335 if(row[1] == string("Authorized"))
336 sqlQuery("update user set state='Offline', ShardId=-1, Cookie='' where UId="+string(row[0]));
337 return;
340 row = mysql_fetch_row(result);
345 const TCallbackItem ClientCallbackArray[] =
347 { "VLP", cbClientVerifyLoginPassword },
348 { "CS", cbClientChooseShard },
352 static void cbWSShardChooseShard (CMessage &msgin, const std::string &serviceName, TServiceId sid)
355 // S10: receive "SCS" message from WS
357 CMessage msgout("SCS");
359 CLoginCookie cookie;
360 string reason;
362 breakable
364 msgin.serial (reason);
365 msgin.serial (cookie);
367 if(!reason.empty())
369 nldebug("SCS from WS failed: %s", reason.c_str());
370 sqlQuery("update user set state='Offline', ShardId=-1, Cookie='' where Cookie='" + cookie.setToString() + "'");
371 break;
374 CMysqlResult result;
375 MYSQL_ROW row;
376 sint32 nbrow;
377 reason = sqlQuery("select UId, Cookie, Privilege, ExtendedPrivilege from user where Cookie='"+cookie.setToString()+"'", nbrow, row, result);
378 if(!reason.empty()) break;
379 if(nbrow != 1)
381 reason = "More than one row was found";
382 nldebug("SCS from WS failed with duplicate cookies, sending disconnect messages.");
383 // disconnect them all
384 while(row != 0)
386 CMessage msgout("DC");
387 uint32 uid = atoui(row[0]);
388 msgout.serial(uid);
389 CUnifiedNetwork::getInstance()->send("WS", msgout);
390 row = mysql_fetch_row(result);
392 break;
395 msgout.serial(reason);
396 string str = cookie.setToString ();
397 msgout.serial (str);
398 string addr;
399 msgin.serial (addr);
400 msgout.serial (addr);
401 ClientsServer->send (msgout, (TSockId)cookie.getUserAddr ()); // FIXME: 64-bit
402 return;
404 msgout.serial(reason);
405 ClientsServer->send (msgout, (TSockId)cookie.getUserAddr ()); // FIXME: 64-bit
408 static const TUnifiedCallbackItem WSCallbackArray[] =
410 { "SCS", cbWSShardChooseShard },
418 void connectionClientInit ()
420 nlassert(ClientsServer == 0);
422 ClientsServer = new CCallbackServer();
423 nlassert(ClientsServer != 0);
425 uint16 port = (uint16) IService::getInstance ()->ConfigFile.getVar ("ClientsPort").asInt();
426 ClientsServer->init (port);
428 ClientsServer->addCallbackArray(ClientCallbackArray, sizeof(ClientCallbackArray)/sizeof(ClientCallbackArray[0]));
429 ClientsServer->setConnectionCallback(cbClientConnection, 0);
430 ClientsServer->setDisconnectionCallback(cbClientDisconnection, 0);
432 // catch the messages from Welcome Service to know if the user can connect or not
433 CUnifiedNetwork::getInstance ()->addCallbackArray (WSCallbackArray, sizeof(WSCallbackArray)/sizeof(WSCallbackArray[0]));
436 void connectionClientUpdate ()
438 nlassert(ClientsServer != 0);
442 ClientsServer->update();
444 catch (Exception &e)
446 nlwarning ("Error during update: '%s'", e.what ());
450 void connectionClientRelease ()
452 nlassert(ClientsServer != 0);
454 delete ClientsServer;
455 ClientsServer = 0;