1 // NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
25 #include <nel/misc/types_nl.h>
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"
51 using namespace NLMISC
;
52 using namespace NLNET
;
59 static CCallbackServer
*ClientsServer
= 0;
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
91 string cpassword
, application
;
93 msgin
.serial (cpassword
);
94 msgin
.serial (application
);
101 //const CInetAddress &ia = netbase.hostAddress (from);
103 reason
= sqlQuery("select * from user where Login='"+login
.toUtf8()+"'", nbrow
, row
, result
);
104 if(!reason
.empty()) break;
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());
119 reason
= toString("Login '%s' doesn't exist", login
.toUtf8().c_str());
126 reason
= toString("Too much login '%s' exists", login
.toUtf8().c_str());
130 // now the user is on the database
132 NLMISC::fromString(row
[0], uid
);
134 if(cpassword
!= row
[2])
136 reason
= toString("Bad password");
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");
149 CUnifiedNetwork::getInstance()->send("WS", msgout
);
151 reason
= "You are already connected.";
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
172 // serial the name of the shard
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
);
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
209 reason
= sqlQuery("select UId, Cookie, Privilege, ExtendedPrivilege from user where State='Authorized'", nbrow
, row
, result
);
210 if(!reason
.empty()) break;
214 reason
= "You are not authorized to select a shard";
222 lc
.setFromString(row
[1]);
223 if(lc
.getUserAddr() == (uint32
)(uintptr_t)from
)
228 row
= mysql_fetch_row(result
);
233 reason
= "You are not authorized to select a shard";
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
244 msgin
.serial(shardid
);
246 reason
= sqlQuery("select * from shard where ShardId="+toString(shardid
), nbrow
, row
, result
);
247 if(!reason
.empty()) break;
251 reason
= "This shard is not available";
255 sint32 s
= findShard (shardid
);
258 reason
= "Cannot find the shard internal id";
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;
271 reason
= "Cannot retrieve the username";
276 name
.fromUtf8(row
[0]);
279 lc
.setFromString(cookie
);
280 CMessage
msgout("CS");
281 msgout
.serial(lc
, name
, priv
, expriv
);
282 CUnifiedNetwork::getInstance()->send(sid
, msgout
);
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 ());
317 reason
= sqlQuery("select UId, State, Cookie from user where State!='Offline'", nbrow
, row
, result
);
318 if(!reason
.empty()) return;
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]));
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");
364 msgin
.serial (reason
);
365 msgin
.serial (cookie
);
369 nldebug("SCS from WS failed: %s", reason
.c_str());
370 sqlQuery("update user set state='Offline', ShardId=-1, Cookie='' where Cookie='" + cookie
.setToString() + "'");
377 reason
= sqlQuery("select UId, Cookie, Privilege, ExtendedPrivilege from user where Cookie='"+cookie
.setToString()+"'", nbrow
, row
, result
);
378 if(!reason
.empty()) break;
381 reason
= "More than one row was found";
382 nldebug("SCS from WS failed with duplicate cookies, sending disconnect messages.");
383 // disconnect them all
386 CMessage
msgout("DC");
387 uint32 uid
= atoui(row
[0]);
389 CUnifiedNetwork::getInstance()->send("WS", msgout
);
390 row
= mysql_fetch_row(result
);
395 msgout
.serial(reason
);
396 string str
= cookie
.setToString ();
400 msgout
.serial (addr
);
401 ClientsServer
->send (msgout
, (TSockId
)cookie
.getUserAddr ()); // FIXME: 64-bit
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();
446 nlwarning ("Error during update: '%s'", e
.what ());
450 void connectionClientRelease ()
452 nlassert(ClientsServer
!= 0);
454 delete ClientsServer
;