diff --git a/src/api/sqliteConnector.cpp b/src/api/sqliteConnector.cpp index 3c33bca..deabb43 100644 --- a/src/api/sqliteConnector.cpp +++ b/src/api/sqliteConnector.cpp @@ -37,32 +37,78 @@ 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, parentPath VARCHAR, key VARCHAR, PRIMARY KEY (key))", 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)", NULL, 0, NULL); return true; } -string SqliteConnector::getEtag(string path) +string SqliteConnector::getEtag(const string &path) { open(); int rs; sqlite3_stmt *stmt = 0; std::vector items; + string etag = "not found"; rs = sqlite3_prepare_v2(_db, "SELECT etag FROM 'metadata' WHERE path = ? LIMIT 1;", -1, &stmt, 0); rs = sqlite3_bind_text(stmt, 1, path.c_str(), path.length(), NULL); - //TODO erase while while (sqlite3_step(stmt) == SQLITE_ROW) { - path = reinterpret_cast(sqlite3_column_text(stmt, 0)); + etag = reinterpret_cast(sqlite3_column_text(stmt, 0)); } sqlite3_finalize(stmt); sqlite3_close(_db); - return path; + return etag; +} + +FileState SqliteConnector::getState(const string &path) +{ + open(); + + int rs; + sqlite3_stmt *stmt = 0; + std::vector items; + FileState state = FileState::ICLOUD; + + rs = sqlite3_prepare_v2(_db, "SELECT state FROM 'metadata' WHERE path = ? LIMIT 1;", -1, &stmt, 0); + rs = sqlite3_bind_text(stmt, 1, path.c_str(), path.length(), NULL); + + while (sqlite3_step(stmt) == SQLITE_ROW) + { + state = static_cast(sqlite3_column_int(stmt,0)); + } + + sqlite3_finalize(stmt); + sqlite3_close(_db); + return state; +} + +bool SqliteConnector::updateState(const string &path, FileState state) +{ + open(); + int rs; + sqlite3_stmt *stmt = 0; + + rs = sqlite3_prepare_v2(_db, "UPDATE 'metadata' SET state=? WHERE path=?", -1, &stmt, 0); + rs = sqlite3_bind_int(stmt, 1, state); + rs = sqlite3_bind_text(stmt, 2, path.c_str(), path.length(), NULL); + 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; } std::vector SqliteConnector::getItemsChildren(const string &parentPath) @@ -73,35 +119,25 @@ 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 parentPath = ?;", -1, &stmt, 0); + 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); while (sqlite3_step(stmt) == SQLITE_ROW) { + WebDAVItem temp; - WebDAVItem temp; - - temp.title = reinterpret_cast(sqlite3_column_text(stmt, 0)); - temp.localPath = reinterpret_cast(sqlite3_column_text(stmt, 1)); - temp.path = reinterpret_cast(sqlite3_column_text(stmt, 2)); - temp.size = reinterpret_cast(sqlite3_column_text(stmt, 3)); - temp.etag = reinterpret_cast(sqlite3_column_text(stmt, 4)); - temp.fileType = reinterpret_cast(sqlite3_column_text(stmt, 5)); - temp.lastEditDate = 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)); - - items.push_back(temp); - // - //TODO also for folders - /* - if (iv_access(tempItem.localPath.c_str(), W_OK) != 0) - tempItem.state = FileState::ICLOUD; - else - tempItem.state = FileState::ISYNCED; - */ + temp.title = reinterpret_cast(sqlite3_column_text(stmt, 0)); + temp.localPath = reinterpret_cast(sqlite3_column_text(stmt, 1)); + temp.path = reinterpret_cast(sqlite3_column_text(stmt, 2)); + temp.size = reinterpret_cast(sqlite3_column_text(stmt, 3)); + temp.etag = reinterpret_cast(sqlite3_column_text(stmt, 4)); + temp.fileType = reinterpret_cast(sqlite3_column_text(stmt, 5)); + temp.lastEditDate = 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)); + items.push_back(temp); } sqlite3_finalize(stmt); @@ -114,7 +150,6 @@ void SqliteConnector::deleteChildren(const string &parentPath) open(); int rs; sqlite3_stmt *stmt = 0; - Log::writeInfoLog(parentPath); rs = sqlite3_prepare_v2(_db, "DELETE FROM 'metadata' WHERE parentPath like ?", -1, &stmt, 0); rs = sqlite3_bind_text(stmt, 1, parentPath.c_str(), parentPath.length(), NULL); @@ -128,14 +163,10 @@ void SqliteConnector::deleteChildren(const string &parentPath) } -//TODO for folder write to all below folder or do singles? -// void SqliteConnector::updateState(const WebDAVItem - bool SqliteConnector::saveItemsChildren(const std::vector &items) { open(); int rs; - string key; sqlite3_stmt *stmt = 0; string parent = items.at(0).path; @@ -144,13 +175,10 @@ bool SqliteConnector::saveItemsChildren(const std::vector &items) //rs = sqlite3_prepare_v2(_db, "INSERT INTO 'metadata' (title, localPath, path, size, parentPath, etag, fileType, lastEditDate, type, state, key) VALUES (?,?,?,?,?,?,?,?,?,?,?) ON CONFLICT(key) DO UPDATE SET etag=?, size=?, lastEditDate=? WHERE metadata.etag <> ?;", -1, &stmt, 0); deleteChildren(parent); - - rs = sqlite3_prepare_v2(_db, "INSERT INTO 'metadata' (title, localPath, path, size, parentPath, etag, fileType, lastEditDate, type, state, key) VALUES (?,?,?,?,?,?,?,?,?,?,?);", -1, &stmt, 0); - rs = sqlite3_exec(_db, "BEGIN TRANSACTION;", NULL, NULL, NULL); - for (auto item : items) { - Log::writeInfoLog("item :" + item.title); + rs = sqlite3_prepare_v2(_db, "INSERT INTO 'metadata' (title, localPath, path, size, parentPath, etag, fileType, lastEditDate, type, state) 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); rs = sqlite3_bind_text(stmt, 3, item.path.c_str(), item.path.length(), NULL); @@ -161,13 +189,27 @@ bool SqliteConnector::saveItemsChildren(const std::vector &items) rs = sqlite3_bind_text(stmt, 8, item.lastEditDate.c_str(), item.lastEditDate.length(), NULL); rs = sqlite3_bind_int(stmt, 9, item.type); rs = sqlite3_bind_int(stmt, 10, item.state); - key = parent + item.title; - rs = sqlite3_bind_text(stmt, 11, key.c_str(),key.length(),NULL); rs = sqlite3_step(stmt); if (rs == SQLITE_CONSTRAINT) { - Log::writeInfoLog("item exists already: " + item.path); + rs = sqlite3_clear_bindings(stmt); + rs = sqlite3_reset(stmt); + + rs = sqlite3_prepare_v2(_db, "UPDATE 'metadata' SET state=?, etag=?, lastEditDate=?, size=? WHERE path=?", -1, &stmt, 0); + rs = sqlite3_bind_int(stmt, 1, item.state); + rs = sqlite3_bind_text(stmt, 2, item.etag.c_str(), item.etag.length(), NULL); + rs = sqlite3_bind_text(stmt, 3, item.lastEditDate.c_str(), item.lastEditDate.length(), NULL); + rs = sqlite3_bind_text(stmt, 4, item.size.c_str(), item.size.length(), NULL); + rs = sqlite3_bind_text(stmt, 5, item.path.c_str(), item.path.length(), NULL); + 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); } else if (rs != SQLITE_DONE) { diff --git a/src/api/sqliteConnector.h b/src/api/sqliteConnector.h index a65d0ca..354a822 100644 --- a/src/api/sqliteConnector.h +++ b/src/api/sqliteConnector.h @@ -28,7 +28,11 @@ public: bool open(); - std::string getEtag(std::string path); + std::string getEtag(const std::string &path); + + FileState getState(const std::string &path); + + bool updateState(const std::string &path, FileState state); std::vector getItemsChildren(const std::string &parenthPath); diff --git a/src/api/webDAV.cpp b/src/api/webDAV.cpp index 75e9ba2..0c78320 100644 --- a/src/api/webDAV.cpp +++ b/src/api/webDAV.cpp @@ -26,10 +26,6 @@ using std::vector; WebDAV::WebDAV() { - - //TODO update on first login only start and create update button, update others just if etag changed - //save all to sqlite - if (iv_access(NEXTCLOUD_PATH.c_str(), W_OK) != 0) iv_mkdir(NEXTCLOUD_PATH.c_str(), 0777); @@ -45,7 +41,6 @@ WebDAV::WebDAV() } -//TODO pass in vector and change that one? std::vector WebDAV::login(const string &Url, const string &Username, const string &Pass) { string uuid; @@ -76,6 +71,7 @@ std::vector WebDAV::login(const string &Url, const string &Username, return tempItems; } +//TODO implement logout void WebDAV::logout(bool deleteFiles) { if (deleteFiles) @@ -91,7 +87,6 @@ void WebDAV::logout(bool deleteFiles) //tempItems.clear(); } -//TODO pas as reversne and no return string WebDAV::getLocalPath(string path) { Util::decodeUrl(path); @@ -101,8 +96,6 @@ string WebDAV::getLocalPath(string path) return NEXTCLOUD_FILE_PATH + "/" + path; } -//TODO SQL CHeck before calling this function --> if is needed... -///TODO rename function vector WebDAV::getDataStructure(const string &pathUrl) { string xmlItem = propfind(pathUrl); @@ -118,7 +111,6 @@ vector WebDAV::getDataStructure(const string &pathUrl) while (begin != std::string::npos) { end = xmlItem.find(endItem); - //TODO use xml lib? //TODO fav is int? //Log::writeInfoLog(Util::getXMLAttribute(xmlItem, "d:favorite")); @@ -169,7 +161,6 @@ vector WebDAV::getDataStructure(const string &pathUrl) tempItem.localPath = tempItem.localPath.substr(0, tempItem.localPath.length() - 1); tempItem.type = Itemtype::IFOLDER; tempItem.title = tempItem.title.substr(0, tempItem.path.length() - 1); - //TODO set sync status of folders --> use sqlite? } else { @@ -190,39 +181,25 @@ vector WebDAV::getDataStructure(const string &pathUrl) begin = xmlItem.find(beginItem); } - - //TODO doppelt if (tempItems.empty()) return {}; - //resize item 1 - string header = tempItems.at(0).path; - header = header.substr(0, header.find_last_of("/")); - header = header.substr(0, header.find_last_of("/") + 1); - tempItems.at(0).path = header; - tempItems.at(0).title += "\nclick to go back"; - tempItems.at(0).lastEditDate = ""; - if (tempItems.at(0).path.compare(NEXTCLOUD_ROOT_PATH) == 0) - tempItems.erase(tempItems.begin()); - string localPath = getLocalPath(pathUrl); //if the current folder does not exist locally, create it if (iv_access(localPath.c_str(), W_OK) != 0) - iv_buildpath(localPath.c_str()); + { + Log::writeInfoLog("Local folder does not exists, creating at " + localPath); + iv_mkdir(localPath.c_str(), 0777); + } return tempItems; } return {}; - //TODO return empty items? } -/* -void Nextcloud::downloadFolder(vector &tempItems, int itemID) -{ - - //TODO etag - BanSleep(2000); +//REMOVE FILE implement TODO + /* if (tempItems.at(itemID).getState() == FileState::ILOCAL) { UpdateProgressbar(("Removing local item " + tempItems.at(itemID).getLocalPath()).c_str(), 0); @@ -249,85 +226,38 @@ void Nextcloud::downloadFolder(vector &tempItems, int itemID) return true; return; } + */ - //check files; where modified date --> modified date of structure - //first check cloud, then check local - - if (tempItems.at(itemID).getType() == Itemtype::IFOLDER) - { - //check etag - - //for folders - //1. check etag --> if is different, cloud has been updated --> need to dig in deeper here; - //2. if is the same, only check local file system and dont show folder anymore - string temp = tempItems.at(itemID).getPath(); - Log::writeInfoLog("Path to look for " + temp); - vector tempItems = getDataStructure(temp); - - //first item of the vector is the root path itself - for (size_t i = 1; i < tempItems.size(); i++) - { - Log::writeInfoLog("Item: " + tempItems.at(i).getPath()); - downloadFolder(tempItems, i); - } - } - else - { - //for files - //1. check etag --> if is differnt, cloud has been updated - //2. check modification date and file size locally --> if is different, local has been updated - - //3. if both --> create conflict - //4. if first, renew file --> reset etag - //5. if second --> upload the local file; test if it has not been update in the cloud - Log::writeInfoLog("started download of " + tempItems.at(itemID).getPath() + " to " + tempItems.at(itemID).getLocalPath()); - get(tempItems, itemID); - } - - return; -} - -void Nextcloud::download(int itemID) -{ - if (!Util::connectToNetwork()) - { - Message(ICON_WARNING, "Warning", "Can not connect to the Internet. Switching to offline modus.", 2000); - _workOffline = true; - return; - } - - this->downloadFolder(tempItems, itemID); - - UpdateProgressbar("Download completed", 100); -} -*/ string WebDAV::propfind(const string &pathUrl) { - //TODO catch here pathUrl is empty! - if (pathUrl.empty()) + if (pathUrl.empty() || _username.empty() || _password.empty()) return ""; - //where to test this?? - //include error messsages - if(_username.empty() || _password.empty()) - return ""; - - //string localPath = getLocalPath(pathUrl); - if (!Util::connectToNetwork()) { - Message(ICON_WARNING, "Warning", "Cannot connect to the internet. Switching to offline modus. To work online turn on online modus in the menu.", 2000); + Message(ICON_WARNING, "Warning", "Cannot connect to the internet. ", 2000); return ""; } - // + + //TODO for upload //get etag from current and then send request with FT_ENC_TAG //need path url and also the etag - //can handle multiple etags --> * if exists + //to use for upload + //curl -I --header 'If-None-Match: "XX"' -u username:password url/test.md + //If-Unmodified-Since + //to use for download + // curl -I --header 'If-Match: "XX"' -u username:password url/test.md + //If-Modified-Since -*/ + //If-None-Match: "XX" + //--header 'If-None-Match: "XX"' + //depth more to get here also childs? --> and depth less to see changes + //case 412 Precondition failed --> etag matches for file + //case 304: + //content not modified string readBuffer; @@ -337,7 +267,6 @@ string WebDAV::propfind(const string &pathUrl) if (curl) { string post = _username + ":" + _password; - Log::writeInfoLog(_url + pathUrl); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Depth: 1"); @@ -376,8 +305,7 @@ string WebDAV::propfind(const string &pathUrl) return readBuffer; break; default: - Message(ICON_ERROR, "Error", ("An unknown error occured. Switching to offline modus. To work online turn on online modus in the menu. (Curl Response Code " + std::to_string(response_code) + ")").c_str(), 4000); - //TODO change default msg + Message(ICON_ERROR, "Error", ("An unknown error occured. (Curl Response Code " + std::to_string(response_code) + ")").c_str(), 5000); } } else @@ -390,45 +318,37 @@ string WebDAV::propfind(const string &pathUrl) return ""; } -/* -void WebDAV::get(vector &tempItems, int itemID) +bool WebDAV::get(WebDAVItem &item) { - //CHECK id - if (tempItems.at(itemID).getState() == FileState::ISYNCED) + if (item.state == FileState::ISYNCED) { - UpdateProgressbar(("The newest version of file " + tempItems.at(itemID).getLocalPath() + " is already downloaded.").c_str(), 0); - return; + UpdateProgressbar(("The newest version of file " + item.path + " is already downloaded.").c_str(), 0); + return false; } - if (tempItems.at(itemID).getPath().empty()) + if (item.path.empty()) { Message(ICON_ERROR, "Error", "Download path is not set, therefore cannot download the file.", 2000); - return; + return false; } - UpdateProgressbar(("Starting Download of " + tempItems.at(itemID).getLocalPath()).c_str(), 0); + UpdateProgressbar(("Starting Download of " + item.localPath).c_str(), 0); CURLcode res; CURL *curl = curl_easy_init(); if (curl) { - string post = this->getUsername() + std::string(":") + this->getPassword(); + string post = _username + std::string(":") + _password; FILE *fp; - fp = iv_fopen(tempItems.at(itemID).getLocalPath().c_str(), "wb"); + fp = iv_fopen(item.localPath.c_str(), "wb"); - curl_easy_setopt(curl, CURLOPT_URL, (_url + tempItems.at(itemID).getPath()).c_str()); + curl_easy_setopt(curl, CURLOPT_URL, (_url + item.path).c_str()); curl_easy_setopt(curl, CURLOPT_USERPWD, post.c_str()); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Util::writeData); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, false); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, Util::progress_callback); - //in case that cacert is available use it - if (iv_access(CACERT_PATH.c_str(), W_OK) == 0) - curl_easy_setopt(curl, CURLOPT_CAINFO, CACERT_PATH.c_str()); - else - Log::writeErrorLog("could not find cacert"); - //Follow redirects curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); res = curl_easy_perform(curl); curl_easy_cleanup(curl); @@ -442,8 +362,8 @@ void WebDAV::get(vector &tempItems, int itemID) switch (response_code) { case 200: - Log::writeInfoLog("finished download of " + tempItems.at(itemID).getPath() + " to " + tempItems.at(itemID).getLocalPath()); - tempItems.at(itemID).setState(FileState::ISYNCED); + Log::writeInfoLog("finished download of " + item.path + " to " + item.localPath); + return true; break; case 401: Message(ICON_ERROR, "Error", "Username/password incorrect.", 2000); @@ -461,5 +381,5 @@ void WebDAV::get(vector &tempItems, int itemID) Message(ICON_ERROR, "Error", response.c_str(), 4000); } } + return false; } -*/ diff --git a/src/api/webDAV.h b/src/api/webDAV.h index 74dcff1..f515f37 100644 --- a/src/api/webDAV.h +++ b/src/api/webDAV.h @@ -15,7 +15,7 @@ #include #include -//TODO rename +//TODO write into config const std::string NEXTCLOUD_FILE_PATH = "/mnt/ext1/nextcloud"; const std::string NEXTCLOUD_ROOT_PATH = "/remote.php/dav/files/"; const std::string NEXTCLOUD_START_PATH = "/remote.php/"; @@ -39,7 +39,6 @@ class WebDAV std::vector getDataStructure(const std::string &pathUrl); - /** * gets the dataStructure of the given URL and writes its WEBDAV items to the items vector * @@ -50,6 +49,8 @@ class WebDAV */ std::string propfind(const std::string &pathUrl); + bool get(WebDAVItem &item); + private: std::string _username; std::string _password; diff --git a/src/handler/eventHandler.cpp b/src/handler/eventHandler.cpp index 58de8a8..0f69a2d 100644 --- a/src/handler/eventHandler.cpp +++ b/src/handler/eventHandler.cpp @@ -31,61 +31,41 @@ EventHandler::EventHandler() _loginView = nullptr; _webDAVView = nullptr; - vector fromDB; std::vector currentWebDAVItems; + string path = NEXTCLOUD_ROOT_PATH + Util::accessConfig(CONFIG_PATH, Action::IReadString,"UUID") + '/'; if (iv_access(CONFIG_PATH.c_str(), W_OK) == 0) - { - //menubar - //explanation on first login? - //TODO here mark folders that are unsynced? - //compare both datasets, if fromDB etag is different, mark as unsycned - string tempPath = NEXTCLOUD_ROOT_PATH + Util::accessConfig(CONFIG_PATH, Action::IReadString,"UUID"); - currentWebDAVItems = _webDAV.getDataStructure(tempPath); - fromDB = _sqllite.getItemsChildren(tempPath); - } - //TODO here or father below? + currentWebDAVItems = _webDAV.getDataStructure(path); + _menu = std::unique_ptr(new MainMenu("Nextcloud")); + if(currentWebDAVItems.empty()) + currentWebDAVItems = _sqllite.getItemsChildren(path); + else + updateItems(currentWebDAVItems); + if(currentWebDAVItems.empty()) { - //use from DB - //this one is always required --> if does not work -> say to the user that it did not work, to sync use - /* - vector Nextcloud::getOfflineStructure(const string &pathUrl) - { - if (pathUrl.compare(NEXTCLOUD_ROOT_PATH + getUUID() + "/") == 0) - { - Message(ICON_ERROR, "Error", "The root structure is not available offline. Please try again to login.", 2000); - logout(); - } - */ - Message(ICON_ERROR, "Error", "Could not login, please try again.", 1200); - if(fromDB.empty()) + int dialogResult = DialogSynchro(ICON_QUESTION, "Action", "Could not login and there is no DB available to restore information. What would you like to do?", "Logout", "Close App", NULL); + switch (dialogResult) { - int dialogResult = DialogSynchro(ICON_QUESTION, "Action", "Could not login and there is no DB available to restore information. What would you like to do?", "Logout", "Close App", NULL); - switch (dialogResult) - { - case 1: - { - _webDAV.logout(); - _loginView = std::unique_ptr(new LoginView(_menu->getContentRect())); - FullUpdate(); - } - break; - case 2: - default: - CloseApp(); - break; - } + case 1: + { + _webDAV.logout(); + _loginView = std::make_unique(LoginView(_menu->getContentRect())); + } + break; + case 2: + default: + CloseApp(); + break; } } else { - _webDAVView = std::unique_ptr(new WebDAVView(_menu->getContentRect(), _currentWebDAVItems,1)); - _sqllite.saveItemsChildren(_currentWebDAVItems); - FullUpdate(); + drawWebDAVItems(currentWebDAVItems); } + } int EventHandler::eventDistributor(const int type, const int par1, const int par2) @@ -107,43 +87,103 @@ void EventHandler::mainMenuHandler(const int index) { switch (index) { - //TODO actualize current folder - case 101: - { - break; - } - //Logout - case 102: - { - int dialogResult = DialogSynchro(ICON_QUESTION, "Action", "Do you want to delete local files?", "Yes", "No", "Cancel"); - switch (dialogResult) - { - case 1: - _webDAV.logout(true); + //Actualize the current folder + case 101: + { + //TODO startfolder use same as above + //TODO parent path not needed + OpenProgressbar(1, "Actualizing current folder", ("Actualizing path" + _currentPath).c_str(), 0, NULL); + string childrenPath = _currentPath; + childrenPath = childrenPath.substr(NEXTCLOUD_ROOT_PATH.length(), childrenPath.length()); + std::string path = NEXTCLOUD_ROOT_PATH; + std::vector currentWebDAVItems; + size_t found = 0; + int i = 0; + while((found = childrenPath.find("/"),found) != std::string::npos) + { + path += childrenPath.substr(0, found+1); + childrenPath = childrenPath.substr(found+1,childrenPath.length()); + auto state = _sqllite.getState(path); + Log::writeInfoLog("cur path " + path); + if(i < 1 || state == FileState::IOUTSYNCED || state == FileState::ICLOUD) + { + UpdateProgressbar(("Upgrading " + path).c_str(), 0); + currentWebDAVItems = _webDAV.getDataStructure(path); + Log::writeInfoLog("syncing"); + } + else + { + break; + } + + if(currentWebDAVItems.empty()) + { + Log::writeErrorLog("Could not sync " + path + " via actualize."); + Message(ICON_WARNING, "Warning", "Could not sync the file structure.", 2000); + HideHourglass(); + break; + } + else + { + updateItems(currentWebDAVItems); + } + i++; + } + + Log::writeInfoLog("stopped at " + path ); + currentWebDAVItems = _sqllite.getItemsChildren(_currentPath); + + for(auto &item : currentWebDAVItems) + { + Log::writeInfoLog(item.path); + if(item.state == FileState::IOUTSYNCED || item.state == FileState::ICLOUD) + { + UpdateProgressbar(("Upgrading " + item.path).c_str(), 0); + vector tempWebDAVItems = _webDAV.getDataStructure(item.path); + updateItems(tempWebDAVItems); + } + + } + //TODO twice + currentWebDAVItems = _sqllite.getItemsChildren(_currentPath); + + CloseProgressbar(); + if(!currentWebDAVItems.empty()) + drawWebDAVItems(currentWebDAVItems); + break; + } + //Logout + case 102: + { + int dialogResult = DialogSynchro(ICON_QUESTION, "Action", "Do you want to delete local files?", "Yes", "No", "Cancel"); + switch (dialogResult) + { + case 1: + _webDAV.logout(true); + break; + case 3: + return; + default: + _webDAV.logout(); + break; + } + _webDAVView.release(); + _loginView = std::make_unique(LoginView(_menu->getContentRect())); + FullUpdate(); + break; + } + //Info + case 103: + { + Message(ICON_INFORMATION, "Information", "Version 0.98 \n For support please open a ticket at https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client/issues", 1200); + break; + } + //Exit + case 104: + CloseApp(); break; - case 3: - return; default: - _webDAV.logout(); break; - } - _webDAVView.release(); - _loginView = std::unique_ptr(new LoginView(_menu->getContentRect())); - FullUpdate(); - break; - } - //Info - case 103: - { - Message(ICON_INFORMATION, "Information", "Version 0.73 \n For support please open a ticket at https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client/issues", 1200); - break; - } - //Exit - case 104: - CloseApp(); - break; - default: - break; } } @@ -155,7 +195,6 @@ std::unique_ptr _contextMenu; void EventHandler::contextMenuHandler(const int index) { - //invert color switch (index) { //Open @@ -181,24 +220,23 @@ void EventHandler::contextMenuHandler(const int index) //remove case 103: { - OpenProgressbar(1, "Removing...", "Removing Files.", 0, NULL); - /* - Log::writeInfoLog("removing file " + _items.at(itemID).getPath()); - if (!_items.at(itemID).removeFile()) - return false; + //TODO remove + //OpenProgressbar(1, "Removing...", "Removing Files.", 0, NULL); + //Log::writeInfoLog("removing file " + _items.at(itemID).getPath()); + //if (!_items.at(itemID).removeFile()) + //return false; - return true; - */ + //return true; /* - if (_nextcloud.removeItem(_tempItemID)) - { - updatePBLibrary(); - CloseProgressbar(); - _webDAVView->reDrawCurrentEntry(); - } - else - */ - CloseProgressbar(); + if (_nextcloud.removeItem(_ItemID)) + { + updatePBLibrary(); + CloseProgressbar(); + _webDAVView->reDrawCurrentEntry(); + } + else + */ + //CloseProgressbar(); Message(ICON_WARNING, "Warning", "Could not delete the file, please try again.", 1200); break; } @@ -207,8 +245,6 @@ void EventHandler::contextMenuHandler(const int index) _webDAVView->invertCurrentEntryColor(); break; } - - _contextMenu.reset(); } } @@ -274,9 +310,9 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) } else { - _webDAVView = std::make_unique(WebDAVView(_menu->getContentRect(), currentWebDAVItems,1)); + //TODO storagelocation picker + drawWebDAVItems(currentWebDAVItems); _loginView.reset(); - FullUpdate(); } return 0; } @@ -285,97 +321,57 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2) return 1; } -void EventHandler::updatePBLibrary() -{ - if (_webDAVView->getCurrentEntry().type == Itemtype::IFOLDER) - { - Util::updatePBLibrary(15); - } - else - { - //if (_nextcloud.getItems().at(_tempItemID).isBook()) - /* - * is needed twice! - if (_fileType.find("application/epub+zip") != string::npos || - _fileType.find("application/pdf") != string::npos || - _fileType.find("application/octet-stream") != string::npos || - _fileType.find("text/plain") != string::npos || - _fileType.find("text/html") != string::npos || - _fileType.find("text/rtf") != string::npos || - _fileType.find("application/msword") != string::npos || - _fileType.find("application/x-mobipocket-ebook") != string::npos || - _fileType.find("application/vnd.openxmlformats-officedocument.wordprocessingml.document") != string::npos || - _fileType.find("application/x-fictionbook+xml") != string::npos) - Util::updatePBLibrary(5); - */ - } -} - -void EventHandler::startDownload() -{ - OpenProgressbar(1, "Downloading...", "Checking network connection", 0, NULL); - try - { - //_nextcloud.download(_tempItemID); - } - catch (const std::exception &e) - { - Log::writeErrorLog(e.what()); - Message(ICON_ERROR, "Error", "Something has gone wrong. Please check the logs. (/system/config/nextcloud/)", 1200); - } - updatePBLibrary(); - - CloseProgressbar(); - _webDAVView->reDrawCurrentEntry(); -} - void EventHandler::openItem() { _webDAVView->invertCurrentEntryColor(); - /* - if (_state == FileState::ICLOUD) + if (_webDAVView->getCurrentEntry().state == FileState::ICLOUD) { Message(ICON_ERROR, "File not found.", "Could not find file.", 1000); } - else if(isBook()) + else if(_webDAVView->getCurrentEntry().fileType.find("application/epub+zip") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("application/pdf") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("application/octet-stream") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("text/plain") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("text/html") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("text/rtf") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("application/msword") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("application/x-mobipocket-ebook") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("application/vnd.openxmlformats-officedocument.wordprocessingml.document") != string::npos || + _webDAVView->getCurrentEntry().fileType.find("application/x-fictionbook+xml") != string::npos) { - - OpenBook(_localPath.c_str(), "", 0); + OpenBook(_webDAVView->getCurrentEntry().localPath.c_str(), "", 0); } else { Message(ICON_INFORMATION, "Warning", "The filetype is currently not supported.", 1200); } - */ - //webDAVView->getCurrentEntry - //_nextcloud.getItems().at(_tempItemID).open(); } void EventHandler::openFolder() { ShowHourglassForce(); - //_nextcloud.setItems(_nextcloud.getDataStructure(_tempPath)); - //TODO if folder is unsynced sync - std::vector currentWebDAVItems = _webDAV.getDataStructure(_webDAVView->getCurrentEntry().path); + + std::vector currentWebDAVItems; + + if(_webDAVView->getCurrentEntry().state == FileState::IOUTSYNCED || _webDAVView->getCurrentEntry().state == FileState::ICLOUD) + currentWebDAVItems = _webDAV.getDataStructure(_webDAVView->getCurrentEntry().path); + + if(currentWebDAVItems.empty()) + currentWebDAVItems = _sqllite.getItemsChildren(_webDAVView->getCurrentEntry().path); + else + updateItems(currentWebDAVItems); + + + if(currentWebDAVItems.empty()) { - Log::writeErrorLog("items empty"); + Message(ICON_ERROR, "Error", "Could not sync the items and there is no offline copy available.", 1200); HideHourglass(); _webDAVView->invertCurrentEntryColor(); } else { - Log::writeInfoLog("got new items"); - _sqllite.saveItemsChildren(currentWebDAVItems); - - //if folder is synced, get only from DB - //vector fromDB = _sqllite.getItemsChildren(_tempPath); - //get etags from DB, if etag for path is unchanged, stays the same, same for foldersjj - FillAreaRect(&_menu->getContentRect(), WHITE); - _webDAVView.release(); - _webDAVView = std::unique_ptr(new WebDAVView(_menu->getContentRect(), _currentWebDAVItems,1)); - //_sqllite.saveItemsChildren(_nextcloud.getItems()); - PartialUpdate(_menu->getContentRect().x, _menu->getContentRect().y, _menu->getContentRect().w, _menu->getContentRect().h); + drawWebDAVItems(currentWebDAVItems); } } @@ -412,80 +408,178 @@ int EventHandler::keyHandler(const int type, const int par1, const int par2) } -/* -void Nextcloud::getLocalFileStructure(vector &tempItems, const string &localPath) +void EventHandler::getLocalFileStructure(vector &items) { //get local files, https://stackoverflow.com/questions/306533/how-do-i-get-a-list-of-files-in-a-directory-in-c DIR *dir; class dirent *ent; class stat st; - dir = opendir(localPath.c_str()); - while ((ent = readdir(dir)) != NULL) + string localPath = items.at(0).localPath + '/'; + if (iv_access(localPath.c_str(), W_OK) == 0) { - const string fileName = ent->d_name; - const string fullFileName = localPath + fileName; - if (fileName[0] == '.') - continue; - - if (stat(fullFileName.c_str(), &st) == -1) - continue; - - const bool isDirectory = (st.st_mode & S_IFDIR) != 0; - - bool found = false; - //looks if files have been modified - for (unsigned int i = 1; i < tempItems.size(); i++) + dir = opendir(localPath.c_str()); + Log::writeInfoLog("localPath = " + localPath); + while ((ent = readdir(dir)) != NULL) { - if (tempItems.at(i).getLocalPath().compare(fullFileName) == 0) + const string fileName = ent->d_name; + const string fullFileName = localPath + fileName; + + if (fileName[0] == '.') + continue; + + if (stat(fullFileName.c_str(), &st) == -1) + continue; + + const bool isDirectory = (st.st_mode & S_IFDIR) != 0; + + bool found = false; + for (unsigned int i = 1; i < items.size(); i++) { - //do via etag and not outsync ! - if (!isDirectory) + if (items.at(i).localPath.compare(fullFileName) == 0) { - //check if was changed on the cloud and here and then update... - //if etag ist different and last modifcated changed local --> create conflict - //compare by etag - //get last modification date and compare; if is different upload this - - std::ifstream in(fullFileName, std::ifstream::binary | std::ifstream::ate); - Log::writeInfoLog(tempItems.at(i).getTitle()); - Log::writeInfoLog(std::to_string(in.tellg())); - - Log::writeInfoLog(std::to_string(tempItems.at(i).getSize())); - if (in.tellg() != tempItems.at(i).getSize()) - { - tempItems.at(i).setState(FileState::IOUTSYNCED); - } + found = true; + break; } - found = true; - break; + } + if (!found) + { + WebDAVItem temp; + temp.localPath = fullFileName; + temp.state = FileState::ILOCAL; + temp.title = fullFileName.substr(fullFileName.find_last_of("/") + 1, fullFileName.length()); + Util::decodeUrl(temp.title); + if (isDirectory) + { + //create new dir in cloud + temp.type = Itemtype::IFOLDER; + } + else + { + //put to cloud + temp.type = Itemtype::IFILE; + } + items.push_back(temp); } } - if (!found) + closedir(dir); + } +} + +void EventHandler::downloadFolder(vector &items, int itemID) +{ + //BanSleep(2000); + string path = items.at(itemID).path; + Log::writeInfoLog("Path to look for " + path); + + if (items.at(itemID).type == Itemtype::IFOLDER) + { + vector tempItems; + if(items.at(itemID).state == FileState::IOUTSYNCED || items.at(itemID).state == FileState::ICLOUD) { - if (isDirectory) + Log::writeInfoLog("outsynced"); + tempItems = _webDAV.getDataStructure(path); + //TODO twice? + updateItems(tempItems); + } + else + { + Log::writeInfoLog("synced"); + tempItems = _sqllite.getItemsChildren(path); + } + //first item of the vector is the root path itself + for (size_t i = 1; i < tempItems.size(); i++) + { + Log::writeInfoLog("Item: " + tempItems.at(i).path); + downloadFolder(tempItems, i); + } + //TODO remove file parts that are no longer there, check for local path and delete these + //TODO else use offline structure to go down + + } + else + { + if(items.at(itemID).state == FileState::IOUTSYNCED || items.at(itemID).state == FileState::ICLOUD) + { + Log::writeInfoLog("outsynced"); + //TODO + //1. check etag --> if is differnt, cloud has been updated + //2. check modification date and file size locally --> if is different, local has been updated + //3. if both --> create conflict + //4. if first, renew file --> reset etag + //5. if second --> upload the local file; test if it has not been update in the cloud + Log::writeInfoLog("started download of " + items.at(itemID).path + " to " + items.at(itemID).localPath); + if(_webDAV.get(items.at(itemID))) { - //create new dir in cloud - tempItems.push_back(Item(fullFileName, FileState::ILOCAL, Itemtype::IFOLDER)); - Item::Item(const string &localPath, FileState state, Itemtype type) : _localPath(localPath), _state(state), _type(type) - { - _title = _localPath; - _title = _title.substr(_title.find_last_of("/") + 1, _title.length()); - Util::decodeUrl(_title); - } - } - else - { - //put to coud - tempItems.push_back(Item(fullFileName, FileState::ILOCAL, Itemtype::IFILE)); + items.at(itemID).state = FileState::ISYNCED; + _sqllite.updateState(items.at(itemID).path,FileState::ISYNCED); } } } - closedir(dir); + + + return; +} + +void EventHandler::startDownload() +{ + OpenProgressbar(1, "Downloading...", "Starting Download.", 0, NULL); + + if(_webDAVView->getCurrentEntry().type == Itemtype::IFILE) + { + Log::writeInfoLog("Started download of " + _webDAVView->getCurrentEntry().path + " to " + _webDAVView->getCurrentEntry().localPath); + if(_webDAV.get(_webDAVView->getCurrentEntry())) + { + _webDAVView->getCurrentEntry().state = FileState::ISYNCED; + _sqllite.updateState(_webDAVView->getCurrentEntry().path,FileState::ISYNCED); + } + } + else + { + vector currentItems = _sqllite.getItemsChildren(_webDAVView->getCurrentEntry().path); + this->downloadFolder(currentItems, 0); + UpdateProgressbar("Download completed", 100); + } + + //Util::updatePBLibrary(15); + CloseProgressbar(); + //TODO does not work + _webDAVView->reDrawCurrentEntry(); +} + +void EventHandler::updateItems(vector &items) +{ + for(auto &item : items) + { + item.state = _sqllite.getState(item.path); + + //TODO integrate for files + //if (iv_access(temp.localPath.c_str(), W_OK) != 0) + //temp.state = FileState::ICLOUD; + if(_sqllite.getEtag(item.path).compare(item.etag) != 0) + { + if( item.state == FileState::ISYNCED) + item.state = FileState::IOUTSYNCED; + else + item.state = FileState::ICLOUD; + } + } + items.at(0).state =FileState::ISYNCED; + _sqllite.saveItemsChildren(items); + + //TODO sync delete when not parentPath existend --> "select * from metadata where parentPath NOT IN (Select + //DISTINCT(parentPath) from metadata; + //what happens with the entries below? } - -*/ - +void EventHandler::drawWebDAVItems(vector &items) +{ + FillAreaRect(&_menu->getContentRect(), WHITE); + _webDAVView.release(); + _currentPath = items.at(0).path; + getLocalFileStructure(items); + _webDAVView = std::unique_ptr(new WebDAVView(_menu->getContentRect(), items,1)); + PartialUpdate(_menu->getContentRect().x, _menu->getContentRect().y, _menu->getContentRect().w, _menu->getContentRect().h); +} diff --git a/src/handler/eventHandler.h b/src/handler/eventHandler.h index 269bfc7..2485c5d 100644 --- a/src/handler/eventHandler.h +++ b/src/handler/eventHandler.h @@ -50,6 +50,7 @@ private: WebDAV _webDAV = WebDAV(); SqliteConnector _sqllite = SqliteConnector(DB_PATH); + std::string _currentPath; /** * Function needed to call C function, redirects to real function @@ -96,12 +97,6 @@ private: */ void updatePBLibrary(); - /** - * Starts the download of an item - * - */ - void startDownload(); - /** * Open a folder * @@ -125,5 +120,15 @@ private: */ int keyHandler(const int type, const int par1, const int par2); + void getLocalFileStructure(std::vector &tempItems); + + void downloadFolder(std::vector &items, int itemID); + + void startDownload(); + + void updateItems(std::vector &items); + + void drawWebDAVItems(std::vector &items); + }; #endif diff --git a/src/ui/webDAVView/webDAVViewEntry.cpp b/src/ui/webDAVView/webDAVViewEntry.cpp index 8b74344..0c10855 100644 --- a/src/ui/webDAVView/webDAVViewEntry.cpp +++ b/src/ui/webDAVView/webDAVViewEntry.cpp @@ -56,9 +56,19 @@ void WebDAVViewEntry::draw(const ifont *entryFont, const ifont *entryFontBold, i } else { - if (_entry.state == FileState::ISYNCED) + switch(_entry.state) { - DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, "Folder synced", ALIGN_RIGHT); + case FileState::ISYNCED: + DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, "Structure synced", ALIGN_RIGHT); + break; + case FileState::IOUTSYNCED: + DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, "Structure out of sync", ALIGN_RIGHT); + break; + case FileState::ICLOUD: + DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, "Cloud", ALIGN_RIGHT); + break; + default: + break; } DrawTextRect(_position.x, _position.y + heightOfTitle, _position.w, fontHeight, _entry.lastEditDate.c_str(), ALIGN_LEFT); DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, _entry.size.c_str(), ALIGN_LEFT);