diff --git a/CMakeLists.txt b/CMakeLists.txt index 0aab08ca..e6e8b94a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ project (airdcpp-webclient) -cmake_minimum_required (VERSION 2.6.3) +cmake_minimum_required (VERSION 2.8.6) if (APPLE) set (PROJECT_NAME_GLOBAL AirDC++ Web Client) diff --git a/airdcpp-core/CMakeLists.txt b/airdcpp-core/CMakeLists.txt index 73abc08b..3c30f0ed 100644 --- a/airdcpp-core/CMakeLists.txt +++ b/airdcpp-core/CMakeLists.txt @@ -39,6 +39,8 @@ add_library (airdcpp ${LINK} ${airdcpp_srcs}) # set_property(SOURCE ${PROJECT_SOURCE_DIR}/DCPlusPlus.cpp ${PROJECT_SOURCE_DIR}/UPnPManager.cpp PROPERTY COMPILE_DEFINITIONS USE_MINIUPNP ) #endif() + +set_property(SOURCE ${PROJECT_SOURCE_DIR}/airdcpp/StringDefs.cpp APPEND_STRING PROPERTY COMPILE_FLAGS " -fno-var-tracking ") set_property(SOURCE ${PROJECT_SOURCE_DIR}/airdcpp/Updater.h PROPERTY COMPILE_DEFINITIONS NO_CLIENT_UPDATER) add_definitions (-DNO_CLIENT_UPDATER) diff --git a/airdcpp-core/airdcpp.vcxproj b/airdcpp-core/airdcpp.vcxproj index 6b6d4520..f92010da 100644 --- a/airdcpp-core/airdcpp.vcxproj +++ b/airdcpp-core/airdcpp.vcxproj @@ -234,6 +234,7 @@ + @@ -346,6 +347,7 @@ + diff --git a/airdcpp-core/airdcpp.vcxproj.filters b/airdcpp-core/airdcpp.vcxproj.filters index 6f1043ee..cf6cbb62 100644 --- a/airdcpp-core/airdcpp.vcxproj.filters +++ b/airdcpp-core/airdcpp.vcxproj.filters @@ -326,6 +326,9 @@ Source Files + + Source Files + @@ -847,6 +850,9 @@ Header Files + + Header Files + diff --git a/airdcpp-core/airdcpp/ActivityManager.cpp b/airdcpp-core/airdcpp/ActivityManager.cpp new file mode 100644 index 00000000..25a4def0 --- /dev/null +++ b/airdcpp-core/airdcpp/ActivityManager.cpp @@ -0,0 +1,80 @@ +/* +* Copyright (C) 2011-2016 AirDC++ Project +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "stdinc.h" + +#include "ActivityManager.h" +#include "ClientManager.h" +#include "SettingsManager.h" + + +namespace dcpp { + +ActivityManager::ActivityManager() { + TimerManager::getInstance()->addListener(this); +} + +ActivityManager::~ActivityManager() { + TimerManager::getInstance()->removeListener(this); +} + +void ActivityManager::updateActivity(time_t aLastActivity) noexcept { + if (aLastActivity < lastActivity) { + return; + } + + lastActivity = aLastActivity; + if (awayMode != AWAY_MANUAL) { + setAway(AWAY_OFF); + } +} + +void ActivityManager::on(TimerManagerListener::Second, uint64_t aTick) noexcept { + if (!SETTING(AWAY_IDLE_TIME) || awayMode != AWAY_OFF) { + return; + } + + if ((lastActivity + SETTING(AWAY_IDLE_TIME) * 60 * 1000ULL) < aTick) { + setAway(AWAY_IDLE); + } +} + +void ActivityManager::setAway(AwayMode aNewMode) { + if (aNewMode == awayMode) { + return; + } + + if (aNewMode == AWAY_MANUAL || (awayMode == AWAY_MANUAL && aNewMode == AWAY_OFF)) { + //only save the state if away mode is set by user + SettingsManager::getInstance()->set(SettingsManager::AWAY, aNewMode != AWAY_OFF); + } + + awayMode = aNewMode; + if (awayMode > AWAY_OFF) + lastActivity = GET_TICK(); + + ClientManager::getInstance()->infoUpdated(); + fire(ActivityManagerListener::AwayModeChanged(), awayMode); +} + +string ActivityManager::getAwayMessage(const string& aAwayMsg, ParamMap& params) const noexcept { + params["idleTI"] = Util::formatSeconds(GET_TICK() - lastActivity); + return Util::formatParams(aAwayMsg, params); +} + +} // namespace dcpp \ No newline at end of file diff --git a/airdcpp-core/airdcpp/ActivityManager.h b/airdcpp-core/airdcpp/ActivityManager.h new file mode 100644 index 00000000..a9347c28 --- /dev/null +++ b/airdcpp-core/airdcpp/ActivityManager.h @@ -0,0 +1,68 @@ +/* +* Copyright (C) 2011-2016 AirDC++ Project +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#ifndef DCPLUSPLUS_DCPP_ACTIVITY_MANAGER_H +#define DCPLUSPLUS_DCPP_ACTIVITY_MANAGER_H + +#include "typedefs.h" + +#include "Speaker.h" +#include "TimerManager.h" + + +namespace dcpp { + //Away modes + enum AwayMode : uint8_t { + AWAY_OFF, + AWAY_IDLE, + AWAY_MANUAL //highest value + }; + + class ActivityManagerListener { + public: + virtual ~ActivityManagerListener() { } + template struct X { enum { TYPE = I }; }; + + typedef X<0> AwayModeChanged; + + virtual void on(AwayModeChanged, AwayMode) noexcept { } + }; + + class ActivityManager : public Speaker, public Singleton, public TimerManagerListener + { + public: + ActivityManager(); + ~ActivityManager(); + + void updateActivity(time_t aLastActivity = GET_TICK()) noexcept; + + bool isAway() const noexcept { return awayMode != AWAY_OFF; } + AwayMode getAwayMode() const noexcept { return awayMode; } + void setAway(AwayMode aAway); + + string getAwayMessage(const string& aAwayMsg, ParamMap& params) const noexcept; + private: + void on(TimerManagerListener::Second, uint64_t aTick) noexcept; + + AwayMode awayMode; + time_t lastActivity = GET_TICK(); + }; + +} // namespace dcpp + +#endif // DCPLUSPLUS_DCPP_ACTIVITY_MANAGER_H diff --git a/airdcpp-core/airdcpp/AdcHub.cpp b/airdcpp-core/airdcpp/AdcHub.cpp index 4710e87b..90ac6e1f 100644 --- a/airdcpp-core/airdcpp/AdcHub.cpp +++ b/airdcpp-core/airdcpp/AdcHub.cpp @@ -19,6 +19,7 @@ #include "stdinc.h" #include "version.h" +#include "ActivityManager.h" #include "AdcCommand.h" #include "AdcHub.h" #include "Message.h" @@ -1436,7 +1437,7 @@ void AdcHub::infoImpl() { addParam(lastInfoMap, c, "HO", Util::toString(counts[COUNT_OP])); addParam(lastInfoMap, c, "VE", shortVersionString); - addParam(lastInfoMap, c, "AW", AirUtil::getAway() ? "1" : Util::emptyString); + addParam(lastInfoMap, c, "AW", ActivityManager::getInstance()->isAway() ? "1" : Util::emptyString); addParam(lastInfoMap, c, "LC", Localization::getCurrentLocale()); int64_t limit = ThrottleManager::getInstance()->getDownLimit() * 1000; diff --git a/airdcpp-core/airdcpp/AirUtil.cpp b/airdcpp-core/airdcpp/AirUtil.cpp index e4abfdc7..3208c348 100644 --- a/airdcpp-core/airdcpp/AirUtil.cpp +++ b/airdcpp-core/airdcpp/AirUtil.cpp @@ -19,20 +19,20 @@ #include "stdinc.h" #include "AirUtil.h" -#include "Util.h" -#include "ThrottleManager.h" +#include "ConnectivityManager.h" #include "File.h" +#include "LogManager.h" #include "QueueManager.h" -#include "SettingsManager.h" -#include "ConnectivityManager.h" #include "ResourceManager.h" -#include "StringTokenizer.h" +#include "SettingsManager.h" +#include "ShareManager.h" #include "SimpleXML.h" #include "Socket.h" -#include "LogManager.h" -#include "Wildcards.h" -#include "ShareManager.h" +#include "StringTokenizer.h" +#include "ThrottleManager.h" +#include "Util.h" + #include #include @@ -63,9 +63,6 @@ boost::regex AirUtil::crcReg; string AirUtil::privKeyFile; string AirUtil::tempDLDir; -AwayMode AirUtil::away = AWAY_OFF; -time_t AirUtil::awayTime; - AirUtil::TimeCounter::TimeCounter(string aMsg) : start(GET_TICK()), msg(move(aMsg)) { } @@ -750,24 +747,6 @@ string AirUtil::regexEscape(const string& aStr, bool isWildcard) { return result; } -void AirUtil::setAway(AwayMode aAway) { - if(aAway != away) - ClientManager::getInstance()->infoUpdated(); - - if((aAway == AWAY_MANUAL) || (getAwayMode() == AWAY_MANUAL && aAway == AWAY_OFF) ) //only save the state if away mode is set by user - SettingsManager::getInstance()->set(SettingsManager::AWAY, aAway > 0); - - away = aAway; - - if (away > AWAY_OFF) - awayTime = time(NULL); -} - -string AirUtil::getAwayMessage(const string& aAwayMsg, ParamMap& params) { - params["idleTI"] = Util::formatSeconds(time(NULL) - awayTime); - return Util::formatParams(aAwayMsg, params); -} - string AirUtil::subtractCommonDirs(const string& toCompare, const string& toSubtract, char separator) { if (toSubtract.length() > 3) { string::size_type i = toSubtract.length()-2; diff --git a/airdcpp-core/airdcpp/AirUtil.h b/airdcpp-core/airdcpp/AirUtil.h index 8ec6fcea..3b45ffa7 100644 --- a/airdcpp-core/airdcpp/AirUtil.h +++ b/airdcpp-core/airdcpp/AirUtil.h @@ -26,14 +26,6 @@ namespace dcpp { -//Away modes -enum AwayMode : uint8_t { - AWAY_OFF, - AWAY_IDLE, - AWAY_MINIMIZE, - AWAY_MANUAL //highest value -}; - enum DupeType: uint8_t { DUPE_NONE, DUPE_SHARE_PARTIAL, @@ -137,11 +129,6 @@ class AirUtil { static string convertMovePath(const string& aPath, const string& aParent, const string& aTarget); static string regexEscape(const string& aStr, bool isWildcard); - static bool getAway() { return away > 0; } - static AwayMode getAwayMode() { return away; } - static void setAway(AwayMode aAway); - static string getAwayMessage(const string& aAwayMsg, ParamMap& params); - /* Removes common dirs from the end of toSubtract */ static string subtractCommonDirs(const string& toCompare, const string& toSubtract, char separator); @@ -151,8 +138,6 @@ class AirUtil { private: static bool removeDirectoryIfEmptyRe(const string& tgt, int maxAttempts, int curAttempts); - static AwayMode away; - static time_t awayTime; }; diff --git a/airdcpp-core/airdcpp/AutoSearchManager.cpp b/airdcpp-core/airdcpp/AutoSearchManager.cpp index a068ea7c..4bba9f60 100644 --- a/airdcpp-core/airdcpp/AutoSearchManager.cpp +++ b/airdcpp-core/airdcpp/AutoSearchManager.cpp @@ -54,20 +54,20 @@ AutoSearchManager::~AutoSearchManager() noexcept { QueueManager::getInstance()->removeListener(this); } -void AutoSearchManager::logMessage(const string& aMsg, bool error) const noexcept { - LogManager::getInstance()->message(STRING(AUTO_SEARCH) + ": " + aMsg, error ? LogMessage::SEV_ERROR : LogMessage::SEV_INFO); +void AutoSearchManager::logMessage(const string& aMsg, LogMessage::Severity aSeverity) const noexcept { + LogManager::getInstance()->message(STRING(AUTO_SEARCH) + ": " + aMsg, aSeverity); } /* Adding new items for external use */ AutoSearchPtr AutoSearchManager::addAutoSearch(const string& ss, const string& aTarget, TargetUtil::TargetType aTargetType, bool isDirectory, AutoSearch::ItemType asType, bool aRemove, int aInterval) noexcept { if (ss.length() <= 5) { - logMessage(STRING_F(AUTOSEARCH_ADD_FAILED, ss % STRING(LINE_EMPTY_OR_TOO_SHORT)), true); + logMessage(STRING_F(AUTOSEARCH_ADD_FAILED, ss % STRING(LINE_EMPTY_OR_TOO_SHORT)), LogMessage::SEV_ERROR); return nullptr; } auto lst = getSearchesByString(ss); if (!lst.empty()) { - logMessage(STRING_F(AUTOSEARCH_ADD_FAILED, ss % STRING(ITEM_NAME_EXISTS)), true); + logMessage(STRING_F(AUTOSEARCH_ADD_FAILED, ss % STRING(ITEM_NAME_EXISTS)), LogMessage::SEV_ERROR); return nullptr; } @@ -97,7 +97,7 @@ void AutoSearchManager::addAutoSearch(AutoSearchPtr aAutoSearch, bool search, bo if (search) { if (!searchItem(aAutoSearch, TYPE_NEW)) { //no hubs - logMessage(CSTRING_F(AUTOSEARCH_ADDED, aAutoSearch->getSearchString()), false); + logMessage(CSTRING_F(AUTOSEARCH_ADDED, aAutoSearch->getSearchString()), LogMessage::SEV_INFO); } } if(!loading) { @@ -330,7 +330,7 @@ void AutoSearchManager::onRemoveBundle(const BundlePtr& aBundle, bool finished) handleExpiredItems(expired); for (auto& as : removed) { removeAutoSearch(as); - logMessage(STRING_F(COMPLETE_ITEM_X_REMOVED, as->getSearchString()), false); + logMessage(STRING_F(COMPLETE_ITEM_X_REMOVED, as->getSearchString()), LogMessage::SEV_INFO); } //One or more items got in searching state again if (itemsEnabled) @@ -341,10 +341,10 @@ void AutoSearchManager::onRemoveBundle(const BundlePtr& aBundle, bool finished) void AutoSearchManager::handleExpiredItems(AutoSearchList& expired) noexcept{ for (auto& as : expired) { if (SETTING(REMOVE_EXPIRED_AS)) { - logMessage(STRING_F(EXPIRED_AS_REMOVED, as->getSearchString()), false); + logMessage(STRING_F(EXPIRED_AS_REMOVED, as->getSearchString()), LogMessage::SEV_INFO); removeAutoSearch(as); } else if (as->getEnabled()) { - logMessage(STRING_F(EXPIRED_AS_DISABLED, as->getSearchString()), false); + logMessage(STRING_F(EXPIRED_AS_DISABLED, as->getSearchString()), LogMessage::SEV_INFO); setItemActive(as, false); } else { // disabled already @@ -449,7 +449,7 @@ void AutoSearchManager::performSearch(AutoSearchPtr& as, StringList& aHubs, Sear msg = STRING_F(ITEM_SEARCHED_IN, searchWord % time); } } - logMessage(msg, false); + logMessage(msg, LogMessage::SEV_INFO); } else { fire(AutoSearchManagerListener::SearchForeground(), as, searchWord); } @@ -850,11 +850,12 @@ void AutoSearchManager::handleAction(const SearchResultPtr& sr, AutoSearchPtr& a } else { TargetUtil::TargetInfo ti; bool hasSpace = TargetUtil::getVirtualTarget(as->getTarget(), as->getTargetType(), ti, sr->getSize()); - if (!hasSpace) - TargetUtil::reportInsufficientSize(ti, sr->getSize()); + if (!hasSpace) { + logMessage(TargetUtil::formatSizeNotification(ti, sr->getSize()), LogMessage::SEV_WARNING); + } try { - auto b = QueueManager::getInstance()->createFileBundle(ti.targetDir + sr->getFileName(), sr->getSize(), sr->getTTH(), + auto b = QueueManager::getInstance()->createFileBundle(ti.getTarget() + sr->getFileName(), sr->getSize(), sr->getTTH(), sr->getUser(), sr->getDate(), 0, ((as->getAction() == AutoSearch::ACTION_QUEUE) ? QueueItem::PAUSED : QueueItem::DEFAULT)); @@ -862,7 +863,7 @@ void AutoSearchManager::handleAction(const SearchResultPtr& sr, AutoSearchPtr& a onBundleCreated(b, as->getToken()); } } catch(const Exception& e) { - onBundleError(as->getToken(), e.getError(), ti.targetDir + sr->getFileName(), sr->getUser()); + onBundleError(as->getToken(), e.getError(), ti.getTarget() + sr->getFileName(), sr->getUser()); return; } } @@ -881,7 +882,7 @@ void AutoSearchManager::handleAction(const SearchResultPtr& sr, AutoSearchPtr& a if (as->getRemove()) { removeAutoSearch(as); - logMessage(STRING_F(COMPLETE_ITEM_X_REMOVED, as->getSearchString()), false); + logMessage(STRING_F(COMPLETE_ITEM_X_REMOVED, as->getSearchString()), LogMessage::SEV_INFO); } } } diff --git a/airdcpp-core/airdcpp/AutoSearchManager.h b/airdcpp-core/airdcpp/AutoSearchManager.h index 904cdb9a..5cb2d96d 100644 --- a/airdcpp-core/airdcpp/AutoSearchManager.h +++ b/airdcpp-core/airdcpp/AutoSearchManager.h @@ -76,8 +76,7 @@ class AutoSearchManager : public Singleton, public SpeakergetQueueItems()) { if (countAll || q->getDownloadedBytes() == 0) { - s->second.queued += q->getSize(); + s->second.addQueued(q->getSize()); } } } diff --git a/airdcpp-core/airdcpp/CryptoManager.cpp b/airdcpp-core/airdcpp/CryptoManager.cpp index 6e61f72a..83a335a8 100644 --- a/airdcpp-core/airdcpp/CryptoManager.cpp +++ b/airdcpp-core/airdcpp/CryptoManager.cpp @@ -54,11 +54,12 @@ namespace dcpp { - void* CryptoManager::tmpKeysMap[KEY_LAST] = { NULL, NULL, NULL }; - CriticalSection* CryptoManager::cs = NULL; - int CryptoManager::idxVerifyData = 0; - char CryptoManager::idxVerifyDataName[] = "AirDC.VerifyData"; - CryptoManager::SSLVerifyData CryptoManager::trustedKeyprint = { false, "trusted_keyp" }; +void* CryptoManager::tmpKeysMap[KEY_LAST] = { NULL, NULL, NULL }; +CriticalSection* CryptoManager::cs = NULL; +int CryptoManager::idxVerifyData = 0; +char CryptoManager::idxVerifyDataName[] = "AirDC.VerifyData"; +CryptoManager::SSLVerifyData CryptoManager::trustedKeyprint = { false, "trusted_keyp" }; + CryptoManager::CryptoManager() : @@ -90,47 +91,53 @@ CryptoManager::CryptoManager() for (int i = KEY_RSA_2048; i != KEY_LAST; ++i) tmpKeysMap[i] = getTmpRSA(getKeyLength(static_cast(i))); - const char ciphersuites[] = - "ECDHE-ECDSA-AES128-GCM-SHA256:" - "ECDHE-RSA-AES128-GCM-SHA256:" - "ECDHE-ECDSA-AES128-SHA256:" - "ECDHE-RSA-AES128-SHA256:" - "ECDHE-ECDSA-AES128-SHA:" - "ECDHE-RSA-AES128-SHA:" - "DHE-RSA-AES128-SHA:" - "AES128-SHA:" - "ECDHE-ECDSA-AES256-GCM-SHA384:" - "ECDHE-RSA-AES256-GCM-SHA384:" - "ECDHE-ECDSA-AES256-SHA384:" - "ECDHE-RSA-AES256-SHA384:" - "ECDHE-ECDSA-AES256-SHA:" - "ECDHE-RSA-AES256-SHA:" - "AES256-GCM-SHA384:" - "AES256-SHA256:" - "AES256-SHA"; - SSL_CTX_set_options(clientContext, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION); - SSL_CTX_set_cipher_list(clientContext, ciphersuites); SSL_CTX_set_options(serverContext, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION); - SSL_CTX_set_cipher_list(serverContext, ciphersuites); + + setContextOptions(clientContext, false); + setContextOptions(serverContext, true); + + SSL_CTX_set_tmp_dh_callback(serverContext, CryptoManager::tmp_dh_cb); + SSL_CTX_set_tmp_rsa_callback(serverContext, CryptoManager::tmp_rsa_cb); + SSL_CTX_set_verify(clientContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); + SSL_CTX_set_verify(serverContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); + } +} + +void CryptoManager::setContextOptions(SSL_CTX* aCtx, bool aServer) { + const char ciphersuites[] = + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "ECDHE-ECDSA-AES128-SHA256:" + "ECDHE-RSA-AES128-SHA256:" + "ECDHE-ECDSA-AES128-SHA:" + "ECDHE-RSA-AES128-SHA:" + "DHE-RSA-AES128-SHA:" + "AES128-SHA:" + "ECDHE-ECDSA-AES256-GCM-SHA384:" + "ECDHE-RSA-AES256-GCM-SHA384:" + "ECDHE-ECDSA-AES256-SHA384:" + "ECDHE-RSA-AES256-SHA384:" + "ECDHE-ECDSA-AES256-SHA:" + "ECDHE-RSA-AES256-SHA:" + "AES256-GCM-SHA384:" + "AES256-SHA256:" + "AES256-SHA"; + + SSL_CTX_set_cipher_list(aCtx, ciphersuites); #if OPENSSL_VERSION_NUMBER >= 0x1000201fL - SSL_CTX_set1_curves_list(clientContext, "P-256"); - SSL_CTX_set1_curves_list(serverContext, "P-256"); + SSL_CTX_set1_curves_list(aCtx, "P-256"); #endif - + + if (aServer) { EC_KEY* tmp_ecdh; if ((tmp_ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) != NULL) { - SSL_CTX_set_options(serverContext, SSL_OP_SINGLE_ECDH_USE); - SSL_CTX_set_tmp_ecdh(serverContext, tmp_ecdh); + SSL_CTX_set_options(aCtx, SSL_OP_SINGLE_ECDH_USE); + SSL_CTX_set_tmp_ecdh(aCtx, tmp_ecdh); EC_KEY_free(tmp_ecdh); } - - SSL_CTX_set_tmp_dh_callback(serverContext, CryptoManager::tmp_dh_cb); - SSL_CTX_set_tmp_rsa_callback(serverContext, CryptoManager::tmp_rsa_cb); - SSL_CTX_set_verify(clientContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); - SSL_CTX_set_verify(serverContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback); } } diff --git a/airdcpp-core/airdcpp/CryptoManager.h b/airdcpp-core/airdcpp/CryptoManager.h index 52bb694b..7bbe5af8 100644 --- a/airdcpp-core/airdcpp/CryptoManager.h +++ b/airdcpp-core/airdcpp/CryptoManager.h @@ -69,6 +69,9 @@ class CryptoManager : public Singleton static void setCertPaths(); static int idxVerifyData; + + // Options that can also be shared with external contexts + static void setContextOptions(SSL_CTX* aSSL, bool aServer); private: friend class Singleton; diff --git a/airdcpp-core/airdcpp/DCPlusPlus.cpp b/airdcpp-core/airdcpp/DCPlusPlus.cpp index 1abb1fe6..f1b2cc6e 100644 --- a/airdcpp-core/airdcpp/DCPlusPlus.cpp +++ b/airdcpp-core/airdcpp/DCPlusPlus.cpp @@ -19,6 +19,7 @@ #include "stdinc.h" #include "DCPlusPlus.h" +#include "ActivityManager.h" #include "ConnectionManager.h" #include "DownloadManager.h" #include "GeoManager.h" @@ -105,6 +106,7 @@ void startup(function stepF, functionload(messageF); @@ -186,6 +188,7 @@ void shutdown(function stepF, function progr announce(STRING(SHUTTING_DOWN)); + ActivityManager::deleteInstance(); ViewFileManager::deleteInstance(); HighlightManager::deleteInstance(); UpdateManager::deleteInstance(); diff --git a/airdcpp-core/airdcpp/DirectoryListing.cpp b/airdcpp-core/airdcpp/DirectoryListing.cpp index b28fc2f9..b1d18ff0 100644 --- a/airdcpp-core/airdcpp/DirectoryListing.cpp +++ b/airdcpp-core/airdcpp/DirectoryListing.cpp @@ -42,7 +42,7 @@ namespace dcpp { using boost::range::for_each; using boost::range::find_if; -DirectoryListing::DirectoryListing(const HintedUser& aUser, bool aPartial, const string& aFileName, bool aIsClientView, bool aIsOwnList) : +DirectoryListing::DirectoryListing(const HintedUser& aUser, bool aPartial, const string& aFileName, bool aIsClientView, bool aIsOwnList) : TrackableDownloadItem(aIsOwnList || (!aPartial && !aFileName.empty())), hintedUser(aUser), root(new Directory(nullptr, Util::emptyString, Directory::TYPE_INCOMPLETE_NOCHILD, 0)), partialList(aPartial), isOwnList(aIsOwnList), fileName(aFileName), isClientView(aIsClientView), matchADL(SETTING(USE_ADLS) && !aPartial), tasks(isClientView, Thread::NORMAL, std::bind(&DirectoryListing::dispatch, this, std::placeholders::_1)) @@ -110,6 +110,34 @@ void stripExtensions(string& name) noexcept { } } +ProfileToken DirectoryListing::getShareProfile() const noexcept { + return Util::toInt(fileName); +} + +void DirectoryListing::setShareProfile(ProfileToken aProfile) noexcept { + setFileName(Util::toString(aProfile)); + if (partialList) { + addAsyncTask([=] { + changeDirectory(Util::emptyString, RELOAD_ALL); + }); + } else { + addFullListTask(Util::emptyString); + } + + SettingsManager::getInstance()->set(SettingsManager::LAST_LIST_PROFILE, aProfile); + fire(DirectoryListingListener::ShareProfileChanged()); +} + +void DirectoryListing::getPartialListInfo(int64_t& totalSize_, size_t& totalFiles_) const noexcept { + if (isOwnList) { + ShareManager::getInstance()->getProfileInfo(getShareProfile(), totalSize_, totalFiles_); + } + + auto si = ClientManager::getInstance()->getShareInfo(hintedUser); + totalSize_ = si.first; + totalFiles_ = si.second; +} + string DirectoryListing::getNickFromFilename(const string& fileName) noexcept { // General file list name format: [username].[CID].[xml|xml.bz2] @@ -506,7 +534,17 @@ int64_t DirectoryListing::getDirSize(const string& aDir) const noexcept { } bool DirectoryListing::viewAsText(const File::Ptr& aFile) const noexcept { - return ViewFileManager::getInstance()->addFileNotify(aFile->getName(), aFile->getSize(), aFile->getTTH(), hintedUser, true); + if (isOwnList) { + StringList paths; + getLocalPaths(aFile, paths); + if (!paths.empty()) { + return ViewFileManager::getInstance()->addLocalFile(paths.front(), aFile->getTTH(), true); + } + + return false; + } + + return ViewFileManager::getInstance()->addUserFileNotify(aFile->getName(), aFile->getSize(), aFile->getTTH(), hintedUser, true); } DirectoryListing::Directory::Ptr DirectoryListing::findDirectory(const string& aName, const Directory::Ptr& current) const noexcept { @@ -544,7 +582,7 @@ void DirectoryListing::findNfoImpl(const string& aPath, bool aAllowQueueList, Du try { SearchResultList results; auto s = unique_ptr(SearchQuery::getSearch(Util::emptyString, Util::emptyString, 0, SearchManager::TYPE_ANY, SearchManager::SIZE_DONTCARE, { ".nfo" }, SearchQuery::MATCH_NAME, false, 10)); - ShareManager::getInstance()->search(results, *s.get(), Util::toInt(getFileName()), ClientManager::getInstance()->getMyCID(), Util::toAdcFile(aPath)); + ShareManager::getInstance()->search(results, *s.get(), getShareProfile(), ClientManager::getInstance()->getMyCID(), Util::toAdcFile(aPath)); if (!results.empty()) { auto paths = AirUtil::getDupePaths(DUPE_SHARE, results.front()->getTTH()); @@ -660,7 +698,7 @@ void DirectoryListing::getLocalPaths(const File::Ptr& f, StringList& ret) const else path = f->getParent()->getPath(); - ShareManager::getInstance()->getRealPaths(Util::toAdcFile(path + f->getName()), ret, Util::toInt(fileName)); + ShareManager::getInstance()->getRealPaths(Util::toAdcFile(path + f->getName()), ret, getShareProfile()); } else { ret = AirUtil::getDupePaths(f->getDupe(), f->getTTH()); } @@ -677,7 +715,7 @@ void DirectoryListing::getLocalPaths(const Directory::Ptr& d, StringList& ret) c path = d->getPath(); if (isOwnList) { - ShareManager::getInstance()->getRealPaths(Util::toAdcFile(path), ret, Util::toInt(fileName)); + ShareManager::getInstance()->getRealPaths(Util::toAdcFile(path), ret, getShareProfile()); } else { ret = ShareManager::getInstance()->getDirPaths(path); } @@ -967,7 +1005,7 @@ void DirectoryListing::searchImpl(const string& aSearchString, int64_t aSize, in if (isOwnList && partialList) { SearchResultList results; try { - ShareManager::getInstance()->search(results, *curSearch, Util::toInt(fileName), CID(), aDir); + ShareManager::getInstance()->search(results, *curSearch, getShareProfile(), CID(), aDir); } catch (...) {} for (const auto& sr : results) @@ -1055,6 +1093,10 @@ void DirectoryListing::loadPartialImpl(const string& aXml, const string& aBaseDi } } +bool DirectoryListing::isLoaded() const noexcept { + return currentLocation.directory && !currentLocation.directory->getLoading(); +} + void DirectoryListing::matchQueueImpl() noexcept { int matches = 0, newFiles = 0; BundleList bundles; @@ -1133,7 +1175,7 @@ void DirectoryListing::endSearch(bool timedOut /*false*/) noexcept { } int DirectoryListing::loadShareDirectory(const string& aPath, bool aRecurse) throw(Exception, AbortException) { - auto mis = ShareManager::getInstance()->generatePartialList(Util::toAdcFile(aPath), aRecurse, Util::toInt(fileName)); + auto mis = ShareManager::getInstance()->generatePartialList(Util::toAdcFile(aPath), aRecurse, getShareProfile()); if (mis) { return loadXML(*mis, true, Util::toAdcFile(aPath)); } @@ -1227,7 +1269,7 @@ void DirectoryListing::on(ShareManagerListener::DirectoriesRefreshed, uint8_t, c string lastVirtual; for (const auto& p : aPaths) { - auto vPath = ShareManager::getInstance()->realToVirtual(p, Util::toInt(fileName)); + auto vPath = ShareManager::getInstance()->realToVirtual(p, getShareProfile()); if (!vPath.empty() && lastVirtual != vPath && findDirectory(vPath)) { addAsyncTask([=] { loadPartialImpl(Util::emptyString, vPath, false, false, nullptr); }); lastVirtual = vPath; diff --git a/airdcpp-core/airdcpp/DirectoryListing.h b/airdcpp-core/airdcpp/DirectoryListing.h index bbecdc68..0dcddefe 100644 --- a/airdcpp-core/airdcpp/DirectoryListing.h +++ b/airdcpp-core/airdcpp/DirectoryListing.h @@ -157,6 +157,7 @@ class DirectoryListing : public intrusive_ptr_base, public Use ~DirectoryListing(); void loadFile() throw(Exception, AbortException); + bool isLoaded() const noexcept; //return the number of loaded dirs int updateXML(const string& aXml, const string& aBase) throw(AbortException); @@ -182,6 +183,10 @@ class DirectoryListing : public intrusive_ptr_base, public Use string getNick(bool firstOnly) const noexcept; static string getNickFromFilename(const string& fileName) noexcept; static UserPtr getUserFromFilename(const string& fileName) noexcept; + + ProfileToken getShareProfile() const noexcept; + void setShareProfile(ProfileToken aProfile) noexcept; + void getPartialListInfo(int64_t& totalSize_, size_t& totalFiles_) const noexcept; const UserPtr& getUser() const noexcept { return hintedUser.user; } const HintedUser& getHintedUser() const noexcept { return hintedUser; } diff --git a/airdcpp-core/airdcpp/DirectoryListingListener.h b/airdcpp-core/airdcpp/DirectoryListingListener.h index d6ca3ee1..efa9100a 100644 --- a/airdcpp-core/airdcpp/DirectoryListingListener.h +++ b/airdcpp-core/airdcpp/DirectoryListingListener.h @@ -42,6 +42,7 @@ class DirectoryListingListener { typedef X<11> UserUpdated; typedef X<12> StateChanged; typedef X<13> Read; + typedef X<14> ShareProfileChanged; virtual void on(LoadingFinished, int64_t /*start*/, const string& /*aDir*/, bool /*reloadList*/, bool /*changeDir*/) noexcept { } virtual void on(LoadingFailed, const string&) noexcept { } @@ -57,6 +58,7 @@ class DirectoryListingListener { virtual void on(UserUpdated) noexcept {} virtual void on(StateChanged) noexcept {} virtual void on(Read) noexcept {} + virtual void on(ShareProfileChanged) noexcept {} }; } // namespace dcpp diff --git a/airdcpp-core/airdcpp/DirectoryListingManager.cpp b/airdcpp-core/airdcpp/DirectoryListingManager.cpp index 98eea9d3..fe19a335 100644 --- a/airdcpp-core/airdcpp/DirectoryListingManager.cpp +++ b/airdcpp-core/airdcpp/DirectoryListingManager.cpp @@ -161,7 +161,7 @@ void DirectoryListingManager::processList(const string& aFileName, const string& processListAction(dirList, aRemotePath, flags); } -bool DirectoryListingManager::download(const DirectoryDownloadInfo::Ptr& di, const DirectoryListingPtr& aList, const string& aTarget) noexcept { +bool DirectoryListingManager::download(const DirectoryDownloadInfo::Ptr& di, const DirectoryListingPtr& aList, const string& aTarget, bool aHasFreeSpace) noexcept { auto getList = [&] { addDirectoryDownload(di->getListPath(), di->getBundleName(), aList->getHintedUser(), di->getTarget(), di->getTargetType(), di->getSizeUnknown(), di->getPriority(), di->getRecursiveListAttempted() ? true : false, di->getAutoSearch(), false, false); }; @@ -182,7 +182,7 @@ bool DirectoryListingManager::download(const DirectoryDownloadInfo::Ptr& di, con } // Queue the directory - return aList->downloadDirImpl(dir, aTarget + di->getBundleName() + PATH_SEPARATOR, di->getPriority(), di->getAutoSearch()); + return aList->downloadDirImpl(dir, aTarget + di->getBundleName() + PATH_SEPARATOR, aHasFreeSpace ? di->getPriority() : QueueItemBase::PAUSED_FORCE, di->getAutoSearch()); } void DirectoryListingManager::handleDownload(DirectoryDownloadInfo::Ptr& di, DirectoryListingPtr& aList) noexcept { @@ -200,29 +200,30 @@ void DirectoryListingManager::handleDownload(DirectoryDownloadInfo::Ptr& di, Dir } if (directDownload) { - download(di, aList, di->getTarget()); + download(di, aList, di->getTarget(), true); return; } //we have a new directory TargetUtil::TargetInfo ti; - int64_t dirSize = aList->getDirSize(di->getListPath()); + auto dirSize = aList->getDirSize(di->getListPath()); TargetUtil::getVirtualTarget(di->getTarget(), di->getTargetType(), ti, dirSize); - bool hasFreeSpace = ti.getFreeSpace() >= dirSize; + auto hasFreeSpace = ti.hasFreeSpace(dirSize); if (di->getSizeUnknown()) { - auto queued = download(di, aList, ti.targetDir); - if (!hasFreeSpace && queued) - TargetUtil::reportInsufficientSize(ti, dirSize); + auto queued = download(di, aList, ti.getTarget(), hasFreeSpace); + if (!hasFreeSpace && queued) { + LogManager::getInstance()->message(TargetUtil::formatSizeNotification(ti, dirSize), LogMessage::SEV_WARNING); + } if (queued) { WLock l(cs); - finishedListings.emplace(di->getFinishedDirName(), new FinishedDirectoryItem(!hasFreeSpace, ti.targetDir)); + finishedListings.emplace(di->getFinishedDirName(), new FinishedDirectoryItem(!hasFreeSpace, ti.getTarget())); } } else { - if (download(di, aList, ti.targetDir)) { + if (download(di, aList, ti.getTarget(), true)) { WLock l(cs); - finishedListings.emplace(di->getFinishedDirName(), new FinishedDirectoryItem(false, ti.targetDir)); + finishedListings.emplace(di->getFinishedDirName(), new FinishedDirectoryItem(false, ti.getTarget())); } } } @@ -362,17 +363,19 @@ void DirectoryListingManager::on(QueueManagerListener::Removed, const QueueItemP void DirectoryListingManager::openOwnList(ProfileToken aProfile, bool useADL /*false*/) noexcept { auto me = HintedUser(ClientManager::getInstance()->getMe(), Util::emptyString); - if (hasList(me.user)) - return; - auto dl = DirectoryListingPtr(new DirectoryListing(me, !useADL, Util::toString(aProfile), true, true)); - dl->setMatchADL(useADL); + auto dl = hasList(me.user); + if (dl) { + if (dl->getShareProfile() != aProfile) { + dl->setShareProfile(aProfile); + } - { - WLock l(cs); - viewedLists[me] = dl; + return; } + dl = createList(me, !useADL, Util::toString(aProfile), true); + dl->setMatchADL(useADL); + fire(DirectoryListingManagerListener::OpenListing(), dl, Util::emptyString, Util::emptyString); } @@ -380,14 +383,20 @@ void DirectoryListingManager::openFileList(const HintedUser& aUser, const string if (hasList(aUser.user)) return; - auto dl = DirectoryListingPtr(new DirectoryListing(aUser, false, aFile, true, false)); + auto dl = createList(aUser, false, aFile, false); + fire(DirectoryListingManagerListener::OpenListing(), dl, Util::emptyString, Util::emptyString); +} + +DirectoryListingPtr DirectoryListingManager::createList(const HintedUser& aUser, bool aPartial, const string& aFileName, bool aIsOwnList) noexcept { + auto dl = DirectoryListingPtr(new DirectoryListing(aUser, aPartial, aFileName, true, aIsOwnList)); { WLock l(cs); - viewedLists[aUser.user] = dl; + viewedLists[dl->getHintedUser()] = dl; } - fire(DirectoryListingManagerListener::OpenListing(), dl, Util::emptyString, Util::emptyString); + fire(DirectoryListingManagerListener::ListingCreated(), dl); + return dl; } void DirectoryListingManager::on(QueueManagerListener::Added, QueueItemPtr& aQI) noexcept { @@ -402,19 +411,12 @@ void DirectoryListingManager::on(QueueManagerListener::Added, QueueItemPtr& aQI) } if (!aQI->isSet(QueueItem::FLAG_PARTIAL_LIST)) { - dl = DirectoryListingPtr(new DirectoryListing(user, false, aQI->getListName(), true, false)); + dl = createList(user, false, aQI->getListName(), false); } else { - dl = DirectoryListingPtr(new DirectoryListing(user, true, Util::emptyString, true, false)); + dl = createList(user, true, Util::emptyString, false); } dl->onAddedQueue(aQI->getTarget()); - - { - WLock l(cs); - viewedLists[user] = dl; - } - - fire(DirectoryListingManagerListener::ListingCreated(), dl); } DirectoryListingPtr DirectoryListingManager::hasList(const UserPtr& aUser) noexcept { diff --git a/airdcpp-core/airdcpp/DirectoryListingManager.h b/airdcpp-core/airdcpp/DirectoryListingManager.h index 9812b363..d09c011c 100644 --- a/airdcpp-core/airdcpp/DirectoryListingManager.h +++ b/airdcpp-core/airdcpp/DirectoryListingManager.h @@ -109,9 +109,11 @@ namespace dcpp { }; - bool download(const DirectoryDownloadInfo::Ptr& di, const DirectoryListingPtr& aList, const string& aTarget) noexcept; + bool download(const DirectoryDownloadInfo::Ptr& di, const DirectoryListingPtr& aList, const string& aTarget, bool aHasFreeSpace) noexcept; void handleDownload(DirectoryDownloadInfo::Ptr& di, DirectoryListingPtr& aList) noexcept; + DirectoryListingPtr createList(const HintedUser& aUser, bool aPartial, const string& aFileName, bool aIsOwnList) noexcept; + friend class Singleton; mutable SharedMutex cs; diff --git a/airdcpp-core/airdcpp/File.cpp b/airdcpp-core/airdcpp/File.cpp index 5c8c92dc..e3d54a1e 100644 --- a/airdcpp-core/airdcpp/File.cpp +++ b/airdcpp-core/airdcpp/File.cpp @@ -403,8 +403,7 @@ int File::extendFile(int64_t len) noexcept { char zero; if( (lseek(h, (off_t)len, SEEK_SET) != -1) && (::write(h, &zero,1) != -1) ) { - ftruncate(h,(off_t)len); - return 1; + return ftruncate(h,(off_t)len); } return -1; } diff --git a/airdcpp-core/airdcpp/HashManager.cpp b/airdcpp-core/airdcpp/HashManager.cpp index 4d8749d2..de7035f4 100644 --- a/airdcpp-core/airdcpp/HashManager.cpp +++ b/airdcpp-core/airdcpp/HashManager.cpp @@ -1206,15 +1206,15 @@ int HashManager::Hasher::run() { int64_t averageSpeed = 0; if (!failed) { - sizeHashed += size; + totalSizeHashed += size; dirSizeHashed += size; dirFilesHashed++; - filesHashed++; + totalFilesHashed++; } if(end > start) { - hashTime += (end - start); + totalHashTime += (end - start); dirHashTime += (end - start); averageSpeed = size * 1000 / (end - start); } @@ -1225,7 +1225,6 @@ int HashManager::Hasher::run() { } else { fi = HashedFile(tt.getRoot(), timestamp, size); getInstance()->hashDone(fname, pathLower, tt, averageSpeed, fi, hasherID); - //tth = tt.getRoot(); } } catch(const FileException& e) { totalBytesLeft -= sizeLeft; @@ -1238,6 +1237,7 @@ int HashManager::Hasher::run() { auto onDirHashed = [&] () -> void { if ((SETTING(HASHERS_PER_VOLUME) == 1 || w.empty()) && (dirFilesHashed > 1 || !failed)) { + getInstance()->fire(HashManagerListener::DirectoryHashed(), initialDir, dirFilesHashed, dirSizeHashed, dirHashTime, hasherID); if (dirFilesHashed == 1) { getInstance()->log(STRING_F(HASHING_FINISHED_FILE, currentFile % Util::formatBytes(dirSizeHashed) % @@ -1252,7 +1252,7 @@ int HashManager::Hasher::run() { } } - dirsHashed++; + totalDirsHashed++; dirHashTime = 0; dirSizeHashed = 0; dirFilesHashed = 0; @@ -1266,15 +1266,16 @@ int HashManager::Hasher::run() { removeDevice(curDevID); if (w.empty()) { - if (sizeHashed > 0) { - if (dirsHashed == 0) { + getInstance()->fire(HashManagerListener::HasherFinished(), totalDirsHashed, totalFilesHashed, totalSizeHashed, totalHashTime, hasherID); + if (totalSizeHashed > 0) { + if (totalDirsHashed == 0) { onDirHashed(); //LogManager::getInstance()->message(STRING(HASHING_FINISHED_TOTAL_PLAIN), LogMessage::SEV_INFO); } else { onDirHashed(); - getInstance()->log(STRING_F(HASHING_FINISHED_TOTAL, filesHashed % Util::formatBytes(sizeHashed) % dirsHashed % - Util::formatTime(hashTime / 1000, true) % - (Util::formatBytes(hashTime > 0 ? ((sizeHashed * 1000) / hashTime) : 0) + "/s" )), hasherID, false, false); + getInstance()->log(STRING_F(HASHING_FINISHED_TOTAL, totalFilesHashed % Util::formatBytes(totalSizeHashed) % totalDirsHashed % + Util::formatTime(totalHashTime / 1000, true) % + (Util::formatBytes(totalHashTime > 0 ? ((totalSizeHashed * 1000) / totalHashTime) : 0) + "/s" )), hasherID, false, false); } } else if(!fname.empty()) { //all files failed to hash? @@ -1284,10 +1285,10 @@ int HashManager::Hasher::run() { initialDir.clear(); } - hashTime = 0; - sizeHashed = 0; - dirsHashed = 0; - filesHashed = 0; + totalHashTime = 0; + totalSizeHashed = 0; + totalDirsHashed = 0; + totalFilesHashed = 0; lastSpeed = 0; deleteThis = hasherID != 0; sfv.unload(); diff --git a/airdcpp-core/airdcpp/HashManager.h b/airdcpp-core/airdcpp/HashManager.h index e0dc1506..7fd1e3f7 100644 --- a/airdcpp-core/airdcpp/HashManager.h +++ b/airdcpp-core/airdcpp/HashManager.h @@ -45,11 +45,15 @@ class HashManagerListener { typedef X<1> HashFailed; typedef X<2> MaintananceFinished; typedef X<3> MaintananceStarted; + typedef X<4> DirectoryHashed; + typedef X<5> HasherFinished; virtual void on(TTHDone, const string& /* filePath */, HashedFile& /* fileInfo */) noexcept { } virtual void on(HashFailed, const string& /* filePath */, HashedFile& /*null*/) noexcept { } virtual void on(MaintananceStarted) noexcept { } virtual void on(MaintananceFinished) noexcept { } + virtual void on(DirectoryHashed, const string& /* dirPath */, int /* filesHashed */, int64_t /*sizeHashed*/, time_t /*hashDuration*/, int /*hasherId*/) noexcept { } + virtual void on(HasherFinished, int /* dirsHashed */, int /* filesHashed */, int64_t /*sizeHashed*/, time_t /*hashDuration*/, int /*hasherId*/) noexcept { } }; class HashLoader; @@ -183,13 +187,13 @@ class HashManager : public Singleton, public SpeakergetReplyTo()->getClient()->getHubUrl()), true); chat->handleMessage(aMessage); - if (AirUtil::getAway() && !myPM && (!SETTING(NO_AWAYMSG_TO_BOTS) || !user->isSet(User::BOT))) { + if (ActivityManager::getInstance()->isAway() && !myPM && (!SETTING(NO_AWAYMSG_TO_BOTS) || !user->isSet(User::BOT))) { ParamMap params; aMessage->getFrom()->getIdentity().getParams(params, "user", false); string error; - chat->sendMessage(AirUtil::getAwayMessage(c->get(HubSettings::AwayMsg), params), error, false); + chat->sendMessage(ActivityManager::getInstance()->getAwayMessage(c->get(HubSettings::AwayMsg), params), error, false); } } diff --git a/airdcpp-core/airdcpp/NmdcHub.cpp b/airdcpp-core/airdcpp/NmdcHub.cpp index 8263e33b..68865fe9 100644 --- a/airdcpp-core/airdcpp/NmdcHub.cpp +++ b/airdcpp-core/airdcpp/NmdcHub.cpp @@ -38,6 +38,7 @@ #include "ThrottleManager.h" #include "UploadManager.h" #include "MessageManager.h" +#include "ActivityManager.h" namespace dcpp { @@ -987,7 +988,7 @@ void NmdcHub::myInfo(bool alwaysSend) { version = VERSIONSTRING; - if(AirUtil::getAway()) { + if(ActivityManager::getInstance()->isAway()) { status |= Identity::AWAY; } if(!isActive()) { diff --git a/airdcpp-core/airdcpp/PrivateChat.cpp b/airdcpp-core/airdcpp/PrivateChat.cpp index 98f1256b..59d9c7a9 100644 --- a/airdcpp-core/airdcpp/PrivateChat.cpp +++ b/airdcpp-core/airdcpp/PrivateChat.cpp @@ -127,6 +127,7 @@ void PrivateChat::handleMessage(const ChatMessagePtr& aMessage) { void PrivateChat::setRead() noexcept { auto updated = cache.setRead(); if (updated > 0) { + sendPMInfo(PrivateChat::MSG_SEEN); fire(PrivateChatListener::MessagesRead(), this); } } diff --git a/airdcpp-core/airdcpp/QueueManager.h b/airdcpp-core/airdcpp/QueueManager.h index 29ac104d..2a0d90fc 100644 --- a/airdcpp-core/airdcpp/QueueManager.h +++ b/airdcpp-core/airdcpp/QueueManager.h @@ -323,8 +323,11 @@ class QueueManager : public Singleton, public Speaker, public Speaker(aPath, aProfile, dirs); + RLock l(cs); + if (aPath == "/") { + boost::algorithm::copy_if(rootPaths | map_values, back_inserter(dirs), Directory::HasRootProfile(aProfile)); + } else { + findVirtuals(aPath, aProfile, dirs); + } if (aPath.back() == '/') { - for(const auto& d: dirs) + // Directory + for (const auto& d : dirs) { ret.push_back(d->getRealPath()); - } else { //its a file + } + } else { + // File auto fileName = Text::toLower(Util::getAdcFileName(aPath)); for(const auto& d: dirs) { auto it = d->files.find(fileName); @@ -1721,7 +1728,6 @@ void ShareManager::getRootsByVirtual(const string& virtualName, ProfileToken aPr } void ShareManager::getRootsByVirtual(const string& virtualName, const ProfileTokenSet& aProfiles, Directory::List& dirs) const noexcept { - RLock l(cs); for(const auto& d: rootPaths | map_values) { // Compare name if (Util::stricmp(d->getProfileDir()->getNameLower(), virtualName) != 0) { diff --git a/airdcpp-core/airdcpp/StringDefs.h b/airdcpp-core/airdcpp/StringDefs.h index eeaa629f..035c3098 100644 --- a/airdcpp-core/airdcpp/StringDefs.h +++ b/airdcpp-core/airdcpp/StringDefs.h @@ -720,8 +720,8 @@ enum Strings { // @DontAdd LOCK, // "Lock" LOCK_TB, // "Lock toolbars" LOG_COMBINE_ADC_PM, // "Use a single log file per CID for ADC users" - LOG_CRC_OK, // "Show SFV check progress (CRC OK) in system log" - LOG_HASHING, // "Show each hashed file in system log" + LOG_CRC_OK, // "Show SFV check progress (CRC OK)" + LOG_HASHING, // "Show each hashed file" LOG_OFF, // "Log off" LOG_SHARE_SCAN, // "Save the results to a log file when scanning the share" LOG_SHARE_SCAN_PATH, // "Log filename (relative to the global log directory)" @@ -1169,10 +1169,10 @@ enum Strings { // @DontAdd RENAME_DLG_DESC, // "Rename also in the following profiles" REPAIRING_X, // "Repairing %1%" REPORT, // "Report user" - REPORT_ALTERNATES, // "Show auto search for alternates in system log" + REPORT_ALTERNATES, // "Show auto search for alternates" REPORT_DUPLICATE_FILES, // "Report duplicate files when generating file lists" - REPORT_IGNORED, // "Report ignored and spam filtered chat messages in system log" - REPORT_SKIPLIST, // "Show files blocked by the skiplist in system log" + REPORT_IGNORED, // "Report ignored and spam filtered chat messages" + REPORT_SKIPLIST, // "Show files blocked by the skiplist" REQUESTING, // "Requesting" REQUESTING_LIST, // "Requesting file list" REQUIRED_BRACKETS, // "(required)" @@ -1307,7 +1307,6 @@ enum Strings { // @DontAdd SETTINGS_ARGUMENT, // "Arguments" SETTINGS_AUTOPRIO, // "Auto priority settings" SETTINGS_AUTOPRIO_FILES, // "Calculate priorities also for individual files" - SETTINGS_AUTO_AWAY, // "Auto-away on minimize (and back on restore)" SETTINGS_AUTO_BUNDLE_SEARCH, // "Automatically search for alternative bundle sources" SETTINGS_AUTO_COMPLETE_BUNDLES, // "Try to complete bundles with missing files automatically" SETTINGS_AUTO_DETECTION_USE_LIMITED, // "Use the limiter values when setting the auto detected speed and slot limits" @@ -1409,7 +1408,7 @@ enum Strings { // @DontAdd SETTINGS_LOG_FILELIST_TRANSFERS, // "Log file list transfers" SETTINGS_LOG_MAIN_CHAT, // "Log main chat" SETTINGS_LOG_PRIVATE_CHAT, // "Log private chat" - SETTINGS_LOG_SCHEDULED_REFRESHES, // "Show scheduled file list refreshes in system log" + SETTINGS_LOG_SCHEDULED_REFRESHES, // "Show scheduled file list refreshes" SETTINGS_LOG_STATUS_MESSAGES, // "Log status messages" SETTINGS_LOG_SYSTEM_MESSAGES, // "Log system messages" SETTINGS_LOG_UPLOADS, // "Log uploads" @@ -1464,7 +1463,7 @@ enum Strings { // @DontAdd SETTINGS_PUBLIC_HUB_LIST_URL, // "Public hubs list URL" SETTINGS_QUEUE, // "Queue" SETTINGS_RECENT_HOURS, // "The maximum age for a bundle dir to consider it as recent" - SETTINGS_REPORT_ADDED_SOURCES, // "Report added sources in System Log" + SETTINGS_REPORT_ADDED_SOURCES, // "Show added bundle sources" SETTINGS_REQUIRES_RESTART, // "Note; most of these options require that you restart AirDC++" SETTINGS_RESET, // "Reset" SETTINGS_SB_DIRTY_BLEND, // "Blending" @@ -1643,8 +1642,8 @@ enum Strings { // @DontAdd SWITCHING_TO_ADDRESS, // "Switching to the address %1%" SYSTEM_DEFAULT, // "System default" SYSTEM_LOG, // "System log" - SYSTEM_SHOW_FINISHED_DOWNLOADS, // "Show finished downloads in system log" - SYSTEM_SHOW_FINISHED_UPLOADS, // "Show finished uploads in system log" + SYSTEM_SHOW_FINISHED_DOWNLOADS, // "Show finished downloads" + SYSTEM_SHOW_FINISHED_UPLOADS, // "Show finished uploads" TABS_ON_TOP, // "Tabs on top" TAB_ACTIVE_BG, // "Active background" TAB_ACTIVE_BORDER, // "Active border" diff --git a/airdcpp-core/airdcpp/TargetUtil.cpp b/airdcpp-core/airdcpp/TargetUtil.cpp index 99e83e82..43b43bf8 100644 --- a/airdcpp-core/airdcpp/TargetUtil.cpp +++ b/airdcpp-core/airdcpp/TargetUtil.cpp @@ -26,6 +26,8 @@ #ifdef _WIN32 #include #include +#else +#include #endif namespace dcpp { @@ -40,6 +42,7 @@ string TargetUtil::getMountPath(const string& aPath, const VolumeSet& aVolumes) l = aPath.rfind(PATH_SEPARATOR, l-2); if (l == string::npos || l <= 1) break; + if (aVolumes.find(aPath.substr(0, l+1)) != aVolumes.end()) { return aPath.substr(0, l+1); } @@ -57,13 +60,16 @@ string TargetUtil::getMountPath(const string& aPath, const VolumeSet& aVolumes) } } } +#else + // Return the root + return PATH_SEPARATOR_STR; #endif return Util::emptyString; } bool TargetUtil::getVirtualTarget(const string& aTarget, TargetUtil::TargetType targetType, TargetInfo& ti_, const int64_t& aSize) { if (targetType == TARGET_PATH) { - ti_.targetDir = aTarget; + ti_.setTarget(aTarget); } else { vector> dirList; if (targetType == TARGET_FAVORITE) { @@ -76,15 +82,15 @@ bool TargetUtil::getVirtualTarget(const string& aTarget, TargetUtil::TargetType if (s != dirList.end()) { const auto& targets = s->second; bool tmp = getTarget(targets, ti_, aSize); - if (!ti_.targetDir.empty()) { + if (ti_.hasTarget()) { return tmp; } } } - if (ti_.targetDir.empty()) { + if (!ti_.hasTarget()) { //failed to get the target, use the default one - ti_.targetDir = SETTING(DOWNLOAD_DIRECTORY); + ti_.setTarget(SETTING(DOWNLOAD_DIRECTORY)); } return getDiskInfo(ti_); } @@ -107,32 +113,33 @@ bool TargetUtil::getTarget(const StringList& targets, TargetInfo& retTi_, const if (targetMap.empty()) { //failed to get the volumes if (!targets.empty()) { - retTi_.targetDir = targets.front(); + retTi_.setTarget(targets.front()); } else { - retTi_.targetDir = SETTING(DOWNLOAD_DIRECTORY); + retTi_.setTarget(SETTING(DOWNLOAD_DIRECTORY)); } - retTi_.diskSpace = File::getFreeSpace(retTi_.targetDir); - return retTi_.getFreeSpace() >= aSize; - } - - QueueManager::getInstance()->getDiskInfo(targetMap, volumes); + retTi_.setFreeDiskSpace(File::getFreeSpace(retTi_.getTarget())); + } else { + QueueManager::getInstance()->getDiskInfo(targetMap, volumes); - compareMap(targetMap, retTi_, aSize, SETTING(DL_AUTOSELECT_METHOD)); - if (retTi_.targetDir.empty()) //no dir with enough space, choose the one with most space available - compareMap(targetMap, retTi_, aSize, (int8_t)SettingsManager::SELECT_MOST_SPACE); + compareMap(targetMap, retTi_, aSize, SETTING(DL_AUTOSELECT_METHOD)); + if (retTi_.getTarget().empty()) { + //no dir with enough space, choose the one with most space available + compareMap(targetMap, retTi_, aSize, (int8_t)SettingsManager::SELECT_MOST_SPACE); + } + } - return retTi_.getFreeSpace() >= aSize; + return retTi_.hasFreeSpace(aSize); } void TargetUtil::compareMap(const TargetInfoMap& aTargetMap, TargetInfo& retTi_, const int64_t& aSize, int aMethod) { for (auto mapTi: aTargetMap | map_values) { if (aMethod == (int8_t)SettingsManager::SELECT_LEAST_SPACE) { - int64_t diff = mapTi.getFreeSpace() - aSize; - if (diff > 0 && (diff < (retTi_.getFreeSpace() - aSize) || !retTi_.isInitialized())) + int64_t diff = mapTi.getRealFreeSpace() - aSize; + if (diff > 0 && (diff < (retTi_.getRealFreeSpace() - aSize) || !retTi_.isInitialized())) retTi_ = mapTi; - } else if (mapTi.getFreeSpace() > retTi_.getFreeSpace() || !retTi_.isInitialized()) { + } else if (mapTi.getRealFreeSpace() > retTi_.getRealFreeSpace() || !retTi_.isInitialized()) { retTi_ = mapTi; } } @@ -141,16 +148,19 @@ void TargetUtil::compareMap(const TargetInfoMap& aTargetMap, TargetInfo& retTi_, bool TargetUtil::getDiskInfo(TargetInfo& targetInfo_) { VolumeSet volumes; getVolumes(volumes); - string pathVol = getMountPath(targetInfo_.targetDir, volumes); + + auto pathVol = getMountPath(targetInfo_.getTarget(), volumes); if (pathVol.empty()) { return false; } - targetInfo_.diskSpace = File::getFreeSpace(pathVol); + targetInfo_.setFreeDiskSpace(File::getFreeSpace(pathVol)); TargetInfoMap targetMap; targetMap[pathVol] = targetInfo_; + //LogManager::getInstance()->message("Target " + targetInfo_.targetDir + ", vol: " + pathVol + ", list " + Util::listToString(volumes) + ", space " + Util::formatBytes(targetInfo_.diskSpace), LogMessage::SEV_INFO); + QueueManager::getInstance()->getDiskInfo(targetMap, volumes); targetInfo_ = targetMap[pathVol]; return true; @@ -194,41 +204,50 @@ void TargetUtil::getVolumes(VolumeSet& volumes) { ++drive[0]; drives = (drives >> 1); } +#else + struct mntent *ent; + FILE *aFile; + + aFile = setmntent("/proc/mounts", "r"); + if (aFile == NULL) { + return; + } + + while ((ent = getmntent(aFile)) != NULL) { + volumes.insert(Util::validatePath(ent->mnt_dir, true)); + } + endmntent(aFile); #endif } -void TargetUtil::reportInsufficientSize(const TargetInfo& ti, int64_t aSize) { - string tmp; - if (ti.queued > 0) { - tmp = STRING(AUTO_SEARCH) + ": " + STRING_F(NOT_ENOUGH_SPACE_QUEUED_PAUSED, - ti.targetDir % - Util::formatBytes(ti.diskSpace) % - Util::formatBytes(ti.queued) % - Util::formatBytes(aSize)); - } else { - tmp = STRING(AUTO_SEARCH) + ": " + STRING_F(NOT_ENOUGH_SPACE_PAUSED, - ti.targetDir.c_str() % - Util::formatBytes(ti.getFreeSpace()) % +string TargetUtil::formatSizeNotification(const TargetInfo& ti, int64_t aSize) { + if (ti.getQueued() > 0) { + return STRING_F(NOT_ENOUGH_SPACE_QUEUED_PAUSED, + ti.getTarget() % + Util::formatBytes(ti.getFreeDiskSpace()) % + Util::formatBytes(ti.getQueued()) % Util::formatBytes(aSize)); } - LogManager::getInstance()->message(tmp, LogMessage::SEV_WARNING); + + return STRING_F(NOT_ENOUGH_SPACE_PAUSED, + ti.getTarget() % + Util::formatBytes(ti.getRealFreeSpace()) % + Util::formatBytes(aSize)); } -string TargetUtil::getInsufficientSizeMessage(const TargetInfo& ti, int64_t aSize) { - string tmp; - if (ti.queued > 0) { - tmp = STRING_F(CONFIRM_SIZE_WARNING_QUEUE, - Util::formatBytes(ti.queued).c_str() % - ti.targetDir.c_str() % - Util::formatBytes(ti.diskSpace).c_str() % - Util::formatBytes(aSize).c_str()); - } else { - tmp = STRING_F(CONFIRM_SIZE_WARNING, - Util::formatBytes(ti.getFreeSpace()).c_str() % - ti.targetDir.c_str() % - Util::formatBytes(aSize).c_str()); +string TargetUtil::formatSizeConfirmation(const TargetInfo& ti, int64_t aSize) { + if (ti.getQueued() > 0) { + return STRING_F(CONFIRM_SIZE_WARNING_QUEUE, + Util::formatBytes(ti.getQueued()) % + ti.getTarget() % + Util::formatBytes(ti.getFreeDiskSpace()) % + Util::formatBytes(aSize)); } - return tmp; + + return STRING_F(CONFIRM_SIZE_WARNING, + Util::formatBytes(ti.getRealFreeSpace()) % + ti.getTarget() % + Util::formatBytes(aSize)); } } //dcpp \ No newline at end of file diff --git a/airdcpp-core/airdcpp/TargetUtil.h b/airdcpp-core/airdcpp/TargetUtil.h index 52253174..cd88f1d9 100644 --- a/airdcpp-core/airdcpp/TargetUtil.h +++ b/airdcpp-core/airdcpp/TargetUtil.h @@ -20,6 +20,8 @@ #define DCPLUSPLUS_DCPP_TARGET_UTIL_H #include "typedefs.h" + +#include "GetSet.h" #include "Util.h" #include @@ -30,19 +32,38 @@ using std::string; class TargetUtil { public: - struct TargetInfo { - explicit TargetInfo() : targetDir(Util::emptyString), diskSpace(0), queued(0) { } - explicit TargetInfo(const string& aPath, int64_t aFreeSpace) : targetDir(aPath), diskSpace(aFreeSpace), queued(0) { } + class TargetInfo { + public: + explicit TargetInfo() { } + explicit TargetInfo(const string& aPath, int64_t aFreeSpace = 0) : target(aPath), freeDiskSpace(aFreeSpace) { } - string targetDir; - int64_t diskSpace, queued; - int64_t getFreeSpace() const { return diskSpace-queued; } - int64_t getDiff(int64_t aSize) const { return getFreeSpace() - aSize; } - bool isInitialized() { return diskSpace != 0 || queued != 0 || !targetDir.empty(); } + int64_t getRealFreeSpace() const { return freeDiskSpace - queued; } + bool isInitialized() { return freeDiskSpace != 0 || queued != 0 || !target.empty(); } bool operator<(const TargetInfo& ti) const { - return (diskSpace-queued) < (ti.diskSpace-ti.queued); + return (freeDiskSpace - queued) < (ti.freeDiskSpace - ti.queued); + } + + int64_t getQueued() const noexcept { + return queued; + } + + bool hasTarget() const noexcept { + return !target.empty(); + } + + bool hasFreeSpace(int64_t aRequiredBytes) const noexcept { + return getRealFreeSpace() >= aRequiredBytes; + } + + GETSET(string, target, Target); + IGETSET(int64_t, freeDiskSpace, FreeDiskSpace, 0); + + void addQueued(int64_t aBytes) noexcept { + queued += aBytes; } + private: + int64_t queued = 0; }; enum TargetType { @@ -65,8 +86,8 @@ class TargetUtil { static bool getDiskInfo(TargetInfo& ti_); static void compareMap(const TargetInfoMap& targets, TargetInfo& retTi_, const int64_t& aSize, int aMethod); - static void reportInsufficientSize(const TargetInfo& ti, int64_t aSize); - static string getInsufficientSizeMessage(const TargetInfo& ti, int64_t aSize); + static string formatSizeNotification(const TargetInfo& ti, int64_t aSize); + static string formatSizeConfirmation(const TargetInfo& ti, int64_t aSize); }; } diff --git a/airdcpp-core/airdcpp/TrackableDownloadItem.cpp b/airdcpp-core/airdcpp/TrackableDownloadItem.cpp index d2ee6d1d..bc88eb0c 100644 --- a/airdcpp-core/airdcpp/TrackableDownloadItem.cpp +++ b/airdcpp-core/airdcpp/TrackableDownloadItem.cpp @@ -25,6 +25,12 @@ namespace dcpp { + TrackableDownloadItem::TrackableDownloadItem(bool aDownloaded) noexcept : state(aDownloaded ? STATE_DOWNLOADED : STATE_DOWNLOAD_PENDING) { + if (aDownloaded) { + timeFinished = GET_TIME(); + } + } + TrackableDownloadItem::~TrackableDownloadItem() { if (hasDownloads()) { DownloadManager::getInstance()->removeListener(this); @@ -68,6 +74,7 @@ namespace dcpp { void TrackableDownloadItem::onRemovedQueue(const string& aPath, bool aFinished) noexcept { if (aFinished) { completedDownloads = true; + timeFinished = GET_TIME(); } dcassert(completedDownloads); diff --git a/airdcpp-core/airdcpp/TrackableDownloadItem.h b/airdcpp-core/airdcpp/TrackableDownloadItem.h index 4ab01484..1102514a 100644 --- a/airdcpp-core/airdcpp/TrackableDownloadItem.h +++ b/airdcpp-core/airdcpp/TrackableDownloadItem.h @@ -32,6 +32,7 @@ namespace dcpp { virtual void onRemovedQueue(const string& aDir, bool aFinished) noexcept; virtual void onAddedQueue(const string& aDir) noexcept; + TrackableDownloadItem(bool aDownloaded) noexcept; ~TrackableDownloadItem() noexcept; enum State : uint8_t { @@ -50,6 +51,8 @@ namespace dcpp { bool hasDownloads() const noexcept; StringList getDownloads() const noexcept; + + IGETSET(time_t, timeFinished, TimeFinished, 0); protected: virtual void onStateChanged() noexcept = 0; diff --git a/airdcpp-core/airdcpp/ViewFile.cpp b/airdcpp-core/airdcpp/ViewFile.cpp index 88d5a306..6010c9f8 100644 --- a/airdcpp-core/airdcpp/ViewFile.cpp +++ b/airdcpp-core/airdcpp/ViewFile.cpp @@ -23,18 +23,22 @@ #include "File.h" namespace dcpp { - ViewFile::ViewFile(const string& aTarget, const TTHValue& aTTH, bool aIsText, UpdateF&& aUpdateFunction) noexcept : - path(aTarget), tth(aTTH), updateFunction(aUpdateFunction), text(aIsText) { + ViewFile::ViewFile(const string& aTarget, const TTHValue& aTTH, bool aIsText, bool aIsLocalFile, UpdateF&& aUpdateFunction) noexcept : + TrackableDownloadItem(aIsLocalFile), path(aTarget), tth(aTTH), updateFunction(aUpdateFunction), text(aIsText), localFile(aIsLocalFile) { - onAddedQueue(path); + if (!aIsLocalFile) { + onAddedQueue(path); + } } ViewFile::~ViewFile() noexcept { - File::deleteFile(path); + if (!localFile) { + File::deleteFile(path); + } } string ViewFile::getDisplayName() const noexcept { - return AirUtil::fromOpenFileName(Util::getFileName(path)); + return localFile ? Util::getFileName(path) : AirUtil::fromOpenFileName(Util::getFileName(path)); } void ViewFile::onStateChanged() noexcept { diff --git a/airdcpp-core/airdcpp/ViewFile.h b/airdcpp-core/airdcpp/ViewFile.h index 9d2bea7a..1e94c128 100644 --- a/airdcpp-core/airdcpp/ViewFile.h +++ b/airdcpp-core/airdcpp/ViewFile.h @@ -29,7 +29,7 @@ namespace dcpp { typedef std::function UpdateF; public: - ViewFile(const string& aTarget, const TTHValue& aTTH, bool aIsText, UpdateF&& aUpdateFunction) noexcept; + ViewFile(const string& aTarget, const TTHValue& aTTH, bool aIsText, bool aIsLocalFile, UpdateF&& aUpdateFunction) noexcept; ~ViewFile() noexcept; const string& getPath() const noexcept { @@ -42,12 +42,15 @@ namespace dcpp { return text; } + bool isLocalFile() const noexcept { + return localFile; + } + const TTHValue& getTTH() const noexcept { return tth; } IGETSET(bool, read, Read, false); - IGETSET(time_t, timeFinished, TimeFinished, 0); protected: void onStateChanged() noexcept; private: @@ -55,6 +58,7 @@ namespace dcpp { const UpdateF updateFunction; const TTHValue tth; const bool text; + const bool localFile; }; } // namespace dcpp diff --git a/airdcpp-core/airdcpp/ViewFileManager.cpp b/airdcpp-core/airdcpp/ViewFileManager.cpp index 4fd41848..cab94975 100644 --- a/airdcpp-core/airdcpp/ViewFileManager.cpp +++ b/airdcpp-core/airdcpp/ViewFileManager.cpp @@ -48,8 +48,6 @@ namespace dcpp { auto file = getFile(aQI->getTTH()); if (file) { - file->setTimeFinished(GET_TIME()); - file->onRemovedQueue(aQI->getTarget(), true); fire(ViewFileManagerListener::FileFinished(), file); } @@ -72,15 +70,20 @@ namespace dcpp { return; } - auto file = make_shared(aQI->getTarget(), aQI->getTTH(), aQI->isSet(QueueItem::FLAG_TEXT), + createFile(aQI->getTarget(), aQI->getTTH(), aQI->isSet(QueueItem::FLAG_TEXT), false); + } + + ViewFilePtr ViewFileManager::createFile(const string& aFileName, const TTHValue& aTTH, bool aIsText, bool aIsLocalFile) noexcept { + auto file = make_shared(aFileName, aTTH, aIsText, aIsLocalFile, std::bind(&ViewFileManager::onFileUpdated, this, std::placeholders::_1)); { WLock l(cs); - viewFiles[aQI->getTTH()] = file; + viewFiles[aTTH] = file; } fire(ViewFileManagerListener::FileAdded(), file); + return file; } void ViewFileManager::onFileUpdated(const TTHValue& aTTH) noexcept { @@ -113,8 +116,28 @@ namespace dcpp { return p->second; } + + bool ViewFileManager::addLocalFile(const string& aPath, const TTHValue& aTTH, bool aIsText) noexcept { + if (getFile(aTTH)) { + return false; + } + + auto file = createFile(aPath, aTTH, aIsText, true); + + fire(ViewFileManagerListener::FileFinished(), file); + return true; + } - bool ViewFileManager::addFileThrow(const string& aFileName, int64_t aSize, const TTHValue& aTTH, const HintedUser& aUser, bool aIsText) throw(QueueException, FileException) { + bool ViewFileManager::addUserFileThrow(const string& aFileName, int64_t aSize, const TTHValue& aTTH, const HintedUser& aUser, bool aIsText) throw(QueueException, FileException) { + if (aUser == ClientManager::getInstance()->getMe()) { + auto paths = ShareManager::getInstance()->getRealPaths(aTTH); + if (!paths.empty()) { + return addLocalFile(paths.front(), aTTH, aIsText); + } + + return false; + } + if (getFile(aTTH)) { return false; } @@ -123,9 +146,9 @@ namespace dcpp { return true; } - bool ViewFileManager::addFileNotify(const string& aFileName, int64_t aSize, const TTHValue& aTTH, const HintedUser& aUser, bool aIsText) noexcept { + bool ViewFileManager::addUserFileNotify(const string& aFileName, int64_t aSize, const TTHValue& aTTH, const HintedUser& aUser, bool aIsText) noexcept { try { - if (ViewFileManager::getInstance()->addFileThrow(aFileName, aSize, aTTH, aUser, aIsText)) { + if (ViewFileManager::getInstance()->addUserFileThrow(aFileName, aSize, aTTH, aUser, aIsText)) { return true; } diff --git a/airdcpp-core/airdcpp/ViewFileManager.h b/airdcpp-core/airdcpp/ViewFileManager.h index 84ff1c63..de6ca890 100644 --- a/airdcpp-core/airdcpp/ViewFileManager.h +++ b/airdcpp-core/airdcpp/ViewFileManager.h @@ -42,16 +42,22 @@ namespace dcpp { ViewFileMap getFiles() const noexcept; // Adds the file and shows a notification in case of errors - bool addFileNotify(const string& aFileName, int64_t aSize, const TTHValue& aTTH, const HintedUser& aUser, bool aIsText) noexcept; + // Can be used for viewing own files by TTH as well + bool addUserFileNotify(const string& aFileName, int64_t aSize, const TTHValue& aTTH, const HintedUser& aUser, bool aIsText) noexcept; // Adds the file and throws if there are errors - bool addFileThrow(const string& aFileName, int64_t aSize, const TTHValue& aTTH, const HintedUser& aUser, bool aIsText) throw(QueueException, FileException); + // Can be used for viewing own files by TTH as well + bool addUserFileThrow(const string& aFileName, int64_t aSize, const TTHValue& aTTH, const HintedUser& aUser, bool aIsText) throw(QueueException, FileException); + + // Add a file by real path + bool addLocalFile(const string& aPath, const TTHValue& aTTH, bool aIsText) noexcept; bool removeFile(const TTHValue& aTTH) noexcept; ViewFilePtr getFile(const TTHValue& aTTH) const noexcept; bool setRead(const TTHValue& aTTH) noexcept; private: + ViewFilePtr createFile(const string& aFileName, const TTHValue& aTTH, bool aIsText, bool aIsLocalFile) noexcept; static bool isViewedItem(const QueueItemPtr& aQI) noexcept; void on(QueueManagerListener::Added, QueueItemPtr& aQI) noexcept; diff --git a/airdcpp-webapi/api/CoreSettings.h b/airdcpp-webapi/api/CoreSettings.h index ab0c6555..9c1da696 100644 --- a/airdcpp-webapi/api/CoreSettings.h +++ b/airdcpp-webapi/api/CoreSettings.h @@ -128,13 +128,11 @@ namespace webserver { { "max_hash_speed", SettingsManager::MAX_HASH_SPEED, ResourceManager::SETTINGS_MAX_HASHER_SPEED, ApiSettingItem::TYPE_GENERAL, { ResourceManager::Strings::MiB, true } }, { "max_total_hashers", SettingsManager::MAX_HASHING_THREADS, ResourceManager::MAX_HASHING_THREADS }, { "max_volume_hashers", SettingsManager::HASHERS_PER_VOLUME, ResourceManager::MAX_VOL_HASHERS }, - { "report_each_hashed_file", SettingsManager::LOG_HASHING, ResourceManager::LOG_HASHING }, //{ ResourceManager::REFRESH_OPTIONS }, { "refresh_time", SettingsManager::AUTO_REFRESH_TIME, ResourceManager::SETTINGS_AUTO_REFRESH_TIME, ApiSettingItem::TYPE_GENERAL, { ResourceManager::Strings::MINUTES, false } }, { "refresh_time_incoming", SettingsManager::INCOMING_REFRESH_TIME, ResourceManager::SETTINGS_INCOMING_REFRESH_TIME, ApiSettingItem::TYPE_GENERAL, { ResourceManager::Strings::MINUTES, false } }, { "refresh_startup", SettingsManager::STARTUP_REFRESH, ResourceManager::SETTINGS_STARTUP_REFRESH }, - { "refresh_report_scheduled_refreshes", SettingsManager::LOG_SCHEDULED_REFRESHES, ResourceManager::SETTINGS_LOG_SCHEDULED_REFRESHES }, { "refresh_threading", SettingsManager::REFRESH_THREADING, ResourceManager::MULTITHREADED_REFRESH }, //{ ResourceManager::SETTINGS_SHARING_OPTIONS }, @@ -145,8 +143,6 @@ namespace webserver { { "share_no_zero_byte", SettingsManager::NO_ZERO_BYTE, ResourceManager::SETTINGS_NO_ZERO_BYTE }, { "share_max_size", SettingsManager::MAX_FILE_SIZE_SHARED, ResourceManager::DONT_SHARE_BIGGER_THAN, ApiSettingItem::TYPE_GENERAL, { ResourceManager::Strings::MiB, false } }, { "share_follow_symlinks", SettingsManager::SHARE_FOLLOW_SYMLINKS, ResourceManager::FOLLOW_SYMLINKS }, - { "share_report_duplicates", SettingsManager::FL_REPORT_FILE_DUPES, ResourceManager::REPORT_DUPLICATE_FILES }, - { "share_report_skiplist", SettingsManager::REPORT_SKIPLIST, ResourceManager::REPORT_SKIPLIST }, //{ ResourceManager::SETTINGS_LOGGING }, { "log_directory", SettingsManager::LOG_DIRECTORY, ResourceManager::SETTINGS_LOG_DIR, ApiSettingItem::TYPE_DIRECTORY_PATH }, @@ -171,6 +167,18 @@ namespace webserver { { "log_list_transfers", SettingsManager::LOG_FILELIST_TRANSFERS, ResourceManager::SETTINGS_LOG_FILELIST_TRANSFERS }, { "single_log_per_cid", SettingsManager::PM_LOG_GROUP_CID, ResourceManager::LOG_COMBINE_ADC_PM }, + // Events + { "report_uploads", SettingsManager::SYSTEM_SHOW_UPLOADS, ResourceManager::SYSTEM_SHOW_FINISHED_UPLOADS }, + { "report_downloads", SettingsManager::SYSTEM_SHOW_DOWNLOADS, ResourceManager::SYSTEM_SHOW_FINISHED_DOWNLOADS }, + { "report_search_alternates", SettingsManager::REPORT_ALTERNATES, ResourceManager::REPORT_ALTERNATES }, + { "report_added_sources", SettingsManager::REPORT_ADDED_SOURCES, ResourceManager::SETTINGS_REPORT_ADDED_SOURCES }, + { "report_share_skiplist", SettingsManager::REPORT_SKIPLIST, ResourceManager::REPORT_SKIPLIST }, + { "report_hashed_files", SettingsManager::LOG_HASHING, ResourceManager::LOG_HASHING }, + { "report_scheduled_refreshes", SettingsManager::LOG_SCHEDULED_REFRESHES, ResourceManager::SETTINGS_LOG_SCHEDULED_REFRESHES }, + { "report_filelist_dupes", SettingsManager::FL_REPORT_FILE_DUPES, ResourceManager::REPORT_DUPLICATE_FILES }, + { "report_ignored_messages", SettingsManager::LOG_IGNORED, ResourceManager::REPORT_IGNORED }, + { "report_crc_ok", SettingsManager::LOG_CRC_OK, ResourceManager::LOG_CRC_OK }, + //{ ResourceManager::HISTORIES }, { "history_search_max", SettingsManager::HISTORY_SEARCH_MAX, ResourceManager::SEARCH_STRINGS }, { "history_search_clear_exit", SettingsManager::HISTORY_SEARCH_CLEAR, ResourceManager::CLEAR_EXIT }, diff --git a/airdcpp-webapi/api/FilelistApi.cpp b/airdcpp-webapi/api/FilelistApi.cpp index e3d666af..5eeb04b1 100644 --- a/airdcpp-webapi/api/FilelistApi.cpp +++ b/airdcpp-webapi/api/FilelistApi.cpp @@ -37,6 +37,7 @@ namespace webserver { METHOD_HANDLER("session", Access::FILELISTS_EDIT, ApiRequest::METHOD_DELETE, (CID_PARAM), false, FilelistApi::handleDeleteList); METHOD_HANDLER("session", Access::FILELISTS_EDIT, ApiRequest::METHOD_POST, (), true, FilelistApi::handlePostList); + METHOD_HANDLER("session", Access::FILELISTS_VIEW, ApiRequest::METHOD_POST, (EXACT_PARAM("me")), true, FilelistApi::handleOwnList); METHOD_HANDLER("download_directory", Access::DOWNLOAD, ApiRequest::METHOD_POST, (), true, FilelistApi::handleDownload); METHOD_HANDLER("find_nfo", Access::VIEW_FILES_EDIT, ApiRequest::METHOD_POST, (), true, FilelistApi::handleFindNfo); @@ -92,6 +93,13 @@ namespace webserver { return handleQueueList(aRequest, QueueItem::FLAG_CLIENT_VIEW); } + api_return FilelistApi::handleOwnList(ApiRequest& aRequest) { + auto profile = JsonUtil::getField("share_profile", aRequest.getRequestBody()); + DirectoryListingManager::getInstance()->openOwnList(profile); + + return websocketpp::http::status_code::ok; + } + api_return FilelistApi::handleDeleteList(ApiRequest& aRequest) { auto list = getSubModule(aRequest.getStringParam(0)); if (!list) { @@ -135,12 +143,9 @@ namespace webserver { } json FilelistApi::serializeList(const DirectoryListingPtr& aList) noexcept { - int64_t shareSize = -1, totalFiles = -1; - auto user = ClientManager::getInstance()->findOnlineUser(aList->getHintedUser()); - if (user) { - shareSize = Util::toInt64(user->getIdentity().getShareSize()); - totalFiles = Util::toInt64(user->getIdentity().getSharedFiles()); - } + int64_t totalSize = -1; + size_t totalFiles = -1; + aList->getPartialListInfo(totalSize, totalFiles); return{ { "id", aList->getUser()->getCID().toBase32() }, @@ -149,8 +154,9 @@ namespace webserver { { "location", FilelistInfo::serializeLocation(aList) }, { "partial", aList->getPartialList() }, { "total_files", totalFiles }, - { "total_size", shareSize }, - { "read", aList->isRead() } + { "total_size", totalSize }, + { "read", aList->isRead() }, + { "share_profile", aList->getIsOwnList() ? Serializer::serializeShareProfileSimple(aList->getShareProfile()) : json() }, }; } diff --git a/airdcpp-webapi/api/FilelistApi.h b/airdcpp-webapi/api/FilelistApi.h index 67781bdb..d2739275 100644 --- a/airdcpp-webapi/api/FilelistApi.h +++ b/airdcpp-webapi/api/FilelistApi.h @@ -45,6 +45,7 @@ namespace webserver { api_return handlePostList(ApiRequest& aRequest); api_return handleDeleteList(ApiRequest& aRequest); + api_return handleOwnList(ApiRequest& aRequest); api_return handleGetLists(ApiRequest& aRequest); api_return handleDownload(ApiRequest& aRequest); diff --git a/airdcpp-webapi/api/FilelistInfo.cpp b/airdcpp-webapi/api/FilelistInfo.cpp index 797085a8..8f356de5 100644 --- a/airdcpp-webapi/api/FilelistInfo.cpp +++ b/airdcpp-webapi/api/FilelistInfo.cpp @@ -63,7 +63,7 @@ namespace webserver { dl->addListener(this); - if (dl->hasCompletedDownloads()) { + if (dl->isLoaded()) { updateItems(dl->getCurrentLocationInfo().directory->getPath()); } } @@ -100,7 +100,7 @@ namespace webserver { string FilelistInfo::formatState(const DirectoryListingPtr& aList) noexcept { if (aList->getDownloadState() == DirectoryListing::STATE_DOWNLOADED) { - return !aList->getCurrentLocationInfo().directory || aList->getCurrentLocationInfo().directory->getLoading() ? "loading" : "loaded"; + return aList->isLoaded() ? "loaded" : "loading"; } return Serializer::serializeDownloadState(aList->getDownloadState()); @@ -200,6 +200,12 @@ namespace webserver { }); } + void FilelistInfo::on(DirectoryListingListener::ShareProfileChanged) noexcept { + onSessionUpdated({ + { "share_profile", Serializer::serializeShareProfileSimple(dl->getShareProfile()) } + }); + } + void FilelistInfo::onSessionUpdated(const json& aData) noexcept { if (!subscriptionActive("filelist_updated")) { return; diff --git a/airdcpp-webapi/api/FilelistInfo.h b/airdcpp-webapi/api/FilelistInfo.h index 1b775463..5ce61e3b 100644 --- a/airdcpp-webapi/api/FilelistInfo.h +++ b/airdcpp-webapi/api/FilelistInfo.h @@ -118,6 +118,7 @@ namespace webserver { void on(DirectoryListingListener::UserUpdated) noexcept; void on(DirectoryListingListener::StateChanged) noexcept; void on(DirectoryListingListener::Read) noexcept; + void on(DirectoryListingListener::ShareProfileChanged) noexcept; void addListTask(CallBack&& aTask) noexcept; diff --git a/airdcpp-webapi/api/HashApi.cpp b/airdcpp-webapi/api/HashApi.cpp index 9e7f4eed..6ba1a080 100644 --- a/airdcpp-webapi/api/HashApi.cpp +++ b/airdcpp-webapi/api/HashApi.cpp @@ -39,8 +39,10 @@ namespace webserver { createSubscription("hash_database_status"); createSubscription("hash_statistics"); + createSubscription("hasher_directory_finished"); + createSubscription("hasher_finished"); - timer->start(); + timer->start(false); } HashApi::~HashApi() { @@ -97,6 +99,30 @@ namespace webserver { updateDbStatus(false); } + void HashApi::on(HashManagerListener::DirectoryHashed, const string& aPath, int aFilesHashed, int64_t aSizeHashed, time_t aHashDuration, int aHasherId) noexcept { + maybeSend("hasher_directory_finished", [&] { + return json({ + { "path", aPath }, + { "size", aSizeHashed }, + { "files", aFilesHashed }, + { "duration", aHashDuration }, + { "hasher_id", aHasherId }, + }); + }); + } + + void HashApi::on(HashManagerListener::HasherFinished, int aDirshashed, int aFilesHashed, int64_t aSizeHashed, time_t aHashDuration, int aHasherId) noexcept { + maybeSend("hasher_finished", [&] { + return json({ + { "size", aSizeHashed }, + { "files", aFilesHashed }, + { "directories", aDirshashed }, + { "duration", aHashDuration }, + { "hasher_id", aHasherId }, + }); + }); + } + void HashApi::updateDbStatus(bool aMaintenanceRunning) noexcept { if (!subscriptionActive("hash_database_status")) return; diff --git a/airdcpp-webapi/api/HashApi.h b/airdcpp-webapi/api/HashApi.h index 2c4f0278..421e0fd6 100644 --- a/airdcpp-webapi/api/HashApi.h +++ b/airdcpp-webapi/api/HashApi.h @@ -49,6 +49,9 @@ namespace webserver { api_return handleOptimize(ApiRequest& aRequest); api_return handleGetDbStatus(ApiRequest& aRequest); + void on(HashManagerListener::DirectoryHashed, const string& aPath, int aFilesHashed, int64_t aSizeHashed, time_t aHashDuration, int aHasherId) noexcept; + void on(HashManagerListener::HasherFinished, int aDirshashed, int aFilesHashed, int64_t aSizeHashed, time_t aHashDuration, int aHasherId) noexcept; + void on(HashManagerListener::MaintananceStarted) noexcept; void on(HashManagerListener::MaintananceFinished) noexcept; diff --git a/airdcpp-webapi/api/HierarchicalApiModule.h b/airdcpp-webapi/api/HierarchicalApiModule.h index e63cf5c0..b5ad9876 100644 --- a/airdcpp-webapi/api/HierarchicalApiModule.h +++ b/airdcpp-webapi/api/HierarchicalApiModule.h @@ -237,6 +237,7 @@ namespace webserver { // Ensure that we have a submodule (the parent must exist if we have a session) auto m = aParentModule->getSubModule(aId); if (!m) { + dcdebug("Trying to run an async task for a removed submodule\n"); return; } diff --git a/airdcpp-webapi/api/HubApi.cpp b/airdcpp-webapi/api/HubApi.cpp index 85013e5f..4fa9442d 100644 --- a/airdcpp-webapi/api/HubApi.cpp +++ b/airdcpp-webapi/api/HubApi.cpp @@ -94,8 +94,7 @@ namespace webserver { { "hub_url", aClient->getHubUrl() }, { "id", aClient->getClientId() }, { "favorite_hub", aClient->getFavToken() }, - { "share_profile", aClient->getShareProfile() } - //{ "share_profile", Serializer::serializeShare aClient->getShareProfile() }, + { "share_profile", Serializer::serializeShareProfileSimple(aClient->getShareProfile()) } }; Serializer::serializeCacheInfo(j, aClient->getCache(), Serializer::serializeUnreadChat); diff --git a/airdcpp-webapi/api/HubInfo.cpp b/airdcpp-webapi/api/HubInfo.cpp index e26082d7..972edc10 100644 --- a/airdcpp-webapi/api/HubInfo.cpp +++ b/airdcpp-webapi/api/HubInfo.cpp @@ -69,14 +69,14 @@ namespace webserver { MessageManager::getInstance()->addListener(this); client->addListener(this); - timer->start(); - METHOD_HANDLER("reconnect", Access::HUBS_EDIT, ApiRequest::METHOD_POST, (), false, HubInfo::handleReconnect); METHOD_HANDLER("favorite", Access::HUBS_EDIT, ApiRequest::METHOD_POST, (), false, HubInfo::handleFavorite); METHOD_HANDLER("password", Access::HUBS_EDIT, ApiRequest::METHOD_POST, (), true, HubInfo::handlePassword); METHOD_HANDLER("redirect", Access::HUBS_EDIT, ApiRequest::METHOD_POST, (), false, HubInfo::handleRedirect); METHOD_HANDLER("counts", Access::HUBS_VIEW, ApiRequest::METHOD_GET, (), false, HubInfo::handleGetCounts); + + timer->start(false); } HubInfo::~HubInfo() { diff --git a/airdcpp-webapi/api/SessionApi.cpp b/airdcpp-webapi/api/SessionApi.cpp index cbe80b2e..ab5ac9ec 100644 --- a/airdcpp-webapi/api/SessionApi.cpp +++ b/airdcpp-webapi/api/SessionApi.cpp @@ -25,7 +25,9 @@ #include #include -#include +#include +#include +#include #include namespace webserver { @@ -80,8 +82,8 @@ namespace webserver { { "token", session->getAuthToken() }, { "user", session->getUser()->getUserName() }, { "system", getSystemInfo(aIp) }, - { "away_idle_time", SETTING(AWAY_IDLE_TIME) }, { "run_wizard", SETTING(WIZARD_RUN) }, + { "cid", ClientManager::getInstance()->getMyCID().toBase32() }, }; if (aSocket) { @@ -93,7 +95,7 @@ namespace webserver { return websocketpp::http::status_code::ok; } - api_return SessionApi::handleAway(ApiRequest& aRequest) { + api_return SessionApi::handleActivity(ApiRequest& aRequest) { auto s = aRequest.getSession(); if (!s) { aRequest.setResponseErrorStr("Not authorized"); @@ -101,13 +103,11 @@ namespace webserver { } if (!s->isUserSession()) { - aRequest.setResponseErrorStr("Away state can only be changed for user sessions"); + aRequest.setResponseErrorStr("Activity can only be updated for user sessions"); return websocketpp::http::status_code::bad_request; } - auto away = JsonUtil::getField("away", aRequest.getRequestBody()); - WebServerManager::getInstance()->getUserManager().setSessionAwayState(s->getId(), away); - + ActivityManager::getInstance()->updateActivity(); return websocketpp::http::status_code::ok; } @@ -125,7 +125,7 @@ namespace webserver { api_return SessionApi::handleSocketConnect(ApiRequest& aRequest, bool aIsSecure, const WebSocketPtr& aSocket) { auto sessionToken = JsonUtil::getField("authorization", aRequest.getRequestBody(), false); - SessionPtr session = WebServerManager::getInstance()->getUserManager().getSession(sessionToken); + auto session = WebServerManager::getInstance()->getUserManager().getSession(sessionToken); if (!session) { aRequest.setResponseErrorStr("Invalid session token"); return websocketpp::http::status_code::bad_request; diff --git a/airdcpp-webapi/api/SessionApi.h b/airdcpp-webapi/api/SessionApi.h index f5bb929a..3eb3fc66 100644 --- a/airdcpp-webapi/api/SessionApi.h +++ b/airdcpp-webapi/api/SessionApi.h @@ -31,7 +31,7 @@ namespace webserver { api_return handleLogin(ApiRequest& aRequest, bool aIsSecure, const WebSocketPtr& aSocket, const string& aIp); api_return handleSocketConnect(ApiRequest& aRequest, bool aIsSecure, const WebSocketPtr& aSocket); api_return handleLogout(ApiRequest& aRequest); - api_return handleAway(ApiRequest& aRequest); + api_return handleActivity(ApiRequest& aRequest); json getSystemInfo(const string& aIp) const noexcept; private: diff --git a/airdcpp-webapi/api/ShareApi.cpp b/airdcpp-webapi/api/ShareApi.cpp index daf94b3d..51e2c128 100644 --- a/airdcpp-webapi/api/ShareApi.cpp +++ b/airdcpp-webapi/api/ShareApi.cpp @@ -35,6 +35,7 @@ namespace webserver { METHOD_HANDLER("refresh", Access::SETTINGS_EDIT, ApiRequest::METHOD_POST, (), false, ShareApi::handleRefreshShare); METHOD_HANDLER("refresh", Access::SETTINGS_EDIT, ApiRequest::METHOD_POST, (EXACT_PARAM("paths")), true, ShareApi::handleRefreshPaths); + METHOD_HANDLER("refresh", Access::SETTINGS_EDIT, ApiRequest::METHOD_POST, (EXACT_PARAM("virtual")), true, ShareApi::handleRefreshVirtual); } ShareApi::~ShareApi() { @@ -50,9 +51,29 @@ namespace webserver { api_return ShareApi::handleRefreshPaths(ApiRequest& aRequest) { auto paths = JsonUtil::getField("paths", aRequest.getRequestBody(), false); + auto ret = ShareManager::getInstance()->refreshPaths(paths); + if (ret == ShareManager::RefreshResult::REFRESH_PATH_NOT_FOUND) { + aRequest.setResponseErrorStr("Invalid paths were supplied"); + return websocketpp::http::status_code::bad_request; + } - //aRequest.setResponseBody(j); + return websocketpp::http::status_code::ok; + } + + api_return ShareApi::handleRefreshVirtual(ApiRequest& aRequest) { + auto path = JsonUtil::getField("path", aRequest.getRequestBody(), false); + auto profile = JsonUtil::getField("profile", aRequest.getRequestBody()); + + StringList refreshPaths; + try { + ShareManager::getInstance()->getRealPaths(path, refreshPaths, profile); + } catch (const ShareException& e) { + aRequest.setResponseErrorStr(e.getError()); + return websocketpp::http::status_code::bad_request; + } + + ShareManager::getInstance()->refreshPaths(refreshPaths); return websocketpp::http::status_code::ok; } diff --git a/airdcpp-webapi/api/ShareApi.h b/airdcpp-webapi/api/ShareApi.h index 849dd26f..e44920f6 100644 --- a/airdcpp-webapi/api/ShareApi.h +++ b/airdcpp-webapi/api/ShareApi.h @@ -37,6 +37,7 @@ namespace webserver { private: api_return handleRefreshShare(ApiRequest& aRequest); api_return handleRefreshPaths(ApiRequest& aRequest); + api_return handleRefreshVirtual(ApiRequest& aRequest); api_return handleGetStats(ApiRequest& aRequest); diff --git a/airdcpp-webapi/api/SystemApi.cpp b/airdcpp-webapi/api/SystemApi.cpp index 2d0afa78..51595f5a 100644 --- a/airdcpp-webapi/api/SystemApi.cpp +++ b/airdcpp-webapi/api/SystemApi.cpp @@ -25,11 +25,11 @@ #include #include -#include +#include #include namespace webserver { - SystemApi::SystemApi(Session* aSession) : ApiModule(aSession, Access::ANY), timer(getTimer([this] { onTimer(); }, 500)) { + SystemApi::SystemApi(Session* aSession) : ApiModule(aSession, Access::ANY) { METHOD_HANDLER("stats", Access::ANY, ApiRequest::METHOD_GET, (), false, SystemApi::handleGetStats); @@ -37,32 +37,22 @@ namespace webserver { METHOD_HANDLER("away", Access::ANY, ApiRequest::METHOD_POST, (), true, SystemApi::handleSetAway); createSubscription("away_state"); - timer->start(true); + + ActivityManager::getInstance()->addListener(this); } SystemApi::~SystemApi() { - timer->stop(true); + ActivityManager::getInstance()->removeListener(this); } - void SystemApi::onTimer() noexcept { - if (!subscriptionActive("away_state")) { - return; - } - - auto newState = serializeAwayState(); - if (newState == previousAway) { - return; - } - - previousAway = newState; - send("away_state", newState); + void SystemApi::on(ActivityManagerListener::AwayModeChanged, AwayMode /*aNewMode*/) noexcept { + send("away_state", serializeAwayState()); } - string SystemApi::getAwayState() noexcept { - switch (AirUtil::getAwayMode()) { + string SystemApi::getAwayState(AwayMode aAwayMode) noexcept { + switch (aAwayMode) { case AWAY_OFF: return "off"; case AWAY_MANUAL: return "manual"; - case AWAY_MINIMIZE: case AWAY_IDLE: return "idle"; } @@ -72,8 +62,7 @@ namespace webserver { json SystemApi::serializeAwayState() noexcept { return { - { "state", getAwayState() }, - { "away_idle_time", SETTING(AWAY_IDLE_TIME) }, + { "id", getAwayState(ActivityManager::getInstance()->getAwayMode()) }, }; } @@ -84,7 +73,7 @@ namespace webserver { api_return SystemApi::handleSetAway(ApiRequest& aRequest) { auto away = JsonUtil::getField("away", aRequest.getRequestBody()); - AirUtil::setAway(away ? AWAY_MANUAL : AWAY_OFF); + ActivityManager::getInstance()->setAway(away ? AWAY_MANUAL : AWAY_OFF); return websocketpp::http::status_code::ok; } diff --git a/airdcpp-webapi/api/SystemApi.h b/airdcpp-webapi/api/SystemApi.h index 0f4d148c..0c8afbfd 100644 --- a/airdcpp-webapi/api/SystemApi.h +++ b/airdcpp-webapi/api/SystemApi.h @@ -20,11 +20,13 @@ #define DCPLUSPLUS_DCPP_SYSTEMAPI_H #include - #include +#include + + namespace webserver { - class SystemApi : public ApiModule { + class SystemApi : public ApiModule, private ActivityManagerListener { public: SystemApi(Session* aSession); ~SystemApi(); @@ -33,17 +35,15 @@ namespace webserver { return 0; } private: - static string getAwayState() noexcept; + static string getAwayState(AwayMode aAwayMode) noexcept; static json serializeAwayState() noexcept; - void onTimer() noexcept; api_return handleGetAwayState(ApiRequest& aRequest); api_return handleSetAway(ApiRequest& aRequest); api_return handleGetStats(ApiRequest& aRequest); - json previousAway; - TimerPtr timer; + void on(ActivityManagerListener::AwayModeChanged, AwayMode aNewMode) noexcept; }; } diff --git a/airdcpp-webapi/api/TransferApi.cpp b/airdcpp-webapi/api/TransferApi.cpp index 289806e5..aa208e06 100644 --- a/airdcpp-webapi/api/TransferApi.cpp +++ b/airdcpp-webapi/api/TransferApi.cpp @@ -34,7 +34,7 @@ namespace webserver { METHOD_HANDLER("stats", Access::ANY, ApiRequest::METHOD_GET, (), false, TransferApi::handleGetStats); createSubscription("transfer_statistics"); - timer->start(); + timer->start(false); } TransferApi::~TransferApi() { diff --git a/airdcpp-webapi/api/ViewFileApi.cpp b/airdcpp-webapi/api/ViewFileApi.cpp index fab30664..e36c0e17 100644 --- a/airdcpp-webapi/api/ViewFileApi.cpp +++ b/airdcpp-webapi/api/ViewFileApi.cpp @@ -55,13 +55,13 @@ namespace webserver { return{ { "id", aFile->getTTH().toBase32() }, { "tth", aFile->getTTH().toBase32() }, - //{ "path", aFile->getPath() }, { "text", aFile->isText() }, { "read", aFile->getRead() }, { "name", aFile->getDisplayName() }, { "state", Serializer::serializeDownloadState(aFile->getDownloadState()) }, { "type", Serializer::serializeFileType(aFile->getPath()) }, { "time_finished", aFile->getTimeFinished() }, + { "downloaded", !aFile->isLocalFile() }, }; } @@ -83,12 +83,12 @@ namespace webserver { auto name = JsonUtil::getField("name", j, false); auto size = JsonUtil::getField("size", j); - auto user = Deserializer::deserializeHintedUser(j); + auto user = Deserializer::deserializeHintedUser(j, true); auto isText = JsonUtil::getOptionalFieldDefault("text", j, false); bool added = false; try { - added = ViewFileManager::getInstance()->addFileThrow(name, size, tth, user, isText); + added = ViewFileManager::getInstance()->addUserFileThrow(name, size, tth, user, isText); } catch (const Exception& e) { aRequest.setResponseErrorStr(e.getError()); return websocketpp::http::status_code::internal_server_error; diff --git a/airdcpp-webapi/api/common/Deserializer.cpp b/airdcpp-webapi/api/common/Deserializer.cpp index 756bc615..2c4b75d2 100644 --- a/airdcpp-webapi/api/common/Deserializer.cpp +++ b/airdcpp-webapi/api/common/Deserializer.cpp @@ -50,8 +50,9 @@ namespace webserver { } HintedUser Deserializer::deserializeHintedUser(const json& aJson, bool aAllowMe, const string& aFieldName) { - auto user = JsonUtil::getRawValue(aFieldName, aJson); - return HintedUser(deserializeUser(user), JsonUtil::getField("hub_url", user, false)); + auto userJson = JsonUtil::getRawValue(aFieldName, aJson); + auto user = deserializeUser(userJson, aAllowMe); + return HintedUser(user, JsonUtil::getField("hub_url", userJson, aAllowMe && user == ClientManager::getInstance()->getMe())); } TTHValue Deserializer::deserializeTTH(const json& aJson) { diff --git a/airdcpp-webapi/api/common/ListViewController.h b/airdcpp-webapi/api/common/ListViewController.h index 48b4e8d3..82ec8e7d 100644 --- a/airdcpp-webapi/api/common/ListViewController.h +++ b/airdcpp-webapi/api/common/ListViewController.h @@ -278,7 +278,7 @@ namespace webserver { if (!active) { setActive(true); updateList(); - timer->start(); + timer->start(true); } return websocketpp::http::status_code::no_content; @@ -329,7 +329,7 @@ namespace webserver { if (paused && timer->isRunning()) { timer->stop(false); } else if (!paused && !timer->isRunning()) { - timer->start(); + timer->start(true); } } diff --git a/airdcpp-webapi/api/common/Serializer.cpp b/airdcpp-webapi/api/common/Serializer.cpp index bfb34731..b8c7b3f4 100644 --- a/airdcpp-webapi/api/common/Serializer.cpp +++ b/airdcpp-webapi/api/common/Serializer.cpp @@ -128,6 +128,18 @@ namespace webserver { }; } + json Serializer::serializeShareProfileSimple(ProfileToken aProfile) noexcept { + auto sp = ShareManager::getInstance()->getShareProfile(aProfile); + if (!sp) { + return nullptr; + } + + return { + { "id", sp->getToken() }, + { "str", sp->getPlainName() }, + }; + } + json Serializer::serializeChatMessage(const ChatMessagePtr& aMessage) noexcept { json ret = { { "id", aMessage->getId()}, diff --git a/airdcpp-webapi/api/common/Serializer.h b/airdcpp-webapi/api/common/Serializer.h index 58cfb666..ee93e194 100644 --- a/airdcpp-webapi/api/common/Serializer.h +++ b/airdcpp-webapi/api/common/Serializer.h @@ -55,6 +55,8 @@ namespace webserver { static json serializeIp(const string& aIP) noexcept; static json serializeIp(const string& aIP, const string& aCountryCode) noexcept; + static json serializeShareProfileSimple(ProfileToken aProfile) noexcept; + static string getDownloadStateId(TrackableDownloadItem::State aState) noexcept; static string getDownloadStateStr(TrackableDownloadItem::State aState) noexcept; static json serializeDownloadState(TrackableDownloadItem::State aState) noexcept; diff --git a/airdcpp-webapi/web-server/ApiRouter.cpp b/airdcpp-webapi/web-server/ApiRouter.cpp index 856f9971..960bcbf8 100644 --- a/airdcpp-webapi/web-server/ApiRouter.cpp +++ b/airdcpp-webapi/web-server/ApiRouter.cpp @@ -149,8 +149,8 @@ namespace webserver { } } else if (aRequest.getStringParam(0) == "socket") { return sessionApi.handleSocketConnect(aRequest, aIsSecure, aSocket); - } else if (aRequest.getStringParam(0) == "away") { - return sessionApi.handleAway(aRequest); + } else if (aRequest.getStringParam(0) == "activity") { + return sessionApi.handleActivity(aRequest); } aRequest.setResponseErrorStr("Invalid command"); diff --git a/airdcpp-webapi/web-server/Session.cpp b/airdcpp-webapi/web-server/Session.cpp index bc57fa8d..e877c09e 100644 --- a/airdcpp-webapi/web-server/Session.cpp +++ b/airdcpp-webapi/web-server/Session.cpp @@ -52,7 +52,7 @@ namespace webserver { Session::Session(WebUserPtr& aUser, const string& aToken, bool aIsSecure, WebServerManager* aServer, uint64_t maxInactivityMinutes, bool aIsUserSession) : id(Util::rand()), user(aUser), token(aToken), started(GET_TICK()), lastActivity(GET_TICK()), secure(aIsSecure), server(aServer), - maxInactivity(maxInactivityMinutes*1000*60), userAway(!aIsUserSession), userSession(aIsUserSession) { + maxInactivity(maxInactivityMinutes*1000*60), userSession(aIsUserSession) { ADD_MODULE("connectivity", ConnectivityApi); ADD_MODULE("favorite_directories", FavoriteDirectoryApi); @@ -103,6 +103,12 @@ namespace webserver { } void Session::onSocketConnected(const WebSocketPtr& aSocket) noexcept { + auto oldSocket = getServer()->getSocket(id); + if (oldSocket) { + oldSocket->debugMessage("Replace session socket"); + oldSocket->close(websocketpp::close::status::policy_violation, "Another socket was connected to this session"); + } + fire(SessionListener::SocketConnected(), aSocket); } diff --git a/airdcpp-webapi/web-server/Session.h b/airdcpp-webapi/web-server/Session.h index b9210353..5d768182 100644 --- a/airdcpp-webapi/web-server/Session.h +++ b/airdcpp-webapi/web-server/Session.h @@ -60,7 +60,6 @@ namespace webserver { Session(Session&) = delete; Session& operator=(Session&) = delete; - GETSET(bool, userAway, UserAway); void onSocketConnected(const WebSocketPtr& aSocket) noexcept; void onSocketDisconnected() noexcept; diff --git a/airdcpp-webapi/web-server/Timer.h b/airdcpp-webapi/web-server/Timer.h index 635f495c..df1afe45 100644 --- a/airdcpp-webapi/web-server/Timer.h +++ b/airdcpp-webapi/web-server/Timer.h @@ -41,21 +41,14 @@ namespace webserver { stop(true); } - bool start(bool aInstantStart = true) { + bool start(bool aInstantTick) { if (shutdown) { return false; } running = true; - timer.expires_from_now(aInstantStart ? boost::posix_time::milliseconds(0) : interval); - timer.async_wait([this](const boost::system::error_code& error) { - if (error == boost::asio::error::operation_aborted) { - // Timer stopped, no calls to this if the timer has been destructed - return; - } - - tick(); - }); + timer.expires_from_now(aInstantTick ? boost::posix_time::milliseconds(0) : interval); + timer.async_wait(std::bind(&Timer::tick, std::placeholders::_1, cbWrapper, this)); return true; } @@ -70,12 +63,17 @@ namespace webserver { return running; } private: - void tick() { + // Static in case the timer has been destructed + static void tick(const boost::system::error_code& error, const CallbackWrapper& cbWrapper, Timer* aTimer) { + if (error == boost::asio::error::operation_aborted) { + return; + } + if (cbWrapper) { // We must ensure that the timer still exists when a new start call is performed - cbWrapper(bind(&Timer::runTask, this)); + cbWrapper(bind(&Timer::runTask, aTimer)); } else { - runTask(); + aTimer->runTask(); } } diff --git a/airdcpp-webapi/web-server/WebServerManager.cpp b/airdcpp-webapi/web-server/WebServerManager.cpp index 66781d50..8014f641 100644 --- a/airdcpp-webapi/web-server/WebServerManager.cpp +++ b/airdcpp-webapi/web-server/WebServerManager.cpp @@ -21,13 +21,19 @@ #include +#include #include #include #include +#include #define CONFIG_NAME "WebServer.xml" #define CONFIG_DIR Util::PATH_USER_CONFIG +#define AUTHENTICATION_TIMEOUT 60 // seconds +#define PING_INTERVAL 30 // seconds +#define PONG_TIMEOUT 10 // seconds + #define HANDSHAKE_TIMEOUT 0 // disabled, affects HTTP downloads #define DEFAULT_THREADS 4 @@ -82,7 +88,7 @@ namespace webserver { void setEndpointLogSettings(T& aEndpoint, std::ostream& aStream) { // Access aEndpoint.set_access_channels(websocketpp::log::alevel::all); - aEndpoint.clear_access_channels(websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::frame_header); + aEndpoint.clear_access_channels(websocketpp::log::alevel::frame_payload | websocketpp::log::alevel::frame_header | websocketpp::log::alevel::control); aEndpoint.get_alog().set_ostream(&aStream); // Errors @@ -90,6 +96,22 @@ namespace webserver { aEndpoint.get_elog().set_ostream(&aStream); } + template + void setEndpointHandlers(T& aEndpoint, bool aIsSecure, WebServerManager* aServer) { + aEndpoint.set_http_handler( + std::bind(&WebServerManager::on_http, aServer, &aEndpoint, _1, aIsSecure)); + aEndpoint.set_message_handler( + std::bind(&WebServerManager::on_message, aServer, &aEndpoint, _1, _2, aIsSecure)); + + aEndpoint.set_close_handler(std::bind(&WebServerManager::on_close_socket, aServer, _1)); + aEndpoint.set_open_handler(std::bind(&WebServerManager::on_open_socket, aServer, &aEndpoint, _1, aIsSecure)); + + aEndpoint.set_open_handshake_timeout(HANDSHAKE_TIMEOUT); + + aEndpoint.set_pong_timeout(PONG_TIMEOUT * 1000); + aEndpoint.set_pong_timeout_handler(std::bind(&WebServerManager::onPongTimeout, aServer, _1, _2)); + } + bool WebServerManager::start(ErrorF errorF, const string& aWebResourcePath) { { auto resourcePath = aWebResourcePath; @@ -115,37 +137,21 @@ namespace webserver { try { // initialize asio with our external io_service rather than an internal one endpoint_plain.init_asio(&ios); - - endpoint_plain.set_http_handler( - std::bind(&WebServerManager::on_http, this, &endpoint_plain, _1, false)); - endpoint_plain.set_message_handler( - std::bind(&WebServerManager::on_message, this, &endpoint_plain, _1, _2, false)); - endpoint_plain.set_close_handler(std::bind(&WebServerManager::on_close_socket, this, _1)); - endpoint_plain.set_open_handler(std::bind(&WebServerManager::on_open_socket, this, &endpoint_plain, _1, false)); - - // Failures (plain) - endpoint_plain.set_open_handshake_timeout(HANDSHAKE_TIMEOUT); - - // set up tls endpoint endpoint_tls.init_asio(&ios); - endpoint_tls.set_message_handler( - std::bind(&WebServerManager::on_message, this, &endpoint_tls, _1, _2, true)); - - endpoint_tls.set_close_handler(std::bind(&WebServerManager::on_close_socket, this, _1)); - endpoint_tls.set_open_handler(std::bind(&WebServerManager::on_open_socket, this, &endpoint_tls, _1, true)); - endpoint_tls.set_http_handler(std::bind(&WebServerManager::on_http, this, &endpoint_tls, _1, true)); - - // Failures (TLS) - endpoint_tls.set_open_handshake_timeout(HANDSHAKE_TIMEOUT); - - // TLS endpoint has an extra handler for the tls init - endpoint_tls.set_tls_init_handler(std::bind(&WebServerManager::on_tls_init, this, _1)); + //endpoint_plain.set_pong_handler(std::bind(&WebServerManager::onPongReceived, this, _1, _2)); } catch (const std::exception& e) { errorF(e.what()); return false; } + // Handlers + setEndpointHandlers(endpoint_plain, false, this); + setEndpointHandlers(endpoint_tls, true, this); + + // TLS endpoint has an extra handler for the tls init + endpoint_tls.set_tls_init_handler(std::bind(&WebServerManager::on_tls_init, this, _1)); + // Logging setEndpointLogSettings(endpoint_plain, debugStreamPlain); setEndpointLogSettings(endpoint_tls, debugStreamTls); @@ -194,10 +200,66 @@ namespace webserver { } } + socketTimer = addTimer([this] { pingTimer(); }, PING_INTERVAL * 1000); + socketTimer->start(false); + fire(WebServerManagerListener::Started()); return hasServer; } + WebSocketPtr WebServerManager::getSocket(websocketpp::connection_hdl hdl) const noexcept { + RLock l(cs); + auto s = sockets.find(hdl); + if (s != sockets.end()) { + return s->second; + } + + return nullptr; + } + + // For debugging only + void WebServerManager::onPongReceived(websocketpp::connection_hdl hdl, const string& aPayload) { + auto socket = getSocket(hdl); + if (!socket) { + return; + } + + socket->debugMessage("PONG succeed"); + } + + void WebServerManager::onPongTimeout(websocketpp::connection_hdl hdl, const string& aPayload) { + auto socket = getSocket(hdl); + if (!socket) { + return; + } + + socket->debugMessage("PONG timed out"); + + socket->close(websocketpp::close::status::internal_endpoint_error, "PONG timed out"); + } + + void WebServerManager::pingTimer() noexcept { + vector inactiveSockets; + auto tick = GET_TICK(); + + { + RLock l(cs); + for (const auto& socket : sockets | map_values) { + //socket->debugMessage("PING"); + socket->ping(); + + // Disconnect sockets without a session after one minute + if (!socket->getSession() && socket->getTimeCreated() + AUTHENTICATION_TIMEOUT * 1000ULL < tick) { + inactiveSockets.push_back(socket); + } + } + } + + for (const auto& s : inactiveSockets) { + s->close(websocketpp::close::status::policy_violation, "Authentication timeout"); + } + } + context_ptr WebServerManager::on_tls_init(websocketpp::connection_hdl hdl) { //std::cout << "on_tls_init called with hdl: " << hdl.lock().get() << std::endl; context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::tlsv12)); @@ -210,8 +272,9 @@ namespace webserver { ctx->use_certificate_file(SETTING(TLS_CERTIFICATE_FILE), boost::asio::ssl::context::pem); ctx->use_private_key_file(SETTING(TLS_PRIVATE_KEY_FILE), boost::asio::ssl::context::pem); + + CryptoManager::setContextOptions(ctx->native_handle(), true); } catch (std::exception& e) { - //std::cout << e.what() << std::endl; dcdebug("TLS init failed: %s", e.what()); } @@ -226,6 +289,7 @@ namespace webserver { } void WebServerManager::stop() { + socketTimer->stop(true); fire(WebServerManagerListener::Stopping()); if(endpoint_plain.is_listening()) @@ -307,6 +371,7 @@ namespace webserver { sockets.erase(s); } + dcdebug("Close socket: %s\n", socket->getSession() ? socket->getSession()->getAuthToken().c_str() : "(no session)"); if (socket->getSession()) { socket->getSession()->onSocketDisconnected(); } diff --git a/airdcpp-webapi/web-server/WebServerManager.h b/airdcpp-webapi/web-server/WebServerManager.h index f05fe1da..16c59350 100644 --- a/airdcpp-webapi/web-server/WebServerManager.h +++ b/airdcpp-webapi/web-server/WebServerManager.h @@ -62,17 +62,10 @@ namespace webserver { void on_message(EndpointType* aServer, websocketpp::connection_hdl hdl, typename EndpointType::message_ptr msg, bool aIsSecure) { - WebSocketPtr socket = nullptr; - - { - WLock l(cs); - auto s = sockets.find(hdl); - if (s != sockets.end()) { - socket = s->second; - } else { - dcassert(0); - return; - } + auto socket = getSocket(hdl); + if (!socket) { + dcassert(0); + return; } api.handleSocketRequest(msg->get_payload(), socket, aIsSecure); @@ -80,7 +73,6 @@ namespace webserver { template void on_open_socket(EndpointType* aServer, websocketpp::connection_hdl hdl, bool aIsSecure) { - WLock l(cs); auto socket = make_shared(aIsSecure, hdl, aServer); sockets.emplace(hdl, socket); @@ -88,6 +80,9 @@ namespace webserver { void on_close_socket(websocketpp::connection_hdl hdl); + void onPongReceived(websocketpp::connection_hdl hdl, const string& aPayload); + void onPongTimeout(websocketpp::connection_hdl hdl, const string& aPayload); + template void on_http(EndpointType* s, websocketpp::connection_hdl hdl, bool aIsSecure) { // Blocking HTTP Handler @@ -180,6 +175,7 @@ namespace webserver { return serverThreads; } private: + WebSocketPtr getSocket(websocketpp::connection_hdl hdl) const noexcept; bool listen(ErrorF& errorF); bool initialize(ErrorF& errorF); @@ -188,6 +184,7 @@ namespace webserver { ServerConfig tlsServerConfig; void loadServer(SimpleXML& xml_, const string& aTagName, ServerConfig& config_) noexcept; + void pingTimer() noexcept; mutable SharedMutex cs; @@ -197,6 +194,7 @@ namespace webserver { context_ptr on_tls_init(websocketpp::connection_hdl hdl); + typedef vector WebSocketList; std::map> sockets; ApiRouter api; @@ -204,6 +202,7 @@ namespace webserver { unique_ptr userManager; + TimerPtr socketTimer; bool has_io_service; server_plain endpoint_plain; diff --git a/airdcpp-webapi/web-server/WebSocket.cpp b/airdcpp-webapi/web-server/WebSocket.cpp index bc871b6b..03430a42 100644 --- a/airdcpp-webapi/web-server/WebSocket.cpp +++ b/airdcpp-webapi/web-server/WebSocket.cpp @@ -19,12 +19,14 @@ #include #include +#include #include namespace webserver { WebSocket::WebSocket(bool aIsSecure, websocketpp::connection_hdl aHdl) : - secure(aIsSecure), hdl(aHdl) { + secure(aIsSecure), hdl(aHdl), timeCreated(GET_TICK()) { + debugMessage("Websocket created"); } WebSocket::~WebSocket() { @@ -41,7 +43,7 @@ namespace webserver { } } - void WebSocket::sendApiResponse(const json& aResponseJson, const json& aErrorJson, websocketpp::http::status_code::value aCode, int aCallbackId) { + void WebSocket::sendApiResponse(const json& aResponseJson, const json& aErrorJson, websocketpp::http::status_code::value aCode, int aCallbackId) noexcept { json j; if (aCallbackId > 0) { @@ -63,7 +65,11 @@ namespace webserver { sendPlain(j.dump(4)); } - void WebSocket::sendPlain(const string& aMsg) { + void WebSocket::debugMessage(const string& aMessage) const noexcept { + dcdebug(string(aMessage + " (%s)\n").c_str(), session ? session->getAuthToken().c_str() : "no session"); + } + + void WebSocket::sendPlain(const string& aMsg) noexcept { //dcdebug("WebSocket::send: %s\n", aMsg.c_str()); try { if (secure) { @@ -73,7 +79,20 @@ namespace webserver { } } catch (const std::exception& e) { - dcdebug("WebSocket::send failed: %s", e.what()); + debugMessage("WebSocket::send failed: " + string(e.what())); + } + } + + void WebSocket::ping() noexcept { + try { + if (secure) { + tlsServer->ping(hdl, Util::emptyString); + } else { + plainServer->ping(hdl, Util::emptyString); + } + + } catch (const std::exception& e) { + debugMessage("WebSocket::ping failed: " + string(e.what())); } } @@ -85,7 +104,7 @@ namespace webserver { plainServer->close(hdl, aCode, aMsg); } } catch (const std::exception& e) { - dcdebug("WebSocket::close failed: %s", e.what()); + debugMessage("WebSocket::close failed: " + string(e.what())); } } } \ No newline at end of file diff --git a/airdcpp-webapi/web-server/WebSocket.h b/airdcpp-webapi/web-server/WebSocket.h index cdfc9c77..e245b939 100644 --- a/airdcpp-webapi/web-server/WebSocket.h +++ b/airdcpp-webapi/web-server/WebSocket.h @@ -43,13 +43,19 @@ namespace webserver { IGETSET(SessionPtr, session, Session, nullptr); - void sendPlain(const std::string& aMsg); - void sendApiResponse(const json& aJsonResponse, const json& aErrorJson, websocketpp::http::status_code::value aCode, int aCallbackId); + void sendPlain(const std::string& aMsg) noexcept; + void sendApiResponse(const json& aJsonResponse, const json& aErrorJson, websocketpp::http::status_code::value aCode, int aCallbackId) noexcept; WebSocket(WebSocket&) = delete; WebSocket& operator=(WebSocket&) = delete; string getIp() const noexcept; + void ping() noexcept; + + void debugMessage(const string& aMessage) const noexcept; + time_t getTimeCreated() const noexcept { + return timeCreated; + } protected: WebSocket(bool aIsSecure, websocketpp::connection_hdl aHdl); private: @@ -61,6 +67,7 @@ namespace webserver { websocketpp::connection_hdl hdl; bool secure; + time_t timeCreated; }; } diff --git a/airdcpp-webapi/web-server/WebUserManager.cpp b/airdcpp-webapi/web-server/WebUserManager.cpp index c8f42e1e..823a8953 100644 --- a/airdcpp-webapi/web-server/WebUserManager.cpp +++ b/airdcpp-webapi/web-server/WebUserManager.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include #include #include @@ -70,36 +70,10 @@ namespace webserver { } if (aUserSession) { - checkAwayState(); - } - return session; - } - - void WebUserManager::setSessionAwayState(LocalSessionId aSessionId, bool aAway) noexcept { - auto s = getSession(aSessionId); - if (!s) { - return; - } - - s->setUserAway(aAway); - checkAwayState(); - } - - void WebUserManager::checkAwayState() noexcept { - bool allAway = true; - { - RLock l(cs); - allAway = boost::find_if(sessionsLocalId | map_values, [](const SessionPtr& aSession) { - return !aSession->getUserAway(); - }).base() == sessionsLocalId.end(); + ActivityManager::getInstance()->updateActivity(); } - bool currentAway = AirUtil::getAwayMode() == AWAY_IDLE; - if (allAway && !currentAway) { - AirUtil::setAway(AWAY_IDLE); - } else if (!allAway && currentAway) { - AirUtil::setAway(AWAY_OFF); - } + return session; } SessionPtr WebUserManager::getSession(const string& aSession) const noexcept { @@ -162,10 +136,6 @@ namespace webserver { sessionsRemoteId.erase(aSession->getAuthToken()); sessionsLocalId.erase(aSession->getId()); } - - if (aSession->isUserSession()) { - checkAwayState(); - } } void WebUserManager::on(WebServerManagerListener::Started) noexcept { diff --git a/airdcpp-webapi/web-server/WebUserManager.h b/airdcpp-webapi/web-server/WebUserManager.h index b874f1cb..903a4eeb 100644 --- a/airdcpp-webapi/web-server/WebUserManager.h +++ b/airdcpp-webapi/web-server/WebUserManager.h @@ -56,9 +56,7 @@ namespace webserver { StringList getUserNames() const noexcept; size_t getSessionCount() const noexcept; - void setSessionAwayState(LocalSessionId aSessionId, bool aAway) noexcept; private: - void checkAwayState() noexcept; mutable SharedMutex cs; std::map users; diff --git a/airdcppd/Client.cpp b/airdcppd/Client.cpp index 3d93be4d..45bda89e 100644 --- a/airdcppd/Client.cpp +++ b/airdcppd/Client.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include #include @@ -95,7 +95,7 @@ bool Client::startup() { return false; } - AirUtil::setAway(AWAY_IDLE); + ActivityManager::getInstance()->setAway(AWAY_IDLE); SettingsManager::getInstance()->setDefault(SettingsManager::LOG_IGNORED, false); SettingsManager::getInstance()->setDefault(SettingsManager::NICK, getDefaultNick()); diff --git a/airdcppd/main.cpp b/airdcppd/main.cpp index 9ddc201e..63c6c725 100755 --- a/airdcppd/main.cpp +++ b/airdcppd/main.cpp @@ -145,16 +145,20 @@ static void installHandler() { #include static void daemonize() { + auto reportError = [](const char* aMessage) { + fprintf(stderr, (string(aMessage) + ": %s\n").c_str(), strerror(errno)); + }; + switch(fork()) { case -1: - fprintf(stderr, "First fork failed: %s\n", strerror(errno)); + reportError("First fork failed"); exit(5); case 0: break; default: _exit(0); } if(setsid() < 0) { - fprintf(stderr, "setsid failed: %s\n", strerror(errno)); + reportError("setsid failed"); exit(6); } @@ -166,12 +170,26 @@ static void daemonize() { default: exit(0); } - chdir("/"); + if (chdir("/") < 0) { + reportError("chdir failed"); + exit(8); + } + close(0); close(1); close(2); + open("/dev/null", O_RDWR); - dup(0); dup(0); + + if (dup(0) < 0) { + reportError("dup failed for stdout"); + exit(9); + } + + if (dup(0) < 0) { + reportError("dup failed for stderr"); + exit(10); + } } #include