From 6f54ccd44cf2125386927b0d01b4571d122dcfd2 Mon Sep 17 00:00:00 2001 From: RPJosh Date: Tue, 4 Oct 2022 00:25:39 +0200 Subject: [PATCH 1/5] Add option to ignore files and folders --- CMakeLists.txt | 3 + src/api/fileBrowser.cpp | 17 +- src/api/fileBrowser.h | 4 + src/api/sqliteConnector.cpp | 49 ++++- src/api/sqliteConnector.h | 7 + src/api/webDAV.cpp | 22 ++- src/api/webDAV.h | 8 +- src/handler/eventHandler.cpp | 76 +++++++- src/handler/eventHandler.h | 7 + src/handler/fileHandler.cpp | 138 ++++++++++++++ src/handler/fileHandler.h | 43 +++++ src/handler/mainMenu.cpp | 8 +- src/handler/mainMenu.h | 1 + src/ui/excludeFileView/excludeFileView.cpp | 200 +++++++++++++++++++++ src/ui/excludeFileView/excludeFileView.h | 88 +++++++++ src/util/util.h | 29 +++ 16 files changed, 687 insertions(+), 13 deletions(-) create mode 100644 src/handler/fileHandler.cpp create mode 100644 src/handler/fileHandler.h create mode 100644 src/ui/excludeFileView/excludeFileView.cpp create mode 100644 src/ui/excludeFileView/excludeFileView.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 31ee8d4..47b51e4 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,6 +80,7 @@ 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/ ) 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..74824ea 100644 --- a/src/api/sqliteConnector.cpp +++ b/src/api/sqliteConnector.cpp @@ -11,6 +11,8 @@ #include "sqlite3.h" #include "log.h" #include "util.h" +#include "fileHandler.h" +#include "webDAV.h" #include #include @@ -18,11 +20,13 @@ using std::string; SqliteConnector::SqliteConnector(const string &DBpath) : _dbpath(DBpath) { + _fileHandler = std::shared_ptr(new FileHandler()); } SqliteConnector::~SqliteConnector() { sqlite3_close(_db); + _fileHandler.reset(); Log::writeInfoLog("closed DB"); } @@ -119,11 +123,13 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa int rs; sqlite3_stmt *stmt = 0; std::vector items; + std::vector itemsToRemove; 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_bind_text(stmt, 1, parentPath.c_str(), parentPath.length(), NULL); rs = sqlite3_bind_text(stmt, 2, parentPath.c_str(), parentPath.length(), NULL); + const int storageLocationLength = (NEXTCLOUD_ROOT_PATH + _fileHandler->getStorageUsername() + "/").length(); while (sqlite3_step(stmt) == SQLITE_ROW) { WebDAVItem temp; @@ -144,14 +150,55 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa temp.state = FileState::ICLOUD; } - items.push_back(temp); + string direcotryPath = temp.path; + if (direcotryPath.length() >= storageLocationLength) { + direcotryPath = direcotryPath.substr(storageLocationLength); + } + if (temp.type == Itemtype::IFILE && ( _fileHandler->excludeFolder(direcotryPath) || _fileHandler->excludeFile(temp.title))) { + //The file was previously cached and should be excluded from now on + //TODO Maybe an SQL statement with REGEX match should be executed directly after changing the conifg + itemsToRemove.push_back(temp); + } else if (temp.type == Itemtype::IFOLDER && _fileHandler->excludeFolder(direcotryPath)) { + itemsToRemove.push_back(temp); + } + else { + items.push_back(temp); + } } sqlite3_finalize(stmt); sqlite3_close(_db); + + for (WebDAVItem itemD : itemsToRemove) { + if (itemD.type == Itemtype::IFOLDER) { + deleteChildren(itemD.path); + } else { + deleteChild(itemD.path, itemD.title); + } + } + 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::deleteChildren(const string &parentPath) { //TODO missing the onces where parentPath is one folder deeper and also destroyed diff --git a/src/api/sqliteConnector.h b/src/api/sqliteConnector.h index 354a822..fe7545d 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: @@ -38,11 +41,15 @@ class SqliteConnector void deleteChildren(const std::string &parentPath); + void deleteChild(const std::string &path, const std::string &title); + 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..b1ac336 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 @@ -37,9 +38,15 @@ WebDAV::WebDAV() _password = Util::accessConfig(Action::IReadSecret,"password",{}); _url = Util::accessConfig(Action::IReadString, "url",{}); _ignoreCert = Util::accessConfig(Action::IReadInt, "ignoreCert",{}); + _fileHandler = std::shared_ptr(new FileHandler()); } } +WebDAV::~WebDAV() +{ + _fileHandler.reset(); +} + std::vector WebDAV::login(const string &Url, const string &Username, const string &Pass, bool ignoreCert) { @@ -176,7 +183,20 @@ vector WebDAV::getDataStructure(const string &pathUrl) tempItem.title = tempItem.title.substr(tempItem.title.find_last_of("/") + 1, tempItem.title.length()); Util::decodeUrl(tempItem.title); - tempItems.push_back(tempItem); + string folderPath = tempItem.path; + string prefix = NEXTCLOUD_ROOT_PATH + _username + "/"; + if (tempItem.path.find(prefix) != string::npos) { + folderPath = tempItem.path.substr(prefix.length()); + if (tempItem.type == Itemtype::IFILE && folderPath.length() >= tempItem.title.length()) { + folderPath = folderPath.substr(0, folderPath.length() - tempItem.title.length()); + } + } + + if (tempItem.type == Itemtype::IFILE && ( !_fileHandler->excludeFolder(folderPath) && !_fileHandler->excludeFile(tempItem.title) )) + tempItems.push_back(tempItem); + else if (tempItem.type == Itemtype::IFOLDER && !_fileHandler->excludeFolder(folderPath)) + tempItems.push_back(tempItem); + xmlItem = xmlItem.substr(end + endItem.length()); begin = xmlItem.find(beginItem); } diff --git a/src/api/webDAV.h b/src/api/webDAV.h index 8386041..f462b75 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); @@ -52,5 +56,7 @@ class WebDAV std::string _url; bool _ignoreCert; + std::shared_ptr _fileHandler; + }; #endif diff --git a/src/handler/eventHandler.cpp b/src/handler/eventHandler.cpp index e52a45c..5c5b1a7 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,6 +37,7 @@ 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) { @@ -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"); @@ -232,13 +249,13 @@ 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); break; } //Exit - case 106: + case 107: CloseApp(); break; default: @@ -364,6 +381,42 @@ 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::accessConfig(Action::IWriteString, "ex_extensionList", _excludeFileView->getExtensionList()); + Util::accessConfig(Action::IWriteString, "ex_pattern",_excludeFileView->getRegex()); + Util::accessConfig(Action::IWriteString, "ex_folderPattern",_excludeFileView->getFolderRegex()); + Util::accessConfig(Action::IWriteInt, "ex_invertMatch", _excludeFileView->getInvertMatch()); + + _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(NEXTCLOUD_ROOT_PATH + Util::accessConfig(Action::IReadString,"UUID",{}) + '/'); + 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(NEXTCLOUD_ROOT_PATH + Util::accessConfig(Action::IReadString,"UUID",{}) + '/'); + updateItems(currentWebDAVItems); + drawWebDAVItems(currentWebDAVItems); + } + } + } //if loginView is shown else if (_loginView != nullptr) { @@ -556,6 +609,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 +621,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); } 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..209b246 --- /dev/null +++ b/src/handler/fileHandler.cpp @@ -0,0 +1,138 @@ +//------------------------------------------------------------------ +// fileHandler.cpp +// +// Author: RPJoshL +// Date: 03.10.2022 +// +//------------------------------------------------------------------- + +#include "fileHandler.h" +#include "log.h" +#include "util.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; + string ta = t ? "true" : "false"; + 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; +} + +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::accessConfig(Action::IReadString, "storageLocation",{}) + getStorageUsername() + "/"; +} +string FileHandler::getStorageUsername() { + return Util::accessConfig(Action::IReadString, "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..445a449 --- /dev/null +++ b/src/handler/fileHandler.h @@ -0,0 +1,43 @@ +//------------------------------------------------------------------ +// fileHandler.h +// +// Author: RPJoshL +// Date: 03.10.2022 +// +//------------------------------------------------------------------- + +#ifndef FILEHANDLER +#define FILEHANDLER + +#include +#include +#include + +#include + +class FileHandler +{ + public: + FileHandler(); + ~FileHandler(); + bool excludeFile(std::string filename); + bool excludeFolder(std::string foldername); + + 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..ecec3b2 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 files"); 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..8c6da82 --- /dev/null +++ b/src/ui/excludeFileView/excludeFileView.cpp @@ -0,0 +1,200 @@ +//------------------------------------------------------------------ +// excludeFileView.cpp +// +// Author: RPJoshL +// Date: 03.10.2022 +// +//------------------------------------------------------------------- + +#include "excludeFileView.h" +#include "log.h" +#include "util.h" + +#include + +using std::string; + +std::shared_ptr ExcludeFileView::_excludeFileViewStatic; +ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(contentRect) +{ + _excludeFileViewStatic.reset(this); + + _extensionList = Util::getConfig("ex_extensionList", ""); + _regex = Util::getConfig("ex_pattern", ""); + _folderRegex = Util::getConfig("ex_folderPattern", ""); + _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); + + _invertMatchButton = iRect(_contentRect.w - 2 * checkBoxWidth, beginY + 6 * 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 + 8 * 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 + 8 * 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); +} + +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, &_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()); + } +} diff --git a/src/ui/excludeFileView/excludeFileView.h b/src/ui/excludeFileView/excludeFileView.h new file mode 100644 index 0000000..0d85a20 --- /dev/null +++ b/src/ui/excludeFileView/excludeFileView.h @@ -0,0 +1,88 @@ +//------------------------------------------------------------------ +// 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 +}; + +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; }; + int getInvertMatch() { return _invertMatch; }; + +private: + static std::shared_ptr _excludeFileViewStatic; + int _fontHeight; + ifont *_font; + const irect _contentRect; + irect _saveButton; + irect _cancelButton; + irect _extensionListButton; + irect _regexButton; + irect _invertMatchButton; + irect _folderRegexButton; + ExcludeFileKeyboardTarget _target; + std::string _extensionList; + std::string _regex; + std::string _folderRegex; + 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/util/util.h b/src/util/util.h index 2f37d2f..575dd8e 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -108,6 +108,35 @@ class Util return returnValue; } + /** + * Reads the value from the config file + * T defines the type of the item (e.g. int, string etc.) + * + * @param name of the requested item + * @param defaultValue value to return when no was found + * + * @return value from config + */ + template + static T getConfig(string name, T defaultValue) + { + iconfigedit *temp = nullptr; + iconfig *config = OpenConfig(CONFIG_PATH.c_str(), temp); + T returnValue; + + if constexpr(std::is_same::value) + { + returnValue = ReadString(config, name.c_str(), ((std::string) defaultValue).c_str()); + } + else if constexpr(std::is_same::value) + { + returnValue = ReadInt(config, name.c_str(), defaultValue); + } + CloseConfig(config); + + return returnValue; + } + /** * Returns an integer representing the download progress * From 2f84fec1a90593f59fd0efb200119af8dcf502b1 Mon Sep 17 00:00:00 2001 From: RPJosh Date: Thu, 6 Oct 2022 18:31:38 +0200 Subject: [PATCH 2/5] Save hide state in db --- CMakeLists.txt | 1 + src/api/sqliteConnector.cpp | 136 +++++++++++++++++---- src/api/sqliteConnector.h | 6 + src/api/webDAV.cpp | 20 +-- src/api/webDAVModel.h | 8 ++ src/handler/eventHandler.cpp | 8 +- src/handler/fileHandler.cpp | 28 +++++ src/handler/fileHandler.h | 3 + src/ui/excludeFileView/excludeFileView.cpp | 9 +- src/ui/excludeFileView/excludeFileView.h | 2 +- src/ui/webDAVView/webDAVView.cpp | 8 +- 11 files changed, 182 insertions(+), 47 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47b51e4..c020d50 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ include_directories( ) 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/sqliteConnector.cpp b/src/api/sqliteConnector.cpp index 74824ea..8b464c0 100644 --- a/src/api/sqliteConnector.cpp +++ b/src/api/sqliteConnector.cpp @@ -21,6 +21,12 @@ 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() @@ -30,6 +36,74 @@ SqliteConnector::~SqliteConnector() 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; @@ -42,7 +116,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; } @@ -123,13 +198,16 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa int rs; sqlite3_stmt *stmt = 0; std::vector items; - std::vector itemsToRemove; - 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 int storageLocationLength = (NEXTCLOUD_ROOT_PATH + _fileHandler->getStorageUsername() + "/").length(); + const string storageLocation = NEXTCLOUD_ROOT_PATH + _fileHandler->getStorageUsername() + "/"; while (sqlite3_step(stmt) == SQLITE_ROW) { WebDAVItem temp; @@ -143,6 +221,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) { @@ -150,33 +229,15 @@ std::vector SqliteConnector::getItemsChildren(const string &parentPa temp.state = FileState::ICLOUD; } - string direcotryPath = temp.path; - if (direcotryPath.length() >= storageLocationLength) { - direcotryPath = direcotryPath.substr(storageLocationLength); - } - if (temp.type == Itemtype::IFILE && ( _fileHandler->excludeFolder(direcotryPath) || _fileHandler->excludeFile(temp.title))) { - //The file was previously cached and should be excluded from now on - //TODO Maybe an SQL statement with REGEX match should be executed directly after changing the conifg - itemsToRemove.push_back(temp); - } else if (temp.type == Itemtype::IFOLDER && _fileHandler->excludeFolder(direcotryPath)) { - itemsToRemove.push_back(temp); - } - else { - items.push_back(temp); + 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); - for (WebDAVItem itemD : itemsToRemove) { - if (itemD.type == Itemtype::IFOLDER) { - deleteChildren(itemD.path); - } else { - deleteChild(itemD.path, itemD.title); - } - } - return items; } @@ -199,6 +260,28 @@ void SqliteConnector::deleteChild(const string &path, const string &title) } +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 @@ -232,7 +315,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); @@ -245,6 +328,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 fe7545d..d3f207d 100644 --- a/src/api/sqliteConnector.h +++ b/src/api/sqliteConnector.h @@ -31,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); @@ -43,6 +47,8 @@ class SqliteConnector void deleteChild(const std::string &path, const std::string &title); + bool resetHideState(); + bool saveItemsChildren(const std::vector &children); private: diff --git a/src/api/webDAV.cpp b/src/api/webDAV.cpp index b1ac336..8565c2a 100644 --- a/src/api/webDAV.cpp +++ b/src/api/webDAV.cpp @@ -115,6 +115,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); @@ -183,20 +184,11 @@ 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 folderPath = tempItem.path; - string prefix = NEXTCLOUD_ROOT_PATH + _username + "/"; - if (tempItem.path.find(prefix) != string::npos) { - folderPath = tempItem.path.substr(prefix.length()); - if (tempItem.type == Itemtype::IFILE && folderPath.length() >= tempItem.title.length()) { - folderPath = folderPath.substr(0, folderPath.length() - tempItem.title.length()); - } - } - - if (tempItem.type == Itemtype::IFILE && ( !_fileHandler->excludeFolder(folderPath) && !_fileHandler->excludeFile(tempItem.title) )) - tempItems.push_back(tempItem); - else if (tempItem.type == Itemtype::IFOLDER && !_fileHandler->excludeFolder(folderPath)) - tempItems.push_back(tempItem); - + 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); } 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 5c5b1a7..1d78cc5 100644 --- a/src/handler/eventHandler.cpp +++ b/src/handler/eventHandler.cpp @@ -251,7 +251,8 @@ void EventHandler::mainMenuHandler(const int index) //Info 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 @@ -388,6 +389,7 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) Util::accessConfig(Action::IWriteString, "ex_pattern",_excludeFileView->getRegex()); Util::accessConfig(Action::IWriteString, "ex_folderPattern",_excludeFileView->getFolderRegex()); Util::accessConfig(Action::IWriteInt, "ex_invertMatch", _excludeFileView->getInvertMatch()); + _sqllite.resetHideState(); _excludeFileView.reset(); ShowHourglassForce(); @@ -655,6 +657,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/fileHandler.cpp b/src/handler/fileHandler.cpp index 209b246..e3f385c 100644 --- a/src/handler/fileHandler.cpp +++ b/src/handler/fileHandler.cpp @@ -9,6 +9,7 @@ #include "fileHandler.h" #include "log.h" #include "util.h" +#include "webDAVModel.h" #include #include @@ -124,6 +125,33 @@ bool FileHandler::excludeFolder(std::string folderName) { 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); diff --git a/src/handler/fileHandler.h b/src/handler/fileHandler.h index 445a449..e31b1fe 100644 --- a/src/handler/fileHandler.h +++ b/src/handler/fileHandler.h @@ -9,6 +9,8 @@ #ifndef FILEHANDLER #define FILEHANDLER +#include "webDAVModel.h" + #include #include #include @@ -22,6 +24,7 @@ class 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(); diff --git a/src/ui/excludeFileView/excludeFileView.cpp b/src/ui/excludeFileView/excludeFileView.cpp index 8c6da82..8bcd088 100644 --- a/src/ui/excludeFileView/excludeFileView.cpp +++ b/src/ui/excludeFileView/excludeFileView.cpp @@ -14,10 +14,10 @@ using std::string; -std::shared_ptr ExcludeFileView::_excludeFileViewStatic; +ExcludeFileView* ExcludeFileView::_excludeFileViewStatic; ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(contentRect) { - _excludeFileViewStatic.reset(this); + _excludeFileViewStatic = this; _extensionList = Util::getConfig("ex_extensionList", ""); _regex = Util::getConfig("ex_pattern", ""); @@ -77,6 +77,7 @@ ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(content 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) @@ -176,8 +177,8 @@ void ExcludeFileView::keyboardHandler(char *text) return; string s(text); - if (s.empty()) - return; + //if (s.empty()) + // return; if (_target == ExcludeFileKeyboardTarget::IEXTENSIONS) { diff --git a/src/ui/excludeFileView/excludeFileView.h b/src/ui/excludeFileView/excludeFileView.h index 0d85a20..b3e10c6 100644 --- a/src/ui/excludeFileView/excludeFileView.h +++ b/src/ui/excludeFileView/excludeFileView.h @@ -54,7 +54,7 @@ class ExcludeFileView int getInvertMatch() { return _invertMatch; }; private: - static std::shared_ptr _excludeFileViewStatic; + static ExcludeFileView *_excludeFileViewStatic; int _fontHeight; ifont *_font; const irect _contentRect; diff --git a/src/ui/webDAVView/webDAVView.cpp b/src/ui/webDAVView/webDAVView.cpp index ab42922..67a7e51 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(); From 75dd96222ed8baf8b5484b4a6fe10c6ea13a2365 Mon Sep 17 00:00:00 2001 From: RPJosh Date: Thu, 6 Oct 2022 18:31:38 +0200 Subject: [PATCH 3/5] Add option to set the default start folder when opening the app --- src/api/sqliteConnector.cpp | 24 +++++++++++ src/api/sqliteConnector.h | 4 +- src/api/webDAV.cpp | 46 +++++++++++++++++++++- src/api/webDAV.h | 6 +++ src/handler/eventHandler.cpp | 15 ++++--- src/handler/mainMenu.h | 2 +- src/ui/excludeFileView/excludeFileView.cpp | 35 ++++++++++++++-- src/ui/excludeFileView/excludeFileView.h | 6 ++- src/ui/webDAVView/webDAVView.cpp | 4 +- src/util/util.cpp | 12 ++++++ src/util/util.h | 11 +++++- 11 files changed, 150 insertions(+), 15 deletions(-) diff --git a/src/api/sqliteConnector.cpp b/src/api/sqliteConnector.cpp index 8b464c0..278d920 100644 --- a/src/api/sqliteConnector.cpp +++ b/src/api/sqliteConnector.cpp @@ -16,6 +16,8 @@ #include #include +#include + using std::string; SqliteConnector::SqliteConnector(const string &DBpath) : _dbpath(DBpath) @@ -259,6 +261,28 @@ void SqliteConnector::deleteChild(const string &path, const string &title) 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() { diff --git a/src/api/sqliteConnector.h b/src/api/sqliteConnector.h index d3f207d..f0bfe9f 100644 --- a/src/api/sqliteConnector.h +++ b/src/api/sqliteConnector.h @@ -32,7 +32,7 @@ class SqliteConnector bool open(); int getDbVersion(); - + void runMigration(int currentVersion); std::string getEtag(const std::string &path); @@ -47,6 +47,8 @@ class SqliteConnector void deleteChild(const std::string &path, const std::string &title); + void deleteItemsNotBeginsWith(std::string beginPath); + bool resetHideState(); bool saveItemsChildren(const std::vector &children); diff --git a/src/api/webDAV.cpp b/src/api/webDAV.cpp index 8565c2a..0f5d6f1 100644 --- a/src/api/webDAV.cpp +++ b/src/api/webDAV.cpp @@ -19,6 +19,7 @@ #include #include #include +#include using std::ifstream; using std::ofstream; @@ -27,6 +28,20 @@ using std::vector; namespace fs = std::experimental::filesystem; +std::string WebDAV::getRootPath(bool encode) { + string rootPath = Util::getConfig("ex_relativeRootPath", "/"); + if (rootPath == "") + rootPath += "/"; + + string rtc = NEXTCLOUD_ROOT_PATH + Util::getConfig("UUID", "") + rootPath; + if (encode) { + Util::encodeUrl(rtc); + rtc = std::regex_replace(rtc, std::regex("%2F"), "/"); + } + + return rtc; +} + WebDAV::WebDAV() { if (iv_access(NEXTCLOUD_PATH.c_str(), W_OK) != 0) @@ -184,7 +199,7 @@ 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; + string pathDecoded = tempItem.path; Util::decodeUrl(pathDecoded); tempItem.hide = _fileHandler->getHideState(tempItem.type, prefix,(pathDecoded), tempItem.title); @@ -233,6 +248,7 @@ string WebDAV::propfind(const string &pathUrl) string readBuffer; CURLcode res; CURL *curl = curl_easy_init(); + Log::writeInfoLog("Path: " + pathUrl); if (curl) { @@ -274,7 +290,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::accessConfig(Action::IWriteString, "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 f462b75..255c21f 100644 --- a/src/api/webDAV.h +++ b/src/api/webDAV.h @@ -38,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 * diff --git a/src/handler/eventHandler.cpp b/src/handler/eventHandler.cpp index 1d78cc5..74bf423 100644 --- a/src/handler/eventHandler.cpp +++ b/src/handler/eventHandler.cpp @@ -49,7 +49,7 @@ EventHandler::EventHandler() iv_mkdir(Util::accessConfig(Action::IReadString, "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")); @@ -234,7 +234,7 @@ 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",{}) + '/'); + std::vector currentWebDAVItems = _webDAV.getDataStructure(WebDAV::getRootPath(true)); if (currentWebDAVItems.empty()) { Message(ICON_ERROR, "Error", "Failed to get items. Please try again.", 1000); @@ -388,8 +388,14 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) Util::accessConfig(Action::IWriteString, "ex_extensionList", _excludeFileView->getExtensionList()); Util::accessConfig(Action::IWriteString, "ex_pattern",_excludeFileView->getRegex()); Util::accessConfig(Action::IWriteString, "ex_folderPattern",_excludeFileView->getFolderRegex()); + Util::accessConfig(Action::IWriteString, "ex_relativeRootPath", _excludeFileView->getStartFolder()); Util::accessConfig(Action::IWriteInt, "ex_invertMatch", _excludeFileView->getInvertMatch()); + _sqllite.resetHideState(); + if (_excludeFileView->getStartFolder() != "") + { + _sqllite.deleteItemsNotBeginsWith(WebDAV::getRootPath(true)); + } _excludeFileView.reset(); ShowHourglassForce(); @@ -399,7 +405,7 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) vector currentFolder = FileBrowser::getFileStructure(_currentPath,false,true); _fileView.reset(new FileView(_menu->getContentRect(), currentFolder,1)); } else { - std::vector currentWebDAVItems = _webDAV.getDataStructure(NEXTCLOUD_ROOT_PATH + Util::accessConfig(Action::IReadString,"UUID",{}) + '/'); + std::vector currentWebDAVItems = _webDAV.getDataStructure(WebDAV::getRootPath(true)); updateItems(currentWebDAVItems); drawWebDAVItems(currentWebDAVItems); } @@ -413,7 +419,7 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) vector currentFolder = FileBrowser::getFileStructure(_currentPath,false,true); _fileView.reset(new FileView(_menu->getContentRect(), currentFolder,1)); } else { - std::vector currentWebDAVItems = _webDAV.getDataStructure(NEXTCLOUD_ROOT_PATH + Util::accessConfig(Action::IReadString,"UUID",{}) + '/'); + std::vector currentWebDAVItems = _webDAV.getDataStructure(WebDAV::getRootPath(true)); updateItems(currentWebDAVItems); drawWebDAVItems(currentWebDAVItems); } @@ -509,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)) diff --git a/src/handler/mainMenu.h b/src/handler/mainMenu.h index ecec3b2..369c6ac 100644 --- a/src/handler/mainMenu.h +++ b/src/handler/mainMenu.h @@ -55,7 +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 files"); + 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 index 8bcd088..af67186 100644 --- a/src/ui/excludeFileView/excludeFileView.cpp +++ b/src/ui/excludeFileView/excludeFileView.cpp @@ -22,6 +22,7 @@ ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(content _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; @@ -54,20 +55,25 @@ ExcludeFileView::ExcludeFileView(const irect &contentRect): _contentRect(content DrawString(_folderRegexButton.x, _folderRegexButton.y, _folderRegex.c_str()); DrawRect(_folderRegexButton.x - 1, _folderRegexButton.y - 1, _folderRegexButton.w + 2, _folderRegexButton.h + 2, BLACK); - _invertMatchButton = iRect(_contentRect.w - 2 * checkBoxWidth, beginY + 6 * contents, checkBoxWidth, contents, ALIGN_CENTER); + _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 + 8 * contents, contentWidth / 2 - 20, contents, ALIGN_CENTER); + _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 + 8 * contents, contentWidth / 2 - 20, contents, ALIGN_CENTER); + _cancelButton = iRect(beginX + contentWidth / 2, beginY + 10 * contents, contentWidth / 2 - 20, contents, ALIGN_CENTER); FillAreaRect(&_cancelButton, BLACK); SetFont(_font, WHITE); DrawTextRect2(&_cancelButton, "Cancel"); @@ -111,6 +117,15 @@ int ExcludeFileView::excludeClicked(int x, int y) 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; @@ -198,4 +213,18 @@ void ExcludeFileView::keyboardHandler(char *text) 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 index b3e10c6..57801ab 100644 --- a/src/ui/excludeFileView/excludeFileView.h +++ b/src/ui/excludeFileView/excludeFileView.h @@ -22,7 +22,8 @@ enum class ExcludeFileKeyboardTarget { IEXTENSIONS, IREGEX, - IFOLDERREGEX + IFOLDERREGEX, + ISTARTFOLDER, }; const int EXCLUDE_FILE_KEYBOARD_STRING_LENGHT = 90; @@ -51,6 +52,7 @@ class ExcludeFileView 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: @@ -64,10 +66,12 @@ class ExcludeFileView 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; diff --git a/src/ui/webDAVView/webDAVView.cpp b/src/ui/webDAVView/webDAVView.cpp index 67a7e51..c7476a2 100644 --- a/src/ui/webDAVView/webDAVView.cpp +++ b/src/ui/webDAVView/webDAVView.cpp @@ -41,7 +41,9 @@ WebDAVView::WebDAVView(const irect &contentRect, vector &itemsUnfilt 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(); 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 575dd8e..1996ce9 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -15,6 +15,8 @@ #include "log.h" #include +using std::string; + enum class Action { IWriteSecret, @@ -25,7 +27,7 @@ enum class Action IReadInt }; -const std::string CONFIG_PATH = CONFIG_FOLDER + "/nextcloud.cfg"; +const std::string CONFIG_PATH = "/mnt/ext1/system/config/nextcloud/nextcloud.cfg"; class Util { @@ -155,6 +157,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 * From 822995981e33afaf712b051a995543032b17b7c8 Mon Sep 17 00:00:00 2001 From: RPJosh Date: Tue, 11 Oct 2022 19:38:44 +0200 Subject: [PATCH 4/5] Fix login on first startup and logout crash --- src/api/webDAV.cpp | 19 +++++++++++++------ src/handler/eventHandler.cpp | 2 +- src/handler/fileHandler.cpp | 1 - 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/api/webDAV.cpp b/src/api/webDAV.cpp index 0f5d6f1..92cb713 100644 --- a/src/api/webDAV.cpp +++ b/src/api/webDAV.cpp @@ -33,7 +33,12 @@ std::string WebDAV::getRootPath(bool encode) { if (rootPath == "") rootPath += "/"; - string rtc = NEXTCLOUD_ROOT_PATH + Util::getConfig("UUID", "") + 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"), "/"); @@ -44,6 +49,8 @@ std::string WebDAV::getRootPath(bool encode) { 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); @@ -53,7 +60,6 @@ WebDAV::WebDAV() _password = Util::accessConfig(Action::IReadSecret,"password",{}); _url = Util::accessConfig(Action::IReadString, "url",{}); _ignoreCert = Util::accessConfig(Action::IReadInt, "ignoreCert",{}); - _fileHandler = std::shared_ptr(new FileHandler()); } } @@ -108,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::accessConfig(Action::IReadString, "storageLocation",{}) + "/" + Util::accessConfig(Action::IReadString,"UUID",{}) + '/'; + if (fs::exists(filesPath)) + fs::remove_all(filesPath); } fs::remove(CONFIG_PATH.c_str()); fs::remove((CONFIG_PATH + ".back.").c_str()); @@ -201,8 +209,8 @@ vector WebDAV::getDataStructure(const string &pathUrl) string pathDecoded = tempItem.path; Util::decodeUrl(pathDecoded); - tempItem.hide = _fileHandler->getHideState(tempItem.type, prefix,(pathDecoded), tempItem.title); - + tempItem.hide = _fileHandler->getHideState(tempItem.type, prefix,pathDecoded, tempItem.title); + tempItems.push_back(tempItem); xmlItem = xmlItem.substr(end + endItem.length()); begin = xmlItem.find(beginItem); @@ -248,7 +256,6 @@ string WebDAV::propfind(const string &pathUrl) string readBuffer; CURLcode res; CURL *curl = curl_easy_init(); - Log::writeInfoLog("Path: " + pathUrl); if (curl) { diff --git a/src/handler/eventHandler.cpp b/src/handler/eventHandler.cpp index 74bf423..b352e7b 100644 --- a/src/handler/eventHandler.cpp +++ b/src/handler/eventHandler.cpp @@ -432,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); diff --git a/src/handler/fileHandler.cpp b/src/handler/fileHandler.cpp index e3f385c..c9815e3 100644 --- a/src/handler/fileHandler.cpp +++ b/src/handler/fileHandler.cpp @@ -93,7 +93,6 @@ bool FileHandler::excludeFile(std::string filename) { if (_useRegex) { try { bool t = std::regex_match(filename, _regex) != _invertMatch; - string ta = t ? "true" : "false"; return t; } catch (std::regex_error err) { string errM = err.what(); From 381d66860eed5beaa081d05e32f846754c538006 Mon Sep 17 00:00:00 2001 From: RPJosh Date: Tue, 11 Oct 2022 20:03:52 +0200 Subject: [PATCH 5/5] Separate accessConfig to read and write --- src/api/webDAV.cpp | 26 ++++++------- src/handler/eventHandler.cpp | 28 ++++++------- src/handler/fileHandler.cpp | 4 +- src/ui/webDAVView/webDAVView.cpp | 2 +- src/util/util.h | 67 +++++++------------------------- 5 files changed, 45 insertions(+), 82 deletions(-) diff --git a/src/api/webDAV.cpp b/src/api/webDAV.cpp index 92cb713..2f8eb6b 100644 --- a/src/api/webDAV.cpp +++ b/src/api/webDAV.cpp @@ -56,10 +56,10 @@ WebDAV::WebDAV() 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); } } @@ -89,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 { @@ -114,7 +114,7 @@ void WebDAV::logout(bool deleteFiles) { if (deleteFiles) { - string filesPath = 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); } @@ -189,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() == '/') @@ -311,7 +311,7 @@ string WebDAV::propfind(const string &pathUrl) { case 1: { - Util::accessConfig(Action::IWriteString, "ex_relativeRootPath", ""); + Util::writeConfig("ex_relativeRootPath", ""); return propfind(NEXTCLOUD_ROOT_PATH + Util::getConfig("UUID", "")); } break; diff --git a/src/handler/eventHandler.cpp b/src/handler/eventHandler.cpp index b352e7b..0d31eee 100644 --- a/src/handler/eventHandler.cpp +++ b/src/handler/eventHandler.cpp @@ -42,11 +42,11 @@ EventHandler::EventHandler() 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 = WebDAV::getRootPath(true); @@ -194,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; @@ -233,7 +233,7 @@ void EventHandler::mainMenuHandler(const int index) } else { - Util::accessConfig(Action::IWriteString, "storageLocation", _currentPath); + Util::writeConfig("storageLocation", _currentPath); std::vector currentWebDAVItems = _webDAV.getDataStructure(WebDAV::getRootPath(true)); if (currentWebDAVItems.empty()) { @@ -385,11 +385,11 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) else if (_excludeFileView != nullptr) { int click = _excludeFileView->excludeClicked(par1, par2); if (click == 3) { - Util::accessConfig(Action::IWriteString, "ex_extensionList", _excludeFileView->getExtensionList()); - Util::accessConfig(Action::IWriteString, "ex_pattern",_excludeFileView->getRegex()); - Util::accessConfig(Action::IWriteString, "ex_folderPattern",_excludeFileView->getFolderRegex()); - Util::accessConfig(Action::IWriteString, "ex_relativeRootPath", _excludeFileView->getStartFolder()); - Util::accessConfig(Action::IWriteInt, "ex_invertMatch", _excludeFileView->getInvertMatch()); + 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() != "") @@ -455,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; diff --git a/src/handler/fileHandler.cpp b/src/handler/fileHandler.cpp index c9815e3..8574e47 100644 --- a/src/handler/fileHandler.cpp +++ b/src/handler/fileHandler.cpp @@ -158,8 +158,8 @@ void FileHandler::update(string regex, string folderRegex, string extensions, in } string FileHandler::getStorageLocation() { - return Util::accessConfig(Action::IReadString, "storageLocation",{}) + getStorageUsername() + "/"; + return Util::getConfig("storageLocation") + getStorageUsername() + "/"; } string FileHandler::getStorageUsername() { - return Util::accessConfig(Action::IReadString, "username",{}); + return Util::getConfig("username"); } \ No newline at end of file diff --git a/src/ui/webDAVView/webDAVView.cpp b/src/ui/webDAVView/webDAVView.cpp index c7476a2..42648cf 100644 --- a/src/ui/webDAVView/webDAVView.cpp +++ b/src/ui/webDAVView/webDAVView.cpp @@ -55,7 +55,7 @@ WebDAVView::WebDAVView(const irect &contentRect, vector &itemsUnfilt 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.h b/src/util/util.h index 1996ce9..6f0e485 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -17,16 +17,6 @@ using std::string; -enum class Action -{ - IWriteSecret, - IReadSecret, - IWriteString, - IReadString, - IWriteInt, - IReadInt -}; - const std::string CONFIG_PATH = "/mnt/ext1/system/config/nextcloud/nextcloud.cfg"; class Util @@ -52,62 +42,31 @@ 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 action option that shall be done * @param name of the requested item - * @param value that shall be written in case - * - * @return value that is saved in case + * @param value that shall be written + * @param secret store the config securely */ template - static T accessConfig(const Action &action, const std::string &name, T value) + static void writeConfig(const std::string &name, T value, bool secret = false) { iconfigedit *temp = nullptr; iconfig *config = OpenConfig(CONFIG_PATH.c_str(), temp); - T returnValue; 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) + WriteSecret(config, name.c_str(), value.c_str()); + else + WriteString(config, name.c_str(), value.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; - } + WriteInt(config, name.c_str(), value); } CloseConfig(config); - - return returnValue; } /** @@ -116,11 +75,12 @@ class Util * * @param name of the requested item * @param defaultValue value to return when no was found + * @param secret load the config from the secure storage * * @return value from config */ template - static T getConfig(string name, T defaultValue) + static T getConfig(string name, T defaultValue = "error", bool secret = false) { iconfigedit *temp = nullptr; iconfig *config = OpenConfig(CONFIG_PATH.c_str(), temp); @@ -128,7 +88,10 @@ class Util if constexpr(std::is_same::value) { - returnValue = ReadString(config, name.c_str(), ((std::string) defaultValue).c_str()); + 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) {