/* * Copyright 2011-2014 Emmanuel Engelhart * * 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 3 of the License, or * 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., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #include "tools/pathTools.h" #ifdef __APPLE__ #include #include #elif _WIN32 #include #include #include "shlwapi.h" #define getcwd _getcwd // stupid MSFT "deprecation" warning #endif #include "tools/stringTools.h" #include #include #include #include #include #include #include #include #ifdef _WIN32 const std::string SEPARATOR("\\"); #else const std::string SEPARATOR("/"); #include #endif #include #ifndef PATH_MAX #define PATH_MAX 1024 #endif bool isRelativePath(const std::string& path) { #ifdef _WIN32 return path.empty() || path.substr(1, 2) == ":\\" ? false : true; #else return path.empty() || path.substr(0, 1) == "/" ? false : true; #endif } std::string computeRelativePath(const std::string& path, const std::string& absolutePath) { std::vector pathParts = kiwix::split(path, SEPARATOR); std::vector absolutePathParts = kiwix::split(absolutePath, SEPARATOR); unsigned int commonCount = 0; while (commonCount < pathParts.size() && commonCount < absolutePathParts.size() && pathParts[commonCount] == absolutePathParts[commonCount]) { commonCount++; } std::string relativePath; #ifdef _WIN32 /* On Windows you have a token more because the root is represented by a letter */ if (commonCount == 0) { relativePath = ".." + SEPARATOR; } #endif for (unsigned int i = commonCount; i < pathParts.size(); i++) { relativePath += ".." + SEPARATOR; } for (unsigned int i = commonCount; i < absolutePathParts.size(); i++) { relativePath += absolutePathParts[i]; relativePath += i + 1 < absolutePathParts.size() ? SEPARATOR : ""; } return relativePath; } #ifdef _WIN32 # define STRTOK strtok_s #else # define STRTOK strtok_r #endif /* Warning: the relative path must be with slashes */ std::string computeAbsolutePath(const std::string& path, const std::string& relativePath) { std::string absolutePath; if (path.empty()) { char* path = NULL; size_t size = 0; #ifdef _WIN32 path = _getcwd(path, size); #else path = getcwd(path, size); #endif absolutePath = std::string(path) + SEPARATOR; } else { absolutePath = path.substr(path.length() - 1, 1) == SEPARATOR ? path : path + SEPARATOR; } #if _WIN32 char* cRelativePath = _strdup(relativePath.c_str()); #else char* cRelativePath = strdup(relativePath.c_str()); #endif char* saveptr = nullptr; char* token = STRTOK(cRelativePath, "/", &saveptr); while (token != NULL) { if (std::string(token) == "..") { absolutePath = removeLastPathElement(absolutePath, true, false); token = STRTOK(NULL, "/", &saveptr); } else if (strcmp(token, ".") && strcmp(token, "")) { absolutePath += std::string(token); token = STRTOK(NULL, "/", &saveptr); if (token != NULL) { absolutePath += SEPARATOR; } } else { token = STRTOK(NULL, "/", &saveptr); } } free(cRelativePath); return absolutePath; } std::string removeLastPathElement(const std::string& path, const bool removePreSeparator, const bool removePostSeparator) { std::string newPath = path; size_t offset = newPath.find_last_of(SEPARATOR); if (removePreSeparator && #ifndef _WIN32 offset != newPath.find_first_of(SEPARATOR) && #endif offset == newPath.length() - 1) { newPath = newPath.substr(0, offset); offset = newPath.find_last_of(SEPARATOR); } newPath = removePostSeparator ? newPath.substr(0, offset) : newPath.substr(0, offset + 1); return newPath; } std::string appendToDirectory(const std::string& directoryPath, const std::string& filename) { std::string newPath = directoryPath + SEPARATOR + filename; return newPath; } std::string getLastPathElement(const std::string& path) { return path.substr(path.find_last_of(SEPARATOR) + 1); } unsigned int getFileSize(const std::string& path) { #ifdef _WIN32 struct _stat filestatus; _stat(path.c_str(), &filestatus); #else struct stat filestatus; stat(path.c_str(), &filestatus); #endif return filestatus.st_size / 1024; } std::string getFileSizeAsString(const std::string& path) { std::ostringstream convert; convert << getFileSize(path); return convert.str(); } std::string getFileContent(const std::string& path) { std::ifstream f(path, std::ios::in|std::ios::ate); std::string content; if (f.is_open()) { auto size = f.tellg(); content.reserve(size); f.seekg(0, std::ios::beg); content.assign((std::istreambuf_iterator(f)), std::istreambuf_iterator()); } return content; } bool fileExists(const std::string& path) { #ifdef _WIN32 return PathFileExists(path.c_str()); #else bool flag = false; std::fstream fin; fin.open(path.c_str(), std::ios::in); if (fin.is_open()) { flag = true; } fin.close(); return flag; #endif } bool makeDirectory(const std::string& path) { #ifdef _WIN32 int status = _mkdir(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 char cbase[MAX_PATH]; char ctmp[MAX_PATH]; GetTempPath(MAX_PATH-14, cbase); // This create a file for us, ensure it is unique. // So we need to delete it and create the directory using the same name. GetTempFileName(cbase, "kiwix", 0, ctmp); DeleteFile(ctmp); _mkdir(ctmp); return std::string(ctmp); #else char _template_array[] = {"/tmp/kiwix-lib_XXXXXX"}; std::string dir = mkdtemp(_template_array); return dir; #endif } /* Try to create a link and if does not work then make a copy */ bool copyFile(const std::string& sourcePath, const std::string& destPath) { try { #ifndef _WIN32 if (link(sourcePath.c_str(), destPath.c_str()) != 0) { #endif std::ifstream infile(sourcePath.c_str(), std::ios_base::binary); std::ofstream outfile(destPath.c_str(), std::ios_base::binary); outfile << infile.rdbuf(); #ifndef _WIN32 } #endif } catch (std::exception& e) { std::cerr << e.what() << std::endl; return false; } return true; } std::string getExecutablePath() { char binRootPath[PATH_MAX]; char* cAppImage = ::getenv("APPIMAGE"); if (cAppImage) { char* cArgv0 = ::getenv("ARGV0"); char* cOwd = ::getenv("OWD"); if (!cArgv0 && !cOwd) { // Nothing to clean, goto in the same function... // This is silly but .. ok.. // And we should pass here, if APPIMAGE is set ARGV0 and OWD should also // (except if there is a bug in appimage) goto normal; } return appendToDirectory(cOwd, cArgv0); } normal: #ifdef _WIN32 GetModuleFileName(NULL, binRootPath, PATH_MAX); return std::string(binRootPath); #elif __APPLE__ uint32_t max = (uint32_t)PATH_MAX; _NSGetExecutablePath(binRootPath, &max); return std::string(binRootPath); #else ssize_t size = readlink("/proc/self/exe", binRootPath, PATH_MAX); if (size != -1) { return std::string(binRootPath, size); } #endif return ""; } bool writeTextFile(const std::string& path, const std::string& content) { std::ofstream file; file.open(path.c_str()); file << content; file.close(); return true; } std::string getCurrentDirectory() { char* a_cwd = getcwd(NULL, 0); std::string s_cwd(a_cwd); free(a_cwd); return s_cwd; } std::string getDataDirectory() { #ifdef _WIN32 char* cDataDir = ::getenv("APPDATA"); #else char* cDataDir = ::getenv("KIWIX_DATA_DIR"); #endif std::string dataDir = cDataDir==nullptr ? "" : cDataDir; if (!dataDir.empty()) return dataDir; #ifdef _WIN32 cDataDir = ::getenv("USERPROFILE"); dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir; #else cDataDir = ::getenv("XDG_DATA_HOME"); dataDir = cDataDir==nullptr ? "" : cDataDir; if (dataDir.empty()) { cDataDir = ::getenv("HOME"); dataDir = cDataDir==nullptr ? getCurrentDirectory() : cDataDir; dataDir = appendToDirectory(dataDir, ".local"); dataDir = appendToDirectory(dataDir, "share"); } #endif return appendToDirectory(dataDir, "kiwix"); } static std::map extMimeTypes = { { "html", "text/html"}, { "htm", "text/html"}, { "png", "image/png"}, { "tiff", "image/tiff"}, { "tif", "image/tiff"}, { "jpeg", "image/jpeg"}, { "jpg", "image/jpeg"}, { "gif", "image/gif"}, { "svg", "image/svg+xml"}, { "txt", "text/plain"}, { "xml", "text/xml"}, { "pdf", "application/pdf"}, { "ogg", "application/ogg"}, { "js", "application/javascript"}, { "css", "text/css"}, { "otf", "application/vnd.ms-opentype"}, { "ttf", "application/font-ttf"}, { "woff", "application/font-woff"}, { "vtt", "text/vtt"} }; /* Try to get the mimeType from the file extension */ std::string getMimeTypeForFile(const std::string& filename) { std::string mimeType = "text/plain"; auto pos = filename.find_last_of("."); if (pos != std::string::npos) { std::string extension = filename.substr(pos + 1); auto it = extMimeTypes.find(extension); if (it != extMimeTypes.end()) { mimeType = it->second; } else { it = extMimeTypes.find(kiwix::lcAll(extension)); if (it != extMimeTypes.end()) { mimeType = it->second; } } } return mimeType; }