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 "FavoriteManager.h"
22 
23 #include "ClientManager.h"
24 #include "CryptoManager.h"
25 //#include "WindowManager.h"
26 
27 #include "HttpConnection.h"
28 #include "StringTokenizer.h"
29 #include "SimpleXML.h"
30 #include "UserCommand.h"
31 //#include "WindowInfo.h"
32 #include "File.h"
33 #include "BZUtils.h"
34 #include "FilteredFile.h"
35 
36 namespace dcpp {
37 
FavoriteManager()38 FavoriteManager::FavoriteManager() : lastId(0), useHttp(false), running(false), c(NULL), lastServer(0), listType(TYPE_NORMAL), dontSave(false) {
39     SettingsManager::getInstance()->addListener(this);
40     ClientManager::getInstance()->addListener(this);
41 
42     File::ensureDirectory(Util::getHubListsPath());
43 }
44 
~FavoriteManager()45 FavoriteManager::~FavoriteManager() {
46     ClientManager::getInstance()->removeListener(this);
47     SettingsManager::getInstance()->removeListener(this);
48     if(c) {
49         c->removeListener(this);
50         delete c;
51         c = NULL;
52     }
53 
54     for_each(favoriteHubs.begin(), favoriteHubs.end(), DeleteFunction());
55 }
56 
addUserCommand(int type,int ctx,int flags,const string & name,const string & command,const string & to,const string & hub)57 UserCommand FavoriteManager::addUserCommand(int type, int ctx, int flags, const string& name, const string& command, const string& to, const string& hub) {
58     // No dupes, add it...
59     Lock l(cs);
60     userCommands.push_back(UserCommand(lastId++, type, ctx, flags, name, command, to, hub));
61     UserCommand& uc = userCommands.back();
62     if(!uc.isSet(UserCommand::FLAG_NOSAVE))
63         save();
64     return userCommands.back();
65 }
66 
getUserCommand(int cid,UserCommand & uc)67 bool FavoriteManager::getUserCommand(int cid, UserCommand& uc) {
68     Lock l(cs);
69     for(auto i = userCommands.begin(); i != userCommands.end(); ++i) {
70         if(i->getId() == cid) {
71             uc = *i;
72             return true;
73         }
74     }
75     return false;
76 }
77 
moveUserCommand(int cid,int pos)78 bool FavoriteManager::moveUserCommand(int cid, int pos) {
79     dcassert(pos == -1 || pos == 1);
80     Lock l(cs);
81     for(auto i = userCommands.begin(); i != userCommands.end(); ++i) {
82         if(i->getId() == cid) {
83             swap(*i, *(i + pos));
84             return true;
85         }
86     }
87     return false;
88 }
89 
updateUserCommand(const UserCommand & uc)90 void FavoriteManager::updateUserCommand(const UserCommand& uc) {
91     bool nosave = true;
92     Lock l(cs);
93     for(auto i = userCommands.begin(); i != userCommands.end(); ++i) {
94         if(i->getId() == uc.getId()) {
95             *i = uc;
96             nosave = uc.isSet(UserCommand::FLAG_NOSAVE);
97             break;
98         }
99     }
100     if(!nosave)
101         save();
102 }
103 
findUserCommand(const string & aName,const string & aUrl)104 int FavoriteManager::findUserCommand(const string& aName, const string& aUrl) {
105     Lock l(cs);
106     for(auto i = userCommands.begin(); i != userCommands.end(); ++i) {
107         if(i->getName() == aName && i->getHub() == aUrl) {
108             return i->getId();
109         }
110     }
111     return -1;
112 }
113 
removeUserCommand(int cid)114 void FavoriteManager::removeUserCommand(int cid) {
115     bool nosave = true;
116     Lock l(cs);
117     for(auto i = userCommands.begin(); i != userCommands.end(); ++i) {
118         if(i->getId() == cid) {
119             nosave = i->isSet(UserCommand::FLAG_NOSAVE);
120             userCommands.erase(i);
121             break;
122         }
123     }
124     if(!nosave)
125         save();
126 }
removeUserCommand(const string & srv)127 void FavoriteManager::removeUserCommand(const string& srv) {
128     Lock l(cs);
129     for(auto i = userCommands.begin(); i != userCommands.end(); ) {
130         if((i->getHub() == srv) && i->isSet(UserCommand::FLAG_NOSAVE)) {
131             i = userCommands.erase(i);
132         } else {
133             ++i;
134         }
135     }
136 }
137 
removeHubUserCommands(int ctx,const string & hub)138 void FavoriteManager::removeHubUserCommands(int ctx, const string& hub) {
139     Lock l(cs);
140     for(auto i = userCommands.begin(); i != userCommands.end(); ) {
141         if(i->getHub() == hub && i->isSet(UserCommand::FLAG_NOSAVE) && i->getCtx() & ctx) {
142             i = userCommands.erase(i);
143         } else {
144             ++i;
145         }
146     }
147 }
148 
addFavoriteUser(const UserPtr & aUser)149 void FavoriteManager::addFavoriteUser(const UserPtr& aUser) {
150     Lock l(cs);
151     if(users.find(aUser->getCID()) == users.end()) {
152         StringList urls = ClientManager::getInstance()->getHubs(aUser->getCID(), Util::emptyString);
153         StringList nicks = ClientManager::getInstance()->getNicks(aUser->getCID(), Util::emptyString);
154 
155         /// @todo make this an error probably...
156         if(urls.empty())
157             urls.push_back(Util::emptyString);
158         if(nicks.empty())
159             nicks.push_back(Util::emptyString);
160 
161         auto i = users.insert(make_pair(aUser->getCID(), FavoriteUser(aUser, nicks[0], urls[0]))).first;
162         fire(FavoriteManagerListener::UserAdded(), i->second);
163         save();
164     }
165 }
166 
removeFavoriteUser(const UserPtr & aUser)167 void FavoriteManager::removeFavoriteUser(const UserPtr& aUser) {
168     Lock l(cs);
169     auto i = users.find(aUser->getCID());
170     if(i != users.end()) {
171         fire(FavoriteManagerListener::UserRemoved(), i->second);
172         users.erase(i);
173         save();
174     }
175 }
176 
getUserURL(const UserPtr & aUser) const177 std::string FavoriteManager::getUserURL(const UserPtr& aUser) const {
178     Lock l(cs);
179     auto i = users.find(aUser->getCID());
180     if(i != users.end()) {
181         const FavoriteUser& fu = i->second;
182         return fu.getUrl();
183     }
184     return Util::emptyString;
185 }
186 
addFavorite(const FavoriteHubEntry & aEntry)187 void FavoriteManager::addFavorite(const FavoriteHubEntry& aEntry) {
188     FavoriteHubEntry* f;
189 
190     FavoriteHubEntryList::iterator i = getFavoriteHub(aEntry.getServer());
191     if(i != favoriteHubs.end()) {
192         return;
193     }
194     f = new FavoriteHubEntry(aEntry);
195     favoriteHubs.push_back(f);
196     fire(FavoriteManagerListener::FavoriteAdded(), f);
197     save();
198 }
199 
removeFavorite(FavoriteHubEntry * entry)200 void FavoriteManager::removeFavorite(FavoriteHubEntry* entry) {
201     FavoriteHubEntryList::iterator i = find(favoriteHubs.begin(), favoriteHubs.end(), entry);
202     if(i == favoriteHubs.end()) {
203         return;
204     }
205 
206     fire(FavoriteManagerListener::FavoriteRemoved(), entry);
207     favoriteHubs.erase(i);
208     delete entry;
209     save();
210 }
211 
isFavoriteHub(const std::string & url)212 bool FavoriteManager::isFavoriteHub(const std::string& url) {
213     FavoriteHubEntryList::iterator i = getFavoriteHub(url);
214     if(i != favoriteHubs.end()) {
215         return true;
216     }
217     return false;
218 }
219 
addFavoriteDir(const string & aDirectory,const string & aName)220 bool FavoriteManager::addFavoriteDir(const string& aDirectory, const string & aName){
221     string path = aDirectory;
222 
223     if( path[ path.length() -1 ] != PATH_SEPARATOR )
224         path += PATH_SEPARATOR;
225 
226     for(auto i = favoriteDirs.begin(); i != favoriteDirs.end(); ++i) {
227         if((Util::strnicmp(path, i->first, i->first.length()) == 0) && (Util::strnicmp(path, i->first, path.length()) == 0)) {
228             return false;
229         }
230         if(Util::stricmp(aName, i->second) == 0) {
231             return false;
232         }
233     }
234     favoriteDirs.push_back(make_pair(aDirectory, aName));
235     save();
236     return true;
237 }
238 
removeFavoriteDir(const string & aName)239 bool FavoriteManager::removeFavoriteDir(const string& aName) {
240     string d(aName);
241 
242     if(d[d.length() - 1] != PATH_SEPARATOR)
243         d += PATH_SEPARATOR;
244 
245     for(auto j = favoriteDirs.begin(); j != favoriteDirs.end(); ++j) {
246         if(Util::stricmp(j->first.c_str(), d.c_str()) == 0) {
247             favoriteDirs.erase(j);
248             save();
249             return true;
250         }
251     }
252     return false;
253 }
254 
renameFavoriteDir(const string & aName,const string & anotherName)255 bool FavoriteManager::renameFavoriteDir(const string& aName, const string& anotherName) {
256 
257     for(auto j = favoriteDirs.begin(); j != favoriteDirs.end(); ++j) {
258         if(Util::stricmp(j->second.c_str(), aName.c_str()) == 0) {
259             j->second = anotherName;
260             save();
261             return true;
262         }
263     }
264     return false;
265 }
266 
267 class XmlListLoader : public SimpleXMLReader::CallBack {
268 public:
XmlListLoader(HubEntryList & lst)269     XmlListLoader(HubEntryList& lst) : publicHubs(lst) { }
~XmlListLoader()270     virtual ~XmlListLoader() { }
startTag(const string & name,StringPairList & attribs,bool)271     virtual void startTag(const string& name, StringPairList& attribs, bool) {
272         if(name == "Hub") {
273             const string& name = getAttrib(attribs, "Name", 0);
274             const string& server = getAttrib(attribs, "Address", 1);
275             const string& description = getAttrib(attribs, "Description", 2);
276             const string& users = getAttrib(attribs, "Users", 3);
277             const string& country = getAttrib(attribs, "Country", 4);
278             const string& shared = getAttrib(attribs, "Shared", 5);
279             const string& minShare = getAttrib(attribs, "Minshare", 5);
280             const string& minSlots = getAttrib(attribs, "Minslots", 5);
281             const string& maxHubs = getAttrib(attribs, "Maxhubs", 5);
282             const string& maxUsers = getAttrib(attribs, "Maxusers", 5);
283             const string& reliability = getAttrib(attribs, "Reliability", 5);
284             const string& rating = getAttrib(attribs, "Rating", 5);
285             publicHubs.push_back(HubEntry(name, server, description, users, country, shared, minShare, minSlots, maxHubs, maxUsers, reliability, rating));
286         }
287     }
endTag(const string &,const string &)288     virtual void endTag(const string&, const string&) {
289 
290     }
291 private:
292     HubEntryList& publicHubs;
293 };
294 
onHttpFinished(bool fromHttp)295 bool FavoriteManager::onHttpFinished(bool fromHttp) noexcept {
296     MemoryInputStream mis(downloadBuf);
297     bool success = true;
298 
299     Lock l(cs);
300     HubEntryList& list = publicListMatrix[publicListServer];
301     list.clear();
302 
303     try {
304         XmlListLoader loader(list);
305 
306         if((listType == TYPE_BZIP2) && (!downloadBuf.empty())) {
307             FilteredInputStream<UnBZFilter, false> f(&mis);
308             SimpleXMLReader(&loader).parse(f);
309         } else {
310             SimpleXMLReader(&loader).parse(mis);
311         }
312     } catch(const Exception&) {
313         success = false;
314         fire(FavoriteManagerListener::Corrupted(), fromHttp ? publicListServer : Util::emptyString);
315     }
316 
317     if(fromHttp) {
318         try {
319             File f(Util::getHubListsPath() + Util::validateFileName(publicListServer, "/"), File::WRITE, File::CREATE | File::TRUNCATE);
320             f.write(downloadBuf);
321             f.close();
322         } catch(const FileException&) { }
323     }
324 
325     downloadBuf = Util::emptyString;
326 
327     return success;
328 }
329 
save()330 void FavoriteManager::save() {
331     if(dontSave)
332         return;
333 
334     Lock l(cs);
335     try {
336         SimpleXML xml;
337 
338         xml.addTag("Favorites");
339         xml.stepIn();
340 
341         xml.addTag("Hubs");
342         xml.stepIn();
343 
344         for(auto i = favHubGroups.begin(), iend = favHubGroups.end(); i != iend; ++i) {
345             xml.addTag("Group");
346             xml.addChildAttrib("Name", i->first);
347             xml.addChildAttrib("Private", i->second.priv);
348             xml.addChildAttrib("Connect", i->second.connect);
349         }
350 
351         for(auto i = favoriteHubs.begin(), iend = favoriteHubs.end(); i != iend; ++i) {
352             xml.addTag("Hub");
353             xml.addChildAttrib("Name", (*i)->getName());
354             xml.addChildAttrib("Connect", (*i)->getConnect());
355             xml.addChildAttrib("Description", (*i)->getDescription());
356             xml.addChildAttrib("Nick", (*i)->getNick(false));
357             xml.addChildAttrib("Password", (*i)->getPassword());
358             xml.addChildAttrib("Server", (*i)->getServer());
359             xml.addChildAttrib("UserDescription", (*i)->getUserDescription());
360             xml.addChildAttrib("Encoding", (*i)->getEncoding());
361             xml.addChildAttrib("ClientId", (*i)->getClientId());
362             xml.addChildAttrib("ExternalIP", (*i)->getExternalIP());
363             xml.addChildAttrib("OverrideId", Util::toString((*i)->getOverrideId()));
364             xml.addChildAttrib("UseInternetIp",(*i)->getUseInternetIP());
365             xml.addChildAttrib("DisableChat", (*i)->getDisableChat());
366             xml.addChildAttrib("Mode", Util::toString((*i)->getMode()));
367             xml.addChildAttrib("SearchInterval", Util::toString((*i)->getSearchInterval()));
368             xml.addChildAttrib("Group", (*i)->getGroup());
369         }
370         xml.stepOut();
371         xml.addTag("Users");
372         xml.stepIn();
373         for(auto i = users.begin(), iend = users.end(); i != iend; ++i) {
374             xml.addTag("User");
375             xml.addChildAttrib("LastSeen", i->second.getLastSeen());
376             xml.addChildAttrib("GrantSlot", i->second.isSet(FavoriteUser::FLAG_GRANTSLOT));
377             xml.addChildAttrib("UserDescription", i->second.getDescription());
378             xml.addChildAttrib("Nick", i->second.getNick());
379             xml.addChildAttrib("URL", i->second.getUrl());
380             xml.addChildAttrib("CID", i->first.toBase32());
381         }
382         xml.stepOut();
383         xml.addTag("UserCommands");
384         xml.stepIn();
385         for(auto i = userCommands.begin(), iend = userCommands.end(); i != iend; ++i) {
386             if(!i->isSet(UserCommand::FLAG_NOSAVE)) {
387                 xml.addTag("UserCommand");
388                 xml.addChildAttrib("Type", i->getType());
389                 xml.addChildAttrib("Context", i->getCtx());
390                 xml.addChildAttrib("Name", i->getName());
391                 xml.addChildAttrib("Command", i->getCommand());
392                 xml.addChildAttrib("To", i->getTo());
393                 xml.addChildAttrib("Hub", i->getHub());
394             }
395         }
396         xml.stepOut();
397         //Favorite download to dirs
398         xml.addTag("FavoriteDirs");
399         xml.stepIn();
400         StringPairList spl = getFavoriteDirs();
401         for(auto i = spl.begin(), iend = spl.end(); i != iend; ++i) {
402             xml.addTag("Directory", i->first);
403             xml.addChildAttrib("Name", i->second);
404         }
405         xml.stepOut();
406 
407         xml.stepOut();
408 
409         string fname = getConfigFile();
410 
411         File f(fname + ".tmp", File::WRITE, File::CREATE | File::TRUNCATE);
412         f.write(SimpleXML::utf8Header);
413         f.write(xml.toXML());
414         f.close();
415         File::deleteFile(fname);
416         File::renameFile(fname + ".tmp", fname);
417 
418     } catch(const Exception& e) {
419         dcdebug("FavoriteManager::save: %s\n", e.getError().c_str());
420     }
421 }
422 
load()423 void FavoriteManager::load() {
424 
425     // Add NMDC standard op commands
426     static const char kickstr[] =
427         "$To: %[userNI] From: %[myNI] $<%[myNI]> You are being kicked because: %[line:Reason]|<%[myNI]> is kicking %[userNI] because: %[line:Reason]|$Kick %[userNI]|";
428     addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_USER | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
429         _("Kick user(s)"), kickstr, "", "op");
430     static const char redirstr[] =
431         "$OpForceMove $Who:%[userNI]$Where:%[line:Target Server]$Msg:%[line:Message]|";
432     addUserCommand(UserCommand::TYPE_RAW_ONCE, UserCommand::CONTEXT_USER | UserCommand::CONTEXT_SEARCH, UserCommand::FLAG_NOSAVE,
433         _("Redirect user(s)"), redirstr, "", "op");
434 
435     try {
436         SimpleXML xml;
437         Util::migrate(getConfigFile());
438         xml.fromXML(File(getConfigFile(), File::READ, File::OPEN).read());
439 
440         if(xml.findChild("Favorites")) {
441             xml.stepIn();
442             load(xml);
443             xml.stepOut();
444         }
445     } catch(const Exception& e) {
446         dcdebug("FavoriteManager::load: %s\n", e.getError().c_str());
447     }
448 }
449 
load(SimpleXML & aXml)450 void FavoriteManager::load(SimpleXML& aXml) {
451     dontSave = true;
452     bool needSave = false;
453 
454     aXml.resetCurrentChild();
455     if(aXml.findChild("Hubs")) {
456         aXml.stepIn();
457 
458     while(aXml.findChild("Group")) {
459         string name = aXml.getChildAttrib("Name");
460         if(name.empty())
461             continue;
462         FavHubGroupProperties props = { aXml.getBoolChildAttrib("Private"), aXml.getBoolChildAttrib("Connect") };
463         favHubGroups[name] = props;
464     }
465 
466     aXml.resetCurrentChild();
467         while(aXml.findChild("Hub")) {
468             FavoriteHubEntry* e = new FavoriteHubEntry();
469             e->setName(aXml.getChildAttrib("Name"));
470             e->setConnect(aXml.getBoolChildAttrib("Connect"));
471             e->setDescription(aXml.getChildAttrib("Description"));
472             e->setNick(aXml.getChildAttrib("Nick"));
473             e->setPassword(aXml.getChildAttrib("Password"));
474             e->setServer(aXml.getChildAttrib("Server"));
475             e->setUserDescription(aXml.getChildAttrib("UserDescription"));
476             e->setEncoding(aXml.getChildAttrib("Encoding"));
477             e->setExternalIP(aXml.getChildAttrib("ExternalIP"));
478             e->setClientId(aXml.getChildAttrib("ClientId"));
479             e->setOverrideId(Util::toInt(aXml.getChildAttrib("OverrideId")) != 0);
480             e->setUseInternetIP(aXml.getBoolChildAttrib("UseInternetIp"));
481             e->setDisableChat(aXml.getBoolChildAttrib("DisableChat"));
482             e->setMode(Util::toInt(aXml.getChildAttrib("Mode")));
483             e->setSearchInterval(Util::toInt(aXml.getChildAttrib("SearchInterval")));
484             e->setGroup(aXml.getChildAttrib("Group"));
485             favoriteHubs.push_back(e);
486 
487             if(aXml.getBoolChildAttrib("Connect")) {
488             // this entry dates from before the window manager & fav hub groups; convert it.
489             const string name = _("Auto-connect group (converted)");
490             if(favHubGroups.find(name) == favHubGroups.end()) {
491                 FavHubGroupProperties props = { false, true };
492                 favHubGroups[name] = props;
493             }
494             e->setGroup(name);
495             needSave = true;
496             }
497         }
498         aXml.stepOut();
499     }
500     // parse groups that have the "Connect" param and send their hubs to WindowManager
501     //for(auto i = favHubGroups.begin(), iend = favHubGroups.end(); i != iend; ++i) {
502         //if(i->second.connect) {
503             //FavoriteHubEntryList hubs = getFavoriteHubs(i->first);
504             //for(auto hub = hubs.begin(), hub_end = hubs.end(); hub != hub_end; ++hub) {
505                 //StringMap map;
506                 //map[WindowInfo::address] = (*hub)->getServer();
507                 //WindowManager::getInstance()->add(WindowManager::hub(), map);
508             //}
509         //}
510     //}
511 
512     aXml.resetCurrentChild();
513     if(aXml.findChild("Users")) {
514         aXml.stepIn();
515         while(aXml.findChild("User")) {
516             UserPtr u;
517             const string& cid = aXml.getChildAttrib("CID");
518             const string& nick = aXml.getChildAttrib("Nick");
519             const string& hubUrl = aXml.getChildAttrib("URL");
520 
521             if(cid.length() != 39) {
522                 if(nick.empty() || hubUrl.empty())
523                     continue;
524                 u = ClientManager::getInstance()->getUser(nick, hubUrl);
525             } else {
526                 u = ClientManager::getInstance()->getUser(CID(cid));
527             }
528             auto i = users.insert(make_pair(u->getCID(), FavoriteUser(u, nick, hubUrl))).first;
529 
530             if(aXml.getBoolChildAttrib("GrantSlot"))
531                 i->second.setFlag(FavoriteUser::FLAG_GRANTSLOT);
532 
533             i->second.setLastSeen((uint32_t)aXml.getIntChildAttrib("LastSeen"));
534             i->second.setDescription(aXml.getChildAttrib("UserDescription"));
535 
536         }
537         aXml.stepOut();
538     }
539     aXml.resetCurrentChild();
540     if(aXml.findChild("UserCommands")) {
541         aXml.stepIn();
542         while(aXml.findChild("UserCommand")) {
543             addUserCommand(aXml.getIntChildAttrib("Type"), aXml.getIntChildAttrib("Context"), 0, aXml.getChildAttrib("Name"),
544             aXml.getChildAttrib("Command"), aXml.getChildAttrib("To"), aXml.getChildAttrib("Hub"));
545         }
546         aXml.stepOut();
547     }
548     //Favorite download to dirs
549     aXml.resetCurrentChild();
550     if(aXml.findChild("FavoriteDirs")) {
551         aXml.stepIn();
552         while(aXml.findChild("Directory")) {
553             string virt = aXml.getChildAttrib("Name");
554             string d(aXml.getChildData());
555             FavoriteManager::getInstance()->addFavoriteDir(d, virt);
556         }
557         aXml.stepOut();
558     }
559 
560     dontSave = false;
561     if(needSave)
562         save();
563 }
564 
userUpdated(const OnlineUser & info)565 void FavoriteManager::userUpdated(const OnlineUser& info) {
566     Lock l(cs);
567     auto i = users.find(info.getUser()->getCID());
568     if(i != users.end()) {
569         FavoriteUser& fu = i->second;
570         fu.update(info);
571         save();
572     }
573 }
574 
getFavoriteHubEntry(const string & aServer) const575 FavoriteHubEntryPtr FavoriteManager::getFavoriteHubEntry(const string& aServer) const {
576     for(auto i = favoriteHubs.begin(), iend = favoriteHubs.end(); i != iend; ++i) {
577         FavoriteHubEntry* hub = *i;
578         if(Util::stricmp(hub->getServer(), aServer) == 0) {
579             return hub;
580         }
581     }
582     return NULL;
583 }
584 
getFavoriteHubs(const string & group) const585 FavoriteHubEntryList FavoriteManager::getFavoriteHubs(const string& group) const {
586     FavoriteHubEntryList ret;
587     for(auto i = favoriteHubs.begin(), iend = favoriteHubs.end(); i != iend; ++i)
588         if((*i)->getGroup() == group)
589             ret.push_back(*i);
590     return ret;
591 }
592 
isPrivate(const string & url) const593 bool FavoriteManager::isPrivate(const string& url) const {
594     if(url.empty())
595         return false;
596 
597     FavoriteHubEntry* fav = getFavoriteHubEntry(url);
598     if(fav) {
599         const string& name = fav->getGroup();
600         if(!name.empty()) {
601             auto group = favHubGroups.find(name);
602             if(group != favHubGroups.end())
603                 return group->second.priv;
604         }
605     }
606     return false;
607 }
608 
hasSlot(const UserPtr & aUser) const609 bool FavoriteManager::hasSlot(const UserPtr& aUser) const {
610     Lock l(cs);
611     auto i = users.find(aUser->getCID());
612     if(i == users.end())
613         return false;
614     return i->second.isSet(FavoriteUser::FLAG_GRANTSLOT);
615 }
616 
getLastSeen(const UserPtr & aUser) const617 time_t FavoriteManager::getLastSeen(const UserPtr& aUser) const {
618     Lock l(cs);
619     auto i = users.find(aUser->getCID());
620     if(i == users.end())
621         return 0;
622     return i->second.getLastSeen();
623 }
624 
setAutoGrant(const UserPtr & aUser,bool grant)625 void FavoriteManager::setAutoGrant(const UserPtr& aUser, bool grant) {
626     Lock l(cs);
627     auto i = users.find(aUser->getCID());
628     if(i == users.end())
629         return;
630     if(grant)
631         i->second.setFlag(FavoriteUser::FLAG_GRANTSLOT);
632     else
633         i->second.unsetFlag(FavoriteUser::FLAG_GRANTSLOT);
634     save();
635 }
setUserDescription(const UserPtr & aUser,const string & description)636 void FavoriteManager::setUserDescription(const UserPtr& aUser, const string& description) {
637     Lock l(cs);
638     auto i = users.find(aUser->getCID());
639     if(i == users.end())
640         return;
641     i->second.setDescription(description);
642     save();
643 }
644 
getHubLists()645 StringList FavoriteManager::getHubLists() {
646     StringTokenizer<string> lists(SETTING(HUBLIST_SERVERS), ';');
647     return lists.getTokens();
648 }
649 
getFavoriteHub(const string & aServer)650 FavoriteHubEntryList::iterator FavoriteManager::getFavoriteHub(const string& aServer) {
651     for(FavoriteHubEntryList::iterator i = favoriteHubs.begin(); i != favoriteHubs.end(); ++i) {
652         if(Util::stricmp((*i)->getServer(), aServer) == 0) {
653             return i;
654         }
655     }
656     return favoriteHubs.end();
657 }
658 
659 
setHubList(int aHubList)660 void FavoriteManager::setHubList(int aHubList) {
661     lastServer = aHubList;
662     refresh();
663 }
664 
refresh(bool forceDownload)665 void FavoriteManager::refresh(bool forceDownload /* = false */) {
666     StringList sl = getHubLists();
667     if(sl.empty())
668         return;
669     publicListServer = sl[(lastServer) % sl.size()];
670     if(Util::strnicmp(publicListServer.c_str(), "http://", 7) != 0) {
671         lastServer++;
672         return;
673     }
674 
675     if(!forceDownload) {
676         string path = Util::getHubListsPath() + Util::validateFileName(publicListServer, "/");
677         if(File::getSize(path) > 0) {
678             useHttp = false;
679             string fileDate;
680             {
681                 Lock l(cs);
682                 publicListMatrix[publicListServer].clear();
683             }
684             listType = (Util::stricmp(path.substr(path.size() - 4), ".bz2") == 0) ? TYPE_BZIP2 : TYPE_NORMAL;
685             try {
686                 File cached(path, File::READ, File::OPEN);
687                 downloadBuf = cached.read();
688                 char buf[20];
689                 time_t fd = cached.getLastModified();
690                 if (strftime(buf, 20, "%x", localtime(&fd))) {
691                     fileDate = string(buf);
692                 }
693             } catch(const FileException&) {
694                 downloadBuf = Util::emptyString;
695             }
696             if(!downloadBuf.empty()) {
697                 if (onHttpFinished(false)) {
698                     fire(FavoriteManagerListener::LoadedFromCache(), publicListServer, fileDate);
699                 }
700                 return;
701             }
702         }
703     }
704 
705     if(!running) {
706         useHttp = true;
707         {
708             Lock l(cs);
709             publicListMatrix[publicListServer].clear();
710         }
711         fire(FavoriteManagerListener::DownloadStarting(), publicListServer);
712         if(c == NULL)
713             c = new HttpConnection();
714         c->addListener(this);
715         c->downloadFile(publicListServer);
716         running = true;
717     }
718 }
719 
getUserCommands(int ctx,const StringList & hubs)720 UserCommand::List FavoriteManager::getUserCommands(int ctx, const StringList& hubs) {
721     vector<bool> isOp(hubs.size());
722 
723     for(size_t i = 0; i < hubs.size(); ++i) {
724         if(ClientManager::getInstance()->isOp(ClientManager::getInstance()->getMe(), hubs[i])) {
725             isOp[i] = true;
726         }
727     }
728 
729     Lock l(cs);
730     UserCommand::List lst;
731     for(auto i = userCommands.begin(); i != userCommands.end(); ++i) {
732         UserCommand& uc = *i;
733         if(!(uc.getCtx() & ctx)) {
734             //printf("%s\n", uc.getName().c_str());
735             //printf("false\n");
736             continue;
737         }
738         for(size_t j = 0; j < hubs.size(); ++j) {
739             const string& hub = hubs[j];
740             bool hubAdc = hub.compare(0, 6, "adc://") == 0 || hub.compare(0, 7, "adcs://") == 0;
741             bool commandAdc = uc.getHub().compare(0, 6, "adc://") == 0 || uc.getHub().compare(0, 7, "adcs://") == 0;
742             if(hubAdc && commandAdc) {
743                 if((uc.getHub() == "adc://" || uc.getHub() == "adcs://") ||
744                    ((uc.getHub() == "adc://op" || uc.getHub() == "adcs://op") && isOp[j]) ||
745                    (uc.getHub() == hub) )
746                 {
747                     //printf("Found ADC command for ADC hub.\n");
748                     lst.push_back(*i);
749                     break;
750                 }
751             } else if((!hubAdc && !commandAdc) || uc.isChat()) {
752                 if((uc.getHub().length() == 0) ||
753                    (uc.getHub() == "op" && isOp[j]) ||
754                    (uc.getHub() == hub) )
755                 {
756                     //printf("Found non-ADC command for non-ADC hub.\n");
757                     lst.push_back(*i);
758                     break;
759                 }
760             }
761         }
762     }
763     return lst;
764 }
765 
766 // HttpConnectionListener
on(Data,HttpConnection *,const uint8_t * buf,size_t len)767 void FavoriteManager::on(Data, HttpConnection*, const uint8_t* buf, size_t len) noexcept {
768     if(useHttp)
769         downloadBuf.append((const char*)buf, len);
770 }
771 
on(Failed,HttpConnection *,const string & aLine)772 void FavoriteManager::on(Failed, HttpConnection*, const string& aLine) noexcept {
773     c->removeListener(this);
774     lastServer++;
775     running = false;
776     if(useHttp){
777         downloadBuf = Util::emptyString;
778         fire(FavoriteManagerListener::DownloadFailed(), aLine);
779     }
780 }
on(Complete,HttpConnection *,const string & aLine,bool fromCoral)781 void FavoriteManager::on(Complete, HttpConnection*, const string& aLine, bool fromCoral) noexcept {
782     bool parseSuccess = false;
783 
784     c->removeListener(this);
785     if(useHttp) {
786         parseSuccess = onHttpFinished(true);
787     }
788     running = false;
789     if(parseSuccess) {
790         fire(FavoriteManagerListener::DownloadFinished(), aLine, fromCoral);
791     }
792 }
793 
on(Retried,HttpConnection *,const bool Connected)794 void FavoriteManager::on(Retried, HttpConnection*, const bool Connected) noexcept {
795     if (Connected)
796         downloadBuf = Util::emptyString;
797 }
798 
on(Redirected,HttpConnection *,const string & aLine)799 void FavoriteManager::on(Redirected, HttpConnection*, const string& aLine) noexcept {
800     if(useHttp)
801         fire(FavoriteManagerListener::DownloadStarting(), aLine);
802 }
on(TypeNormal,HttpConnection *)803 void FavoriteManager::on(TypeNormal, HttpConnection*) noexcept {
804     if(useHttp)
805         listType = TYPE_NORMAL;
806 }
on(TypeBZ2,HttpConnection *)807 void FavoriteManager::on(TypeBZ2, HttpConnection*) noexcept {
808     if(useHttp)
809         listType = TYPE_BZIP2;
810 }
811 
on(UserUpdated,const OnlineUser & user)812 void FavoriteManager::on(UserUpdated, const OnlineUser& user) noexcept {
813     userUpdated(user);
814 }
815 
816 //NOTE: freedcpp
on(UserDisconnected,const UserPtr & user)817 void FavoriteManager::on(UserDisconnected, const UserPtr& user) noexcept {
818     Lock l(cs);
819 
820     auto i = users.find(user->getCID());
821     if (i != users.end())
822     {
823         i->second.setLastSeen(GET_TIME());
824         fire(FavoriteManagerListener::StatusChanged(), i->second);
825         save();
826     }
827 }
828 
on(UserConnected,const UserPtr & user)829 void FavoriteManager::on(UserConnected, const UserPtr& user) noexcept {
830     Lock l(cs);
831 
832     auto i = users.find(user->getCID());
833     if (i != users.end())
834         fire(FavoriteManagerListener::StatusChanged(), i->second);
835 }
836 
getConfigFile()837 string FavoriteManager::getConfigFile() {
838     return Util::getPath(Util::PATH_USER_CONFIG) + "Favorites.xml";
839 }
840 //NOTE: freedcpp
841 } // namespace dcpp
842