diff --git a/include/downloader.h b/include/downloader.h index cf42e726f..4cc7b7730 100644 --- a/include/downloader.h +++ b/include/downloader.h @@ -172,12 +172,7 @@ class Downloader typedef std::vector> Options; public: // functions - /* - * Create a new Downloader object. - * - * @param sessionFileDir: The directory where aria2 will store its session file. - */ - explicit Downloader(std::string sessionFileDir); + Downloader(); virtual ~Downloader(); void close(); @@ -196,11 +191,10 @@ class Downloader * User should call `update` on the returned `Download` to have an accurate status. * * @param uri: The uri of the thing to download. - * @param downloadDir: The download directory where the thing should be stored (takes precedence over any "dir" in `options`). * @param options: A series of pair to pass to aria. * @return: The newly created Download. */ - std::shared_ptr startDownload(const std::string& uri, const std::string& downloadDir, Options options = {}); + std::shared_ptr startDownload(const std::string& uri, const Options& options = {}); /** * Get a download corrsponding to a download id (did) diff --git a/include/tools.h b/include/tools.h index 52a6af661..13931e876 100644 --- a/include/tools.h +++ b/include/tools.h @@ -45,6 +45,32 @@ typedef std::vector FeedCategories; */ std::string getCurrentDirectory(); +/** + * Return the data directory + * + * The data directory is the default directory where downloaded files + * should be saved (it can be overriden via the options parameter of + * `kiwix::Downloader::startDownload()`). + * + * Its path can vary and is determined as follows: + * + * * `$KIWIX_DATA_DIR` if `$KIWIX_DATA_DIR` environment variable set, *otherwise...* + * * On Windows: + * + * * `$APPDATA/kiwix` if environment variable `$APPDATA` set, *otherwise...* + * * `$USERPROFILE/kiwix` if environment variable `$USERPROFILE` set, *otherwise...* + * + * * On other Operating Systems: + * + * * `$XDG_DATA_HOME/kiwix` if environment variable `$XDG_DATA_HOME` set, *otherwise...* + * * `$HOME/.local/share/kiwx` if environment variable `$HOME` set, *otherwise...* + * + * * Current working directory. + * + * @return the path of the data directory (UTF-8 encoded) + */ +std::string getDataDirectory(); + /** Return the path of the executable * * Some application may be packaged in auto extractible archive (Appimage) and the diff --git a/src/aria2.cpp b/src/aria2.cpp index d7fbbf84c..206a3032e 100644 --- a/src/aria2.cpp +++ b/src/aria2.cpp @@ -55,15 +55,18 @@ void pauseAnyActiveDownloads(const std::string& ariaSessionFilePath) } // unnamed namespace -Aria2::Aria2(std::string sessionFileDir): +Aria2::Aria2(): mp_aria(nullptr), m_port(42042), m_secret(getNewRpcSecret()) { + m_downloadDir = getDataDirectory(); + makeDirectory(m_downloadDir); std::vector callCmd; std::string rpc_port = "--rpc-listen-port=" + to_string(m_port); - std::string session_file = appendToDirectory(sessionFileDir, "kiwix.session"); + std::string download_dir = "--dir=" + getDataDirectory(); + std::string session_file = appendToDirectory(getDataDirectory(), "kiwix.session"); pauseAnyActiveDownloads(session_file); std::string session = "--save-session=" + session_file; std::string inputFile = "--input-file=" + session_file; @@ -91,6 +94,7 @@ Aria2::Aria2(std::string sessionFileDir): callCmd.push_back("--enable-rpc"); callCmd.push_back(rpc_secret.c_str()); callCmd.push_back(rpc_port.c_str()); + callCmd.push_back(download_dir.c_str()); if (fileReadable(session_file)) { callCmd.push_back(inputFile.c_str()); } diff --git a/src/aria2.h b/src/aria2.h index e7b5ed259..f6cd633b8 100644 --- a/src/aria2.h +++ b/src/aria2.h @@ -22,10 +22,11 @@ class Aria2 std::unique_ptr mp_aria; int m_port; std::string m_secret; + std::string m_downloadDir; std::string doRequest(const MethodCall& methodCall); public: - explicit Aria2(std::string sessionFileDir); + Aria2(); virtual ~Aria2() = default; void close(); diff --git a/src/downloader.cpp b/src/downloader.cpp index 380523f5e..5193c51de 100644 --- a/src/downloader.cpp +++ b/src/downloader.cpp @@ -125,8 +125,8 @@ void Download::cancelDownload() } /* Constructor */ -Downloader::Downloader(std::string sessionFileDir) : - mp_aria(new Aria2(sessionFileDir)) +Downloader::Downloader() : + mp_aria(new Aria2()) { try { for (auto gid : mp_aria->tellWaiting()) { @@ -209,13 +209,9 @@ bool downloadCanBeReused(const Download& d, } // unnamed namespace -std::shared_ptr Downloader::startDownload(const std::string& uri, const std::string& downloadDir, Options options) +std::shared_ptr Downloader::startDownload(const std::string& uri, const Options& options) { std::unique_lock lock(m_lock); - options.erase(std::remove_if(options.begin(), options.end(), [](const auto& option) { - return option.first == "dir"; - }), options.end()); - options.push_back({"dir", downloadDir}); for (auto& p: m_knownDownloads) { auto& d = p.second; if ( downloadCanBeReused(*d, uri, options) ) diff --git a/src/tools/pathTools.cpp b/src/tools/pathTools.cpp index ae11dcd0e..54f513b0a 100644 --- a/src/tools/pathTools.cpp +++ b/src/tools/pathTools.cpp @@ -320,6 +320,16 @@ bool kiwix::fileReadable(const std::string& path) #endif } +bool makeDirectory(const std::string& path) +{ +#ifdef _WIN32 + int status = _wmkdir(Utf8ToWide(path).c_str()); +#else + int status = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +#endif + return status == 0; +} + std::string makeTmpDirectory() { #ifdef _WIN32 @@ -428,6 +438,52 @@ std::string kiwix::getCurrentDirectory() return ret; } +std::string kiwix::getDataDirectory() +{ +// Try to get the dataDir from the `KIWIX_DATA_DIR` env var +#ifdef _WIN32 + wchar_t* cDataDir = ::_wgetenv(L"KIWIX_DATA_DIR"); + if (cDataDir != nullptr) { + return WideToUtf8(cDataDir); + } +#else + char* cDataDir = ::getenv("KIWIX_DATA_DIR"); + if (cDataDir != nullptr) { + return cDataDir; + } +#endif + +// Compute the dataDir from the user directory. + std::string dataDir; +#ifdef _WIN32 + cDataDir = ::_wgetenv(L"APPDATA"); + if (cDataDir == nullptr) + cDataDir = ::_wgetenv(L"USERPROFILE"); + if (cDataDir != nullptr) + dataDir = WideToUtf8(cDataDir); +#else + cDataDir = ::getenv("XDG_DATA_HOME"); + if (cDataDir != nullptr) { + dataDir = cDataDir; + } else { + cDataDir = ::getenv("HOME"); + if (cDataDir != nullptr) { + dataDir = cDataDir; + dataDir = appendToDirectory(dataDir, ".local"); + dataDir = appendToDirectory(dataDir, "share"); + } + } +#endif + if (!dataDir.empty()) { + dataDir = appendToDirectory(dataDir, "kiwix"); + makeDirectory(dataDir); + return dataDir; + } + +// Let's use the currentDirectory + return getCurrentDirectory(); +} + static std::map extMimeTypes = { { "html", "text/html"}, { "htm", "text/html"}, diff --git a/src/tools/pathTools.h b/src/tools/pathTools.h index 0dc4286b3..82c2a8678 100644 --- a/src/tools/pathTools.h +++ b/src/tools/pathTools.h @@ -29,6 +29,7 @@ std::wstring Utf8ToWide(const std::string& str); unsigned int getFileSize(const std::string& path); std::string getFileSizeAsString(const std::string& path); +bool makeDirectory(const std::string& path); std::string makeTmpDirectory(); bool copyFile(const std::string& sourcePath, const std::string& destPath); bool writeTextFile(const std::string& path, const std::string& content);