1 /*
2 * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
3 *
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 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include "stdinc.h"
20
21 #include "ClientManager.h"
22
23 #include "ShareManager.h"
24 #include "SearchManager.h"
25 #include "ConnectionManager.h"
26 #include "CryptoManager.h"
27 #include "FavoriteManager.h"
28 #include "SimpleXML.h"
29 #include "UserCommand.h"
30 #include "SearchResult.h"
31
32 #include "AdcHub.h"
33 #include "NmdcHub.h"
34 #ifdef WITH_DHT
35 #include "dht/DHT.h"
36 #endif
37 #include "FinishedManager.h"//sdc
38 #include "QueueManager.h"
39
40 namespace dcpp {
41
getClient(const string & aHubURL)42 Client* ClientManager::getClient(const string& aHubURL) {
43 Client* c;
44 if(strncmp("adc://", aHubURL.c_str(), 6) == 0) {
45 c = new AdcHub(aHubURL, false);
46 } else if(strncmp("adcs://", aHubURL.c_str(), 7) == 0) {
47 c = new AdcHub(aHubURL, true);
48 } else if(strncmp("nmdcs://", aHubURL.c_str(), 8) == 0) {
49 c = new NmdcHub(aHubURL, true);
50 } else {
51 c = new NmdcHub(aHubURL, false);
52 }
53
54 {
55 Lock l(cs);
56 clients.push_back(c);
57 }
58
59 c->addListener(this);
60
61 return c;
62 }
63
putClient(Client * aClient)64 void ClientManager::putClient(Client* aClient) {
65 fire(ClientManagerListener::ClientDisconnected(), aClient);
66 aClient->removeListeners();
67
68 {
69 Lock l(cs);
70 clients.remove(aClient);
71 }
72 aClient->shutdown();
73 delete aClient;
74 }
75
getUserCount() const76 size_t ClientManager::getUserCount() const {
77 Lock l(cs);
78 return onlineUsers.size();
79 }
80
getHubs(const CID & cid,const string & hintUrl)81 StringList ClientManager::getHubs(const CID& cid, const string& hintUrl) {
82 return getHubs(cid, hintUrl, FavoriteManager::getInstance()->isPrivate(hintUrl));
83 }
84
getHubNames(const CID & cid,const string & hintUrl)85 StringList ClientManager::getHubNames(const CID& cid, const string& hintUrl) {
86 return getHubNames(cid, hintUrl, FavoriteManager::getInstance()->isPrivate(hintUrl));
87 }
88
getNicks(const CID & cid,const string & hintUrl)89 StringList ClientManager::getNicks(const CID& cid, const string& hintUrl) {
90 return getNicks(cid, hintUrl, FavoriteManager::getInstance()->isPrivate(hintUrl));
91 }
92
getHubs(const CID & cid,const string & hintUrl,bool priv)93 StringList ClientManager::getHubs(const CID& cid, const string& hintUrl, bool priv) {
94 Lock l(cs);
95 StringList lst;
96 if(!priv) {
97 OnlinePairC op = onlineUsers.equal_range(cid);
98 for(auto i = op.first; i != op.second; ++i) {
99 lst.push_back(i->second->getClient().getHubUrl());
100 }
101 } else {
102 OnlineUser* u = findOnlineUserHint(cid, hintUrl);
103 if(u)
104 lst.push_back(u->getClient().getHubUrl());
105 }
106 return lst;
107 }
108
getHubNames(const CID & cid,const string & hintUrl,bool priv)109 StringList ClientManager::getHubNames(const CID& cid, const string& hintUrl, bool priv) {
110 Lock l(cs);
111 StringList lst;
112 if(!priv) {
113 OnlinePairC op = onlineUsers.equal_range(cid);
114 for(auto i = op.first; i != op.second; ++i) {
115 lst.push_back(i->second->getClient().getHubName());
116 }
117 } else {
118 OnlineUser* u = findOnlineUserHint(cid, hintUrl);
119 if(u)
120 lst.push_back(u->getClient().getHubName());
121 }
122 return lst;
123 }
124
getNicks(const CID & cid,const string & hintUrl,bool priv)125 StringList ClientManager::getNicks(const CID& cid, const string& hintUrl, bool priv) {
126 Lock l(cs);
127 StringSet ret;
128 if(!priv) {
129 OnlinePairC op = onlineUsers.equal_range(cid);
130 for(auto i = op.first; i != op.second; ++i) {
131 ret.insert(i->second->getIdentity().getNick());
132 }
133 } else {
134 OnlineUser* u = findOnlineUserHint(cid, hintUrl);
135 if(u)
136 ret.insert(u->getIdentity().getNick());
137 }
138 if(ret.empty()) {
139 auto i = nicks.find(cid);
140 if(i != nicks.end()) {
141 ret.insert(i->second.first);
142 } else {
143 // Offline perhaps?
144 ret.insert('{' + cid.toBase32() + '}');
145 }
146 }
147 return StringList(ret.begin(), ret.end());
148 }
149
getField(const CID & cid,const string & hint,const char * field) const150 string ClientManager::getField(const CID& cid, const string& hint, const char* field) const {
151 Lock l(cs);
152
153 OnlinePairC p;
154 auto u = findOnlineUserHint(cid, hint, p);
155 if(u) {
156 auto value = u->getIdentity().get(field);
157 if(!value.empty()) {
158 return value;
159 }
160 }
161
162 for(auto i = p.first; i != p.second; ++i) {
163 auto value = i->second->getIdentity().get(field);
164 if(!value.empty()) {
165 return value;
166 }
167 }
168
169 return Util::emptyString;
170 }
171
getConnection(const CID & cid) const172 string ClientManager::getConnection(const CID& cid) const {
173 Lock l(cs);
174 auto i = onlineUsers.find(cid);
175 if(i != onlineUsers.end()) {
176 return i->second->getIdentity().getConnection();
177 }
178 return _("Offline");
179 }
180
getAvailable() const181 int64_t ClientManager::getAvailable() const {
182 Lock l(cs);
183 int64_t bytes = 0;
184 for(auto i = onlineUsers.begin(); i != onlineUsers.end(); ++i) {
185 bytes += i->second->getIdentity().getBytesShared();
186 }
187
188 return bytes;
189 }
190
getSlots(const CID & cid) const191 uint8_t ClientManager::getSlots(const CID& cid) const {
192 Lock l(cs);
193 OnlineIterC i = onlineUsers.find(cid);
194 if(i != onlineUsers.end()) {
195 return static_cast<uint8_t>(Util::toInt(i->second->getIdentity().get("SL")));
196 }
197 return 0;
198 }
199
isConnected(const string & aUrl) const200 bool ClientManager::isConnected(const string& aUrl) const {
201 Lock l(cs);
202
203 for(auto i = clients.begin(); i != clients.end(); ++i) {
204 if((*i)->getHubUrl() == aUrl) {
205 return true;
206 }
207 }
208 return false;
209 }
210
findHub(const string & ipPort) const211 string ClientManager::findHub(const string& ipPort) const {
212 Lock l(cs);
213
214 string ip;
215 uint16_t port = 411;
216 Util::parseIpPort(ipPort, ip, port);
217
218 string url;
219 for(auto i = clients.begin(); i != clients.end(); ++i) {
220 const Client* c = *i;
221 if(c->getIp() == ip) {
222 // If exact match is found, return it
223 if(c->getPort() == port)
224 return c->getHubUrl();
225
226 // Port is not always correct, so use this as a best guess...
227 url = c->getHubUrl();
228 }
229 }
230
231 return url;
232 }
233
findHubEncoding(const string & aUrl) const234 string ClientManager::findHubEncoding(const string& aUrl) const {
235 Lock l(cs);
236
237 for(auto i = clients.begin(); i != clients.end(); ++i) {
238 if((*i)->getHubUrl() == aUrl) {
239 return (*i)->getEncoding();
240 }
241 }
242 return Text::hubDefaultCharset;
243 }
244
findLegacyUser(const string & aNick) const245 UserPtr ClientManager::findLegacyUser(const string& aNick) const noexcept {
246 if (aNick.empty())
247 return UserPtr();
248 Lock l(cs);
249
250 for(auto i = onlineUsers.begin(); i != onlineUsers.end(); ++i) {
251 const OnlineUser* ou = i->second;
252 if(ou->getUser()->isSet(User::NMDC) && Util::stricmp(ou->getIdentity().getNick(), aNick) == 0)
253 return ou->getUser();
254 }
255 return UserPtr();
256 }
257
getUser(const string & aNick,const string & aHubUrl)258 UserPtr ClientManager::getUser(const string& aNick, const string& aHubUrl) noexcept {
259 CID cid = makeCid(aNick, aHubUrl);
260 Lock l(cs);
261
262 auto ui = users.find(cid);
263 if(ui != users.end()) {
264 ui->second->setFlag(User::NMDC);
265 return ui->second;
266 }
267
268 UserPtr p(new User(cid));
269 p->setFlag(User::NMDC);
270 users.insert(make_pair(cid, p));
271
272 return p;
273 }
274
getUser(const CID & cid)275 UserPtr ClientManager::getUser(const CID& cid) noexcept {
276 Lock l(cs);
277 auto ui = users.find(cid);
278 if(ui != users.end()) {
279 return ui->second;
280 }
281
282 UserPtr p(new User(cid));
283 users.insert(make_pair(cid, p));
284 return p;
285 }
286
findUser(const CID & cid) const287 UserPtr ClientManager::findUser(const CID& cid) const noexcept {
288 Lock l(cs);
289 auto ui = users.find(cid);
290 return ui == users.end() ? 0 : ui->second;
291 }
292
isOp(const UserPtr & user,const string & aHubUrl) const293 bool ClientManager::isOp(const UserPtr& user, const string& aHubUrl) const {
294 Lock l(cs);
295 OnlinePairC p = onlineUsers.equal_range(user->getCID());
296 for(auto i = p.first; i != p.second; ++i) {
297 if(i->second->getClient().getHubUrl() == aHubUrl) {
298 return i->second->getIdentity().isOp();
299 }
300 }
301 return false;
302 }
303
makeCid(const string & aNick,const string & aHubUrl) const304 CID ClientManager::makeCid(const string& aNick, const string& aHubUrl) const noexcept {
305 TigerHash th;
306 th.update(aNick.c_str(), aNick.length());
307 th.update(aHubUrl.c_str(), aHubUrl.length());
308 // Construct hybrid CID from the bits of the tiger hash - should be
309 // fairly random, and hopefully low-collision
310 return CID(th.finalize());
311 }
312
putOnline(OnlineUser * ou)313 void ClientManager::putOnline(OnlineUser* ou) noexcept {
314 {
315 Lock l(cs);
316 onlineUsers.insert(make_pair(ou->getUser()->getCID(), ou));
317 }
318
319 if(!ou->getUser()->isOnline()) {
320 ou->getUser()->setFlag(User::ONLINE);
321 fire(ClientManagerListener::UserConnected(), ou->getUser());
322 }
323 }
324
putOffline(OnlineUser * ou,bool disconnect)325 void ClientManager::putOffline(OnlineUser* ou, bool disconnect) noexcept {
326 bool lastUser = false;
327 {
328 Lock l(cs);
329 OnlinePair op = onlineUsers.equal_range(ou->getUser()->getCID());
330 dcassert(op.first != op.second);
331 for(OnlineIter i = op.first; i != op.second; ++i) {
332 OnlineUser* ou2 = i->second;
333 if(ou == ou2) {
334 lastUser = (distance(op.first, op.second) == 1);
335 onlineUsers.erase(i);
336 break;
337 }
338 }
339 }
340
341 if(lastUser) {
342 UserPtr& u = ou->getUser();
343 u->unsetFlag(User::ONLINE);
344 if(disconnect)
345 ConnectionManager::getInstance()->disconnect(u);
346 fire(ClientManagerListener::UserDisconnected(), u);
347 }
348 }
349
findOnlineUserHint(const CID & cid,const string & hintUrl,OnlinePairC & p) const350 OnlineUser* ClientManager::findOnlineUserHint(const CID& cid, const string& hintUrl, OnlinePairC& p) const {
351 p = onlineUsers.equal_range(cid);
352 if(p.first == p.second) // no user found with the given CID.
353 return 0;
354
355 if(!hintUrl.empty()) {
356 for(auto i = p.first; i != p.second; ++i) {
357 OnlineUser* u = i->second;
358 if(u->getClient().getHubUrl() == hintUrl) {
359 return u;
360 }
361 }
362 }
363
364 return 0;
365 }
366
findOnlineUser(const HintedUser & user,bool priv)367 OnlineUser* ClientManager::findOnlineUser(const HintedUser& user, bool priv) {
368 return findOnlineUser(user.user->getCID(), user.hint, priv);
369 }
370
findOnlineUser(const CID & cid,const string & hintUrl,bool priv)371 OnlineUser* ClientManager::findOnlineUser(const CID& cid, const string& hintUrl, bool priv) {
372 OnlinePairC p;
373 OnlineUser* u = findOnlineUserHint(cid, hintUrl, p);
374 if(u) // found an exact match (CID + hint).
375 return u;
376
377 if(p.first == p.second) // no user found with the given CID.
378 return 0;
379
380 // if the hint hub is private, don't allow connecting to the same user from another hub.
381 if(priv)
382 return 0;
383
384 // ok, hub not private, return a random user that matches the given CID but not the hint.
385 return p.first->second;
386 }
387
connect(const HintedUser & user,const string & token)388 void ClientManager::connect(const HintedUser& user, const string& token) {
389 bool priv = FavoriteManager::getInstance()->isPrivate(user.hint);
390
391 Lock l(cs);
392 OnlineUser* u = findOnlineUser(user, priv);
393
394 if(u) {
395 u->getClient().connect(*u, token);
396 }
397 }
398
privateMessage(const HintedUser & user,const string & msg,bool thirdPerson)399 void ClientManager::privateMessage(const HintedUser& user, const string& msg, bool thirdPerson) {
400 bool priv = FavoriteManager::getInstance()->isPrivate(user.hint);
401
402 Lock l(cs);
403 OnlineUser* u = findOnlineUser(user, priv);
404
405 if(u) {
406 u->getClient().privateMessage(*u, msg, thirdPerson);
407 }
408 }
409
userCommand(const HintedUser & user,const UserCommand & uc,StringMap & params,bool compatibility)410 void ClientManager::userCommand(const HintedUser& user, const UserCommand& uc, StringMap& params, bool compatibility) {
411 Lock l(cs);
412 /** @todo we allow wrong hints for now ("false" param of findOnlineUser) because users
413 * extracted from search results don't always have a correct hint; see
414 * SearchManager::onRES(const AdcCommand& cmd, ...). when that is done, and SearchResults are
415 * switched to storing only reliable HintedUsers (found with the token of the ADC command),
416 * change this call to findOnlineUserHint. */
417 OnlineUser* ou = findOnlineUser(user.user->getCID(), user.hint.empty() ? uc.getHub() : user.hint, false);
418 if(!ou
419 #ifdef WITH_DHT
420 || ou->getClientBase().type == ClientBase::DHT
421 #endif
422 )
423 return;
424
425 ou->getIdentity().getParams(params, "user", compatibility);
426 ou->getClient().getHubIdentity().getParams(params, "hub", false);
427 ou->getClient().getMyIdentity().getParams(params, "my", compatibility);
428 ou->getClient().escapeParams(params);
429 ou->getClient().sendUserCmd(uc, params);
430 }
431
send(AdcCommand & cmd,const CID & cid)432 void ClientManager::send(AdcCommand& cmd, const CID& cid) {
433 Lock l(cs);
434 auto i = onlineUsers.find(cid);
435 if(i != onlineUsers.end()) {
436 OnlineUser& u = *i->second;
437 if(cmd.getType() == AdcCommand::TYPE_UDP && !u.getIdentity().isUdpActive()) {
438 if(u.getUser()->isNMDC()
439 #ifdef WITH_DHT
440 || u.getClientBase().getType() == Client::DHT
441 #endif
442 )
443 return;
444 cmd.setType(AdcCommand::TYPE_DIRECT);
445 cmd.setTo(u.getIdentity().getSID());
446 u.getClient().send(cmd);
447 } else {
448 try {
449 udp.writeTo(u.getIdentity().getIp(), static_cast<uint16_t>(Util::toInt(u.getIdentity().getUdpPort())), cmd.toString(getMe()->getCID()));
450 } catch(const SocketException&) {
451 dcdebug("Socket exception sending ADC UDP command\n");
452 }
453 }
454 }
455 }
456
infoUpdated()457 void ClientManager::infoUpdated() {
458 Lock l(cs);
459 for(auto i = clients.begin(); i != clients.end(); ++i) {
460 if((*i)->isConnected()) {
461 (*i)->info(false);
462 }
463 }
464 }
465
on(NmdcSearch,Client * aClient,const string & aSeeker,int aSearchType,int64_t aSize,int aFileType,const string & aString)466 void ClientManager::on(NmdcSearch, Client* aClient, const string& aSeeker, int aSearchType, int64_t aSize,
467 int aFileType, const string& aString) noexcept
468 {
469 Speaker<ClientManagerListener>::fire(ClientManagerListener::IncomingSearch(), aString);
470
471 bool isPassive = (aSeeker.compare(0, 4, "Hub:") == 0);
472 bool isTTHSearch = ((aFileType == SearchManager::TYPE_TTH) && (aString.compare(0, 4, "TTH:") == 0));
473
474 // We don't wan't to answer passive searches if we're in passive mode...
475 if(isPassive && !ClientManager::getInstance()->isActive(aClient->getHubUrl())) {
476 return;
477 }
478
479 SearchResultList l;
480 ShareManager::getInstance()->search(l, aString, aSearchType, aSize, aFileType, aClient, isPassive ? 5 : 10);
481 // dcdebug("Found %d items (%s)\n", l.size(), aString.c_str());
482 if(!l.empty()) {
483 if(isPassive) {
484 string name = aSeeker.substr(4);
485 // Good, we have a passive seeker, those are easier...
486 string str;
487 for(auto i = l.begin(); i != l.end(); ++i) {
488 const SearchResultPtr& sr = *i;
489 str += sr->toSR(*aClient);
490 str[str.length()-1] = 5;
491 str += name;
492 str += '|';
493 }
494
495 if(!str.empty())
496 aClient->send(str);
497
498 } else {
499 try {
500 string ip;
501 uint16_t port = 0;
502 Util::parseIpPort(aSeeker, ip, port);
503 if(port == 0)
504 port = 412;
505 for(auto i = l.begin(); i != l.end(); ++i) {
506 const SearchResultPtr& sr = *i;
507 udp.writeTo(ip, port, sr->toSR(*aClient));
508 }
509 } catch(const SocketException& /* e */) {
510 dcdebug("Search caught error\n");
511 }
512 }
513 } else if(!isPassive && isTTHSearch) {
514 PartsInfo partialInfo;
515 TTHValue aTTH(aString.substr(4));
516 if(!QueueManager::getInstance()->handlePartialSearch(aTTH, partialInfo)) {
517 // if not found, try to find in finished list
518 if(!FinishedManager::getInstance()->handlePartialRequest(aTTH, partialInfo)) {
519 return;
520 }
521 }
522
523 string ip;
524 uint16_t port = 0;
525 Util::parseIpPort(aSeeker, ip, port);
526 if (port == 0) {
527 return;
528 }
529
530 try {
531 AdcCommand cmd = SearchManager::getInstance()->toPSR(true, aClient->getMyNick(), aClient->getIpPort(), aTTH.toBase32(), partialInfo);
532 Socket s;
533 s.writeTo(ip, port, cmd.toString(ClientManager::getInstance()->getMe()->getCID()));
534 } catch(...) {
535 dcdebug("Partial search caught error\n");
536 }
537 }
538 }
539
on(AdcSearch,Client * c,const AdcCommand & adc,const CID & from)540 void ClientManager::on(AdcSearch, Client* c, const AdcCommand& adc, const CID& from) noexcept {
541 bool isUdpActive = false;
542 {
543 Lock l(cs);
544
545 auto i = onlineUsers.find(from);
546 if(i != onlineUsers.end()) {
547 OnlineUser& u = *i->second;
548 isUdpActive = u.getIdentity().isUdpActive();
549 }
550
551 }
552 SearchManager::getInstance()->respond(adc, from, isUdpActive, c->getIpPort());
553
554 Speaker<ClientManagerListener>::fire(ClientManagerListener::IncomingSearch(), [&adc]() -> string
555 {
556 auto toCode = [](char a, char b) -> uint16_t {
557 return (uint16_t)a | ((uint16_t)b)<<8;
558 };
559
560 string result;
561 const StringList ¶ms = adc.getParameters();
562
563 for(const string ¶m: params) {
564 if(param.length() <= 2)
565 continue;
566 uint16_t cmd = toCode(param[0], param[1]);
567 if (toCode('T', 'R') == cmd)
568 result = "TTH:" + param.substr(2);
569 else if (toCode('A', 'N') == cmd)
570 result += param.substr(2) + ' ';
571 }
572 return result;
573 }());
574 }
575
576
search(int aSizeMode,int64_t aSize,int aFileType,const string & aString,const string & aToken,void * aOwner)577 void ClientManager::search(int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken, void* aOwner) {
578 #ifdef WITH_DHT
579 if(BOOLSETTING(USE_DHT) && aFileType == SearchManager::TYPE_TTH)
580 dht::DHT::getInstance()->findFile(aString);
581 #endif
582 Lock l(cs);
583 for(auto i = clients.begin(); i != clients.end(); ++i) {
584 if((*i)->isConnected()) {
585 (*i)->search(aSizeMode, aSize, aFileType, aString, aToken, StringList() /*ExtList*/, aOwner);
586 }
587 }
588 }
589
search(StringList & who,int aSizeMode,int64_t aSize,int aFileType,const string & aString,const string & aToken,const StringList & aExtList,void * aOwner)590 uint64_t ClientManager::search(StringList& who, int aSizeMode, int64_t aSize, int aFileType, const string& aString, const string& aToken, const StringList& aExtList, void* aOwner) {
591 #ifdef WITH_DHT
592 if(BOOLSETTING(USE_DHT) && aFileType == SearchManager::TYPE_TTH)
593 dht::DHT::getInstance()->findFile(aString, aToken);
594 #endif
595 Lock l(cs);
596 uint64_t estimateSearchSpan = 0;
597 for(auto it = who.begin(); it != who.end(); ++it) {
598 string& client = *it;
599 for(auto j = clients.begin(); j != clients.end(); ++j) {
600 Client* c = *j;
601 if(c->isConnected() && c->getHubUrl() == client) {
602 uint64_t ret = c->search(aSizeMode, aSize, aFileType, aString, aToken, aExtList, aOwner);
603 estimateSearchSpan = max(estimateSearchSpan, ret);
604 }
605 }
606 }
607 return estimateSearchSpan;
608 }
609
on(TimerManagerListener::Minute,uint64_t)610 void ClientManager::on(TimerManagerListener::Minute, uint64_t /* aTick */) noexcept {
611 Lock l(cs);
612
613 // Collect some garbage...
614 auto i = users.begin();
615 while(i != users.end()) {
616 if(i->second->unique()) {
617 users.erase(i++);
618 } else {
619 ++i;
620 }
621 }
622
623 for(auto j = clients.begin(); j != clients.end(); ++j) {
624 (*j)->info(false);
625 }
626 }
627
getMe()628 UserPtr& ClientManager::getMe() {
629 if(!me) {
630 Lock l(cs);
631 if(!me) {
632 me = new User(getMyCID());
633 users.insert(make_pair(me->getCID(), me));
634 }
635 }
636 return me;
637 }
638
getMyPID()639 const CID& ClientManager::getMyPID() {
640 if(pid.isZero())
641 pid = CID(SETTING(PRIVATE_ID));
642 return pid;
643 }
644
getMyCID()645 CID ClientManager::getMyCID() {
646 TigerHash tiger;
647 tiger.update(getMyPID().data(), CID::SIZE);
648 return CID(tiger.finalize());
649 }
650
updateNick(const OnlineUser & user)651 void ClientManager::updateNick(const OnlineUser& user) noexcept {
652 if(!user.getIdentity().getNick().empty()) {
653 Lock l(cs);
654 auto i = nicks.find(user.getUser()->getCID());
655 if(i == nicks.end()) {
656 nicks[user.getUser()->getCID()] = std::make_pair(user.getIdentity().getNick(), false);
657 } else {
658 i->second.first = user.getIdentity().getNick();
659 }
660 }
661 }
662
loadUsers()663 void ClientManager::loadUsers() {
664 try {
665 SimpleXML xml;
666 xml.fromXML(File(getUsersFile(), File::READ, File::OPEN).read());
667
668 if(xml.findChild("Users")) {
669 xml.stepIn();
670
671 {
672 Lock l(cs);
673 while(xml.findChild("User")) {
674 nicks[CID(xml.getChildAttrib("CID"))] = std::make_pair(xml.getChildAttrib("Nick"), false);
675 }
676 }
677
678 xml.stepOut();
679 }
680 } catch(const Exception&) { }
681 }
682
saveUsers() const683 void ClientManager::saveUsers() const {
684 try {
685 SimpleXML xml;
686 xml.addTag("Users");
687 xml.stepIn();
688
689 {
690 Lock l(cs);
691 for(auto i = nicks.begin(), iend = nicks.end(); i != iend; ++i) {
692 if(i->second.second) {
693 xml.addTag("User");
694 xml.addChildAttrib("CID", i->first.toBase32());
695 xml.addChildAttrib("Nick", i->second.first);
696 }
697 }
698 }
699
700 xml.stepOut();
701
702 const string fName = getUsersFile();
703 File out(fName + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
704 BufferedOutputStream<false> f(&out);
705 f.write(SimpleXML::utf8Header);
706 xml.toXML(&f);
707 f.flush();
708 out.close();
709 File::deleteFile(fName);
710 File::renameFile(fName + ".tmp", fName);
711 } catch(const Exception&) { }
712 }
713
saveUser(const CID & cid)714 void ClientManager::saveUser(const CID& cid) {
715 Lock l(cs);
716 auto i = nicks.find(cid);
717 if(i != nicks.end())
718 i->second.second = true;
719 }
720
on(Connected,Client * c)721 void ClientManager::on(Connected, Client* c) noexcept {
722 fire(ClientManagerListener::ClientConnected(), c);
723 }
724
on(UserUpdated,Client *,const OnlineUser & user)725 void ClientManager::on(UserUpdated, Client*, const OnlineUser& user) noexcept {
726 updateNick(user);
727 fire(ClientManagerListener::UserUpdated(), user);
728 }
729
on(UsersUpdated,Client * c,const OnlineUserList & l)730 void ClientManager::on(UsersUpdated, Client* c, const OnlineUserList& l) noexcept {
731 for(auto i = l.cbegin(), iend = l.cend(); i != iend; ++i) {
732 updateNick(*(*i));
733 fire(ClientManagerListener::UserUpdated(), *(*i));
734 }
735 }
736
on(HubUpdated,Client * c)737 void ClientManager::on(HubUpdated, Client* c) noexcept {
738 fire(ClientManagerListener::ClientUpdated(), c);
739 }
740
on(Failed,Client * client,const string &)741 void ClientManager::on(Failed, Client* client, const string&) noexcept {
742 fire(ClientManagerListener::ClientDisconnected(), client);
743 }
744
on(HubUserCommand,Client * client,int aType,int ctx,const string & name,const string & command)745 void ClientManager::on(HubUserCommand, Client* client, int aType, int ctx, const string& name, const string& command) noexcept {
746 if(BOOLSETTING(HUB_USER_COMMANDS)) {
747 if(aType == UserCommand::TYPE_REMOVE) {
748 int cmd = FavoriteManager::getInstance()->findUserCommand(name, client->getHubUrl());
749 if(cmd != -1)
750 FavoriteManager::getInstance()->removeUserCommand(cmd);
751 } else if(aType == UserCommand::TYPE_CLEAR) {
752 FavoriteManager::getInstance()->removeHubUserCommands(ctx, client->getHubUrl());
753 } else {
754 FavoriteManager::getInstance()->addUserCommand(aType, ctx, UserCommand::FLAG_NOSAVE, name, command, "", client->getHubUrl());
755 }
756 }
757 }
getMode(const string & aHubUrl) const758 int ClientManager::getMode(const string& aHubUrl) const {
759
760 if(aHubUrl.empty())
761 return SETTING(INCOMING_CONNECTIONS);
762
763 int mode = 0;
764 const FavoriteHubEntry* hub = FavoriteManager::getInstance()->getFavoriteHubEntry(aHubUrl);
765 if(hub) {
766 switch(hub->getMode()) {
767 case 1 :
768 mode = SettingsManager::INCOMING_DIRECT;
769 break;
770 case 2 :
771 mode = SettingsManager::INCOMING_FIREWALL_PASSIVE;
772 break;
773 default:
774 mode = SETTING(INCOMING_CONNECTIONS);
775 }
776 } else {
777 mode = SETTING(INCOMING_CONNECTIONS);
778 }
779 return mode;
780 }
781
cancelSearch(void * aOwner)782 void ClientManager::cancelSearch(void* aOwner) {
783 Lock l(cs);
784
785 for(auto i = clients.begin(); i != clients.end(); ++i) {
786 (*i)->cancelSearch(aOwner);
787 }
788 }
789
790 #ifdef WITH_DHT
findDHTNode(const CID & cid) const791 OnlineUserPtr ClientManager::findDHTNode(const CID& cid) const {
792 Lock l(cs);
793
794 OnlinePairC op = onlineUsers.equal_range(cid);
795 for(auto i = op.first; i != op.second; ++i) {
796 OnlineUser* ou = i->second;
797
798 // user not in DHT, so don't bother with other hubs
799 if(!ou->getUser()->isSet(User::DHT))
800 break;
801
802 if(ou->getClientBase().getType() == Client::DHT)
803 return ou;
804 }
805
806 return NULL;
807 }
808 #endif
809
810 #ifdef LUA_SCRIPT
ucExecuteLua(const string & ucCommand,StringMap & params)811 bool ClientManager::ucExecuteLua(const string& ucCommand, StringMap& params) noexcept {
812 bool executedlua = false;
813 string::size_type i, j, k;
814 i = j = k = 0;
815 string tmp = ucCommand;
816 while( (i = tmp.find("%[lua:", i)) != string::npos) {
817 i += 6;
818 j = tmp.find(']', i);
819 if(j == string::npos)
820 break;
821 string chunk = tmp.substr(i, j-i);
822 // for making possible using %[nick] and similar parameters too
823 // !%!{nick!}
824 k = 0;
825 while( (k = chunk.find("!%")) != string::npos) {
826 chunk.erase(k, 2);
827 chunk.insert(k, "%");
828 }
829 k = 0;
830 while( (k = chunk.find("!{")) != string::npos) {
831 chunk.erase(k, 2);
832 chunk.insert(k, "[");
833 }
834 k = 0;
835 while( (k = chunk.find("!}")) != string::npos) {
836 chunk.erase(k, 2);
837 chunk.insert(k, "]");
838 }
839 //@todo: use filter? I opted for no here, but this means Lua has to be careful about
840 //filtering if it cares.
841 ScriptManager::getInstance()->EvaluateChunk(Util::formatParams(chunk, params, false));
842 executedlua = true;
843 i = j + 1;
844 }
845 return executedlua;
846 }
847 #endif // LUA_SCRIPT
848 } // namespace dcpp
849