Merge pull request #25 from JuanJakobo/refactor

Add second part implementing etag
master
JuanJakobo 2022-07-29 07:42:41 +02:00 committed by GitHub
commit 05109b780e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 465 additions and 389 deletions

View File

@ -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<WebDAVItem> 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<const char *>(sqlite3_column_text(stmt, 0));
etag = reinterpret_cast<const char *>(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<WebDAVItem> 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<FileState>(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<WebDAVItem> SqliteConnector::getItemsChildren(const string &parentPath)
@ -73,13 +119,12 @@ std::vector<WebDAVItem> SqliteConnector::getItemsChildren(const string &parentPa
sqlite3_stmt *stmt = 0;
std::vector<WebDAVItem> 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;
temp.title = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
@ -93,15 +138,6 @@ std::vector<WebDAVItem> SqliteConnector::getItemsChildren(const string &parentPa
temp.state = static_cast<FileState>(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;
*/
}
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<WebDAVItem> &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<WebDAVItem> &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<WebDAVItem> &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)
{

View File

@ -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<WebDAVItem> getItemsChildren(const std::string &parenthPath);

View File

@ -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<WebDAVItem> WebDAV::login(const string &Url, const string &Username, const string &Pass)
{
string uuid;
@ -76,6 +71,7 @@ std::vector<WebDAVItem> 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<WebDAVItem> WebDAV::getDataStructure(const string &pathUrl)
{
string xmlItem = propfind(pathUrl);
@ -118,7 +111,6 @@ vector<WebDAVItem> 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<WebDAVItem> 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<WebDAVItem> 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<Item> &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<Item> &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<Item> 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<Item> &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<Item> &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<Item> &tempItems, int itemID)
Message(ICON_ERROR, "Error", response.c_str(), 4000);
}
}
return false;
}
*/

View File

@ -15,7 +15,7 @@
#include <string>
#include <vector>
//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<WebDAVItem> 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;

View File

@ -31,37 +31,20 @@ EventHandler::EventHandler()
_loginView = nullptr;
_webDAVView = nullptr;
vector<WebDAVItem> fromDB;
std::vector<WebDAVItem> 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<MainMenu>(new MainMenu("Nextcloud"));
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<Item> 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())
currentWebDAVItems = _sqllite.getItemsChildren(path);
else
updateItems(currentWebDAVItems);
if(currentWebDAVItems.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)
@ -69,8 +52,7 @@ EventHandler::EventHandler()
case 1:
{
_webDAV.logout();
_loginView = std::unique_ptr<LoginView>(new LoginView(_menu->getContentRect()));
FullUpdate();
_loginView = std::make_unique<LoginView>(LoginView(_menu->getContentRect()));
}
break;
case 2:
@ -79,13 +61,11 @@ EventHandler::EventHandler()
break;
}
}
}
else
{
_webDAVView = std::unique_ptr<WebDAVView>(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,9 +87,69 @@ void EventHandler::mainMenuHandler(const int index)
{
switch (index)
{
//TODO actualize current folder
//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<WebDAVItem> 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<WebDAVItem> tempWebDAVItems = _webDAV.getDataStructure(item.path);
updateItems(tempWebDAVItems);
}
}
//TODO twice
currentWebDAVItems = _sqllite.getItemsChildren(_currentPath);
CloseProgressbar();
if(!currentWebDAVItems.empty())
drawWebDAVItems(currentWebDAVItems);
break;
}
//Logout
@ -128,14 +168,14 @@ void EventHandler::mainMenuHandler(const int index)
break;
}
_webDAVView.release();
_loginView = std::unique_ptr<LoginView>(new LoginView(_menu->getContentRect()));
_loginView = std::make_unique<LoginView>(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);
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
@ -155,7 +195,6 @@ std::unique_ptr<ContextMenu> _contextMenu;
void EventHandler::contextMenuHandler(const int index)
{
//invert color
switch (index)
{
//Open
@ -181,16 +220,15 @@ 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))
if (_nextcloud.removeItem(_ItemID))
{
updatePBLibrary();
CloseProgressbar();
@ -198,7 +236,7 @@ void EventHandler::contextMenuHandler(const int index)
}
else
*/
CloseProgressbar();
//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>(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<WebDAVItem> currentWebDAVItems = _webDAV.getDataStructure(_webDAVView->getCurrentEntry().path);
std::vector<WebDAVItem> 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<WebDAVItem> 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<WebDAVView>(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,15 +408,19 @@ int EventHandler::keyHandler(const int type, const int par1, const int par2)
}
/*
void Nextcloud::getLocalFileStructure(vector<Item> &tempItems, const string &localPath)
void EventHandler::getLocalFileStructure(vector<WebDAVItem> &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;
string localPath = items.at(0).localPath + '/';
if (iv_access(localPath.c_str(), W_OK) == 0)
{
dir = opendir(localPath.c_str());
Log::writeInfoLog("localPath = " + localPath);
while ((ent = readdir(dir)) != NULL)
{
const string fileName = ent->d_name;
@ -435,57 +435,151 @@ void Nextcloud::getLocalFileStructure(vector<Item> &tempItems, const string &loc
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++)
for (unsigned int i = 1; i < items.size(); i++)
{
if (tempItems.at(i).getLocalPath().compare(fullFileName) == 0)
if (items.at(i).localPath.compare(fullFileName) == 0)
{
//do via etag and not outsync !
if (!isDirectory)
{
//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;
}
}
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
tempItems.push_back(Item(fullFileName, FileState::ILOCAL, Itemtype::IFOLDER));
Item::Item(const string &localPath, FileState state, Itemtype type) : _localPath(localPath), _state(state), _type(type)
temp.type = Itemtype::IFOLDER;
}
else
{
_title = _localPath;
_title = _title.substr(_title.find_last_of("/") + 1, _title.length());
Util::decodeUrl(_title);
//put to cloud
temp.type = Itemtype::IFILE;
}
items.push_back(temp);
}
}
closedir(dir);
}
}
void EventHandler::downloadFolder(vector<WebDAVItem> &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<WebDAVItem> tempItems;
if(items.at(itemID).state == FileState::IOUTSYNCED || items.at(itemID).state == FileState::ICLOUD)
{
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)))
{
items.at(itemID).state = FileState::ISYNCED;
_sqllite.updateState(items.at(itemID).path,FileState::ISYNCED);
}
}
}
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
{
//put to coud
tempItems.push_back(Item(fullFileName, FileState::ILOCAL, Itemtype::IFILE));
vector<WebDAVItem> 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<WebDAVItem> &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;
}
}
}
closedir(dir);
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<WebDAVItem> &items)
{
FillAreaRect(&_menu->getContentRect(), WHITE);
_webDAVView.release();
_currentPath = items.at(0).path;
getLocalFileStructure(items);
_webDAVView = std::unique_ptr<WebDAVView>(new WebDAVView(_menu->getContentRect(), items,1));
PartialUpdate(_menu->getContentRect().x, _menu->getContentRect().y, _menu->getContentRect().w, _menu->getContentRect().h);
}

View File

@ -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<WebDAVItem> &tempItems);
void downloadFolder(std::vector<WebDAVItem> &items, int itemID);
void startDownload();
void updateItems(std::vector<WebDAVItem> &items);
void drawWebDAVItems(std::vector<WebDAVItem> &items);
};
#endif

View File

@ -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);