replace nextcloud and item classes

pull/23/head
JuanJakobo 2022-07-20 14:56:52 +02:00
parent e87ddf534b
commit 8531df2c0a
9 changed files with 305 additions and 975 deletions

View File

@ -52,8 +52,6 @@ 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/util/nextcloud.cpp
${CMAKE_SOURCE_DIR}/src/util/item.cpp
${CMAKE_SOURCE_DIR}/src/ui/listView/listView.cpp
${CMAKE_SOURCE_DIR}/src/ui/listView/listViewEntry.cpp
${CMAKE_SOURCE_DIR}/src/ui/webDAVView/webDAVView.cpp

View File

@ -3,7 +3,7 @@
//
// Author: JuanJakobo
// Date: 23.04.2021
// Description:
// Description: Base model
//-------------------------------------------------------------------
#ifndef MODEL

View File

@ -10,10 +10,13 @@
#include "webDAV.h"
#include "util.h"
#include "log.h"
#include "eventHandler.h"
#include <string>
#include <curl/curl.h>
#include <fstream>
#include <sstream>
#include <math.h>
using std::ifstream;
using std::ofstream;
@ -27,24 +30,84 @@ WebDAV::WebDAV()
//TODO update on first login only start and create update button, update others just if etag changed
//save all to sqlite
Log::writeInfoLog(NEXTCLOUD_PATH);
if (iv_access(NEXTCLOUD_PATH.c_str(), W_OK) != 0)
iv_mkdir(NEXTCLOUD_PATH.c_str(), 0777);
Log::writeInfoLog(NEXTCLOUD_FILE_PATH);
if (iv_access(NEXTCLOUD_FILE_PATH.c_str(), W_OK) != 0)
iv_mkdir(NEXTCLOUD_FILE_PATH.c_str(), 0777);
Log::writeInfoLog(CONFIG_PATH);
if (iv_access(CONFIG_PATH.c_str(), W_OK) == 0)
{
_username = Util::accessConfig(Action::IReadString,"username");
_password = Util::accessConfig(Action::IReadSecret,"password");
_username = Util::accessConfig(CONFIG_PATH,Action::IReadString,"username");
_password = Util::accessConfig(CONFIG_PATH,Action::IReadSecret,"password");
_url = Util::accessConfig(CONFIG_PATH, Action::IReadString, "url");
}
}
/*
//TODO is just getDatastructure?
bool Nextcloud::login()
{
string tempPath = getStartFolder();
if (tempPath.empty())
tempPath = NEXTCLOUD_ROOT_PATH + this->getUUID() + "/";
if (setItems(getDataStructure(tempPath)))
{
_loggedIn = true;
return true;
}
if(_username.empty())
Log::writeErrorLog("username not set");
//test login
return false;
}
*/
//TODO pass in vector and change that one?
std::vector<WebDAVItem> WebDAV::login(const string &Url, const string &Username, const string &Pass)
{
string uuid;
std::size_t found = Url.find(NEXTCLOUD_ROOT_PATH);
if (found != std::string::npos)
{
_url = Url.substr(0, found);
uuid = Url.substr(found + NEXTCLOUD_ROOT_PATH.length());
}
else
{
_url = Url;
uuid = Username;
}
auto tempPath = NEXTCLOUD_ROOT_PATH + uuid + "/";
std::vector<WebDAVItem> tempItems = getDataStructure(tempPath);
if (!tempItems.empty())
{
if (iv_access(CONFIG_PATH.c_str(), W_OK) != 0)
iv_buildpath(CONFIG_PATH.c_str());
Util::accessConfig(CONFIG_PATH, Action::IWriteString, "url", _url);
Util::accessConfig(CONFIG_PATH, Action::IWriteString, "username", Username);
Util::accessConfig(CONFIG_PATH, Action::IWriteString, "uuid", uuid);
Util::accessConfig(CONFIG_PATH, Action::IWriteSecret, "password", Pass);
Util::accessConfig(CONFIG_PATH, Action::IWriteString, "startFolder", tempPath);
}
return tempItems;
}
void WebDAV::logout(bool deleteFiles)
{
if (deleteFiles)
{
//string cmd = "rm -rf " + NEXTCLOUD_FILE_PATH + "/" + getUUID() + "/";
//system(cmd.c_str());
}
remove(CONFIG_PATH.c_str());
remove((CONFIG_PATH + ".back.").c_str());
//_url.clear();
//TODO where?
//_items.clear();
}
//TODO pas as reversne and no return
@ -61,6 +124,18 @@ string WebDAV::getLocalPath(string path)
///TODO rename function
vector<WebDAVItem> WebDAV::getDataStructure(const string &pathUrl)
{
/*
*TODO resize item one and make clickable, what to do with the start?
//resize item 1
string header = _items.at(0).getPath();
header = header.substr(0, header.find_last_of("/"));
header = header.substr(0, header.find_last_of("/") + 1);
_items.at(0).setPath(header);
_items.at(0).setTitle("...");
_items.at(0).setLastEditDate("");
if (_items.at(0).getPath().compare(NEXTCLOUD_ROOT_PATH) == 0)
_items.erase(_items.begin());
*/
string xmlItem = propfind(pathUrl);
if(!xmlItem.empty())
{
@ -82,7 +157,37 @@ vector<WebDAVItem> WebDAV::getDataStructure(const string &pathUrl)
tempItem.etag = Util::getXMLAttribute(xmlItem, "d:getetag");
tempItem.path = Util::getXMLAttribute(xmlItem, "d:href");
tempItem.lastEditDate = Util::getXMLAttribute(xmlItem, "d:getlastmodified");
tempItem.size = atof(Util::getXMLAttribute(xmlItem, "oc:size").c_str());
double size = atof(Util::getXMLAttribute(xmlItem, "oc:size").c_str());
if (size < 1024)
tempItem.size = "< 1 KB";
else
{
double departBy;
double tempSize;
string unit;
if (size < 1048576)
{
departBy = 1024;
unit = "KB";
}
else if (size < 1073741824)
{
departBy = 1048576;
unit = "MB";
}
else
{
departBy = 1073741824;
unit = "GB";
}
tempSize = round((size / departBy) * 10.0) / 10.0;
std::ostringstream stringStream;
stringStream << tempSize;
tempItem.size = stringStream.str() + " " + unit;
}
//replaces everthing in front of /remote.php as this is already part of the url
if(tempItem.path.find(NEXTCLOUD_START_PATH) != 0)
tempItem.path.erase(0,tempItem.path.find(NEXTCLOUD_START_PATH));
@ -132,25 +237,119 @@ vector<WebDAVItem> WebDAV::getDataStructure(const string &pathUrl)
return {};
//TODO return empty items?
}
/*
void Nextcloud::downloadFolder(vector<Item> &tempItems, int itemID)
{
//TODO etag
BanSleep(2000);
if (tempItems.at(itemID).getState() == FileState::ILOCAL)
{
UpdateProgressbar(("Removing local item " + tempItems.at(itemID).getLocalPath()).c_str(), 0);
tempItems.at(itemID).removeFile();
if (_type == Itemtype::IFOLDER)
{
string cmd = "rm -rf " + _localPath + "/";
system(cmd.c_str());
return true;
}
if (remove(_localPath.c_str()) != 0)
return false;
if (_state == FileState::ISYNCED || _state == FileState::IOUTSYNCED)
{
_state = FileState::ICLOUD;
}
else
{
//TODO applies if file is only local
//only show if person is inside this folder
Message(ICON_INFORMATION, "Warning", "The file will be shown until next folder update.", 1200);
}
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(_items, itemID);
UpdateProgressbar("Download completed", 100);
}
*/
string WebDAV::propfind(const string &pathUrl)
{
/*
string localPath = getLocalPath(pathUrl);
//TODO catch here pathUrl is empty!
if (pathUrl.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);
_workOffline = true;
return getOfflineStructure(pathUrl);
Message(ICON_WARNING, "Warning", "Cannot connect to the internet. Switching to offline modus. To work online turn on online modus in the menu.", 2000);
return "";
}
//
//get etag from current and then send request with FT_ENC_TAG
//need path url and also the etag
if (_url.empty())
_url = this->getUrl();
//can handle multiple etags --> * if exists
*/
auto _url = Util::accessConfig(Action::IReadString, "url");
string readBuffer;
CURLcode res;
@ -159,12 +358,11 @@ string WebDAV::propfind(const string &pathUrl)
if (curl)
{
string post = _username + ":" + _password;
Log::writeInfoLog(post);
Log::writeInfoLog(_url + NEXTCLOUD_ROOT_PATH + pathUrl);
Log::writeInfoLog(_url + pathUrl);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Depth: 1");
curl_easy_setopt(curl, CURLOPT_URL, (_url + NEXTCLOUD_ROOT_PATH + pathUrl).c_str());
curl_easy_setopt(curl, CURLOPT_URL, (_url + pathUrl).c_str());
curl_easy_setopt(curl, CURLOPT_USERPWD, post.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PROPFIND");
@ -212,3 +410,77 @@ string WebDAV::propfind(const string &pathUrl)
}
return "";
}
/*
void WebDAV::get(vector<Item> &tempItems, int itemID)
{
//CHECK id
if (tempItems.at(itemID).getState() == FileState::ISYNCED)
{
UpdateProgressbar(("The newest version of file " + tempItems.at(itemID).getLocalPath() + " is already downloaded.").c_str(), 0);
return;
}
if (tempItems.at(itemID).getPath().empty())
{
Message(ICON_ERROR, "Error", "Download path is not set, therefore cannot download the file.", 2000);
return;
}
UpdateProgressbar(("Starting Download of " + tempItems.at(itemID).getLocalPath()).c_str(), 0);
CURLcode res;
CURL *curl = curl_easy_init();
if (curl)
{
string post = this->getUsername() + std::string(":") + this->getPassword();
FILE *fp;
fp = iv_fopen(tempItems.at(itemID).getLocalPath().c_str(), "wb");
curl_easy_setopt(curl, CURLOPT_URL, (_url + tempItems.at(itemID).getPath()).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);
iv_fclose(fp);
if (res == CURLE_OK)
{
long response_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
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);
break;
case 401:
Message(ICON_ERROR, "Error", "Username/password incorrect.", 2000);
break;
default:
Message(ICON_ERROR, "Error", ("An unknown error occured. (Curl Response Code " + std::to_string(response_code) + ")").c_str(), 2000);
break;
}
}
else
{
string response = std::string("An error occured. (") + curl_easy_strerror(res) + " (Curl Error Code: " + std::to_string(res) + ")). Please try again.";
if(res == 60)
response = "Seems as if you are using Let's Encrypt Certs. Please follow the guide on Github (https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client) to use a custom Cert Store on PB.";
Message(ICON_ERROR, "Error", response.c_str(), 4000);
}
}
}
*/

View File

@ -11,17 +11,16 @@
#define WEBDAV
#include "webDAVModel.h"
#include "eventHandler.h"
#include <string>
#include <vector>
const std::string CONFIG_PATH = CONFIG_FOLDER + "/nextcloud.cfg";
//const std::string NEXTCLOUD_FILE_PATH = "/mnt/ext1/nextcloud";
//const std::string NEXTCLOUD_ROOT_PATH = "/remote.php/dav/files/";
//const std::string NEXTCLOUD_STRUCTURE_EXTENSION = ".structure";
//const std::string NEXTCLOUD_START_PATH = "/remote.php/";
//const std::string CACERT_PATH = "/mnt/ext1/applications/cacert.pem";
//TODO rename
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/";
const std::string CACERT_PATH = "/mnt/ext1/applications/cacert.pem";
const std::string NEXTCLOUD_PATH = "/mnt/ext1/system/config/nextcloud";
class WebDAV
{
@ -32,6 +31,10 @@ class WebDAV
*/
WebDAV();
std::vector<WebDAVItem> login(const std::string &Url, const std::string &Username, const std::string &Pass);
void logout(bool deleteFiles = false);
std::string getLocalPath(std::string path);
std::vector<WebDAVItem> getDataStructure(const std::string &pathUrl);
@ -45,12 +48,12 @@ class WebDAV
* @param Pass the pass of the Nextcloud instance
* @return vector of Items
*/
//TODO return xml argument?h
std::string propfind(const std::string &pathUrl);
private:
std::string _username;
std::string _password;
std::string _url;
};
#endif

View File

@ -11,7 +11,6 @@
#include "contextMenu.h"
#include "mainMenu.h"
#include "nextcloud.h"
#include "webDAVView.h"
#include "loginView.h"
#include "sqliteConnector.h"
@ -19,6 +18,7 @@
#include <memory>
const std::string CONFIG_FOLDER = "/mnt/ext1/system/config/nextcloud";
const std::string CONFIG_PATH = CONFIG_FOLDER + "/nextcloud.cfg";
//TODO use folder of nextcloud conifg temp
const std::string DB_PATH = CONFIG_FOLDER + "/data.db";

View File

@ -1,168 +0,0 @@
//------------------------------------------------------------------
// item.cpp
//
// Author: JuanJakobo
// Date: 04.08.2020
//
//-------------------------------------------------------------------
#include "item.h"
#include "inkview.h"
#include "util.h"
#include "nextcloud.h"
#include "log.h"
#include <math.h>
#include <string>
#include <curl/curl.h>
using namespace std;
Item::Item(const string &xmlItem)
{
_path = Util::getXMLAttribute(xmlItem, "d:href");
//replaces everthing in front of /remote.php as this is already part of the url
Log::writeLog("path before transformation " + _path);
if(_path.find(NEXTCLOUD_START_PATH) != 0)
{
_path.erase(0,_path.find(NEXTCLOUD_START_PATH));
Log::writeLog("path after transformation " + _path);
}
_lastEditDate = Util::getXMLAttribute(xmlItem, "d:getlastmodified");
_title = _path;
_localPath = Nextcloud::getLocalPath(_path);
if (_path.back() == '/')
{
_localPath = _localPath.substr(0, _localPath.length() - 1);
_type = Itemtype::IFOLDER;
_title = _title.substr(0, _path.length() - 1);
_size = atof(Util::getXMLAttribute(xmlItem, "d:quota-used-bytes").c_str());
}
else
{
_type = Itemtype::IFILE;
_size = atof(Util::getXMLAttribute(xmlItem, "d:getcontentlength").c_str());
_fileType = Util::getXMLAttribute(xmlItem, "d:getcontenttype");
if (iv_access(_localPath.c_str(), W_OK) != 0)
{
_state = FileState::ICLOUD;
}
else
{
_state = FileState::ISYNCED;
}
}
_title = _title.substr(_title.find_last_of("/") + 1, _title.length());
Util::decodeUrl(_title);
}
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);
}
bool Item::isBook() const
{
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)
return true;
return false;
}
void Item::open() const
{
if (_state == FileState::ICLOUD)
{
Message(ICON_ERROR, "File not found.", "Could not find file.", 1000);
}
else if(isBook())
{
OpenBook(_localPath.c_str(), "", 0);
}
/*
else if (_fileType.find("audio/mpeg") != string::npos ||
_fileType.find("audio/ogg") != string::npos ||
_fileType.find("audio/mp4") != string::npos ||
_fileType.find("audio/m4b") != string::npos)
{
PlayFile(_localPath.c_str());
OpenPlayer();
}
else if (_fileType.find("image/jpeg") != string::npos)
{
Message(3, "Info", "Opening image", 600);
}
*/
else
{
Message(ICON_INFORMATION, "Warning", "The filetype is currently not supported.", 1200);
}
}
bool Item::removeFile()
{
if (_type == Itemtype::IFOLDER)
{
string cmd = "rm -rf " + _localPath + "/";
system(cmd.c_str());
return true;
}
if (remove(_localPath.c_str()) != 0)
return false;
if (_state == FileState::ISYNCED || _state == FileState::IOUTSYNCED)
{
_state = FileState::ICLOUD;
}
else
{
Message(ICON_INFORMATION, "Warning", "The file will be shown until next folder update.", 1200);
}
return true;
}
string Item::getSizeString() const
{
if (_size < 1024)
return "< 1 KB";
double departBy;
double tempSize;
string unit;
if (_size < 1048576)
{
departBy = 1024;
unit = "KB";
}
else if (_size < 1073741824)
{
departBy = 1048576;
unit = "MB";
}
else
{
departBy = 1073741824;
unit = "GB";
}
tempSize = round((_size / departBy) * 10.0) / 10.0;
return std::to_string(tempSize) + " " + unit;
}

View File

@ -1,100 +0,0 @@
//------------------------------------------------------------------
// item.h
//
// Author: JuanJakobo
// Date: 04.08.2020
// Description: Describes an WEBDAV item
//-------------------------------------------------------------------
#ifndef ITEM
#define ITEM
#include "inkview.h"
#include <string>
enum Itemtype
{
IFILE,
IFOLDER
};
enum FileState
{
ICLOUD,
ISYNCED,
IOUTSYNCED,
ILOCAL
};
class Item
{
public:
/**
* Creates an item by receiving the xml from nextcloud and parses it into an object
*
* @param xmlItem result of the nextcloud request
*/
Item(const std::string &xmlItem);
/**
* Creates a new item by receiving localPath from the pocketbook
*
* @param localPath path where the file is placed
* @param state state of the file
* @param type type of the item (folder/file)
*/
Item(const std::string &localPath, FileState state, Itemtype type);
/**
* Checks if item is a book
*/
bool isBook() const;
/**
* Tries to open the item by checking the file format and then executes the fitting action
*/
void open() const;
bool removeFile();
void setPath(const std::string &path) { _path = path; };
std::string getPath() const { return _path; };
std::string getLocalPath() const { return _localPath; };
Itemtype getType() const { return _type; };
void setTitle(const std::string &title) { _title = title; };
std::string getTitle() const { return _title; };
void setState(FileState state) { _state = state; };
FileState getState() const { return _state; };
std::string getLastEditDate() const { return _lastEditDate; };
void setLastEditDate(const std::string &date) { _lastEditDate = date; };
double getSize() const { return _size; };
std::string getSizeString() const;
std::string getFiletype() const { return _fileType; };
private:
std::string _path;
std::string _title;
std::string _localPath;
FileState _state{FileState::ICLOUD};
Itemtype _type;
std::string _lastEditDate{"Error"};
double _size;
std::string _fileType;
/**
* Converts the size to an easier readble format
*
* @param tempSize Size of the item in bytes
*/
void setSize(double tempSize);
};
#endif

View File

@ -1,503 +0,0 @@
//------------------------------------------------------------------
// nextcloud.cpp
//
// Author: JuanJakobo
// Date: 04.08.2020
//
//-------------------------------------------------------------------
#include "inkview.h"
#include "nextcloud.h"
#include "util.h"
#include "item.h"
#include "log.h"
#include <string>
#include <curl/curl.h>
#include <fstream>
#include <sstream>
using std::ifstream;
using std::ofstream;
using std::string;
using std::vector;
Nextcloud::Nextcloud()
{
if (iv_access(NEXTCLOUD_PATH.c_str(), W_OK) != 0)
iv_mkdir(NEXTCLOUD_PATH.c_str(), 0777);
if (iv_access(NEXTCLOUD_FILE_PATH.c_str(), W_OK) != 0)
iv_mkdir(NEXTCLOUD_FILE_PATH.c_str(), 0777);
//create database
}
bool Nextcloud::setItems(const vector<Item> &tempItems)
{
if (tempItems.empty())
return false;
if (!_items.empty())
_items.clear();
_items = tempItems;
//resize item 1
string header = _items.at(0).getPath();
header = header.substr(0, header.find_last_of("/"));
header = header.substr(0, header.find_last_of("/") + 1);
_items.at(0).setPath(header);
_items.at(0).setTitle("...");
_items.at(0).setLastEditDate("");
if (_items.at(0).getPath().compare(NEXTCLOUD_ROOT_PATH) == 0)
_items.erase(_items.begin());
return true;
}
bool Nextcloud::login()
{
string tempPath = getStartFolder();
if (tempPath.empty())
tempPath = NEXTCLOUD_ROOT_PATH + this->getUUID() + "/";
if (setItems(getDataStructure(tempPath)))
{
_loggedIn = true;
return true;
}
return false;
}
bool Nextcloud::login(const string &Url, const string &Username, const string &Pass)
{
string uuid;
std::size_t found = Url.find(NEXTCLOUD_ROOT_PATH);
if (found != std::string::npos)
{
_url = Url.substr(0, found);
uuid = Url.substr(found + NEXTCLOUD_ROOT_PATH.length());
}
else
{
_url = Url;
uuid = Username;
}
string tempPath = NEXTCLOUD_ROOT_PATH + uuid + "/";
if (setItems(getDataStructure(tempPath, Username, Pass)))
{
Log::writeLog("Got items");
if (iv_access(NEXTCLOUD_CONFIG_PATH.c_str(), W_OK) != 0)
iv_buildpath(NEXTCLOUD_CONFIG_PATH.c_str());
this->setUsername(Username);
this->setUUID(uuid);
this->setPassword(Pass);
this->setURL(_url);
this->setStartFolder(tempPath);
_loggedIn = true;
return true;
}
return false;
}
void Nextcloud::logout(bool deleteFiles)
{
if (deleteFiles)
{
string cmd = "rm -rf " + NEXTCLOUD_FILE_PATH + "/" + getUUID() + "/";
system(cmd.c_str());
}
remove(NEXTCLOUD_CONFIG_PATH.c_str());
remove((NEXTCLOUD_CONFIG_PATH + ".back.").c_str());
_url.clear();
_items.clear();
_workOffline = false;
_loggedIn = false;
}
void Nextcloud::downloadItem(vector<Item> &tempItems, int itemID)
{
if (tempItems.at(itemID).getState() == FileState::ISYNCED)
{
UpdateProgressbar(("The newest version of file " + tempItems.at(itemID).getLocalPath() + " is already downloaded.").c_str(), 0);
return;
}
if (tempItems.at(itemID).getPath().empty())
{
Message(ICON_ERROR, "Error", "Download path is not set, therefore cannot download the file.", 2000);
return;
}
UpdateProgressbar(("Starting Download of " + tempItems.at(itemID).getLocalPath()).c_str(), 0);
CURLcode res;
CURL *curl = curl_easy_init();
if (curl)
{
string post = this->getUsername() + std::string(":") + this->getPassword();
FILE *fp;
fp = iv_fopen(tempItems.at(itemID).getLocalPath().c_str(), "wb");
curl_easy_setopt(curl, CURLOPT_URL, (_url + tempItems.at(itemID).getPath()).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::writeLog("could not find cacert");
//Follow redirects
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
iv_fclose(fp);
if (res == CURLE_OK)
{
long response_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
switch (response_code)
{
case 200:
Log::writeLog("finished download of " + tempItems.at(itemID).getPath() + " to " + tempItems.at(itemID).getLocalPath());
tempItems.at(itemID).setState(FileState::ISYNCED);
break;
case 401:
Message(ICON_ERROR, "Error", "Username/password incorrect.", 2000);
break;
default:
Message(ICON_ERROR, "Error", ("An unknown error occured. (Curl Response Code " + std::to_string(response_code) + ")").c_str(), 2000);
break;
}
}
else
{
string response = std::string("An error occured. (") + curl_easy_strerror(res) + " (Curl Error Code: " + std::to_string(res) + ")). Please try again.";
if(res == 60)
response = "Seems as if you are using Let's Encrypt Certs. Please follow the guide on Github (https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client) to use a custom Cert Store on PB.";
Message(ICON_ERROR, "Error", response.c_str(), 4000);
}
}
}
void Nextcloud::downloadFolder(vector<Item> &tempItems, int itemID)
{
BanSleep(2000);
if (tempItems.at(itemID).getState() == FileState::ILOCAL)
{
UpdateProgressbar(("Removing local item " + tempItems.at(itemID).getLocalPath()).c_str(), 0);
tempItems.at(itemID).removeFile();
return;
}
if (tempItems.at(itemID).getType() == Itemtype::IFOLDER)
{
string temp = tempItems.at(itemID).getPath();
Log::writeLog("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::writeLog("Item: " + tempItems.at(i).getPath());
downloadFolder(tempItems, i);
}
}
else
{
Log::writeLog("started download of " + tempItems.at(itemID).getPath() + " to " + tempItems.at(itemID).getLocalPath());
downloadItem(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(_items, itemID);
UpdateProgressbar("Download completed", 100);
}
bool Nextcloud::removeItem(int itemID)
{
Log::writeLog("removing file " + _items.at(itemID).getPath());
if (!_items.at(itemID).removeFile())
return false;
return true;
}
vector<Item> Nextcloud::getDataStructure(string &pathUrl)
{
return getDataStructure(pathUrl, this->getUsername(), this->getPassword());
}
vector<Item> Nextcloud::getDataStructure(const string &pathUrl, const string &Username, const string &Pass)
{
if (_workOffline)
return getOfflineStructure(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);
_workOffline = true;
return getOfflineStructure(pathUrl);
}
if (_url.empty())
_url = this->getUrl();
if (Username.empty() || Pass.empty())
{
Message(ICON_ERROR, "Error", "Username/password not set.", 2000);
return {};
}
Log::writeLog("Starting download of DataStructure");
Log::writeLog(_url + pathUrl);
string readBuffer;
CURLcode res;
CURL *curl = curl_easy_init();
if (curl)
{
string post = Username + ":" + Pass;
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Depth: 1");
curl_easy_setopt(curl, CURLOPT_URL, (_url + pathUrl).c_str());
curl_easy_setopt(curl, CURLOPT_USERPWD, post.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PROPFIND");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Util::writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
if (iv_access(CACERT_PATH.c_str(), R_OK) == 0)
curl_easy_setopt(curl, CURLOPT_CAINFO, CACERT_PATH.c_str());
else
Log::writeLog("could not find cacert");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res == CURLE_OK)
{
long response_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
switch (response_code)
{
case 404:
Message(ICON_ERROR, "Error", "The URL seems to be incorrect. You can look up the WebDav URL in the files app under settings. ", 4000);
break;
case 401:
Message(ICON_ERROR, "Error", "Username/password incorrect.", 4000);
break;
case 207:
{
string localPath = this->getLocalPath(pathUrl);
//create items_
vector<Item> tempItems = readInXML(readBuffer);
if (tempItems.empty())
return {};
if (iv_access(localPath.c_str(), W_OK) != 0)
{
//if the current folder does not exist locally, create it
iv_buildpath(localPath.c_str());
}
else
{
//get items from local path
if (iv_access(localPath.c_str(), R_OK) != 0)
{
Log::writeLog("Local structure of " + localPath + " found.");
}
getLocalFileStructure(tempItems, localPath);
}
//update the .structure file acording to items in the folder
localPath = localPath + NEXTCLOUD_STRUCTURE_EXTENSION;
//save xml to make the structure available offline
ofstream outFile(localPath);
if (outFile)
{
outFile << readBuffer;
Log::writeLog("Saved local copy of tree structure to " + localPath);
}
else
{
Log::writeLog(localPath + " Couldnt save copy of tree structure locally.");
}
outFile.close();
return tempItems;
}
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);
_workOffline = true;
return getOfflineStructure(pathUrl);
}
}
else
{
string response = std::string("An error occured. (") + curl_easy_strerror(res) + " (Curl Error Code: " + std::to_string(res) + ")). Please try again.";
if(res == 60)
response = "Seems as if you are using Let's Encrypt Certs. Please follow the guide on Github (https://github.com/JuanJakobo/Pocketbook-Nextcloud-Client) to use a custom Cert Store on PB.";
Message(ICON_ERROR, "Error", response.c_str(), 4000);
}
}
return {};
}
//TODO Do generic?
//use boost xml
vector<Item> Nextcloud::readInXML(string xml)
{
size_t begin;
size_t end;
string beginItem = "<d:response>";
string endItem = "</d:response>";
vector<Item> tempItems;
begin = xml.find(beginItem);
while (begin != std::string::npos)
{
end = xml.find(endItem);
tempItems.push_back(xml.substr(begin, end));
xml = xml.substr(end + endItem.length());
begin = xml.find(beginItem);
}
return tempItems;
}
string Nextcloud::getLocalPath(string path)
{
Util::decodeUrl(path);
if (path.find(NEXTCLOUD_ROOT_PATH) != string::npos)
path = path.substr(NEXTCLOUD_ROOT_PATH.length());
return NEXTCLOUD_FILE_PATH + "/" + path;
}
vector<Item> Nextcloud::getOfflineStructure(const string &pathUrl)
{
string localPath = this->getLocalPath(pathUrl) + NEXTCLOUD_STRUCTURE_EXTENSION;
if (iv_access(localPath.c_str(), W_OK) == 0)
{
ifstream inFile(localPath);
std::stringstream buffer;
buffer << inFile.rdbuf();
vector<Item> tempItems = readInXML(buffer.str());
if (tempItems.empty())
return {};
getLocalFileStructure(tempItems, this->getLocalPath(pathUrl));
return tempItems;
}
else
{
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();
}
else
{
//Structure is not available offline, stay at the tree
Message(ICON_ERROR, "Error", "The selected structure is not available offline.", 2000);
}
}
return {};
}
void Nextcloud::getLocalFileStructure(vector<Item> &tempItems, const string &localPath)
{
//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)
{
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 < tempItems.size(); i++)
{
if (tempItems.at(i).getLocalPath().compare(fullFileName) == 0)
{
if (!isDirectory)
{
std::ifstream in(fullFileName, std::ifstream::binary | std::ifstream::ate);
Log::writeLog(tempItems.at(i).getTitle());
Log::writeLog(std::to_string(in.tellg()));
Log::writeLog(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)
{
if (isDirectory)
{
tempItems.push_back(Item(fullFileName, FileState::ILOCAL, Itemtype::IFOLDER));
}
else
{
tempItems.push_back(Item(fullFileName, FileState::ILOCAL, Itemtype::IFILE));
}
}
}
closedir(dir);
}

View File

@ -1,172 +0,0 @@
//------------------------------------------------------------------
// nextcloud.h
//
// Author: JuanJakobo
// Date: 04.08.2020
// Description: Handles the userdata and issues, login and download of data structure for nextcloud WEBDAV
//
//-------------------------------------------------------------------
#ifndef NEXTCLOUD
#define NEXTCLOUD
#include "inkview.h"
#include "util.h"
#include "item.h"
#include <string>
#include <vector>
#include <memory>
const std::string NEXTCLOUD_PATH = "/mnt/ext1/system/config/nextcloud";
const std::string NEXTCLOUD_CONFIG_PATH = NEXTCLOUD_PATH + "/nextcloud.cfg";
const std::string NEXTCLOUD_FILE_PATH = "/mnt/ext1/nextcloud";
const std::string NEXTCLOUD_ROOT_PATH = "/remote.php/dav/files/";
const std::string NEXTCLOUD_STRUCTURE_EXTENSION = ".structure";
const std::string NEXTCLOUD_START_PATH = "/remote.php/";
const std::string CACERT_PATH = "/mnt/ext1/applications/cacert.pem";
class Nextcloud
{
public:
explicit Nextcloud();
//TODO public?
void setURL(const std::string &Url) { Util::accessConfig(Action::IWriteString, "url", Url); };
void setUsername(const std::string &Username) { Util::accessConfig(Action::IWriteString, "username", Username); };
void setUUID(const std::string &UUID) { Util::accessConfig(Action::IWriteString, "UUID", UUID); };
void setPassword(const std::string &Pass) { Util::accessConfig(Action::IWriteSecret, "password", Pass); };
void setStartFolder(const std::string &Path) { Util::accessConfig(Action::IReadString, "startFolder", Path); };
std::string getUsername() { return Util::accessConfig(Action::IReadString, "username"); };
bool setItems(const std::vector<Item> &tempItems);
const std::vector<Item> &getItems() const { return _items; };
bool isLoggedIn() const { return _loggedIn; };
bool isWorkOffline() const { return _workOffline; };
void switchWorkOffline() { _workOffline = !_workOffline; };
/**
* Handles login by receiving userdat from config
*
* @return true - login succeeded, false - login failed
*/
bool login();
/**
* Handles first login to nextcloud, if sucessfull saves userdata
*
* @param Username the username of the Nextcloud instance
* @param Pass the pass of the Nextcloud instance
* @return true - login succeeded, false - login failed
*/
bool login(const std::string &Url, const std::string &Username, const std::string &Pass);
/**
* Deletes the config files and deletes are temp files
* @param deleteFiles default false, true - local files are deleted, false local files are kept
*/
void logout(bool deleteFiles = false);
/**
* Downloads a certain item from the Nextcloud and saves it locally
* @param tempItems set of items where the item is in
* @param itemID id of the item
*/
void downloadItem(std::vector<Item> &tempItems, int itemID);
/**
* Downloads a certain folder from the Nextcloud and saves it locally
* @param tempItems set of items where the item is in
* @param itemID id of the item
*/
void downloadFolder(std::vector<Item> &tempItems, int itemId);
/**
* Checks the network connection and starts the download of the certain itemId
*
* @param itemID id of the item that shall be downloaded
*/
void download(int itemID);
/**
* Removes the item
*
* @param itemID id of the item that shall be removed
* @return bool true - success, false - failure
*/
bool removeItem(int itemID);
/**
* gets the dataStructure of the given URL and writes its WEBDAV items to the items vector, reads Userdata from configfile
*
* @param pathUrl URL to get the dataStructure of
* @return vector of items
*/
std::vector<Item> getDataStructure(std::string &pathUrl);
/**
* formats the pathUrl to a local path
*
* @param path online path
* @return local path
*/
static std::string getLocalPath(std::string path);
/**
* checks the local storage checks if the items are locally available
*
* @param tempItems items that shall be compared
* @param localPath path that has to be checked
*/
void getLocalFileStructure(std::vector<Item> &tempItems, const std::string &localPath);
private:
std::vector<Item> _items;
bool _loggedIn{false};
std::string _url;
std::string _username;
std::string _password;
bool _workOffline{false};
/**
* gets the dataStructure of the given URL and writes its WEBDAV items to the items vector
*
* @param pathUrl URL to get the dataStructure of
* @param Username the username of the Nextcloud instance
* @param Pass the pass of the Nextcloud instance
* @return vector of Items
*/
std::vector<Item> getDataStructure(const std::string &pathUrl, const std::string &Username, const std::string &Pass);
std::string getUrl() { return Util::accessConfig(Action::IReadString, "url"); };
std::string getUUID() { return Util::accessConfig(Action::IReadString, "UUID"); };
std::string getPassword() { return Util::accessConfig(Action::IReadSecret, "password"); };
std::string getStartFolder() { return Util::accessConfig(Action::IReadString, "startFolder"); };
/**
* Handles the end of the game dialog and lets the user
* choose inbetween starting a new game or closing the app
*
* @param Button id of the button that was pressed
* @return void
*/
static void DialogHandlerStatic(int Button);
/**
* Reads in the xml and creates a vector of items of it
*
* @param xml xml formatted result from curl call
* @return vector<Item> the items that have been read from the xml
*/
std::vector<Item> readInXML(std::string xml);
/**
* Checks if a .structure file exists and if that is the case return a vector of items
* @param pathUrl path that shall be checked
* @return return the items for the structure if available offline
*/
std::vector<Item> getOfflineStructure(const std::string &pathUrl);
};
#endif