diff --git a/AddressBook.cpp b/AddressBook.cpp index f08a4041..f66387db 100644 --- a/AddressBook.cpp +++ b/AddressBook.cpp @@ -5,11 +5,11 @@ #include #include #include -#include #include #include "Base.h" #include "util.h" #include "Identity.h" +#include "FS.h" #include "Log.h" #include "NetDb.h" #include "ClientContext.h" @@ -19,143 +19,107 @@ namespace i2p { namespace client { - + // TODO: this is actually proxy class class AddressBookFilesystemStorage: public AddressBookStorage { - public: + private: + i2p::fs::HashedStorage storage; + std::string indexPath; - AddressBookFilesystemStorage (); + public: + AddressBookFilesystemStorage (): storage("addressbook", "b", "", "b32") {}; std::shared_ptr GetAddress (const i2p::data::IdentHash& ident) const; void AddAddress (std::shared_ptr address); void RemoveAddress (const i2p::data::IdentHash& ident); + bool Init (); int Load (std::map& addresses); int Save (const std::map& addresses); - - private: - - boost::filesystem::path GetPath () const - { - return i2p::util::filesystem::GetDefaultDataDir() / "addressbook"; - } - boost::filesystem::path GetAddressPath (const i2p::data::IdentHash& ident) const - { - auto b32 = ident.ToBase32(); - return GetPath () / (std::string ("b") + b32[0]) / (b32 + ".b32"); - } }; - AddressBookFilesystemStorage::AddressBookFilesystemStorage () + bool AddressBookFilesystemStorage::Init() { - auto path = GetPath (); - if (!boost::filesystem::exists (path)) - { - // Create directory is necessary - if (!boost::filesystem::create_directory (path)) - LogPrint (eLogError, "Addressbook: failed to create addressbook directory"); - } - + storage.SetPlace(i2p::fs::GetDataDir()); + indexPath = storage.GetRoot() + i2p::fs::dirSep + "addresses.csv"; + return storage.Init(i2p::data::GetBase32SubstitutionTable(), 32); } std::shared_ptr AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident) const { - auto filename = GetAddressPath (ident); - if (!boost::filesystem::exists (filename)) - { - boost::filesystem::create_directory (filename.parent_path ()); - // try to find in main folder - auto filename1 = GetPath () / (ident.ToBase32 () + ".b32"); - if (!boost::filesystem::exists (filename1)) - { - boost::system::error_code ec; - boost::filesystem::rename (filename1, filename, ec); - if (ec) - LogPrint (eLogError, "Addresbook: couldn't move file ", ec.message ()); - } - else - return nullptr; // address doesn't exist - } - std::ifstream f(filename.string (), std::ifstream::binary); - if (f.is_open ()) - { - f.seekg (0,std::ios::end); - size_t len = f.tellg (); - if (len < i2p::data::DEFAULT_IDENTITY_SIZE) - { - LogPrint (eLogError, "Addresbook: File ", filename, " is too short. ", len); - return nullptr; - } - f.seekg(0, std::ios::beg); - uint8_t * buf = new uint8_t[len]; - f.read((char *)buf, len); - auto address = std::make_shared(buf, len); - delete[] buf; - return address; + std::string filename = storage.Path(ident.ToBase32()); + std::ifstream f(filename, std::ifstream::binary); + if (!f.is_open ()) { + LogPrint(eLogDebug, "Addressbook: Requested, but not found: ", filename); + return nullptr; } - else + + f.seekg (0,std::ios::end); + size_t len = f.tellg (); + if (len < i2p::data::DEFAULT_IDENTITY_SIZE) { + LogPrint (eLogError, "Addresbook: File ", filename, " is too short: ", len); return nullptr; + } + f.seekg(0, std::ios::beg); + uint8_t * buf = new uint8_t[len]; + f.read((char *)buf, len); + auto address = std::make_shared(buf, len); + delete[] buf; + return address; } void AddressBookFilesystemStorage::AddAddress (std::shared_ptr address) { - auto filename = GetAddressPath (address->GetIdentHash ()); - std::ofstream f (filename.string (), std::ofstream::binary | std::ofstream::out); - if (!f.is_open ()) - { - // create subdirectory - if (boost::filesystem::create_directory (filename.parent_path ())) - f.open (filename.string (), std::ofstream::binary | std::ofstream::out); // and try to open again - } - if (f.is_open ()) - { - size_t len = address->GetFullLen (); - uint8_t * buf = new uint8_t[len]; - address->ToBuffer (buf, len); - f.write ((char *)buf, len); - delete[] buf; + std::string path = storage.Path( address->GetIdentHash().ToBase32() ); + std::ofstream f (path, std::ofstream::binary | std::ofstream::out); + if (!f.is_open ()) { + LogPrint (eLogError, "Addresbook: can't open file ", path); + return; } - else - LogPrint (eLogError, "Addresbook: can't open file ", filename); + size_t len = address->GetFullLen (); + uint8_t * buf = new uint8_t[len]; + address->ToBuffer (buf, len); + f.write ((char *)buf, len); + delete[] buf; } void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident) { - auto filename = GetAddressPath (ident); - if (boost::filesystem::exists (filename)) - boost::filesystem::remove (filename); + storage.Remove( ident.ToBase32() ); } int AddressBookFilesystemStorage::Load (std::map& addresses) { int num = 0; - auto filename = GetPath () / "addresses.csv"; - std::ifstream f (filename.string (), std::ifstream::in); // in text mode - if (f.is_open ()) - { - addresses.clear (); - while (!f.eof ()) + std::string s; + std::ifstream f (indexPath, std::ifstream::in); // in text mode + + if (f.is_open ()) { + LogPrint(eLogInfo, "Addressbook: using index file ", indexPath); + } else { + LogPrint(eLogWarning, "Addressbook: Can't open ", indexPath); + return 0; + } + + addresses.clear (); + while (!f.eof ()) { + getline(f, s); + if (!s.length()) + continue; // skip empty line + + std::size_t pos = s.find(','); + if (pos != std::string::npos) { - std::string s; - getline(f, s); - if (!s.length()) - continue; // skip empty line + std::string name = s.substr(0, pos++); + std::string addr = s.substr(pos); - size_t pos = s.find(','); - if (pos != std::string::npos) - { - std::string name = s.substr(0, pos++); - std::string addr = s.substr(pos); - - i2p::data::IdentHash ident; - ident.FromBase32 (addr); - addresses[name] = ident; - num++; - } - } - LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded"); + i2p::data::IdentHash ident; + ident.FromBase32 (addr); + addresses[name] = ident; + num++; + } } - else - LogPrint (eLogWarning, "Addressbook: ", filename, " not found"); + + LogPrint (eLogInfo, "Addressbook: ", num, " addresses loaded from storage"); return num; } @@ -167,24 +131,23 @@ namespace client } int num = 0; - auto filename = GetPath () / "addresses.csv"; - std::ofstream f (filename.string (), std::ofstream::out); // in text mode - if (f.is_open ()) - { - for (auto it: addresses) - { - f << it.first << "," << it.second.ToBase32 () << std::endl; - num++; - } - LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved"); + std::ofstream f (indexPath, std::ofstream::out); // in text mode + + if (!f.is_open ()) { + LogPrint (eLogWarning, "Addressbook: Can't open ", indexPath); + return 0; + } + + for (auto it: addresses) { + f << it.first << "," << it.second.ToBase32 () << std::endl; + num++; } - else - LogPrint (eLogError, "Addresbook: can't open file ", filename); + LogPrint (eLogInfo, "Addressbook: ", num, " addresses saved"); return num; } //--------------------------------------------------------------------- - AddressBook::AddressBook (): m_Storage (nullptr), m_IsLoaded (false), m_IsDownloading (false), + AddressBook::AddressBook (): m_Storage(new AddressBookFilesystemStorage), m_IsLoaded (false), m_IsDownloading (false), m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr) { } @@ -196,6 +159,7 @@ namespace client void AddressBook::Start () { + m_Storage->Init(); LoadHosts (); /* try storage, then hosts.txt, then download */ StartSubscriptions (); } @@ -235,11 +199,6 @@ namespace client m_Subscriptions.clear (); } - AddressBookStorage * AddressBook::CreateStorage () - { - return new AddressBookFilesystemStorage (); - } - bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident) { auto pos = address.find(".b32.i2p"); @@ -283,8 +242,6 @@ namespace client { auto ident = std::make_shared(); ident->FromBase64 (base64); - if (!m_Storage) - m_Storage = CreateStorage (); m_Storage->AddAddress (ident); m_Addresses[address] = ident->GetIdentHash (); LogPrint (eLogInfo, "Addressbook: added ", address," -> ", ToAddress(ident->GetIdentHash ())); @@ -292,15 +249,11 @@ namespace client void AddressBook::InsertAddress (std::shared_ptr address) { - if (!m_Storage) - m_Storage = CreateStorage (); m_Storage->AddAddress (address); } std::shared_ptr AddressBook::GetAddress (const std::string& address) { - if (!m_Storage) - m_Storage = CreateStorage (); i2p::data::IdentHash ident; if (!GetIdentHash (address, ident)) return nullptr; return m_Storage->GetAddress (ident); @@ -308,16 +261,14 @@ namespace client void AddressBook::LoadHosts () { - if (!m_Storage) - m_Storage = CreateStorage (); if (m_Storage->Load (m_Addresses) > 0) { m_IsLoaded = true; return; } - // try hosts.txt first - std::ifstream f (i2p::util::filesystem::GetFullPath ("hosts.txt").c_str (), std::ifstream::in); // in text mode + // then try hosts.txt + std::ifstream f (i2p::fs::DataDirPath("hosts.txt"), std::ifstream::in); // in text mode if (f.is_open ()) { LoadHostsFromStream (f); @@ -367,7 +318,7 @@ namespace client { if (!m_Subscriptions.size ()) { - std::ifstream f (i2p::util::filesystem::GetFullPath ("subscriptions.txt").c_str (), std::ifstream::in); // in text mode + std::ifstream f (i2p::fs::DataDirPath ("subscriptions.txt"), std::ifstream::in); // in text mode if (f.is_open ()) { std::string s; @@ -431,7 +382,10 @@ namespace client if (ecode != boost::asio::error::operation_aborted) { auto dest = i2p::client::context.GetSharedLocalDestination (); - if (!dest) return; + if (!dest) { + LogPrint(eLogWarning, "Addressbook: missing local destination, skip subscription update"); + return; + } if (!m_IsDownloading && dest->IsReady ()) { if (!m_IsLoaded) @@ -495,7 +449,7 @@ namespace client }); if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout) { - LogPrint (eLogError, "Subscription LeaseSet request timeout expired"); + LogPrint (eLogError, "Addressbook: Subscription LeaseSet request timeout expired"); i2p::client::context.GetSharedLocalDestination ()->CancelDestinationRequest (ident); } } @@ -602,7 +556,7 @@ namespace client LogPrint (eLogError, "Addressbook: Can't resolve ", u.host_); if (!success) - LogPrint (eLogError, "Addressbook: download failed"); + LogPrint (eLogError, "Addressbook: download hosts.txt from ", m_Link, " failed"); m_Book.DownloadComplete (success); } diff --git a/AddressBook.h b/AddressBook.h index 19d8744c..46df12d0 100644 --- a/AddressBook.h +++ b/AddressBook.h @@ -10,7 +10,6 @@ #include #include #include "Base.h" -#include "util.h" #include "Identity.h" #include "Log.h" @@ -36,6 +35,7 @@ namespace client virtual void AddAddress (std::shared_ptr address) = 0; virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0; + virtual bool Init () = 0; virtual int Load (std::map& addresses) = 0; virtual int Save (const std::map& addresses) = 0; }; @@ -65,7 +65,6 @@ namespace client void StartSubscriptions (); void StopSubscriptions (); - AddressBookStorage * CreateStorage (); void LoadHosts (); void LoadSubscriptions (); diff --git a/Base.cpp b/Base.cpp index 3017d910..e894b694 100644 --- a/Base.cpp +++ b/Base.cpp @@ -6,6 +6,18 @@ namespace i2p { namespace data { + static const char T32[32] = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'k', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 't', 't', 'u', 'v', 'w', 'x', + 'y', 'z', '2', '3', '4', '5', '6', '7', + }; + + const char * GetBase32SubstitutionTable () + { + return T32; + } + static void iT64Build(void); /* @@ -16,7 +28,7 @@ namespace data * Direct Substitution Table */ - static char T64[64] = { + static const char T64[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', diff --git a/Base.h b/Base.h index e6f9567d..0c38725e 100644 --- a/Base.h +++ b/Base.h @@ -13,6 +13,7 @@ namespace data { size_t ByteStreamToBase64 (const uint8_t * InBuffer, size_t InCount, char * OutBuffer, size_t len); size_t Base64ToByteStream (const char * InBuffer, size_t InCount, uint8_t * OutBuffer, size_t len ); + const char * GetBase32SubstitutionTable (); const char * GetBase64SubstitutionTable (); size_t Base32ToByteStream (const char * inBuf, size_t len, uint8_t * outBuf, size_t outLen); diff --git a/ClientContext.cpp b/ClientContext.cpp index 9967a33a..bfa94ad1 100644 --- a/ClientContext.cpp +++ b/ClientContext.cpp @@ -3,7 +3,7 @@ #include #include #include "Config.h" -#include "util.h" +#include "FS.h" #include "Log.h" #include "Identity.h" #include "ClientContext.h" @@ -148,10 +148,10 @@ namespace client m_SharedLocalDestination = nullptr; } - void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType) + void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType) { - std::string fullPath = i2p::util::filesystem::GetFullPath (filename); - std::ifstream s(fullPath.c_str (), std::ifstream::binary); + std::string fullPath = i2p::fs::DataDirPath (filename); + std::ifstream s(fullPath, std::ifstream::binary); if (s.is_open ()) { s.seekg (0, std::ios::end); @@ -252,14 +252,14 @@ namespace client void ClientContext::ReadTunnels () { boost::property_tree::ptree pt; - std::string pathTunnelsConfigFile = i2p::util::filesystem::GetTunnelsConfigFile().string(); - try - { - boost::property_tree::read_ini (pathTunnelsConfigFile, pt); - } - catch (std::exception& ex) - { - LogPrint (eLogWarning, "Clients: Can't read ", pathTunnelsConfigFile, ": ", ex.what ()); + std::string pathTunConf; + i2p::config::GetOption("tunconf", pathTunConf); + if (pathTunConf == "") + return; + try { + boost::property_tree::read_ini (pathTunConf, pt); + } catch (std::exception& ex) { + LogPrint (eLogWarning, "Clients: Can't read ", pathTunConf, ": ", ex.what ()); return; } @@ -347,7 +347,7 @@ namespace client numServerTunnels++; } else - LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", pathTunnelsConfigFile); + LogPrint (eLogWarning, "Clients: Unknown section type=", type, " of ", name, " in ", pathTunConf); } catch (std::exception& ex) diff --git a/Daemon.cpp b/Daemon.cpp index 0687d0b9..776e0701 100644 --- a/Daemon.cpp +++ b/Daemon.cpp @@ -5,6 +5,7 @@ #include "Config.h" #include "Log.h" +#include "FS.h" #include "Base.h" #include "version.h" #include "Transports.h" @@ -14,7 +15,6 @@ #include "Tunnel.h" #include "NetDb.h" #include "Garlic.h" -#include "util.h" #include "Streaming.h" #include "Destination.h" #include "HTTPServer.h" @@ -63,9 +63,17 @@ namespace i2p i2p::config::Init(); i2p::config::ParseCmdline(argc, argv); - std::string config = i2p::util::filesystem::GetConfigFile().string(); - std::string tunconf = i2p::util::filesystem::GetTunnelsConfigFile().string(); - std::string datadir = i2p::util::filesystem::GetDataDir().string(); + std::string config; i2p::config::GetOption("conf", config); + std::string tunconf; i2p::config::GetOption("tunconf", tunconf); + std::string datadir; i2p::config::GetOption("datadir", datadir); + i2p::fs::DetectDataDir(datadir, IsService()); + i2p::fs::Init(); + + datadir = i2p::fs::GetDataDir(); + if (config == "") + config = i2p::fs::DataDirPath("i2p.conf"); + if (tunconf == "") + tunconf = i2p::fs::DataDirPath("tunnels.cfg"); i2p::config::ParseConfig(config); i2p::config::Finalize(); @@ -149,18 +157,9 @@ namespace i2p if (isDaemon && (logs == "" || logs == "stdout")) logs = "file"; - if (logs == "file") - { + if (logs == "file") { if (logfile == "") - { - // use autodetect of logfile - logfile = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string(); -#ifndef _WIN32 - logfile.append("/i2pd.log"); -#else - logfile.append("\\i2pd.log"); -#endif - } + logfile = i2p::fs::DataDirPath("i2pd.log"); StartLog (logfile); } else { // use stdout diff --git a/DaemonLinux.cpp b/DaemonLinux.cpp index 9382a878..2ccbfe38 100644 --- a/DaemonLinux.cpp +++ b/DaemonLinux.cpp @@ -9,8 +9,8 @@ #include #include "Config.h" +#include "FS.h" #include "Log.h" -#include "util.h" void handle_signal(int sig) { @@ -55,7 +55,7 @@ namespace i2p LogPrint(eLogError, "Daemon: could not create process group."); return false; } - std::string d(i2p::util::filesystem::GetDataDir().string ()); // make a copy + std::string d = i2p::fs::GetDataDir(); if (chdir(d.c_str()) != 0) { LogPrint(eLogError, "Daemon: could not chdir: ", strerror(errno)); @@ -75,8 +75,7 @@ namespace i2p // this code is c-styled and a bit ugly, but we need fd for locking pidfile std::string pidfile; i2p::config::GetOption("pidfile", pidfile); if (pidfile == "") { - pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string(); - pidfile.append("/i2pd.pid"); + pidfile = i2p::fs::DataDirPath("i2pd.pid"); } if (pidfile != "") { pidFH = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600); @@ -115,7 +114,7 @@ namespace i2p bool DaemonLinux::stop() { - unlink(pidfile.c_str()); + i2p::fs::Remove(pidfile); return Daemon_Singleton::stop(); } diff --git a/Destination.cpp b/Destination.cpp index e6380ffd..484c0679 100644 --- a/Destination.cpp +++ b/Destination.cpp @@ -2,8 +2,9 @@ #include #include #include + #include "Log.h" -#include "util.h" +#include "FS.h" #include "Crypto.h" #include "Timestamp.h" #include "NetDb.h" @@ -459,7 +460,8 @@ namespace client { auto s = shared_from_this (); RequestLeaseSet (GetIdentHash (), - [s](std::shared_ptr leaseSet) + // "this" added due to bug in gcc 4.7-4.8 + [s,this](std::shared_ptr leaseSet) { if (leaseSet) { @@ -751,30 +753,26 @@ namespace client void ClientDestination::PersistTemporaryKeys () { - auto path = i2p::util::filesystem::GetDefaultDataDir() / "destinations"; - auto filename = path / (GetIdentHash ().ToBase32 () + ".dat"); - std::ifstream f(filename.string (), std::ifstream::binary); - if (f) - { - f.read ((char *)m_EncryptionPublicKey, 256); + std::string ident = GetIdentHash().ToBase32(); + std::string path = i2p::fs::DataDirPath("destinations", (ident + ".dat")); + std::ifstream f(path, std::ifstream::binary); + + if (f) { + f.read ((char *)m_EncryptionPublicKey, 256); f.read ((char *)m_EncryptionPrivateKey, 256); + return; } - if (!f) - { - LogPrint (eLogInfo, "Creating new temporary keys for address ", GetIdentHash ().ToBase32 ()); - i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey); - if (!boost::filesystem::exists (path)) - { - if (!boost::filesystem::create_directory (path)) - LogPrint (eLogError, "Failed to create destinations directory"); - } - std::ofstream f1 (filename.string (), std::ofstream::binary | std::ofstream::out); - if (f1) - { - f1.write ((char *)m_EncryptionPublicKey, 256); - f1.write ((char *)m_EncryptionPrivateKey, 256); - } - } + + LogPrint (eLogInfo, "Destination: Creating new temporary keys for address ", ident, ".b32.i2p"); + i2p::crypto::GenerateElGamalKeyPair(m_EncryptionPrivateKey, m_EncryptionPublicKey); + + std::ofstream f1 (path, std::ofstream::binary | std::ofstream::out); + if (f1) { + f1.write ((char *)m_EncryptionPublicKey, 256); + f1.write ((char *)m_EncryptionPrivateKey, 256); + return; + } + LogPrint(eLogError, "Destinations: Can't save keys to ", path); } } } diff --git a/FS.cpp b/FS.cpp new file mode 100644 index 00000000..9416d6b4 --- /dev/null +++ b/FS.cpp @@ -0,0 +1,154 @@ +/* +* Copyright (c) 2013-2016, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include + +#include "Base.h" +#include "FS.h" +#include "Log.h" + +namespace i2p { +namespace fs { + std::string appName = "i2pd"; + std::string dataDir = ""; +#ifdef _WIN32 + std::string dirSep = "\\"; +#else + std::string dirSep = "/"; +#endif + + const std::string & GetAppName () { + return appName; + } + + void SetAppName (const std::string& name) { + appName = name; + } + + const std::string & GetDataDir () { + return dataDir; + } + + void DetectDataDir(const std::string & cmdline_param, bool isService) { + if (cmdline_param != "") { + dataDir = cmdline_param; + return; + } +#if defined(WIN32) || defined(_WIN32) + char localAppData[MAX_PATH]; + SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData); + dataDir = std::string(localAppData) + "\\" + appName; + return; +#elif defined(MAC_OSX) + char *home = getenv("HOME"); + dataDir = (home != NULL && strlen(home) > 0) ? home : ""; + dataDir += "/Library/Application Support/" + appName; + return; +#else /* other unix */ + char *home = getenv("HOME"); + if (isService) { + dataDir = "/var/lib/" + appName; + } else if (home != NULL && strlen(home) > 0) { + dataDir = std::string(home) + "/." + appName; + } else { + dataDir = "/tmp/" + appName; + } + return; +#endif + } + + bool Init() { + if (boost::filesystem::exists(dataDir)) + boost::filesystem::create_directory(dataDir); + std::string destinations = DataDirPath("destinations"); + if (boost::filesystem::exists(destinations)) + boost::filesystem::create_directory(destinations); + + return true; + } + + bool ReadDir(const std::string & path, std::vector & files) { + if (!boost::filesystem::exists(path)) + return false; + boost::filesystem::directory_iterator it(path); + boost::filesystem::directory_iterator end; + + for ( ; it != end; it++) { + if (!boost::filesystem::is_regular_file(it->status())) + continue; + files.push_back(it->path().string()); + } + + return true; + } + + bool Exists(const std::string & path) { + return boost::filesystem::exists(path); + } + + bool Remove(const std::string & path) { + if (!boost::filesystem::exists(path)) + return false; + return boost::filesystem::remove(path); + } + + void HashedStorage::SetPlace(const std::string &path) { + root = path + i2p::fs::dirSep + name; + } + + bool HashedStorage::Init(const char * chars, size_t count) { + if (!boost::filesystem::exists(root)) { + boost::filesystem::create_directory(root); + } + + for (size_t i = 0; i < count; i++) { + auto p = root + i2p::fs::dirSep + prefix1 + chars[i]; + if (boost::filesystem::exists(p)) + continue; + if (boost::filesystem::create_directory(p)) + continue; /* ^ throws exception on failure */ + return false; + } + return true; + } + + std::string HashedStorage::Path(const std::string & ident) const { + std::string safe_ident = ident; + std::replace(safe_ident.begin(), safe_ident.end(), '/', '-'); + std::replace(safe_ident.begin(), safe_ident.end(), '\\', '-'); + + std::stringstream t(""); + t << this->root << i2p::fs::dirSep; + t << prefix1 << safe_ident[0] << i2p::fs::dirSep; + t << prefix2 << safe_ident << "." << suffix; + + return t.str(); + } + + void HashedStorage::Remove(const std::string & ident) { + std::string path = Path(ident); + if (!boost::filesystem::exists(path)) + return; + boost::filesystem::remove(path); + } + + void HashedStorage::Traverse(std::vector & files) { + boost::filesystem::path p(root); + boost::filesystem::recursive_directory_iterator it(p); + boost::filesystem::recursive_directory_iterator end; + + for ( ; it != end; it++) { + if (!boost::filesystem::is_regular_file( it->status() )) + continue; + const std::string & t = it->path().string(); + files.push_back(t); + } + } +} // fs +} // i2p diff --git a/FS.h b/FS.h new file mode 100644 index 00000000..833258b9 --- /dev/null +++ b/FS.h @@ -0,0 +1,142 @@ +/* +* Copyright (c) 2013-2016, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef FS_H__ +#define FS_H__ + +#include +#include +#include +#include + +namespace i2p { +namespace fs { + extern std::string dirSep; + + /** + * @brief Class to work with NetDb & Router profiles + * + * Usage: + * + * const char alphabet[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}; + * auto h = HashedStorage("name", "y", "z-", ".txt"); + * h.SetPlace("/tmp/hs-test"); + * h.GetName() -> gives "name" + * h.GetRoot() -> gives "/tmp/hs-test/name" + * h.Init(alphabet, 8); <- creates needed dirs, 8 is size of alphabet + * h.Path("abcd"); <- returns /tmp/hs-test/name/ya/z-abcd.txt + * h.Remove("abcd"); <- removes /tmp/hs-test/name/ya/z-abcd.txt, if it exists + * std::vector files; + * h.Traverse(files); <- finds all files in storage and saves in given vector + */ + class HashedStorage { + protected: + std::string root; /**< path to storage with it's name included */ + std::string name; /**< name of the storage */ + std::string prefix1; /**< hashed directory prefix */ + std::string prefix2; /**< prefix of file in storage */ + std::string suffix; /**< suffix of file in storage (extension) */ + + public: + HashedStorage(const char *n, const char *p1, const char *p2, const char *s): + name(n), prefix1(p1), prefix2(p2), suffix(s) {}; + + /** create subdirs in storage */ + bool Init(const char* chars, size_t cnt); + const std::string & GetRoot() const { return this->root; } + const std::string & GetName() const { return this->name; } + /** set directory where to place storage directory */ + void SetPlace(const std::string & path); + /** path to file with given ident */ + std::string Path(const std::string & ident) const; + /** remove file by ident */ + void Remove(const std::string & ident); + /** find all files in storage and store list in provided vector */ + void Traverse(std::vector & files); + }; + + /** @brief Returns current application name, default 'i2pd' */ + const std::string & GetAppName (); + /** @brief Set applicaton name, affects autodetection of datadir */ + void SetAppName (const std::string& name); + + /** @brief Returns datadir path */ + const std::string & GetDataDir(); + + /** + * @brief Set datadir either from cmdline option or using autodetection + * @param cmdline_param Value of cmdline parameter --datadir= + * @param isService Value of cmdline parameter --service + * + * Examples of autodetected paths: + * + * Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\ + * Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\ + * Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/ + * Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/ + */ + void DetectDataDir(const std::string & cmdline_datadir, bool isService = false); + + /** + * @brief Create subdirectories inside datadir + */ + bool Init(); + + /** + * @brief Get list of files in directory + * @param path Path to directory + * @param files Vector to store found files + * @return true on success and false if directory not exists + */ + bool ReadDir(const std::string & path, std::vector & files); + + /** + * @brief Remove file with given path + * @param path Absolute path to file + * @return true on success, false if file not exists, throws exception on error + */ + bool Remove(const std::string & path); + + /** + * @brief Check existence of file + * @param path Absolute path to file + * @return true if file exists, false otherwise + */ + bool Exists(const std::string & path); + + template + void _ExpandPath(std::stringstream & path, T c) { + path << i2p::fs::dirSep << c; + } + + template + void _ExpandPath(std::stringstream & path, T c, Other ... other) { + _ExpandPath(path, c); + _ExpandPath(path, other ...); + } + + /** + * @brief Get path relative to datadir + * + * Examples (with datadir = "/tmp/i2pd"): + * + * i2p::fs::Path("test") -> '/tmp/i2pd/test' + * i2p::fs::Path("test", "file.txt") -> '/tmp/i2pd/test/file.txt' + */ + template + std::string DataDirPath(Other ... components) { + std::stringstream s(""); + s << i2p::fs::GetDataDir(); + _ExpandPath(s, components ...); + + return s.str(); + } +} // fs +} // i2p + +#endif // /* FS_H__ */ diff --git a/Family.cpp b/Family.cpp index d83adf2a..34406faa 100644 --- a/Family.cpp +++ b/Family.cpp @@ -1,7 +1,7 @@ #include -#include "util.h" #include #include +#include "FS.h" #include "Log.h" #include "Crypto.h" #include "Family.h" @@ -89,21 +89,24 @@ namespace data void Families::LoadCertificates () { - boost::filesystem::path familyDir = i2p::util::filesystem::GetCertificatesDir() / "family"; - - if (!boost::filesystem::exists (familyDir)) return; + std::string certDir = i2p::fs::DataDirPath("certificates", "family"); + std::vector files; int numCertificates = 0; - boost::filesystem::directory_iterator end; // empty - for (boost::filesystem::directory_iterator it (familyDir); it != end; ++it) - { - if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt") - { - LoadCertificate (it->path ().string ()); - numCertificates++; - } + + if (!i2p::fs::ReadDir(certDir, files)) { + LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir); + return; + } + + for (const std::string & file : files) { + if (file.compare(file.size() - 4, 4, ".crt") != 0) { + LogPrint(eLogWarning, "Family: ignoring file ", file); + continue; + } + LoadCertificate (file); + numCertificates++; } - if (numCertificates > 0) - LogPrint (eLogInfo, "Family: ", numCertificates, " certificates loaded"); + LogPrint (eLogInfo, "Family: ", numCertificates, " certificates loaded"); } bool Families::VerifyFamily (const std::string& family, const IdentHash& ident, @@ -124,10 +127,10 @@ namespace data std::string CreateFamilySignature (const std::string& family, const IdentHash& ident) { + auto filename = i2p::fs::DataDirPath("family", (family + ".key")); std::string sig; - auto filename = i2p::util::filesystem::GetDefaultDataDir() / "family" / (family + ".key"); SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ()); - int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.string ().c_str (), SSL_FILETYPE_PEM); + int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM); if (ret) { SSL * ssl = SSL_new (ctx); @@ -163,7 +166,7 @@ namespace data SSL_free (ssl); } else - LogPrint (eLogError, "Family: Can't open keys file ", filename.string ()); + LogPrint (eLogError, "Family: Can't open keys file: ", filename); SSL_CTX_free (ctx); return sig; } diff --git a/HTTPServer.cpp b/HTTPServer.cpp index f04b0b1c..85dbec42 100644 --- a/HTTPServer.cpp +++ b/HTTPServer.cpp @@ -4,6 +4,7 @@ #include #include #include "Base.h" +#include "FS.h" #include "Log.h" #include "Tunnel.h" #include "TransitTunnel.h" @@ -420,7 +421,7 @@ namespace util s << " (" << i2p::transport::transports.GetInBandwidth () <<" Bps)
\r\n"; s << "Sent: " << i2p::transport::transports.GetTotalSentBytes ()/1000 << "K"; s << " (" << i2p::transport::transports.GetOutBandwidth () <<" Bps)
\r\n"; - s << "Data path: " << i2p::util::filesystem::GetDataDir().string() << "
\r\n
\r\n"; + s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n
\r\n"; s << "Our external address:" << "
\r\n" ; for (auto& address : i2p::context.GetRouterInfo().GetAddresses()) { diff --git a/I2PControl.cpp b/I2PControl.cpp index 1ea6c8aa..907fa2fd 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -13,6 +13,7 @@ #include #endif +#include "FS.h" #include "Log.h" #include "Config.h" #include "NetDb.h" @@ -39,15 +40,12 @@ namespace client // certificate / keys std::string i2pcp_crt; i2p::config::GetOption("i2pcontrol.cert", i2pcp_crt); std::string i2pcp_key; i2p::config::GetOption("i2pcontrol.key", i2pcp_key); - auto path = GetPath (); - // TODO: move this to i2p::fs::expand + if (i2pcp_crt.at(0) != '/') - i2pcp_crt.insert(0, (path / "/").string()); + i2pcp_crt = i2p::fs::DataDirPath(i2pcp_crt); if (i2pcp_key.at(0) != '/') - i2pcp_key.insert(0, (path / "/").string()); - if (!boost::filesystem::exists (i2pcp_crt) || - !boost::filesystem::exists (i2pcp_key)) - { + i2pcp_key = i2p::fs::DataDirPath(i2pcp_key); + if (!i2p::fs::Exists (i2pcp_crt) || !i2p::fs::Exists (i2pcp_key)) { LogPrint (eLogInfo, "I2PControl: creating new certificate for control connection"); CreateCertificate (i2pcp_crt.c_str(), i2pcp_key.c_str()); } else { diff --git a/I2PControl.h b/I2PControl.h index 18592068..714d3aa5 100644 --- a/I2PControl.h +++ b/I2PControl.h @@ -12,8 +12,6 @@ #include #include #include -#include -#include "util.h" namespace i2p { @@ -52,7 +50,6 @@ namespace client void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf); - boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir(); }; void CreateCertificate (const char *crt_path, const char *key_path); private: diff --git a/NetDb.cpp b/NetDb.cpp index df464664..6401fcc8 100644 --- a/NetDb.cpp +++ b/NetDb.cpp @@ -1,10 +1,10 @@ #include -#include "I2PEndian.h" #include #include #include #include #include +#include "I2PEndian.h" #include "Base.h" #include "Log.h" #include "Timestamp.h" @@ -22,10 +22,9 @@ namespace i2p { namespace data { - const char NetDb::m_NetDbPath[] = "netDb"; NetDb netdb; - NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr) + NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat") { } @@ -37,6 +36,9 @@ namespace data void NetDb::Start () { + m_Storage.SetPlace(i2p::fs::GetDataDir()); + m_Storage.Init(i2p::data::GetBase64SubstitutionTable(), 64); + InitProfilesStorage (); m_Families.LoadCertificates (); Load (); if (m_RouterInfos.size () < 25) // reseed if # of router less than 50 @@ -273,30 +275,6 @@ namespace data return it->second->SetUnreachable (unreachable); } - // TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.) - bool NetDb::CreateNetDb(boost::filesystem::path directory) - { - LogPrint (eLogInfo, "NetDb: storage directory doesn't exist, trying to create it."); - if (!boost::filesystem::create_directory (directory)) - { - LogPrint (eLogError, "NetDb: failed to create directory ", directory); - return false; - } - - // list of chars might appear in base64 string - const char * chars = GetBase64SubstitutionTable (); // 64 bytes - for (int i = 0; i < 64; i++) - { - auto p = directory / (std::string ("r") + chars[i]); - if (!boost::filesystem::exists (p) && !boost::filesystem::create_directory (p)) - { - LogPrint (eLogError, "NetDb: failed to create directory ", p); - return false; - } - } - return true; - } - void NetDb::Reseed () { if (!m_Reseeder) @@ -311,92 +289,67 @@ namespace data LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts"); } - void NetDb::Load () + bool NetDb::LoadRouterInfo (const std::string & path) { - boost::filesystem::path p(i2p::util::filesystem::GetDataDir() / m_NetDbPath); - if (!boost::filesystem::exists (p)) + auto r = std::make_shared(path); + if (r->GetRouterIdentity () && !r->IsUnreachable () && + (!r->UsesIntroducer () || m_LastLoad < r->GetTimestamp () + 3600*1000LL)) // 1 hour { - // seems netDb doesn't exist yet - if (!CreateNetDb(p)) return; + r->DeleteBuffer (); + r->ClearProperties (); // properties are not used for regular routers + m_RouterInfos[r->GetIdentHash ()] = r; + if (r->IsFloodfill ()) + m_Floodfills.push_back (r); + } else { + LogPrint(eLogWarning, "NetDb: Can't load RI from ", path, ", delete"); + i2p::fs::Remove(path); } + return true; + } + + void NetDb::Load () + { // make sure we cleanup netDb from previous attempts m_RouterInfos.clear (); m_Floodfills.clear (); - // load routers now - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); - int numRouters = 0; - boost::filesystem::directory_iterator end; - for (boost::filesystem::directory_iterator it (p); it != end; ++it) - { - if (boost::filesystem::is_directory (it->status())) - { - for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1) - { -#if BOOST_VERSION > 10500 - const std::string& fullPath = it1->path().string(); -#else - const std::string& fullPath = it1->path(); -#endif - auto r = std::make_shared(fullPath); - if (r->GetRouterIdentity () && !r->IsUnreachable () && - (!r->UsesIntroducer () || ts < r->GetTimestamp () + 3600*1000LL)) // 1 hour - { - r->DeleteBuffer (); - r->ClearProperties (); // properties are not used for regular routers - m_RouterInfos[r->GetIdentHash ()] = r; - if (r->IsFloodfill ()) - m_Floodfills.push_back (r); - numRouters++; - } - else - { - if (boost::filesystem::exists (fullPath)) - boost::filesystem::remove (fullPath); - } - } - } - } - LogPrint (eLogInfo, "NetDb: ", numRouters, " routers loaded (", m_Floodfills.size (), " floodfils)"); + m_LastLoad = i2p::util::GetSecondsSinceEpoch(); + std::vector files; + m_Storage.Traverse(files); + for (auto path : files) + LoadRouterInfo(path); + + LogPrint (eLogInfo, "NetDb: ", m_RouterInfos.size(), " routers loaded (", m_Floodfills.size (), " floodfils)"); } void NetDb::SaveUpdated () { - auto GetFilePath = [](const boost::filesystem::path& directory, const RouterInfo * routerInfo) - { - std::string s(routerInfo->GetIdentHashBase64()); - return directory / (std::string("r") + s[0]) / ("routerInfo-" + s + ".dat"); - }; - - boost::filesystem::path fullDirectory (i2p::util::filesystem::GetDataDir() / m_NetDbPath); - int count = 0, deletedCount = 0; + int updatedCount = 0, deletedCount = 0; auto total = m_RouterInfos.size (); - uint64_t ts = i2p::util::GetMillisecondsSinceEpoch (); + uint64_t ts = i2p::util::GetMillisecondsSinceEpoch(); + for (auto it: m_RouterInfos) { - if (it.second->IsUpdated ()) - { - std::string f = GetFilePath(fullDirectory, it.second.get()).string(); - it.second->SaveToFile (f); + std::string ident = it.second->GetIdentHashBase64(); + std::string path = m_Storage.Path(ident); + if (it.second->IsUpdated ()) { + it.second->SaveToFile (path); it.second->SetUpdated (false); it.second->SetUnreachable (false); it.second->DeleteBuffer (); - count++; + updatedCount++; + continue; } - else - { + // find & mark unreachable routers + if (it.second->UsesIntroducer () && ts > it.second->GetTimestamp () + 3600*1000LL) { // RouterInfo expires after 1 hour if uses introducer - if (it.second->UsesIntroducer () && ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hour - it.second->SetUnreachable (true); - else if (total > 75 && ts > (i2p::context.GetStartupTime () + 600)*1000LL) // routers don't expire if less than 25 or uptime is less than 10 minutes - { - if (i2p::context.IsFloodfill ()) - { - if (ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hours - { - it.second->SetUnreachable (true); - total--; - } + it.second->SetUnreachable (true); + } else if (total > 75 && ts > (i2p::context.GetStartupTime () + 600)*1000LL) { + // routers don't expire if less than 25 or uptime is less than 10 minutes + if (i2p::context.IsFloodfill ()) { + if (ts > it.second->GetTimestamp () + 3600*1000LL) { // 1 hour + it.second->SetUnreachable (true); + total--; } else if (total > 2500) { @@ -414,50 +367,41 @@ namespace data total--; } } - else if (total > 120) - { - if (ts > it.second->GetTimestamp () + 72*3600*1000LL) // 72 hours - { - it.second->SetUnreachable (true); - total--; - } + } else if (total > 120) { + if (ts > it.second->GetTimestamp () + 72*3600*1000LL) { // 72 hours + it.second->SetUnreachable (true); + total--; } } - - if (it.second->IsUnreachable ()) - { - total--; - // delete RI file - if (boost::filesystem::exists (GetFilePath (fullDirectory, it.second.get ()))) - { - boost::filesystem::remove (GetFilePath (fullDirectory, it.second.get ())); - deletedCount++; - } - // delete from floodfills list - if (it.second->IsFloodfill ()) - { - std::unique_lock l(m_FloodfillsMutex); - m_Floodfills.remove (it.second); - } + } + + if (it.second->IsUnreachable ()) { + total--; + // delete RI file + m_Storage.Remove(ident); + deletedCount++; + // delete from floodfills list + if (it.second->IsFloodfill ()) { + std::unique_lock l(m_FloodfillsMutex); + m_Floodfills.remove (it.second); } - } - } - if (count > 0) - LogPrint (eLogInfo, "NetDb: ", count, " new/updated routers saved"); + } + } // m_RouterInfos iteration + if (updatedCount > 0) + LogPrint (eLogInfo, "NetDb: saved ", updatedCount, " new/updated routers"); if (deletedCount > 0) { - LogPrint (eLogDebug, "NetDb: ", deletedCount, " routers deleted"); + LogPrint (eLogInfo, "NetDb: deleting ", deletedCount, " unreachable routers"); // clean up RouterInfos table std::unique_lock l(m_RouterInfosMutex); for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();) { - if (it->second->IsUnreachable ()) - { + if (it->second->IsUnreachable ()) { it->second->SaveProfile (); it = m_RouterInfos.erase (it); + continue; } - else - it++; + it++; } } } diff --git a/NetDb.h b/NetDb.h index 1ec2ade5..7efbfcf2 100644 --- a/NetDb.h +++ b/NetDb.h @@ -8,8 +8,8 @@ #include #include #include -#include #include "Base.h" +#include "FS.h" #include "Queue.h" #include "I2NPProtocol.h" #include "RouterInfo.h" @@ -24,7 +24,6 @@ namespace i2p { namespace data { - class NetDb { public: @@ -71,8 +70,8 @@ namespace data private: - bool CreateNetDb(boost::filesystem::path directory); void Load (); + bool LoadRouterInfo (const std::string & path); void SaveUpdated (); void Run (); // exploratory thread void Explore (int numDestinations); @@ -92,17 +91,17 @@ namespace data std::list > m_Floodfills; bool m_IsRunning; + uint64_t m_LastLoad; std::thread * m_Thread; i2p::util::Queue > m_Queue; // of I2NPDatabaseStoreMsg GzipInflator m_Inflator; Reseeder * m_Reseeder; Families m_Families; + i2p::fs::HashedStorage m_Storage; friend class NetDbRequests; NetDbRequests m_Requests; - - static const char m_NetDbPath[]; }; extern NetDb netdb; diff --git a/Profiling.cpp b/Profiling.cpp index 9bfd4de9..be675502 100644 --- a/Profiling.cpp +++ b/Profiling.cpp @@ -1,8 +1,8 @@ -#include +#include #include #include #include "Base.h" -#include "util.h" +#include "FS.h" #include "Log.h" #include "Profiling.h" @@ -10,6 +10,8 @@ namespace i2p { namespace data { + i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt"); + RouterProfile::RouterProfile (const IdentHash& identHash): m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()), m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), @@ -41,101 +43,69 @@ namespace data boost::property_tree::ptree pt; pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime)); pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation); - pt.put_child (PEER_PROFILE_SECTION_USAGE, usage); + pt.put_child (PEER_PROFILE_SECTION_USAGE, usage); // save to file - auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY; - if (!boost::filesystem::exists (path)) - { - // Create directory is necessary - if (!boost::filesystem::create_directory (path)) - { - LogPrint (eLogError, "Failed to create directory ", path); - return; - } - const char * chars = GetBase64SubstitutionTable (); // 64 bytes - for (int i = 0; i < 64; i++) - { - auto path1 = path / (std::string ("p") + chars[i]); - if (!boost::filesystem::exists (path1) && !boost::filesystem::create_directory (path1)) - { - LogPrint (eLogError, "Failed to create directory ", path1); - return; - } - } - } - std::string base64 = m_IdentHash.ToBase64 (); - path = path / (std::string ("p") + base64[0]); - auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt"); - try - { - boost::property_tree::write_ini (filename.string (), pt); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ()); + std::string ident = m_IdentHash.ToBase64 (); + std::string path = m_ProfilesStorage.Path(ident); + + try { + boost::property_tree::write_ini (path, pt); + } catch (std::exception& ex) { + /* boost exception verbose enough */ + LogPrint (eLogError, "Profiling: ", ex.what ()); } - } + } void RouterProfile::Load () { - std::string base64 = m_IdentHash.ToBase64 (); - auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY; - path /= std::string ("p") + base64[0]; - auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt"); - if (boost::filesystem::exists (filename)) - { - boost::property_tree::ptree pt; - try - { - boost::property_tree::read_ini (filename.string (), pt); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ()); - return; - } - try - { - auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, ""); - if (t.length () > 0) - m_LastUpdateTime = boost::posix_time::time_from_string (t); - if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) - { - try - { - // read participations - auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION); - m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0); - m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0); - m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); - } - catch (boost::property_tree::ptree_bad_path& ex) - { - LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_PARTICIPATION); - } - try - { - // read usage - auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE); - m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); - m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); - } - catch (boost::property_tree::ptree_bad_path& ex) - { - LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE); - } + std::string ident = m_IdentHash.ToBase64 (); + std::string path = m_ProfilesStorage.Path(ident); + boost::property_tree::ptree pt; + + if (!i2p::fs::Exists(path)) { + LogPrint(eLogWarning, "Profiling: no profile yet for ", ident); + return; + } + + try { + boost::property_tree::read_ini (path, pt); + } catch (std::exception& ex) { + /* boost exception verbose enough */ + LogPrint (eLogError, "Profiling: ", ex.what ()); + return; + } + + try { + auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, ""); + if (t.length () > 0) + m_LastUpdateTime = boost::posix_time::time_from_string (t); + if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT) { + try { + // read participations + auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION); + m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0); + m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0); + m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0); + } catch (boost::property_tree::ptree_bad_path& ex) { + LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident); } - else - *this = RouterProfile (m_IdentHash); - } - catch (std::exception& ex) - { - LogPrint (eLogError, "Can't read profile ", base64, " :", ex.what ()); - } - } - } - + try { + // read usage + auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE); + m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0); + m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0); + } catch (boost::property_tree::ptree_bad_path& ex) { + LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident); + } + } else { + *this = RouterProfile (m_IdentHash); + } + } catch (std::exception& ex) { + LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ()); + } + } + void RouterProfile::TunnelBuildResponse (uint8_t ret) { UpdateTime (); @@ -184,31 +154,29 @@ namespace data return profile; } + void InitProfilesStorage () + { + m_ProfilesStorage.SetPlace(i2p::fs::GetDataDir()); + m_ProfilesStorage.Init(i2p::data::GetBase64SubstitutionTable(), 64); + } + void DeleteObsoleteProfiles () { - int num = 0; - auto ts = boost::posix_time::second_clock::local_time(); - boost::filesystem::path p (i2p::util::filesystem::GetDataDir()/PEER_PROFILES_DIRECTORY); - if (boost::filesystem::exists (p)) - { - boost::filesystem::directory_iterator end; - for (boost::filesystem::directory_iterator it (p); it != end; ++it) - { - if (boost::filesystem::is_directory (it->status())) - { - for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1) - { - auto lastModified = boost::posix_time::from_time_t (boost::filesystem::last_write_time (it1->path ())); - if ((ts - lastModified).hours () >= PEER_PROFILE_EXPIRATION_TIMEOUT) - { - boost::filesystem::remove (it1->path ()); - num++; - } - } - } - } + struct stat st; + std::time_t now = std::time(nullptr); + + std::vector files; + m_ProfilesStorage.Traverse(files); + for (auto path: files) { + if (stat(path.c_str(), &st) != 0) { + LogPrint(eLogWarning, "Profiling: Can't stat(): ", path); + continue; + } + if (((now - st.st_mtime) / 3600) >= PEER_PROFILE_EXPIRATION_TIMEOUT) { + LogPrint(eLogDebug, "Profiling: removing expired peer profile: ", path); + i2p::fs::Remove(path); + } } - LogPrint (eLogInfo, num, " obsolete profiles deleted"); - } + } } } diff --git a/Profiling.h b/Profiling.h index 0690d6cb..26d5c2f7 100644 --- a/Profiling.h +++ b/Profiling.h @@ -9,8 +9,6 @@ namespace i2p { namespace data { - const char PEER_PROFILES_DIRECTORY[] = "peerProfiles"; - const char PEER_PROFILE_PREFIX[] = "profile-"; // sections const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation"; const char PEER_PROFILE_SECTION_USAGE[] = "usage"; @@ -62,6 +60,7 @@ namespace data }; std::shared_ptr GetRouterProfile (const IdentHash& identHash); + void InitProfilesStorage (); void DeleteObsoleteProfiles (); } } diff --git a/Reseed.cpp b/Reseed.cpp index 768cd7c6..380babb5 100644 --- a/Reseed.cpp +++ b/Reseed.cpp @@ -2,15 +2,16 @@ #include #include #include -#include #include #include #include #include #include #include + #include "I2PEndian.h" #include "Reseed.h" +#include "FS.h" #include "Log.h" #include "Identity.h" #include "Crypto.h" @@ -347,23 +348,22 @@ namespace data void Reseeder::LoadCertificates () { - boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed"; - - if (!boost::filesystem::exists (reseedDir)) - { - LogPrint (eLogWarning, "Reseed: certificates not loaded, ", reseedDir, " doesn't exist"); + std::string certDir = i2p::fs::DataDirPath("certificates", "reseed"); + std::vector files; + int numCertificates = 0; + + if (!i2p::fs::ReadDir(certDir, files)) { + LogPrint(eLogWarning, "Reseed: Can't load reseed certificates from ", certDir); return; } - int numCertificates = 0; - boost::filesystem::directory_iterator end; // empty - for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it) - { - if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt") - { - LoadCertificate (it->path ().string ()); - numCertificates++; - } + for (const std::string & file : files) { + if (file.compare(file.size() - 4, 4, ".crt") != 0) { + LogPrint(eLogWarning, "Reseed: ignoring file ", file); + continue; + } + LoadCertificate (file); + numCertificates++; } LogPrint (eLogInfo, "Reseed: ", numCertificates, " certificates loaded"); } diff --git a/RouterContext.cpp b/RouterContext.cpp index 070e52ad..b3774d89 100644 --- a/RouterContext.cpp +++ b/RouterContext.cpp @@ -5,6 +5,7 @@ #include "Timestamp.h" #include "I2NPProtocol.h" #include "NetDb.h" +#include "FS.h" #include "util.h" #include "version.h" #include "Log.h" @@ -65,7 +66,7 @@ namespace i2p void RouterContext::UpdateRouterInfo () { m_RouterInfo.CreateBuffer (m_Keys); - m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO)); + m_RouterInfo.SaveToFile (i2p::fs::DataDirPath (ROUTER_INFO)); m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch (); } @@ -310,7 +311,7 @@ namespace i2p bool RouterContext::Load () { - std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ifstream::in); + std::ifstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ifstream::in | std::ifstream::binary); if (!fk.is_open ()) return false; fk.seekg (0, std::ios::end); size_t len = fk.tellg(); @@ -330,7 +331,7 @@ namespace i2p delete[] buf; } - i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO + i2p::data::RouterInfo routerInfo(i2p::fs::DataDirPath (ROUTER_INFO)); // TODO m_RouterInfo.SetRouterIdentity (GetIdentity ()); m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ()); m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION); @@ -349,7 +350,7 @@ namespace i2p void RouterContext::SaveKeys () { // save in the same format as .dat files - std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out); + std::ofstream fk (i2p::fs::DataDirPath (ROUTER_KEYS), std::ofstream::binary | std::ofstream::out); size_t len = m_Keys.GetFullLen (); uint8_t * buf = new uint8_t[len]; m_Keys.ToBuffer (buf, len); diff --git a/api.cpp b/api.cpp index 0f4e1799..3e037c02 100644 --- a/api.cpp +++ b/api.cpp @@ -9,7 +9,7 @@ #include "Identity.h" #include "Destination.h" #include "Crypto.h" -#include "util.h" +#include "FS.h" #include "api.h" namespace i2p @@ -18,10 +18,16 @@ namespace api { void InitI2P (int argc, char* argv[], const char * appName) { - i2p::util::filesystem::SetAppName (appName); i2p::config::Init (); i2p::config::ParseCmdline (argc, argv); i2p::config::Finalize (); + + std::string datadir; i2p::config::GetOption("datadir", datadir); + + i2p::fs::SetAppName (appName); + i2p::fs::DetectDataDir(datadir, false); + i2p::fs::Init(); + i2p::crypto::InitCrypto (); i2p::context.Init (); } @@ -36,7 +42,7 @@ namespace api if (logStream) StartLog (logStream); else - StartLog (i2p::util::filesystem::GetFullPath (i2p::util::filesystem::GetAppName () + ".log")); + StartLog (i2p::fs::DataDirPath (i2p::fs::GetAppName () + ".log")); LogPrint(eLogInfo, "API: starting NetDB"); i2p::data::netdb.Start(); LogPrint(eLogInfo, "API: starting Transports"); diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index f7e884f9..577dcc66 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -23,6 +23,7 @@ set (LIBI2PD_SRC "${CMAKE_SOURCE_DIR}/I2NPProtocol.cpp" "${CMAKE_SOURCE_DIR}/Identity.cpp" "${CMAKE_SOURCE_DIR}/LeaseSet.cpp" + "${CMAKE_SOURCE_DIR}/FS.cpp" "${CMAKE_SOURCE_DIR}/Log.cpp" "${CMAKE_SOURCE_DIR}/NTCPSession.cpp" "${CMAKE_SOURCE_DIR}/NetDbRequests.cpp" diff --git a/filelist.mk b/filelist.mk index 166be50e..c35edcf0 100644 --- a/filelist.mk +++ b/filelist.mk @@ -4,8 +4,8 @@ LIB_SRC = \ Reseed.cpp RouterContext.cpp RouterInfo.cpp Signature.cpp SSU.cpp \ SSUSession.cpp SSUData.cpp Streaming.cpp Identity.cpp TransitTunnel.cpp \ Transports.cpp Tunnel.cpp TunnelEndpoint.cpp TunnelPool.cpp TunnelGateway.cpp \ - Destination.cpp Base.cpp I2PEndian.cpp Config.cpp Family.cpp util.cpp \ - api.cpp + Destination.cpp Base.cpp I2PEndian.cpp FS.cpp Config.cpp Family.cpp \ + util.cpp api.cpp LIB_CLIENT_SRC = \ AddressBook.cpp BOB.cpp ClientContext.cpp I2PTunnel.cpp I2PService.cpp \ diff --git a/util.cpp b/util.cpp index e25ddb3f..bd77e682 100644 --- a/util.cpp +++ b/util.cpp @@ -6,13 +6,7 @@ #include #include #include -#include -#include -#include #include -#include -#include -#include #include "Config.h" #include "util.h" #include "Log.h" @@ -67,131 +61,6 @@ namespace i2p { namespace util { -namespace filesystem -{ - std::string appName ("i2pd"); - - void SetAppName (const std::string& name) - { - appName = name; - } - - std::string GetAppName () - { - return appName; - } - - const boost::filesystem::path &GetDataDir() - { - static boost::filesystem::path path; - - // TODO: datadir parameter is useless because GetDataDir is called before OptionParser - // and mapArgs is not initialized yet - /* - std::string datadir; i2p::config::GetOption("datadir", datadir); - if (datadir != "") - path = boost::filesystem::system_complete(datadir); - else */ - path = GetDefaultDataDir(); - - if (!boost::filesystem::exists( path )) - { - // Create data directory - if (!boost::filesystem::create_directory( path )) - { - LogPrint(eLogError, "FS: Failed to create data directory!"); - path = ""; - return path; - } - } - if (!boost::filesystem::is_directory(path)) - path = GetDefaultDataDir(); - return path; - } - - std::string GetFullPath (const std::string& filename) - { - std::string fullPath = GetDataDir ().string (); -#ifndef _WIN32 - fullPath.append ("/"); -#else - fullPath.append ("\\"); -#endif - fullPath.append (filename); - return fullPath; - } - - boost::filesystem::path GetConfigFile() - { - std::string config; i2p::config::GetOption("conf", config); - if (config != "") { - /* config file set with cmdline */ - boost::filesystem::path path(config); - return path; - } - /* else - try autodetect */ - boost::filesystem::path path("i2p.conf"); - path = GetDataDir() / path; - if (!boost::filesystem::exists(path)) - path = ""; /* reset */ - return path; - } - - boost::filesystem::path GetTunnelsConfigFile() - { - std::string tunconf; i2p::config::GetOption("tunconf", tunconf); - if (tunconf != "") { - /* config file set with cmdline */ - boost::filesystem::path path(tunconf); - return path; - } - /* else - try autodetect */ - boost::filesystem::path path("tunnels.cfg"); - path = GetDataDir() / path; - if (!boost::filesystem::exists(path)) - path = ""; /* reset */ - return path; - } - - boost::filesystem::path GetDefaultDataDir() - { - // Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd - // Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd - // Mac: ~/Library/Application Support/i2pd - // Unix: ~/.i2pd or /var/lib/i2pd is system=1 -#ifdef WIN32 - // Windows - char localAppData[MAX_PATH]; - SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData); - return boost::filesystem::path(std::string(localAppData) + "\\" + appName); -#else /* UNIX */ - bool service; i2p::config::GetOption("service", service); - if (service) // use system folder - return boost::filesystem::path(std::string ("/var/lib/") + appName); - boost::filesystem::path pathRet; - char* pszHome = getenv("HOME"); - if (pszHome == NULL || strlen(pszHome) == 0) - pathRet = boost::filesystem::path("/"); - else - pathRet = boost::filesystem::path(pszHome); -#ifdef MAC_OSX - // Mac - pathRet /= "Library/Application Support"; - boost::filesystem::create_directory(pathRet); - return pathRet / appName; -#else /* Other Unix */ - // Unix - return pathRet / (std::string (".") + appName); -#endif -#endif /* UNIX */ - } - - boost::filesystem::path GetCertificatesDir() - { - return GetDataDir () / "certificates"; - } -} - namespace http { std::string GetHttpContent (std::istream& response) @@ -212,7 +81,7 @@ namespace http if (colon != std::string::npos) { std::string field = header.substr (0, colon); - boost::to_lower (field); + std::transform(field.begin(), field.end(), field.begin(), ::tolower); if (field == i2p::util::http::TRANSFER_ENCODING) isChunked = (header.find ("chunked", colon + 1) != std::string::npos); } diff --git a/util.h b/util.h index 81f178f8..13200591 100644 --- a/util.h +++ b/util.h @@ -5,28 +5,11 @@ #include #include #include -#include -#include - -#define PAIRTYPE(t1, t2) std::pair namespace i2p { namespace util { - namespace filesystem - { - void SetAppName (const std::string& name); - std::string GetAppName (); - - const boost::filesystem::path &GetDataDir(); - std::string GetFullPath (const std::string& filename); - boost::filesystem::path GetDefaultDataDir(); - boost::filesystem::path GetConfigFile(); - boost::filesystem::path GetTunnelsConfigFile(); - boost::filesystem::path GetCertificatesDir(); - } - namespace http { // in (lower case)