diff --git a/CMakeLists.txt b/CMakeLists.txt index 31ee8d4..c020d50 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ set(SOURCES ${CMAKE_SOURCE_DIR}/src/main.cpp ${CMAKE_SOURCE_DIR}/src/handler/contextMenu.cpp ${CMAKE_SOURCE_DIR}/src/handler/eventHandler.cpp ${CMAKE_SOURCE_DIR}/src/handler/mainMenu.cpp + ${CMAKE_SOURCE_DIR}/src/handler/fileHandler.cpp ${CMAKE_SOURCE_DIR}/src/ui/listView.cpp ${CMAKE_SOURCE_DIR}/src/ui/listViewEntry.cpp ${CMAKE_SOURCE_DIR}/src/ui/webDAVView/webDAVView.cpp @@ -59,6 +60,7 @@ set(SOURCES ${CMAKE_SOURCE_DIR}/src/main.cpp ${CMAKE_SOURCE_DIR}/src/ui/loginView/loginView.cpp ${CMAKE_SOURCE_DIR}/src/ui/fileView/fileView.cpp ${CMAKE_SOURCE_DIR}/src/ui/fileView/fileViewEntry.cpp + ${CMAKE_SOURCE_DIR}/src/ui/excludeFileView/excludeFileView.cpp ${CMAKE_SOURCE_DIR}/src/util/util.cpp ${CMAKE_SOURCE_DIR}/src/util/log.cpp ${CMAKE_SOURCE_DIR}/src/api/webDAV.cpp @@ -78,10 +80,12 @@ include_directories( ${CMAKE_SOURCE_DIR}/src/ui/webDAVView/ ${CMAKE_SOURCE_DIR}/src/ui/fileView/ ${CMAKE_SOURCE_DIR}/src/ui/loginView/ + ${CMAKE_SOURCE_DIR}/src/ui/excludeFileView/ ${CMAKE_SOURCE_DIR}/src/api/ ) TARGET_LINK_LIBRARIES (Nextcloud.app PRIVATE inkview freetype curl sqlite3 stdc++fs) +target_compile_definitions(Nextcloud.app PRIVATE DBVERSION=2 PROGRAMVERSION="1.02") INSTALL (TARGETS Nextcloud.app) diff --git a/src/api/fileBrowser.cpp b/src/api/fileBrowser.cpp index 1aa0245..a212438 100644 --- a/src/api/fileBrowser.cpp +++ b/src/api/fileBrowser.cpp @@ -9,6 +9,8 @@ #include "fileBrowser.h" #include "inkview.h" +#include "fileHandler.h" +#include "log.h" #include #include @@ -18,6 +20,7 @@ using std::vector; namespace fs = std::experimental::filesystem; +std::shared_ptr FileBrowser::_fileHandler = std::shared_ptr(new FileHandler()); std::vector FileBrowser::getFileStructure(const std::string &path, const bool includeFiles, const bool includeHeader) { string localPath = path; @@ -38,6 +41,7 @@ std::vector FileBrowser::getFileStructure(const std::string &path, con items.push_back(temp); } + const int storageLocationLength = _fileHandler->getStorageLocation().length(); if (iv_access(localPath.c_str(), R_OK) == 0) { for (const auto &entry : fs::directory_iterator(localPath)) @@ -46,20 +50,27 @@ std::vector FileBrowser::getFileStructure(const std::string &path, con auto time = std::chrono::system_clock::to_time_t(fs::last_write_time(entry)); temp.lastEditDate = *gmtime(&time); - + string directoryPath = temp.path; + if (directoryPath.length() > storageLocationLength + 1) { + directoryPath = directoryPath.substr(storageLocationLength + 1); + } if(is_directory(entry)) { temp.path = entry.path(); temp.name = temp.path.substr(temp.path.find_last_of('/') + 1, temp.path.length()); temp.type = Type::FFOLDER; - items.push_back(temp); + if (!_fileHandler->excludeFolder(directoryPath + "/")) { + items.push_back(temp); + } } else if (includeFiles) { temp.path = entry.path(); temp.name = temp.path.substr(temp.path.find_last_of('/') + 1, temp.path.length()); temp.type = Type::FFILE; - items.push_back(temp); + if (!_fileHandler->excludeFolder(directoryPath.substr(0, directoryPath.length())) || !_fileHandler->excludeFile(temp.name)) { + items.push_back(temp); + } } } } diff --git a/src/api/fileBrowser.h b/src/api/fileBrowser.h index 17bc954..df4ca2d 100644 --- a/src/api/fileBrowser.h +++ b/src/api/fileBrowser.h @@ -11,9 +11,11 @@ #define FILEBROWSER #include "fileModel.h" +#include "fileHandler.h" #include #include +#include class FileBrowser { @@ -23,5 +25,7 @@ class FileBrowser private: FileBrowser(){}; + static std::shared_ptr _fileHandler; + }; #endif diff --git a/src/api/sqliteConnector.cpp b/src/api/sqliteConnector.cpp index 9b090b6..278d920 100644 --- a/src/api/sqliteConnector.cpp +++ b/src/api/sqliteConnector.cpp @@ -11,21 +11,101 @@ #include "sqlite3.h" #include "log.h" #include "util.h" +#include "fileHandler.h" +#include "webDAV.h" #include #include +#include + using std::string; SqliteConnector::SqliteConnector(const string &DBpath) : _dbpath(DBpath) { + _fileHandler = std::shared_ptr(new FileHandler()); + + // check if migration has to be run + int currentVersion = getDbVersion(); + if (currentVersion != DBVERSION) { + runMigration(currentVersion); + } } SqliteConnector::~SqliteConnector() { sqlite3_close(_db); + _fileHandler.reset(); Log::writeInfoLog("closed DB"); } +void SqliteConnector::runMigration(int currentVersion) +{ + open(); + + char* log; + sprintf(log, "Running migration from db version %i to %i (Program version %s)", currentVersion, DBVERSION, PROGRAMVERSION); + Log::writeInfoLog(log); + + // currently there are no migrations + + // updating to current version + int rs; + sqlite3_stmt *stmt = 0; + + rs = sqlite3_prepare_v2(_db, "INSERT INTO 'version' (dbversion) VALUES (?)", -1, &stmt, 0); + rs = sqlite3_bind_int(stmt, 1, DBVERSION); + + rs = sqlite3_step(stmt); + if (rs != SQLITE_DONE) { + // this is critical + Log::writeErrorLog(std::string("error inserting into version") + sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")"); + } + + sqlite3_finalize(stmt); + sqlite3_close(_db); +} + +int SqliteConnector::getDbVersion() +{ + open(); + + int rs; + sqlite3_stmt *stmt = 0; + + int version; + rs = sqlite3_prepare_v2(_db, "SELECT MAX(dbversion) FROM 'version'", -1, &stmt, 0); + while (sqlite3_step(stmt) == SQLITE_ROW) + { + version = sqlite3_column_int(stmt, 0); + } + + if (version != 0) + { + sqlite3_finalize(stmt); + sqlite3_close(_db); + return version; + } else { + // this is probably the first start -> the version is up to date and insert the current version + rs = sqlite3_prepare_v2(_db, "INSERT INTO 'version' (dbversion) VALUES (?)", -1, &stmt, 0); + rs = sqlite3_bind_int(stmt, 1, DBVERSION); + + rs = sqlite3_step(stmt); + if (rs != SQLITE_DONE) { + Log::writeErrorLog(std::string("error inserting into version") + sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")"); + } + rs = sqlite3_clear_bindings(stmt); + rs = sqlite3_reset(stmt); + + // for compatibility alter the table because at this point db migrations doesn't exist + rs = sqlite3_exec(_db, "ALTER TABLE metadata ADD hide INT DEFAULT 0 NOT NULL", NULL, 0, NULL); + + sqlite3_finalize(stmt); + sqlite3_close(_db); + + return DBVERSION; + } +} + bool SqliteConnector::open() { int rs; @@ -38,7 +118,8 @@ bool SqliteConnector::open() return false; } - rs = sqlite3_exec(_db, "CREATE TABLE IF NOT EXISTS metadata (title VARCHAR, localPath VARCHAR, size VARCHAR, fileType VARCHAR, lasteditDate VARCHAR, type INT, state INT, etag VARCHAR, path VARCHAR PRIMARY KEY, parentPath VARCHAR)", NULL, 0, NULL); + rs = sqlite3_exec(_db, "CREATE TABLE IF NOT EXISTS metadata (title VARCHAR, localPath VARCHAR, size VARCHAR, fileType VARCHAR, lasteditDate VARCHAR, type INT, state INT, etag VARCHAR, path VARCHAR PRIMARY KEY, parentPath VARCHAR, hide INT DEFAULT 0 NOT NULL)", NULL, 0, NULL); + rs = sqlite3_exec(_db, "CREATE TABLE IF NOT EXISTS version (dbversion INT)", NULL, 0, NULL); return true; } @@ -120,10 +201,15 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa sqlite3_stmt *stmt = 0; std::vector items; - rs = sqlite3_prepare_v2(_db, "SELECT title, localPath, path, size, etag, fileType, lastEditDate, type, state FROM 'metadata' WHERE path=? OR parentPath=? ORDER BY parentPath;", -1, &stmt, 0); + rs = sqlite3_prepare_v2( + _db, + "SELECT title, localPath, path, size, etag, fileType, lastEditDate, type, state, hide FROM 'metadata' WHERE (path=? OR parentPath=?) AND hide <> 2 ORDER BY parentPath;", + -1, &stmt, 0 + ); rs = sqlite3_bind_text(stmt, 1, parentPath.c_str(), parentPath.length(), NULL); rs = sqlite3_bind_text(stmt, 2, parentPath.c_str(), parentPath.length(), NULL); + const string storageLocation = NEXTCLOUD_ROOT_PATH + _fileHandler->getStorageUsername() + "/"; while (sqlite3_step(stmt) == SQLITE_ROW) { WebDAVItem temp; @@ -137,6 +223,7 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa temp.lastEditDate = Util::webDAVStringToTm(reinterpret_cast(sqlite3_column_text(stmt, 6))); temp.type = static_cast(sqlite3_column_int(stmt,7)); temp.state = static_cast(sqlite3_column_int(stmt,8)); + temp.hide = static_cast(sqlite3_column_int(stmt,9)); if (iv_access(temp.localPath.c_str(), W_OK) != 0) { @@ -144,14 +231,81 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa temp.state = FileState::ICLOUD; } + if (temp.hide == HideState::INOTDEFINED) { + temp.hide = _fileHandler->getHideState(temp.type, storageLocation, temp.path, temp.title); + } items.push_back(temp); } sqlite3_finalize(stmt); sqlite3_close(_db); + return items; } +void SqliteConnector::deleteChild(const string &path, const string &title) +{ + open(); + int rs; + sqlite3_stmt *stmt = 0; + rs = sqlite3_prepare_v2(_db, "DELETE FROM 'metadata' WHERE path = ? AND title = ?", -1, &stmt, 0); + rs = sqlite3_bind_text(stmt, 1, path.c_str(), path.length(), NULL); + rs = sqlite3_bind_text(stmt, 1, title.c_str(), title.length(), NULL); + + rs = sqlite3_step(stmt); + if (rs != SQLITE_DONE) + { + Log::writeErrorLog(std::string("An error ocurred trying to delete the item ") + sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")"); + } + rs = sqlite3_clear_bindings(stmt); + rs = sqlite3_reset(stmt); + +} +void SqliteConnector::deleteItemsNotBeginsWith(string beginPath) +{ + open(); + + // escape characters + beginPath = std::regex_replace(beginPath, std::regex("%"), "#%"); + beginPath = std::regex_replace(beginPath, std::regex("_"), "#_"); + beginPath = beginPath + "%"; + + int rs; + sqlite3_stmt *stmt = 0; + rs = sqlite3_prepare_v2(_db, "DELETE FROM 'metadata' WHERE path NOT LIKE ? ESCAPE '#'", -1, &stmt, 0); + rs = sqlite3_bind_text(stmt, 1, beginPath.c_str(), beginPath.length(), NULL); + + rs = sqlite3_step(stmt); + if (rs != SQLITE_DONE) + { + Log::writeErrorLog(std::string("An error ocurred trying to delete the items that begins with " + beginPath) + sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")"); + } + rs = sqlite3_clear_bindings(stmt); + rs = sqlite3_reset(stmt); +} + +bool SqliteConnector::resetHideState() +{ + open(); + int rs; + sqlite3_stmt *stmt = 0; + + rs = sqlite3_prepare_v2(_db, "UPDATE 'metadata' SET hide=0", -1, &stmt, 0); + rs = sqlite3_step(stmt); + + if (rs != SQLITE_DONE) + { + Log::writeErrorLog(sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")"); + } + rs = sqlite3_clear_bindings(stmt); + rs = sqlite3_reset(stmt); + + sqlite3_finalize(stmt); + sqlite3_close(_db); + + return true; +} + void SqliteConnector::deleteChildren(const string &parentPath) { //TODO missing the onces where parentPath is one folder deeper and also destroyed @@ -185,7 +339,7 @@ bool SqliteConnector::saveItemsChildren(const std::vector &items) for (auto item : items) { - rs = sqlite3_prepare_v2(_db, "INSERT INTO 'metadata' (title, localPath, path, size, parentPath, etag, fileType, lastEditDate, type, state) VALUES (?,?,?,?,?,?,?,?,?,?);", -1, &stmt, 0); + rs = sqlite3_prepare_v2(_db, "INSERT INTO 'metadata' (title, localPath, path, size, parentPath, etag, fileType, lastEditDate, type, state, hide) VALUES (?,?,?,?,?,?,?,?,?,?,?);", -1, &stmt, 0); rs = sqlite3_exec(_db, "BEGIN TRANSACTION;", NULL, NULL, NULL); rs = sqlite3_bind_text(stmt, 1, item.title.c_str(), item.title.length(), NULL); rs = sqlite3_bind_text(stmt, 2, item.localPath.c_str(), item.localPath.length(), NULL); @@ -198,6 +352,7 @@ bool SqliteConnector::saveItemsChildren(const std::vector &items) rs = sqlite3_bind_text(stmt, 8, lastEditDateString.c_str(), lastEditDateString.length(), NULL); rs = sqlite3_bind_int(stmt, 9, item.type); rs = sqlite3_bind_int(stmt, 10, item.state); + rs = sqlite3_bind_int(stmt, 11, item.hide); rs = sqlite3_step(stmt); if (rs == SQLITE_CONSTRAINT) diff --git a/src/api/sqliteConnector.h b/src/api/sqliteConnector.h index 354a822..f0bfe9f 100644 --- a/src/api/sqliteConnector.h +++ b/src/api/sqliteConnector.h @@ -12,10 +12,13 @@ #include "webDAVModel.h" #include "sqlite3.h" +#include "fileHandler.h" #include #include +#include + class SqliteConnector { public: @@ -28,6 +31,10 @@ class SqliteConnector bool open(); + int getDbVersion(); + + void runMigration(int currentVersion); + std::string getEtag(const std::string &path); FileState getState(const std::string &path); @@ -38,11 +45,19 @@ class SqliteConnector void deleteChildren(const std::string &parentPath); + void deleteChild(const std::string &path, const std::string &title); + + void deleteItemsNotBeginsWith(std::string beginPath); + + bool resetHideState(); + bool saveItemsChildren(const std::vector &children); private: std::string _dbpath; sqlite3 *_db; + + std::shared_ptr _fileHandler; }; #endif diff --git a/src/api/webDAV.cpp b/src/api/webDAV.cpp index 1790940..2f8eb6b 100644 --- a/src/api/webDAV.cpp +++ b/src/api/webDAV.cpp @@ -11,6 +11,7 @@ #include "util.h" #include "log.h" #include "eventHandler.h" +#include "fileHandler.h" #include #include @@ -18,6 +19,7 @@ #include #include #include +#include using std::ifstream; using std::ofstream; @@ -26,20 +28,46 @@ using std::vector; namespace fs = std::experimental::filesystem; +std::string WebDAV::getRootPath(bool encode) { + string rootPath = Util::getConfig("ex_relativeRootPath", "/"); + if (rootPath == "") + rootPath += "/"; + + string user = Util::getConfig("UUID", ""); + if (user == "") + user = Util::getConfig("username", "error"); + + string rtc = NEXTCLOUD_ROOT_PATH + user + rootPath; + + if (encode) { + Util::encodeUrl(rtc); + rtc = std::regex_replace(rtc, std::regex("%2F"), "/"); + } + + return rtc; +} + WebDAV::WebDAV() { + _fileHandler = std::shared_ptr(new FileHandler()); + if (iv_access(NEXTCLOUD_PATH.c_str(), W_OK) != 0) iv_mkdir(NEXTCLOUD_PATH.c_str(), 0777); if (iv_access(CONFIG_PATH.c_str(), W_OK) == 0) { - _username = Util::accessConfig(Action::IReadString,"username",{}); - _password = Util::accessConfig(Action::IReadSecret,"password",{}); - _url = Util::accessConfig(Action::IReadString, "url",{}); - _ignoreCert = Util::accessConfig(Action::IReadInt, "ignoreCert",{}); + _username = Util::getConfig("username"); + _password = Util::getConfig("password", "", true); + _url = Util::getConfig("url"); + _ignoreCert = Util::getConfig("ignoreCert", -1); } } +WebDAV::~WebDAV() +{ + _fileHandler.reset(); +} + std::vector WebDAV::login(const string &Url, const string &Username, const string &Pass, bool ignoreCert) { @@ -61,17 +89,17 @@ std::vector WebDAV::login(const string &Url, const string &Username, uuid = Username; } auto tempPath = NEXTCLOUD_ROOT_PATH + uuid + "/"; - Util::accessConfig( Action::IWriteString, "storageLocation", "/mnt/ext1/nextcloud"); + Util::writeConfig("storageLocation", "/mnt/ext1/nextcloud"); std::vector tempItems = getDataStructure(tempPath); if (!tempItems.empty()) { if (iv_access(CONFIG_PATH.c_str(), W_OK) != 0) iv_buildpath(CONFIG_PATH.c_str()); - Util::accessConfig( Action::IWriteString, "url", _url); - Util::accessConfig( Action::IWriteString, "username", _username); - Util::accessConfig( Action::IWriteString, "UUID", uuid); - Util::accessConfig( Action::IWriteSecret, "password", _password); - Util::accessConfig( Action::IWriteInt, "ignoreCert", _ignoreCert); + Util::writeConfig("url", _url); + Util::writeConfig("username", _username); + Util::writeConfig("UUID", uuid); + Util::writeConfig("password", _password, true); + Util::writeConfig("ignoreCert", _ignoreCert); } else { @@ -86,7 +114,9 @@ void WebDAV::logout(bool deleteFiles) { if (deleteFiles) { - fs::remove_all(Util::accessConfig(Action::IReadString, "storageLocation",{}) + "/" + Util::accessConfig(Action::IReadString,"UUID",{}) + '/'); + string filesPath = Util::getConfig("storageLocation") + "/" + Util::getConfig("UUID") + '/'; + if (fs::exists(filesPath)) + fs::remove_all(filesPath); } fs::remove(CONFIG_PATH.c_str()); fs::remove((CONFIG_PATH + ".back.").c_str()); @@ -108,6 +138,7 @@ vector WebDAV::getDataStructure(const string &pathUrl) size_t begin = xmlItem.find(beginItem); size_t end; + string prefix = NEXTCLOUD_ROOT_PATH + _username + "/"; while (begin != std::string::npos) { end = xmlItem.find(endItem); @@ -158,7 +189,7 @@ vector WebDAV::getDataStructure(const string &pathUrl) Util::decodeUrl(tempItem.localPath); if (tempItem.localPath.find(NEXTCLOUD_ROOT_PATH) != string::npos) tempItem.localPath = tempItem.localPath.substr(NEXTCLOUD_ROOT_PATH.length()); - tempItem.localPath = Util::accessConfig(Action::IReadString, "storageLocation",{}) + "/" + tempItem.localPath; + tempItem.localPath = Util::getConfig("storageLocation") + "/" + tempItem.localPath; if (tempItem.path.back() == '/') @@ -176,6 +207,10 @@ vector WebDAV::getDataStructure(const string &pathUrl) tempItem.title = tempItem.title.substr(tempItem.title.find_last_of("/") + 1, tempItem.title.length()); Util::decodeUrl(tempItem.title); + string pathDecoded = tempItem.path; + Util::decodeUrl(pathDecoded); + tempItem.hide = _fileHandler->getHideState(tempItem.type, prefix,pathDecoded, tempItem.title); + tempItems.push_back(tempItem); xmlItem = xmlItem.substr(end + endItem.length()); begin = xmlItem.find(beginItem); @@ -262,7 +297,33 @@ string WebDAV::propfind(const string &pathUrl) switch (response_code) { case 404: - Message(ICON_ERROR, "Error", "The URL seems to be incorrect. You can look up the WebDav URL in the settings of the files webapp. ", 4000); + if (getRootPath().compare( NEXTCLOUD_ROOT_PATH + Util::getConfig("uuid", "")) != 0) { + if (propfind(NEXTCLOUD_ROOT_PATH + Util::getConfig("UUID", "")) != "") { + // Own root path defined + string output; + int dialogResult = DialogSynchro( + ICON_ERROR, + "Action", + output.append("The specified start folder does not seem to exist:\n").append(Util::getConfig("ex_relativeRootPath", "/")).append("\n\nWhat would you like to do?").c_str(), + "Reset start folder", "Close App", NULL + ); + switch (dialogResult) + { + case 1: + { + Util::writeConfig("ex_relativeRootPath", ""); + return propfind(NEXTCLOUD_ROOT_PATH + Util::getConfig("UUID", "")); + } + break; + case 2: + default: + CloseApp(); + break; + } + } + } else { + Message(ICON_ERROR, "Error", "The URL seems to be incorrect. You can look up the WebDav URL in the settings of the files webapp. ", 4000); + } break; case 401: Message(ICON_ERROR, "Error", "Username/password incorrect.", 4000); diff --git a/src/api/webDAV.h b/src/api/webDAV.h index 8386041..255c21f 100644 --- a/src/api/webDAV.h +++ b/src/api/webDAV.h @@ -11,11 +11,14 @@ #define WEBDAV #include "webDAVModel.h" +#include "fileHandler.h" #include #include -const std::string NEXTCLOUD_ROOT_PATH = "/remote.php/dav/files/"; +#include + +const static std::string NEXTCLOUD_ROOT_PATH = "/remote.php/dav/files/"; const std::string NEXTCLOUD_START_PATH = "/remote.php/"; const std::string NEXTCLOUD_PATH = "/mnt/ext1/system/config/nextcloud"; @@ -27,6 +30,7 @@ class WebDAV * */ WebDAV(); + ~WebDAV(); std::vector login(const std::string &Url, const std::string &Username, const std::string &Pass, bool ignoreCert = false); @@ -34,6 +38,12 @@ class WebDAV std::vector getDataStructure(const std::string &pathUrl); + /** + * Returns the root path of the nextcloud server + * (e.g. /remote.php/dav/files/userName/startFolder/) + */ + static std::string getRootPath(bool encode = false); + /** * gets the dataStructure of the given URL and writes its WEBDAV items to the items vector * @@ -52,5 +62,7 @@ class WebDAV std::string _url; bool _ignoreCert; + std::shared_ptr _fileHandler; + }; #endif diff --git a/src/api/webDAVModel.h b/src/api/webDAVModel.h index c4b5ab0..10744ba 100644 --- a/src/api/webDAVModel.h +++ b/src/api/webDAVModel.h @@ -28,6 +28,13 @@ enum FileState IDOWNLOADED }; +enum HideState +{ + INOTDEFINED, + ISHOW, + IHIDE +}; + struct WebDAVItem : Entry{ std::string etag; std::string path; @@ -38,6 +45,7 @@ struct WebDAVItem : Entry{ tm lastEditDate = { 0 }; std::string size; std::string fileType; + HideState hide; }; #endif diff --git a/src/handler/eventHandler.cpp b/src/handler/eventHandler.cpp index e52a45c..0d31eee 100644 --- a/src/handler/eventHandler.cpp +++ b/src/handler/eventHandler.cpp @@ -18,6 +18,7 @@ #include "fileBrowser.h" #include "fileView.h" #include "fileModel.h" +#include "fileHandler.h" #include #include @@ -36,18 +37,19 @@ EventHandler::EventHandler() //create an copy of the eventhandler to handle methods that require static functions _eventHandlerStatic = std::unique_ptr(this); + _fileHandler = std::shared_ptr(new FileHandler()); _menu = std::unique_ptr(new MainMenu("Nextcloud")); if (iv_access(CONFIG_PATH.c_str(), W_OK) == 0) { //for backwards compatibilty - if (Util::accessConfig(Action::IReadString, "storageLocation",{}).compare("error") == 0) - Util::accessConfig(Action::IWriteString, "storageLocation", "/mnt/ext1/nextcloud"); + if (Util::getConfig("storageLocation", "error").compare("error") == 0) + Util::writeConfig("storageLocation", "/mnt/ext1/nextcloud"); - if (iv_access(Util::accessConfig(Action::IReadString, "storageLocation",{}).c_str(), W_OK) != 0) - iv_mkdir(Util::accessConfig(Action::IReadString, "storageLocation",{}).c_str(), 0777); + if (iv_access(Util::getConfig("storageLocation").c_str(), W_OK) != 0) + iv_mkdir(Util::getConfig("storageLocation").c_str(), 0777); std::vector currentWebDAVItems; - string path = NEXTCLOUD_ROOT_PATH + Util::accessConfig(Action::IReadString,"UUID",{}) + '/'; + string path = WebDAV::getRootPath(true); currentWebDAVItems = _webDAV.getDataStructure(path); _menu = std::unique_ptr(new MainMenu("Nextcloud")); @@ -192,10 +194,10 @@ void EventHandler::mainMenuHandler(const int index) switch (dialogResult) { case 1: - Util::accessConfig(Action::IWriteInt, "sortBy", 1); + Util::writeConfig("sortBy", 1); break; case 2: - Util::accessConfig(Action::IWriteInt, "sortBy", 2); + Util::writeConfig("sortBy", 2); break; default: return; @@ -203,8 +205,23 @@ void EventHandler::mainMenuHandler(const int index) Message(ICON_INFORMATION, "Info", "Reload page to see new order method in effect.", 2000); break; } - //Select folder + // Exclude and hide files case 104: + { + if (_fileView != nullptr) { + _currentPath = _fileView->getCurrentEntry().path; + _fileView.reset(); + } else { + _currentPath = ""; + } + + _webDAVView.reset(); + FillAreaRect(&_menu->getContentRect(), WHITE); + _excludeFileView = std::unique_ptr(new ExcludeFileView(_menu->getContentRect())); + break; + } + //Select folder + case 105: { _currentPath = _currentPath + ((_currentPath.back() != '/') ? "/nextcloud" : "nextcloud"); @@ -216,8 +233,8 @@ void EventHandler::mainMenuHandler(const int index) } else { - Util::accessConfig(Action::IWriteString, "storageLocation", _currentPath); - std::vector currentWebDAVItems = _webDAV.getDataStructure(NEXTCLOUD_ROOT_PATH + Util::accessConfig(Action::IReadString,"UUID",{}) + '/'); + Util::writeConfig("storageLocation", _currentPath); + std::vector currentWebDAVItems = _webDAV.getDataStructure(WebDAV::getRootPath(true)); if (currentWebDAVItems.empty()) { Message(ICON_ERROR, "Error", "Failed to get items. Please try again.", 1000); @@ -232,13 +249,14 @@ void EventHandler::mainMenuHandler(const int index) break; } //Info - case 105: + case 106: { - Message(ICON_INFORMATION, "Info", "Version 1.02 \n For support please open a ticket at https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client/issues", 1200); + string message; + Message(ICON_INFORMATION, "Info", message.append("Version ").append(PROGRAMVERSION).append("\nFor support please open a ticket at https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client/issues").c_str(), 1200); break; } //Exit - case 106: + case 107: CloseApp(); break; default: @@ -364,6 +382,49 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) return 0; } + else if (_excludeFileView != nullptr) { + int click = _excludeFileView->excludeClicked(par1, par2); + if (click == 3) { + Util::writeConfig("ex_extensionList", _excludeFileView->getExtensionList()); + Util::writeConfig("ex_pattern",_excludeFileView->getRegex()); + Util::writeConfig("ex_folderPattern",_excludeFileView->getFolderRegex()); + Util::writeConfig("ex_relativeRootPath", _excludeFileView->getStartFolder()); + Util::writeConfig("ex_invertMatch", _excludeFileView->getInvertMatch()); + + _sqllite.resetHideState(); + if (_excludeFileView->getStartFolder() != "") + { + _sqllite.deleteItemsNotBeginsWith(WebDAV::getRootPath(true)); + } + + _excludeFileView.reset(); + ShowHourglassForce(); + + FillAreaRect(&_menu->getContentRect(), WHITE); + if (_currentPath != ""){ + vector currentFolder = FileBrowser::getFileStructure(_currentPath,false,true); + _fileView.reset(new FileView(_menu->getContentRect(), currentFolder,1)); + } else { + std::vector currentWebDAVItems = _webDAV.getDataStructure(WebDAV::getRootPath(true)); + updateItems(currentWebDAVItems); + drawWebDAVItems(currentWebDAVItems); + } + } + else if (click == -1) { + _excludeFileView.reset(); + ShowHourglassForce(); + + FillAreaRect(&_menu->getContentRect(), WHITE); + if (_currentPath != ""){ + vector currentFolder = FileBrowser::getFileStructure(_currentPath,false,true); + _fileView.reset(new FileView(_menu->getContentRect(), currentFolder,1)); + } else { + std::vector currentWebDAVItems = _webDAV.getDataStructure(WebDAV::getRootPath(true)); + updateItems(currentWebDAVItems); + drawWebDAVItems(currentWebDAVItems); + } + } + } //if loginView is shown else if (_loginView != nullptr) { @@ -371,7 +432,7 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) { ShowHourglassForce(); - std::vector currentWebDAVItems = _webDAV.login(_loginView->getURL(), _loginView->getUsername(), _loginView->getPassword(), _loginView->getIgnoreCert());; + std::vector currentWebDAVItems = _webDAV.login(_loginView->getURL(), _loginView->getUsername(), _loginView->getPassword(), _loginView->getIgnoreCert()); if (currentWebDAVItems.empty()) { Message(ICON_ERROR, "Error", "Login failed.", 1000); @@ -394,8 +455,8 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) break; case 2: default: - if (iv_access(Util::accessConfig(Action::IReadString, "storageLocation",{}).c_str(), W_OK) != 0) - iv_mkdir(Util::accessConfig(Action::IReadString, "storageLocation",{}).c_str(), 0777); + if (iv_access(Util::getConfig("storageLocation").c_str(), W_OK) != 0) + iv_mkdir(Util::getConfig("storageLocation").c_str(), 0777); updateItems(currentWebDAVItems); drawWebDAVItems(currentWebDAVItems); break; @@ -454,7 +515,6 @@ void EventHandler::openItem() void EventHandler::openFolder() { - std::vector currentWebDAVItems; switch ((_webDAVView->getCurrentEntry().state == FileState::ILOCAL) ? FileState::ILOCAL : _sqllite.getState(_webDAVView->getCurrentEntry().path)) @@ -556,6 +616,7 @@ void EventHandler::getLocalFileStructure(std::vector &tempItems) { vector currentFolder = FileBrowser::getFileStructure(localPath,true,false); + const int storageLocationLength = _fileHandler->getStorageLocation().length(); for(const FileItem &local : currentFolder) { auto p = find_if(tempItems.begin()+1, tempItems.end(), [&] (const WebDAVItem &item) {return item.localPath.compare(local.path) == 0;}); @@ -567,16 +628,30 @@ void EventHandler::getLocalFileStructure(std::vector &tempItems) temp.title = temp.localPath.substr(temp.localPath.find_last_of('/') + 1, temp.localPath.length()); //Log::writeInfoLog(std::to_string(fs::file_size(entry))); temp.lastEditDate = local.lastEditDate; + + string directoryPath = temp.localPath; + if (directoryPath.length() > storageLocationLength) { + directoryPath = directoryPath.substr(storageLocationLength + 1); + } if(local.type == Type::FFOLDER) { + if (_fileHandler->excludeFolder(directoryPath + "/")) { + continue; + } //create new dir in cloud temp.type = Itemtype::IFOLDER; } else { //put to cloud - temp.fileType = "File"; temp.type = Itemtype::IFILE; + if (directoryPath.length() > temp.title.length()) { + directoryPath = directoryPath.substr(0, directoryPath.length() - temp.title.length()); + } + if (_fileHandler->excludeFolder(directoryPath) || _fileHandler->excludeFile(temp.title)) + { + continue; + } } tempItems.push_back(temp); } @@ -587,6 +662,10 @@ void EventHandler::getLocalFileStructure(std::vector &tempItems) void EventHandler::downloadFolder(vector &items, int itemID) { + //Don't sync hidden files + if (items.at(itemID).hide == HideState::IHIDE) + return; + //BanSleep(2000); string path = items.at(itemID).path; diff --git a/src/handler/eventHandler.h b/src/handler/eventHandler.h index 05e402b..de95858 100644 --- a/src/handler/eventHandler.h +++ b/src/handler/eventHandler.h @@ -15,7 +15,10 @@ #include "webDAVView.h" #include "loginView.h" #include "fileView.h" +#include "excludeFileView.h" #include "sqliteConnector.h" +#include "log.h" +#include "fileHandler.h" #include @@ -40,13 +43,17 @@ class EventHandler */ int eventDistributor(const int type, const int par1, const int par2); + private: static std::unique_ptr _eventHandlerStatic; std::unique_ptr _webDAVView; std::unique_ptr _loginView; std::unique_ptr _fileView; + std::unique_ptr _excludeFileView; std::unique_ptr _menu; + std::shared_ptr _fileHandler; + ContextMenu _contextMenu = ContextMenu(); WebDAV _webDAV = WebDAV(); SqliteConnector _sqllite = SqliteConnector(DB_PATH); diff --git a/src/handler/fileHandler.cpp b/src/handler/fileHandler.cpp new file mode 100644 index 0000000..8574e47 --- /dev/null +++ b/src/handler/fileHandler.cpp @@ -0,0 +1,165 @@ +//------------------------------------------------------------------ +// fileHandler.cpp +// +// Author: RPJoshL +// Date: 03.10.2022 +// +//------------------------------------------------------------------- + +#include "fileHandler.h" +#include "log.h" +#include "util.h" +#include "webDAVModel.h" + +#include +#include +#include +#include + +using std::string; +using std::find; +using std::regex; + +std::vector FileHandler::_instances; +FileHandler::FileHandler() +{ + _instances.push_back(this); + + parseConfig( + Util::getConfig("ex_pattern", ""), + Util::getConfig("ex_folderPattern", ""), + Util::getConfig("ex_extensionList", ""), + Util::getConfig("ex_invertMatch", 0) + ); + +} + +void FileHandler::parseConfig(string regex, string folderRegex, string extensions, int invertMatch) { + _extensions.clear(); + // split the comma seperated string + if (!extensions.empty()) { + string line; + + std::stringstream ss(extensions); + while(getline(ss, line, ',')) { + _extensions.push_back(line); + } + } + + // parse the regex only onces + if (!regex.empty()) { + try { + _regex = std::regex(regex); + _useRegex = true; + } catch(std::regex_error err) { + Log::writeErrorLog("Unable to parse regex '" + regex + "' for file: " + err.what()); + } + } else { + _useRegex = false; + } + + if (!folderRegex.empty()) { + try { + _folderRegex = std::regex(folderRegex); + _useFolderRegex = true; + } catch(std::regex_error err) { + Log::writeErrorLog("Unable to parse regex '" + folderRegex + "' for folder: " + err.what()); + } + } else { + _useFolderRegex = false; + } + + _invertMatch = invertMatch; +} + +FileHandler::~FileHandler() +{ + _instances.erase(std::remove(_instances.begin(), _instances.end(), this), _instances.end()); +} + +bool FileHandler::excludeFile(std::string filename) { + + // check for file extensions + if (!_extensions.empty()) { + int indexOfDot = filename.find_last_of("."); + if (indexOfDot != std::string::npos && filename.length() > indexOfDot + 1) { + string extension = filename.substr(filename.find_last_of(".") + 1); + if(std::find(_extensions.begin(),_extensions.end(), extension) != _extensions.end()) { + return !_invertMatch; + } + } + } + + if (_useRegex) { + try { + bool t = std::regex_match(filename, _regex) != _invertMatch; + return t; + } catch (std::regex_error err) { + string errM = err.what(); + Log::writeErrorLog("Unable to parse regex for file:'" + errM); + } + } + + return _invertMatch; +} + +bool FileHandler::excludeFolder(std::string folderName) { + folderName = "/" + folderName; + + // always display root folder because that can't be matched + if (folderName == "/" || folderName == "//") { + return false; + } + + if (_useFolderRegex) { + try { + bool t = std::regex_match(folderName, _folderRegex) != _invertMatch; + return t; + } catch (std::regex_error err) { + string errM = err.what(); + Log::writeErrorLog("Unable to parse regex for folder:'" + errM); + } + } + + return _invertMatch; +} + +HideState FileHandler::getHideState(Itemtype itemType, std::string prefix, std::string path, std::string title = "") { + + string folderPath = path; + if (path.find(prefix) != string::npos) { + folderPath = path.substr(prefix.length()); + if (itemType == Itemtype::IFILE && folderPath.length() >= title.length()) { + folderPath = folderPath.substr(0, folderPath.length() - title.length()); + } + } + folderPath = "/" + folderPath; + + if (itemType == Itemtype::IFILE) { + if (!excludeFolder(folderPath) && !excludeFile(title)) { + return HideState::ISHOW; + } else { + return HideState::IHIDE; + } + } else { + if (!excludeFolder(folderPath)) { + return HideState::ISHOW; + } else { + return HideState::IHIDE; + } + } +} + + +void FileHandler::update(string regex, string folderRegex, string extensions, int invertMatch) { + for (FileHandler *handler : _instances) { + handler->parseConfig(regex, folderRegex, extensions, invertMatch); + } +} + +string FileHandler::getStorageLocation() { + return Util::getConfig("storageLocation") + getStorageUsername() + "/"; +} +string FileHandler::getStorageUsername() { + return Util::getConfig("username"); +} \ No newline at end of file diff --git a/src/handler/fileHandler.h b/src/handler/fileHandler.h new file mode 100644 index 0000000..e31b1fe --- /dev/null +++ b/src/handler/fileHandler.h @@ -0,0 +1,46 @@ +//------------------------------------------------------------------ +// fileHandler.h +// +// Author: RPJoshL +// Date: 03.10.2022 +// +//------------------------------------------------------------------- + +#ifndef FILEHANDLER +#define FILEHANDLER + +#include "webDAVModel.h" + +#include +#include +#include + +#include + +class FileHandler +{ + public: + FileHandler(); + ~FileHandler(); + bool excludeFile(std::string filename); + bool excludeFolder(std::string foldername); + HideState getHideState(Itemtype itemType, std::string prefixToStripe, std::string path, std::string title); + + std::string getStorageLocation(); + std::string getStorageUsername(); + static void update(std::string regex, std::string folderRegex, std::string extensions, int invertMatch); + + private: + std::regex _regex; + std::regex _folderRegex; + // can't use pointers with regex... Why? -> unable to null check + bool _useRegex = false; + bool _useFolderRegex = false; + std::vector _extensions; + bool _invertMatch; + + void parseConfig(std::string regex, std::string folderRegex, std::string extensions, int invertMatch); + static std::vector _instances; + +}; +#endif \ No newline at end of file diff --git a/src/handler/mainMenu.cpp b/src/handler/mainMenu.cpp index 5be4de5..61aaee2 100644 --- a/src/handler/mainMenu.cpp +++ b/src/handler/mainMenu.cpp @@ -42,6 +42,7 @@ MainMenu::~MainMenu() free(_menu); free(_logout); free(_sortBy); + free(_excludeFiles); free(_info); free(_exit); free(_chooseFolder); @@ -61,14 +62,15 @@ int MainMenu::createMenu(bool filePicker, bool loggedIn, iv_menuhandler handler) //show logged in {loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 101, _syncFolder, NULL}, {loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 103, _sortBy, NULL}, + {loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 104, _excludeFiles, NULL}, //show if filePicker is shown - {filePicker ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 104, _chooseFolder, NULL}, + {filePicker ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 105, _chooseFolder, NULL}, //show always - {ITEM_ACTIVE, 105, _info, NULL}, + {ITEM_ACTIVE, 106, _info, NULL}, //show logged in {loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 102, _logout, NULL}, //show always - {ITEM_ACTIVE, 106, _exit, NULL}, + {ITEM_ACTIVE, 107, _exit, NULL}, {0, 0, NULL, NULL}}; OpenMenu(mainMenu, 0, _panelMenuBeginX, _panelMenuBeginY, handler); diff --git a/src/handler/mainMenu.h b/src/handler/mainMenu.h index ab1d3cb..369c6ac 100644 --- a/src/handler/mainMenu.h +++ b/src/handler/mainMenu.h @@ -55,6 +55,7 @@ class MainMenu char *_logout = strdup("Logout"); char *_chooseFolder = strdup("Create here"); char *_sortBy = strdup("Order items by"); + char *_excludeFiles = strdup("Exclude and hide items"); char *_info = strdup("Info"); char *_exit = strdup("Close App"); diff --git a/src/ui/excludeFileView/excludeFileView.cpp b/src/ui/excludeFileView/excludeFileView.cpp new file mode 100644 index 0000000..af67186 --- /dev/null +++ b/src/ui/excludeFileView/excludeFileView.cpp @@ -0,0 +1,230 @@ +//------------------------------------------------------------------ +// excludeFileView.cpp +// +// Author: RPJoshL +// Date: 03.10.2022 +// +//------------------------------------------------------------------- + +#include "excludeFileView.h" +#include "log.h" +#include "util.h" + +#include + +using std::string; + +ExcludeFileView* ExcludeFileView::_excludeFileViewStatic; +ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(contentRect) +{ + _excludeFileViewStatic = this; + + _extensionList = Util::getConfig("ex_extensionList", ""); + _regex = Util::getConfig("ex_pattern", ""); + _folderRegex = Util::getConfig("ex_folderPattern", ""); + _startFolder = Util::getConfig("ex_relativeRootPath", ""); + _invertMatch = Util::getConfig("ex_invertMatch",0); + + int contentHeight = contentRect.h / 2; + int contentWidth = _contentRect.w * 0.9; + int checkBoxWidth = _contentRect.w * 0.1; + + int beginY = 0.4 * contentHeight; + int beginX = (_contentRect.w - contentWidth) / 2; + + int contents = contentHeight / 7; + + _fontHeight = contents / 2; + + _font = OpenFont("LiberationMono", _fontHeight, FONT_STD); + SetFont(_font, BLACK); + FillAreaRect(&_contentRect, WHITE); + + _extensionListButton = iRect(beginX, beginY, contentWidth, contents, ALIGN_CENTER); + DrawTextRect(_extensionListButton.x, _extensionListButton.y - _fontHeight - _fontHeight/2, _extensionListButton.w, _extensionListButton.h, "Extensions:", ALIGN_LEFT); + DrawString(_extensionListButton.x, _extensionListButton.y, _extensionList.c_str()); + DrawRect(_extensionListButton.x - 1, _extensionListButton.y - 1, _extensionListButton.w + 2, _extensionListButton.h + 2, BLACK); + + _regexButton = iRect(beginX, beginY + 2 * contents, contentWidth, contents, ALIGN_CENTER); + DrawTextRect(_regexButton.x, _regexButton.y - _fontHeight - _fontHeight/3, _regexButton.w, _regexButton.h, "File-Regex:", ALIGN_LEFT); + DrawString(_regexButton.x, _regexButton.y, _regex.c_str()); + DrawRect(_regexButton.x - 1, _regexButton.y - 1, _regexButton.w + 2, _regexButton.h + 2, BLACK); + + _folderRegexButton = iRect(beginX, beginY + 4 * contents, contentWidth, contents, ALIGN_CENTER); + DrawTextRect(_folderRegexButton.x, _folderRegexButton.y - _fontHeight - _fontHeight/3, _folderRegexButton.w, _folderRegexButton.h, "Folder-Regex:", ALIGN_LEFT); + DrawString(_folderRegexButton.x, _folderRegexButton.y, _folderRegex.c_str()); + DrawRect(_folderRegexButton.x - 1, _folderRegexButton.y - 1, _folderRegexButton.w + 2, _folderRegexButton.h + 2, BLACK); + + _startFolderButton = iRect(beginX, beginY + 6 * contents, contentWidth, contents, ALIGN_CENTER); + DrawTextRect(_startFolderButton.x, _startFolderButton.y - _fontHeight - _fontHeight/3, _startFolderButton.w, _startFolderButton.h, "Start folder:", ALIGN_LEFT); + DrawString(_startFolderButton.x, _startFolderButton.y, _startFolder.c_str()); + DrawRect(_startFolderButton.x - 1, _startFolderButton.y - 1, _startFolderButton.w + 2, _startFolderButton.h + 2, BLACK); + + _invertMatchButton = iRect(_contentRect.w - 2 * checkBoxWidth, beginY + 8 * contents, checkBoxWidth, contents, ALIGN_CENTER); + DrawTextRect(beginX, _invertMatchButton.y, contentWidth, _invertMatchButton.h, "Only include matching:", ALIGN_LEFT); + DrawRect(_invertMatchButton.x - 1, _invertMatchButton.y - 1, _invertMatchButton.w + 2, _invertMatchButton.h + 2, BLACK); + if (_invertMatch) { + FillArea(_invertMatchButton.x - 1, _invertMatchButton.y - 1, _invertMatchButton.w + 2, _invertMatchButton.h + 2, BLACK); + } + + _saveButton = iRect(beginX, beginY + 10 * contents, contentWidth / 2 - 20, contents, ALIGN_CENTER); + FillAreaRect(&_saveButton, BLACK); + SetFont(_font, WHITE); + DrawTextRect2(&_saveButton, "Save"); + PartialUpdate(_contentRect.x, _contentRect.y, _contentRect.w, _contentRect.h); + + _cancelButton = iRect(beginX + contentWidth / 2, beginY + 10 * contents, contentWidth / 2 - 20, contents, ALIGN_CENTER); + FillAreaRect(&_cancelButton, BLACK); + SetFont(_font, WHITE); + DrawTextRect2(&_cancelButton, "Cancel"); + PartialUpdate(_contentRect.x, _contentRect.y, _contentRect.w, _contentRect.h); +} + +ExcludeFileView::~ExcludeFileView() +{ + CloseFont(_font); + // Pointer cannot be freeded... It would crash when optining the window >~ 2 times (as well as an shared_ptr) +} + +int ExcludeFileView::excludeClicked(int x, int y) +{ + _temp = ""; + + if (IsInRect(x, y, &_extensionListButton)) + { + _target = ExcludeFileKeyboardTarget::IEXTENSIONS; + if (!_extensionList.empty()) + _temp = _extensionList; + _temp.resize(EXCLUDE_FILE_KEYBOARD_STRING_LENGHT); + OpenKeyboard("docx,pdf,sdr", &_temp[0], EXCLUDE_FILE_KEYBOARD_STRING_LENGHT - 1, KBD_NORMAL, &keyboardHandlerStatic); + return 1; + } + else if (IsInRect(x, y, &_regexButton)) + { + _target = ExcludeFileKeyboardTarget::IREGEX; + if (!_regex.empty()) + _temp = _regex; + _temp.resize(EXCLUDE_FILE_KEYBOARD_STRING_LENGHT); + OpenKeyboard("Pre.*Post\\.pdf", &_temp[0], EXCLUDE_FILE_KEYBOARD_STRING_LENGHT, KBD_NORMAL, &keyboardHandlerStatic); + return 1; + } + else if (IsInRect(x, y, &_folderRegexButton)) + { + _target = ExcludeFileKeyboardTarget::IFOLDERREGEX; + if (!_folderRegex.empty()) + _temp = _folderRegex; + _temp.resize(EXCLUDE_FILE_KEYBOARD_STRING_LENGHT); + OpenKeyboard("/root/.*/test/", &_temp[0], EXCLUDE_FILE_KEYBOARD_STRING_LENGHT, KBD_NORMAL, &keyboardHandlerStatic); + return 1; + } + else if (IsInRect(x, y, &_startFolderButton)) + { + _target = ExcludeFileKeyboardTarget::ISTARTFOLDER; + if (!_startFolder.empty()) + _temp = _startFolder; + _temp.resize(EXCLUDE_FILE_KEYBOARD_STRING_LENGHT); + OpenKeyboard("/MyBooks/", &_temp[0], EXCLUDE_FILE_KEYBOARD_STRING_LENGHT, KBD_NORMAL, &keyboardHandlerStatic); + return 1; + } + else if (IsInRect(x, y, &_invertMatchButton)) + { + _invertMatch = !_invertMatch; + FillAreaRect(&_invertMatchButton, WHITE); + if(_invertMatch) + FillArea(_invertMatchButton.x - 1, _invertMatchButton.y - 1, _invertMatchButton.w + 2, _invertMatchButton.h + 2, BLACK); + else + DrawRect(_invertMatchButton.x - 1, _invertMatchButton.y - 1, _invertMatchButton.w + 2, _invertMatchButton.h + 2, BLACK); + + PartialUpdate(_invertMatchButton.x, _invertMatchButton.y, _invertMatchButton.w, _invertMatchButton.h); + + return 1; + } + else if (IsInRect(x, y, &_saveButton)) + { + if (!_regex.empty()) { + try { + std::regex regP = std::regex(_regex); + } catch(std::regex_error err) { + Log::writeErrorLog("Unable to parse regex '" + _regex + "': " + err.what()); + Message(ICON_ERROR, "Error", "Unable to parse the regex for the files.", 1200); + return 1; + } catch(...) { + Message(ICON_ERROR, "Error", "Unknown error occured while parsing the regex for the files.", 1200); + return 1; + } + } + + if (!_folderRegex.empty()) { + try { + std::regex regP = std::regex(_folderRegex); + } catch(std::regex_error err) { + Log::writeErrorLog("Unable to parse regex for folder '" + _folderRegex + "': " + err.what()); + Message(ICON_ERROR, "Error", "Unable to parse the regex for the folders.", 1200); + return 1; + } catch(...) { + Message(ICON_ERROR, "Error", "Unknown error occured while parsing the regex for the folders.", 1200); + return 1; + } + } + + FileHandler::update(_regex, _folderRegex, _extensionList, _invertMatch); + + return 3; + } + else if (IsInRect(x, y, &_cancelButton)) + { + return -1; + } + + return 0; +} + +void ExcludeFileView::keyboardHandlerStatic(char *text) +{ + if (_excludeFileViewStatic != nullptr) { + _excludeFileViewStatic->keyboardHandler(text); + } +} + +void ExcludeFileView::keyboardHandler(char *text) +{ + if (!text) + return; + + string s(text); + //if (s.empty()) + // return; + + if (_target == ExcludeFileKeyboardTarget::IEXTENSIONS) + { + _extensionList = s.c_str(); + FillAreaRect(&_extensionListButton, WHITE); + DrawTextRect2(&_extensionListButton, s.c_str()); + } + else if (_target == ExcludeFileKeyboardTarget::IREGEX) + { + _regex = s.c_str(); + FillAreaRect(&_regexButton, WHITE); + DrawTextRect2(&_regexButton, s.c_str()); + } + else if (_target == ExcludeFileKeyboardTarget::IFOLDERREGEX) + { + _folderRegex = s.c_str(); + FillAreaRect(&_folderRegexButton, WHITE); + DrawTextRect2(&_folderRegexButton, s.c_str()); + } + else if (_target == ExcludeFileKeyboardTarget::ISTARTFOLDER) + { + _startFolder = s.c_str(); + if (_startFolder.length() > 1 && _startFolder != "/") { + if (_startFolder.substr(0, 1) != "/") { + _startFolder = "/" + _startFolder; + } + if (_startFolder.substr(_startFolder.length() - 1) != "/") { + _startFolder = _startFolder + "/"; + } + } + FillAreaRect(&_startFolderButton, WHITE); + DrawTextRect2(&_startFolderButton, _startFolder.c_str()); + } +} diff --git a/src/ui/excludeFileView/excludeFileView.h b/src/ui/excludeFileView/excludeFileView.h new file mode 100644 index 0000000..57801ab --- /dev/null +++ b/src/ui/excludeFileView/excludeFileView.h @@ -0,0 +1,92 @@ +//------------------------------------------------------------------ +// excludeFileView.h +// +// Author: RPJoshL +// Date: 03.10.2022 +// +//------------------------------------------------------------------- + +#include + +#ifndef EXCLUDEFILEVIEW +#define EXCLUDEFILEVIEW + +using std::string; + +#include "inkview.h" + +#include +#include + +enum class ExcludeFileKeyboardTarget +{ + IEXTENSIONS, + IREGEX, + IFOLDERREGEX, + ISTARTFOLDER, +}; + +const int EXCLUDE_FILE_KEYBOARD_STRING_LENGHT = 90; + +class ExcludeFileView +{ +public: + /** + * Draws the excludeFileView including an textfield with Extension list and regex, checkbox for inverting the "excluding" and the + * save and cancel button inside the contentRect + * + * @param contentRect area where the excludeFileView shall be drawn + */ + ExcludeFileView(const irect &contentRect); + ~ExcludeFileView(); + + /** + * Checks which part of the view is shown and reacts accordingly + * + * @param x x-coordinate + * @param y y-coordinate + * @return int if event has been handled. Returns 2 if save has been clicked and all items are set, or -1 when cancel was fired + */ + int excludeClicked(int x, int y); + + std::string getExtensionList() { return _extensionList; }; + std::string getRegex() { return _regex; }; + std::string getFolderRegex() { return _folderRegex; }; + std::string getStartFolder() { return _startFolder; }; + int getInvertMatch() { return _invertMatch; }; + +private: + static ExcludeFileView *_excludeFileViewStatic; + int _fontHeight; + ifont *_font; + const irect _contentRect; + irect _saveButton; + irect _cancelButton; + irect _extensionListButton; + irect _regexButton; + irect _invertMatchButton; + irect _folderRegexButton; + irect _startFolderButton; + ExcludeFileKeyboardTarget _target; + std::string _extensionList; + std::string _regex; + std::string _folderRegex; + std::string _startFolder; + bool _invertMatch = false; + std::string _temp; + + /** + * Functions needed to call C function, handles the keyboard + * + * @param text text that has been typed in by the user + */ + static void keyboardHandlerStatic(char *text); + + /** + * Called by the static method and saves and writes the input from the user to the screen + * + * @param text text that has been typed in by the user + */ + void keyboardHandler(char *text); +}; +#endif \ No newline at end of file diff --git a/src/ui/webDAVView/webDAVView.cpp b/src/ui/webDAVView/webDAVView.cpp index ab42922..42648cf 100644 --- a/src/ui/webDAVView/webDAVView.cpp +++ b/src/ui/webDAVView/webDAVView.cpp @@ -17,8 +17,14 @@ using std::vector; -WebDAVView::WebDAVView(const irect &contentRect, vector &items, int page) : ListView(contentRect, page) +WebDAVView::WebDAVView(const irect &contentRect, vector &itemsUnfiltered, int page) : ListView(contentRect, page) { + vector items; + std::copy_if (itemsUnfiltered.begin(), itemsUnfiltered.end(), std::back_inserter(items), [](WebDAVItem i) + { + return i.hide != HideState::IHIDE; + }); + auto pageHeight = 0; auto contentHeight = _contentRect.h - _footerHeight; auto entrycount = items.size(); @@ -35,7 +41,9 @@ WebDAVView::WebDAVView(const irect &contentRect, vector &items, int std::vector::iterator begin; - if (items.at(0).path.compare(NEXTCLOUD_ROOT_PATH) == 0) + string rootPath = WebDAV::getRootPath(false); + string parentRootPath = rootPath.substr(0, rootPath.substr(0, rootPath.length() - 1).find_last_of("/") + 1); + if (items.at(0).path.compare(parentRootPath) == 0) { items.erase(items.begin()); begin = items.begin(); @@ -47,7 +55,7 @@ WebDAVView::WebDAVView(const irect &contentRect, vector &items, int sort(begin, items.end(), []( WebDAVItem &w1, WebDAVItem &w2) -> bool { - if(Util::accessConfig(Action::IReadInt, "sortBy", 0) == 2) + if(Util::getConfig("sortBy", -1) == 2) { //sort by lastmodified time_t t1 = mktime(&w1.lastEditDate); diff --git a/src/util/util.cpp b/src/util/util.cpp index 1939984..07f1895 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -100,6 +100,18 @@ void Util::decodeUrl(string &text) curl_easy_cleanup(curl); } +void Util::encodeUrl(string &text) +{ + char *buffer; + CURL *curl = curl_easy_init(); + + buffer = curl_easy_escape(curl, text.c_str(), 0); + text = buffer; + + curl_free(buffer); + curl_easy_cleanup(curl); +} + void kill_child(int sig) { //SIGKILL diff --git a/src/util/util.h b/src/util/util.h index 2f37d2f..6f0e485 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -15,17 +15,9 @@ #include "log.h" #include -enum class Action -{ - IWriteSecret, - IReadSecret, - IWriteString, - IReadString, - IWriteInt, - IReadInt -}; +using std::string; -const std::string CONFIG_PATH = CONFIG_FOLDER + "/nextcloud.cfg"; +const std::string CONFIG_PATH = "/mnt/ext1/system/config/nextcloud/nextcloud.cfg"; class Util { @@ -50,17 +42,45 @@ class Util static bool connectToNetwork(); /** - * Read and write access to config file + * Writes a value to the config + * T defines the type of the item (e.g. int, string etc.) + * + * @param name of the requested item + * @param value that shall be written + * @param secret store the config securely + */ + template + static void writeConfig(const std::string &name, T value, bool secret = false) + { + iconfigedit *temp = nullptr; + iconfig *config = OpenConfig(CONFIG_PATH.c_str(), temp); + + if constexpr(std::is_same::value) + { + if (secret) + WriteSecret(config, name.c_str(), value.c_str()); + else + WriteString(config, name.c_str(), value.c_str()); + } + else if constexpr(std::is_same::value) + { + WriteInt(config, name.c_str(), value); + } + CloseConfig(config); + } + + /** + * Reads the value from the config file * T defines the type of the item (e.g. int, string etc.) * - * @param action option that shall be done * @param name of the requested item - * @param value that shall be written in case + * @param defaultValue value to return when no was found + * @param secret load the config from the secure storage * - * @return value that is saved in case + * @return value from config */ template - static T accessConfig(const Action &action, const std::string &name, T value) + static T getConfig(string name, T defaultValue = "error", bool secret = false) { iconfigedit *temp = nullptr; iconfig *config = OpenConfig(CONFIG_PATH.c_str(), temp); @@ -68,40 +88,14 @@ class Util if constexpr(std::is_same::value) { - switch (action) - { - case Action::IWriteSecret: - WriteSecret(config, name.c_str(), value.c_str()); - returnValue = {}; - break; - case Action::IReadSecret: - returnValue = ReadSecret(config, name.c_str(), "error"); - break; - case Action::IWriteString: - WriteString(config, name.c_str(), value.c_str()); - returnValue = {}; - break; - case Action::IReadString: - returnValue = ReadString(config, name.c_str(), "error"); - break; - default: - break; - } + if (secret) + returnValue = ReadSecret(config, name.c_str(), defaultValue.c_str()); + else + returnValue = ReadString(config, name.c_str(), ((std::string) defaultValue).c_str()); } else if constexpr(std::is_same::value) { - switch(action) - { - case Action::IWriteInt: - WriteInt(config, name.c_str(), value); - returnValue = 0; - break; - case Action::IReadInt: - returnValue = ReadInt(config, name.c_str(), -1); - break; - default: - break; - } + returnValue = ReadInt(config, name.c_str(), defaultValue); } CloseConfig(config); @@ -126,6 +120,13 @@ class Util */ static void decodeUrl(std::string &text); + /** + * Encodes an URL + * + * @param text text that shall be converted + */ + static void encodeUrl(std::string &text); + /** * Updates the library of the Pocketbook *