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 &params = adc.getParameters();
562 
563         for(const string &param: 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