Merge pull request #23 from JuanJakobo/refactor

Refactor
master
JuanJakobo 2022-07-22 16:23:20 +02:00 committed by GitHub
commit b627b966c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 1890 additions and 1473 deletions

29
CMakeLists.txt 100644 → 100755
View File

@ -48,18 +48,19 @@ list(APPEND PB_LINK_DIRECTORIES "${TOOLCHAIN_PATH}/usr/arm-obreey-linux-gnueabi/
list(APPEND PB_LINK_DIRECTORIES "${TOOLCHAIN_PATH}/usr/arm-obreey-linux-gnueabi/sysroot/usr/local/lib")
list(APPEND PB_INCLUDE_DIRECTORIES "${TOOLCHAIN_PATH}/usr/arm-obreey-linux-gnueabi/sysroot/usr/include")
set(SOURCES ${CMAKE_SOURCE_DIR}/src/main.cpp
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.cpp
${CMAKE_SOURCE_DIR}/src/ui/listViewEntry.cpp
${CMAKE_SOURCE_DIR}/src/ui/loginView.cpp
${CMAKE_SOURCE_DIR}/src/util/util.cpp
${CMAKE_SOURCE_DIR}/src/util/log.cpp
${CMAKE_SOURCE_DIR}/src/handler/eventHandler.cpp
${CMAKE_SOURCE_DIR}/src/handler/mainMenu.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
${CMAKE_SOURCE_DIR}/src/ui/webDAVView/webDAVViewEntry.cpp
${CMAKE_SOURCE_DIR}/src/ui/loginView/loginView.cpp
${CMAKE_SOURCE_DIR}/src/util/util.cpp
${CMAKE_SOURCE_DIR}/src/util/log.cpp
${CMAKE_SOURCE_DIR}/src/api/webDAV.cpp
${CMAKE_SOURCE_DIR}/src/api/sqliteConnector.cpp
)
add_executable(Nextcloud.app ${SOURCES})
@ -71,9 +72,13 @@ include_directories(
${CMAKE_SOURCE_DIR}/src/handler/
${CMAKE_SOURCE_DIR}/src/util/
${CMAKE_SOURCE_DIR}/src/ui/
${CMAKE_SOURCE_DIR}/src/ui/listView/
${CMAKE_SOURCE_DIR}/src/ui/webDAVView/
${CMAKE_SOURCE_DIR}/src/ui/loginView/
${CMAKE_SOURCE_DIR}/src/api/
)
TARGET_LINK_LIBRARIES (Nextcloud.app PRIVATE inkview freetype curl)
TARGET_LINK_LIBRARIES (Nextcloud.app PRIVATE inkview freetype curl sqlite3)
INSTALL (TARGETS Nextcloud.app)

View File

@ -10,7 +10,6 @@ A basic client to access an existing nextcloud instance via Pocketbook.
* Remove files from local storage system
* Show local files that are saved but no longer available in the cloud
* Open epub, pdf, text, html, word and mobi with default ebook reader application
* Set Start folder to custom path
* Offline Modus
* Navigaten via keys and touch

89
pb.sh 100755
View File

@ -0,0 +1,89 @@
#!/bin/bash
LOGFILE="/mnt/Pocketbook/system/config/nextcloud/logfile.txt"
APPLOCATION="bin/Nextcloud.app"
APPDIR="/mnt/Pocketbook/applications/"
############################################################
# Help #
############################################################
Help()
{
# Display Help
echo "Functions to interact with the PB"
echo
echo "Syntax: pb [h|c|l|u]"
echo "options:"
echo "-h Print this Help."
echo "-c Copy the app to the appfolder"
echo "-l Show a logfile, if it exists"
echo "-u Unmount PB"
echo
}
############################################################
# MOUNT #
############################################################
Mount()
{
MOUNTPOINT=$(sudo blkid | awk '/PB632/ {print $1;}' | tr -d ':')
if [ -n "$MOUNTPOINT" ]
then
if mount | grep $MOUNTPOINT > /dev/null; then
echo "Already mounted."
else
sudo mount $MOUNTPOINT /mnt/Pocketbook
fi
else
echo "Could not find PB"
fi
}
############################################################
############################################################
# Main program #
############################################################
############################################################
[ $# -eq 0 ] && Help
while getopts ":hclu" o; do
case $o in
h) #Display help
Help
exit;;
l)
Mount
if [ -f "$LOGFILE" ];
then
less $LOGFILE;
while true; do
read -p "Do you want to delete log?" yn
case $yn in
[Yy]* ) sudo rm -r $LOGFILE; break;;
[Nn]* ) exit;;
* ) echo "Y/N";;
esac
done
else
echo "No log file found."
fi
;;
c)
Mount
if [ -d "$APPDIR" ]
then
make
sudo cp $APPLOCATION $APPDIR
fi
;;
u)
if mount | grep /mnt/Pocketbook > /dev/null; then
sudo umount /mnt/Pocketbook
echo "Pocketbook umounted"
else
echo "/mnt/Pocketbook not mounted."
fi
;;
*)
echo "Invalid command. To see available commands add -h."
esac
done

15
src/api/model.h 100644
View File

@ -0,0 +1,15 @@
//------------------------------------------------------------------
// model.h
//
// Author: JuanJakobo
// Date: 23.04.2021
// Description: Base model
//-------------------------------------------------------------------
#ifndef MODEL
#define MODEL
struct Entry
{
};
#endif

View File

@ -0,0 +1,186 @@
//------------------------------------------------------------------
// sqliteconnector.cpp
//
// Author: JuanJakobo
// Date: 18.07.2021
// Description:
//
//-------------------------------------------------------------------
#include "sqliteConnector.h"
#include "sqlite3.h"
#include "log.h"
#include <string>
#include <vector>
using std::string;
SqliteConnector::SqliteConnector(const string &DBpath) : _dbpath(DBpath)
{
}
SqliteConnector::~SqliteConnector()
{
sqlite3_close(_db);
Log::writeInfoLog("closed DB");
}
bool SqliteConnector::open()
{
int rs;
rs = sqlite3_open(_dbpath.c_str(), &_db);
if (rs)
{
Log::writeErrorLog("Could not open DB at " + _dbpath);
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);
return true;
}
string SqliteConnector::getEtag(string path)
{
open();
int rs;
sqlite3_stmt *stmt = 0;
std::vector<WebDAVItem> items;
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));
}
sqlite3_finalize(stmt);
sqlite3_close(_db);
return path;
}
std::vector<WebDAVItem> SqliteConnector::getItemsChildren(const string &parentPath)
{
open();
int rs;
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_bind_text(stmt, 1, 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));
temp.localPath = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1));
temp.path = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2));
temp.size = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 3));
temp.etag = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 4));
temp.fileType = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 5));
temp.lastEditDate = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 6));
temp.type = static_cast<Itemtype>(sqlite3_column_int(stmt,7));
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);
sqlite3_close(_db);
return items;
}
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);
rs = sqlite3_step(stmt);
if (rs != SQLITE_DONE)
{
Log::writeErrorLog(std::string("An error ocurred trying to delete items of the path ") + sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")");
}
rs = sqlite3_clear_bindings(stmt);
rs = sqlite3_reset(stmt);
}
//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;
//Sqlite version to old... is 3.18, require 3.24
//Log::writeInfoLog(sqlite3_libversion());
//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_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);
rs = sqlite3_bind_text(stmt, 4, item.size.c_str(), item.size.length(), NULL);
rs = sqlite3_bind_text(stmt, 5, parent.c_str(), parent.length(), NULL);
rs = sqlite3_bind_text(stmt, 6, item.etag.c_str(), item.etag.length(), NULL);
rs = sqlite3_bind_text(stmt, 7, item.fileType.c_str(), item.fileType.length(), NULL);
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);
}
else if (rs != SQLITE_DONE)
{
Log::writeErrorLog(std::string("error inserting into table ") + sqlite3_errmsg(_db) + std::string(" (Error Code: ") + std::to_string(rs) + ")");
}
rs = sqlite3_clear_bindings(stmt);
rs = sqlite3_reset(stmt);
}
sqlite3_exec(_db, "END TRANSACTION;", NULL, NULL, NULL);
sqlite3_finalize(stmt);
sqlite3_close(_db);
return true;
}

View File

@ -0,0 +1,44 @@
//------------------------------------------------------------------
// sqliteconnector.h
//
// Author: JuanJakobo
// Date: 18.07.2021
// Description:
//
//-------------------------------------------------------------------
#ifndef SQLITECONNECTOR
#define SQLITECONNECTOR
#include "webDAVModel.h"
#include "sqlite3.h"
#include <string>
#include <vector>
class SqliteConnector
{
public:
/**
*
*/
SqliteConnector(const std::string &DBpath);
~SqliteConnector();
bool open();
std::string getEtag(std::string path);
std::vector<WebDAVItem> getItemsChildren(const std::string &parenthPath);
void deleteChildren(const std::string &parentPath);
bool saveItemsChildren(const std::vector<WebDAVItem> &children);
private:
std::string _dbpath;
sqlite3 *_db;
};
#endif

465
src/api/webDAV.cpp 100644
View File

@ -0,0 +1,465 @@
//
//------------------------------------------------------------------
// webdav.cpp
//
// Author: JuanJakobo
// Date: 06.07.2022
//
//-------------------------------------------------------------------
#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;
using std::string;
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);
if (iv_access(NEXTCLOUD_FILE_PATH.c_str(), W_OK) != 0)
iv_mkdir(NEXTCLOUD_FILE_PATH.c_str(), 0777);
if (iv_access(CONFIG_PATH.c_str(), W_OK) == 0)
{
_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 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);
}
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?
//tempItems.clear();
}
//TODO pas as reversne and no return
string WebDAV::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;
}
//TODO SQL CHeck before calling this function --> if is needed...
///TODO rename function
vector<WebDAVItem> WebDAV::getDataStructure(const string &pathUrl)
{
string xmlItem = propfind(pathUrl);
if(!xmlItem.empty())
{
string beginItem = "<d:response>";
string endItem = "</d:response>";
vector<WebDAVItem> tempItems;
WebDAVItem tempItem;
size_t begin = xmlItem.find(beginItem);
size_t end;
while (begin != std::string::npos)
{
end = xmlItem.find(endItem);
//TODO use xml lib?
//TODO fav is int?
//Log::writeInfoLog(Util::getXMLAttribute(xmlItem, "d:favorite"));
tempItem.etag = Util::getXMLAttribute(xmlItem, "d:getetag");
tempItem.path = Util::getXMLAttribute(xmlItem, "d:href");
tempItem.lastEditDate = Util::getXMLAttribute(xmlItem, "d:getlastmodified");
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));
tempItem.title = tempItem.path;
tempItem.localPath = getLocalPath(tempItem.path);
if (tempItem.path.back() == '/')
{
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
{
tempItem.type = Itemtype::IFILE;
tempItem.fileType = Util::getXMLAttribute(xmlItem, "d:getcontenttype");
if (iv_access(tempItem.localPath.c_str(), W_OK) != 0)
tempItem.state = FileState::ICLOUD;
else
tempItem.state = FileState::ISYNCED;
}
tempItem.title = tempItem.title.substr(tempItem.title.find_last_of("/") + 1, tempItem.title.length());
Util::decodeUrl(tempItem.title);
tempItems.push_back(tempItem);
xmlItem = xmlItem.substr(end + endItem.length());
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());
return tempItems;
}
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(tempItems, itemID);
UpdateProgressbar("Download completed", 100);
}
*/
string WebDAV::propfind(const string &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);
return "";
}
//
//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
*/
string readBuffer;
CURLcode res;
CURL *curl = curl_easy_init();
if (curl)
{
string post = _username + ":" + _password;
Log::writeInfoLog(_url + pathUrl);
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);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "<\?xml version=\"1.0\" encoding=\"UTF-8\"\?> \
<d:propfind xmlns:d=\"DAV:\"><d:prop xmlns:oc=\"http://owncloud.org/ns\"> \
<d:getlastmodified/> \
<d:getcontenttype/> \
<oc:size/> \
<d:getetag/> \
<oc:favorite/> \
</d:prop></d:propfind>");
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:
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
}
}
else
{
string response = std::string("An error occured. (") + curl_easy_strerror(res) + " (Curl Error Code: " + std::to_string(res) + ")). Please try again.";
Log::writeErrorLog(response);
Message(ICON_ERROR, "Error", response.c_str(), 4000);
}
}
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);
}
}
}
*/

59
src/api/webDAV.h 100644
View File

@ -0,0 +1,59 @@
//------------------------------------------------------------------
// webdav.h
//
// Author: JuanJakobo
// Date: 06.07.2022
// Description: Interface to the webdav API
//
//-------------------------------------------------------------------
#ifndef WEBDAV
#define WEBDAV
#include "webDAVModel.h"
#include <string>
#include <vector>
//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
{
public:
/**
* Creates a new pocket object containing token to access the api
*
*/
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);
/**
* 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::string propfind(const std::string &pathUrl);
private:
std::string _username;
std::string _password;
std::string _url;
};
#endif

View File

@ -0,0 +1,42 @@
//------------------------------------------------------------------
// webDAVItem.h
//
// Author: JuanJakobo
// Date: 07.07.2022
// Description:
//-------------------------------------------------------------------
#ifndef WEBDAVITEM
#define WEBDAVITEM
#include "model.h"
#include <string>
enum Itemtype
{
IFILE,
IFOLDER
};
enum FileState
{
ICLOUD,
ISYNCED,
IOUTSYNCED,
ILOCAL
};
struct WebDAVItem : Entry{
std::string etag;
std::string path;
std::string title;
std::string localPath;
FileState state{FileState::ICLOUD};
Itemtype type;
std::string lastEditDate{"Error"};
std::string size;
std::string fileType;
};
#endif

View File

@ -38,4 +38,4 @@ int ContextMenu::createMenu(int y, FileState itemstate, iv_menuhandler handler)
OpenMenu(contextMenu, 0, ScreenWidth(),y, handler);
return 1;
}
}

View File

@ -10,7 +10,7 @@
#define CONTEXT_MENU
#include "inkview.h"
#include "item.h"
#include "webDAVModel.h"
#include <string>
@ -23,10 +23,10 @@ public:
/**
* Shows the menu on the screen, lets the user choose menu options and then redirects the handler to the caller
*
* @param y y-coordinate of the item
*
* @param y y-coordinate of the item
* @param FileState status of the item for that the menu is created
* @param handler which action does the menu buttons start
* @param handler which action does the menu buttons start
* @return int returns if the event was handled
*/
int createMenu(int y, FileState itemstate, iv_menuhandler handler);
@ -37,4 +37,4 @@ private:
char *_sync = strdup("Sync");
char *_remove = strdup("Remove");
};
#endif
#endif

View File

@ -10,14 +10,17 @@
#include "eventHandler.h"
#include "mainMenu.h"
#include "contextMenu.h"
#include "listView.h"
#include "webDAVView.h"
#include "util.h"
#include "log.h"
#include "webDAV.h"
#include "webDAVModel.h"
#include <string>
#include <memory>
using std::string;
using std::vector;
std::unique_ptr<EventHandler> EventHandler::_eventHandlerStatic;
@ -27,26 +30,62 @@ EventHandler::EventHandler()
_eventHandlerStatic = std::unique_ptr<EventHandler>(this);
_loginView = nullptr;
_listView = nullptr;
_webDAVView = nullptr;
vector<WebDAVItem> fromDB;
std::vector<WebDAVItem> currentWebDAVItems;
if (iv_access(NEXTCLOUD_CONFIG_PATH.c_str(), W_OK) == 0)
if (iv_access(CONFIG_PATH.c_str(), W_OK) == 0)
{
if (_nextcloud.login())
//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?
_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())
{
_listView = std::unique_ptr<ListView>(new ListView(_menu.getContentRect(), _nextcloud.getItems()));
FullUpdate();
return;
}
else
{
Message(ICON_ERROR, "Error", "Could not login, please try again.", 1200);
_nextcloud.logout();
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<LoginView>(new LoginView(_menu->getContentRect()));
FullUpdate();
}
break;
case 2:
default:
CloseApp();
break;
}
}
}
_loginView = std::unique_ptr<LoginView>(new LoginView(_menu.getContentRect()));
FullUpdate();
else
{
_webDAVView = std::unique_ptr<WebDAVView>(new WebDAVView(_menu->getContentRect(), _currentWebDAVItems,1));
_sqllite.saveItemsChildren(_currentWebDAVItems);
FullUpdate();
}
}
int EventHandler::eventDistributor(const int type, const int par1, const int par2)
@ -68,63 +107,39 @@ void EventHandler::mainMenuHandler(const int index)
{
switch (index)
{
//offlineModus
//TODO actualize current folder
case 101:
{
if (_nextcloud.isWorkOffline())
{
if (Util::connectToNetwork())
{
_nextcloud.switchWorkOffline();
}
else
{
Message(ICON_WARNING, "Warning", "Could not connect to the internet.", 1200);
}
}
else
{
_nextcloud.switchWorkOffline();
}
break;
}
//Make startfolder
case 102:
{
_nextcloud.setStartFolder(_tempPath);
Message(ICON_INFORMATION, "Info", ("On the next startup the folder" + _tempPath + " will be shown.").c_str(), 1200);
break;
}
//Logout
case 103:
case 102:
{
int dialogResult = DialogSynchro(ICON_QUESTION, "Action", "Do you want to delete local files?", "Yes", "No", "Cancel");
switch (dialogResult)
{
case 1:
_nextcloud.logout(true);
_webDAV.logout(true);
break;
case 3:
return;
default:
_nextcloud.logout();
_webDAV.logout();
break;
}
_listView.release();
_loginView = std::unique_ptr<LoginView>(new LoginView(_menu.getContentRect()));
_webDAVView.release();
_loginView = std::unique_ptr<LoginView>(new LoginView(_menu->getContentRect()));
FullUpdate();
break;
}
//Info
case 104:
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 105:
case 104:
CloseApp();
break;
default:
@ -146,7 +161,7 @@ void EventHandler::contextMenuHandler(const int index)
//Open
case 101:
{
if (_nextcloud.getItems().at(_tempItemID).getType() == Itemtype::IFOLDER)
if (_webDAVView->getCurrentEntry().type == Itemtype::IFOLDER)
{
openFolder();
}
@ -167,22 +182,29 @@ void EventHandler::contextMenuHandler(const int index)
case 103:
{
OpenProgressbar(1, "Removing...", "Removing Files.", 0, NULL);
/*
Log::writeInfoLog("removing file " + _items.at(itemID).getPath());
if (!_items.at(itemID).removeFile())
return false;
return true;
*/
/*
if (_nextcloud.removeItem(_tempItemID))
{
updatePBLibrary();
CloseProgressbar();
_listView->drawEntry(_tempItemID);
_webDAVView->reDrawCurrentEntry();
}
else
{
CloseProgressbar();
Message(ICON_WARNING, "Warning", "Could not delete the file, please try again.", 1200);
}
*/
CloseProgressbar();
Message(ICON_WARNING, "Warning", "Could not delete the file, please try again.", 1200);
break;
}
default:
{
_listView->invertEntryColor(_tempItemID);
_webDAVView->invertCurrentEntryColor();
break;
}
@ -195,42 +217,36 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2)
//long press to open up context menu
if (type == EVT_POINTERLONG)
{
if (_listView != nullptr)
if (_webDAVView != nullptr)
{
_tempItemID = _listView->listClicked(par1, par2);
_listView->invertEntryColor(_tempItemID);
if (_tempItemID != -1)
_webDAVView->checkIfEntryClicked(par1, par2);
_webDAVView->invertCurrentEntryColor();
if (_webDAVView->getCurrentEntry().title.compare("...") != 0)
{
if (_nextcloud.getItems().at(_tempItemID).getTitle().compare("...") != 0)
{
_contextMenu = std::unique_ptr<ContextMenu>(new ContextMenu());
_contextMenu->createMenu(par2, _nextcloud.getItems().at(_tempItemID).getState(), EventHandler::contextMenuHandlerStatic);
}
_contextMenu = std::unique_ptr<ContextMenu>(new ContextMenu());
_contextMenu->createMenu(par2, _webDAVView->getCurrentEntry().state, EventHandler::contextMenuHandlerStatic);
}
}
}
else if (type == EVT_POINTERUP)
{
//menu is clicked
if (IsInRect(par1, par2, _menu.getMenuButtonRect()) == 1)
if (IsInRect(par1, par2, &_menu->getMenuButtonRect()) == 1)
{
return _menu.createMenu(_nextcloud.isLoggedIn(), _nextcloud.isWorkOffline(), EventHandler::mainMenuHandlerStatic);
return _menu->createMenu((_webDAVView != nullptr), EventHandler::mainMenuHandlerStatic);
}
//if listView is shown
else if (_listView != nullptr)
else if (_webDAVView != nullptr)
{
_tempItemID = _listView->listClicked(par1, par2);
if (_tempItemID != -1)
if(_webDAVView->checkIfEntryClicked(par1, par2))
{
_listView->invertEntryColor(_tempItemID);
_webDAVView->invertCurrentEntryColor();
if (_nextcloud.getItems().at(_tempItemID).getType() == Itemtype::IFOLDER)
if (_webDAVView->getCurrentEntry().type == Itemtype::IFOLDER)
{
openFolder();
}
else
{
if (_nextcloud.getItems().at(_tempItemID).getState() == FileState::ISYNCED || (_nextcloud.isWorkOffline() && _nextcloud.getItems().at(_tempItemID).getState() == FileState::IOUTSYNCED))
if (_webDAVView->getCurrentEntry().state != FileState::ICLOUD)
{
openItem();
}
@ -250,17 +266,17 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2)
{
ShowHourglassForce();
if (_nextcloud.login(_loginView->getURL(), _loginView->getUsername(), _loginView->getPassword()))
std::vector<WebDAVItem> currentWebDAVItems = _webDAV.login(_loginView->getURL(), _loginView->getUsername(), _loginView->getPassword());
if(currentWebDAVItems.empty())
{
_listView = std::unique_ptr<ListView>(new ListView(_menu.getContentRect(), _nextcloud.getItems()));
_loginView.reset();
FullUpdate();
HideHourglass();
Log::writeErrorLog("login failed.");
}
else
{
HideHourglass();
Log::writeLog("login failed.");
_webDAVView = std::make_unique<WebDAVView>(WebDAVView(_menu->getContentRect(), currentWebDAVItems,1));
_loginView.reset();
FullUpdate();
}
return 0;
}
@ -271,60 +287,96 @@ int EventHandler::pointerHandler(const int type, const int par1, const int par2)
void EventHandler::updatePBLibrary()
{
if (_nextcloud.getItems().at(_tempItemID).getType() == Itemtype::IFOLDER)
if (_webDAVView->getCurrentEntry().type == Itemtype::IFOLDER)
{
Util::updatePBLibrary(15);
}
else
{
if (_nextcloud.getItems().at(_tempItemID).isBook())
//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()
{
if (_nextcloud.isWorkOffline())
{
int dialogResult = DialogSynchro(ICON_QUESTION, "Action", "You are in offline modus. Go back online?", "Yes", "No", "Cancel");
if (dialogResult == 2 || dialogResult == 3)
return; // 1;
_nextcloud.switchWorkOffline();
}
OpenProgressbar(1, "Downloading...", "Checking network connection", 0, NULL);
try
{
_nextcloud.download(_tempItemID);
//_nextcloud.download(_tempItemID);
}
catch (const std::exception &e)
{
Log::writeLog(e.what());
Log::writeErrorLog(e.what());
Message(ICON_ERROR, "Error", "Something has gone wrong. Please check the logs. (/system/config/nextcloud/)", 1200);
}
updatePBLibrary();
CloseProgressbar();
_listView->drawEntry(_tempItemID);
_webDAVView->reDrawCurrentEntry();
}
void EventHandler::openItem()
{
_listView->invertEntryColor(_tempItemID);
_nextcloud.getItems().at(_tempItemID).open();
_webDAVView->invertCurrentEntryColor();
/*
if (_state == FileState::ICLOUD)
{
Message(ICON_ERROR, "File not found.", "Could not find file.", 1000);
}
else if(isBook())
{
OpenBook(_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()
{
FillAreaRect(_menu.getContentRect(), WHITE);
ShowHourglassForce();
//_nextcloud.setItems(_nextcloud.getDataStructure(_tempPath));
//TODO if folder is unsynced sync
std::vector<WebDAVItem> currentWebDAVItems = _webDAV.getDataStructure(_webDAVView->getCurrentEntry().path);
if(currentWebDAVItems.empty())
{
Log::writeErrorLog("items empty");
HideHourglass();
_webDAVView->invertCurrentEntryColor();
}
else
{
Log::writeInfoLog("got new items");
_sqllite.saveItemsChildren(currentWebDAVItems);
_tempPath = _nextcloud.getItems().at(_tempItemID).getPath();
if (!_tempPath.empty())
_nextcloud.setItems(_nextcloud.getDataStructure(_tempPath));
_listView.release();
_listView = std::unique_ptr<ListView>(new ListView(_menu.getContentRect(), _nextcloud.getItems()));
_listView->drawHeader(_tempPath.substr(NEXTCLOUD_ROOT_PATH.length()));
PartialUpdate(_menu.getContentRect()->x, _menu.getContentRect()->y, _menu.getContentRect()->w, _menu.getContentRect()->h);
//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);
}
}
int EventHandler::keyHandler(const int type, const int par1, const int par2)
@ -334,19 +386,19 @@ int EventHandler::keyHandler(const int type, const int par1, const int par2)
//menu button
if (par1 == 23)
{
_listView->firstPage();
_webDAVView->firstPage();
}
else if (_listView != nullptr)
else if (_webDAVView != nullptr)
{
//left button
if (par1 == 24)
{
_listView->prevPage();
_webDAVView->prevPage();
}
//right button
else if (par1 == 25)
{
_listView->nextPage();
_webDAVView->nextPage();
}
}
else
@ -358,3 +410,82 @@ int EventHandler::keyHandler(const int type, const int par1, const int par2)
return 1;
}
/*
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;
//looks if files have been modified
for (unsigned int i = 1; i < tempItems.size(); i++)
{
if (tempItems.at(i).getLocalPath().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)
{
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)
{
_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));
}
}
}
closedir(dir);
}
*/

View File

@ -11,25 +11,29 @@
#include "contextMenu.h"
#include "mainMenu.h"
#include "nextcloud.h"
#include "listView.h"
#include "webDAV.h"
#include "webDAVView.h"
#include "loginView.h"
#include "sqliteConnector.h"
#include <memory>
const std::string LOG_PATH = "/mnt/ext1/system/config/nextcloud";
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";
class EventHandler
{
public:
/**
* Defines fonds, sets global Event Handler and starts new content
* Defines fonds, sets global Event Handler and starts new content
*/
EventHandler();
/**
* Handles events and redirects them
*
*
* @param type event type
* @param par1 first argument of the event
* @param par2 second argument of the event
@ -39,38 +43,38 @@ public:
private:
static std::unique_ptr<EventHandler> _eventHandlerStatic;
std::unique_ptr<ListView> _listView;
std::unique_ptr<WebDAVView> _webDAVView;
std::unique_ptr<LoginView> _loginView;
std::unique_ptr<ContextMenu> _contextMenu;
MainMenu _menu = MainMenu("Nextcloud");
Nextcloud _nextcloud = Nextcloud();
std::string _tempPath;
int _tempItemID;
std::unique_ptr<MainMenu> _menu;
WebDAV _webDAV = WebDAV();
SqliteConnector _sqllite = SqliteConnector(DB_PATH);
/**
* Function needed to call C function, redirects to real function
*
*
* @param index int of the menu that is set
*/
static void mainMenuHandlerStatic(const int index);
/**
* Handles menu events and redirects them
*
*
* @param index int of the menu that is set
*/
void mainMenuHandler(const int index);
/**
* Function needed to call C function, redirects to real function
*
*
* @param index int of the menu that is set
*/
static void contextMenuHandlerStatic(const int index);
/**
* Handlescontext menu events and redirects them
*
*
* @param index int of the menu that is set
*/
@ -78,7 +82,7 @@ private:
/**
* Handles pointer Events
*
*
* @param type event type
* @param par1 first argument of the event
* @param par2 second argument of the event
@ -88,13 +92,13 @@ private:
/**
* Updates PB Library
*
*
*/
void updatePBLibrary();
/**
* Starts the download of an item
*
*
*/
void startDownload();
@ -113,7 +117,7 @@ private:
/**
* Handles key Events
*
*
* @param type event type
* @param par1 first argument of the event (is the key)
* @param par2 second argument of the event

View File

@ -15,10 +15,9 @@ using std::string;
MainMenu::MainMenu(const string &name)
{
//Define panel size
_panelMenuHeight = ScreenHeight() / 18;
_panelMenuBeginY = 0;
_mainMenuWidth = ScreenWidth() / 3;
_panelMenuBeginY = 0;
_panelMenuBeginX = ScreenWidth() - _mainMenuWidth;
_menuButtonRect = iRect(_mainMenuWidth * 2, _panelMenuBeginY, _mainMenuWidth, _panelMenuHeight, ALIGN_RIGHT);
@ -30,18 +29,17 @@ MainMenu::MainMenu(const string &name)
DrawTextRect2(&_menuButtonRect, "Menu");
DrawLine(0, _panelMenuHeight - 1, ScreenWidth(), _panelMenuHeight - 1, BLACK);
_contentRect = iRect(0, _panelMenuHeight, ScreenWidth(), (ScreenHeight() - PanelHeight() - _panelMenuHeight), 0);
_contentRect = iRect(0, _panelMenuHeight, ScreenWidth(), (ScreenHeight() - _panelMenuHeight), 0);
SetHardTimer("PANELUPDATE", panelHandlerStatic, 110000);
DrawPanel(NULL, "", NULL, -1);
SetPanelType(0);
PartialUpdate(0, _panelMenuBeginY, ScreenWidth(), _panelMenuHeight);
}
MainMenu::~MainMenu()
{
CloseFont(_menuFont);
free(_text);
free(_syncFolder);
free(_menu);
free(_makeStartfolder);
free(_logout);
free(_info);
free(_exit);
@ -53,27 +51,20 @@ void MainMenu::panelHandlerStatic()
SetHardTimer("PANELUPDATE", panelHandlerStatic, 110000);
}
int MainMenu::createMenu(bool loggedIn, bool workOffline, iv_menuhandler handler)
int MainMenu::createMenu(bool loggedIn, iv_menuhandler handler)
{
string text = "Work offline";
if (workOffline)
text = "Work online";
_text = strdup(text.c_str());
imenu mainMenu[] =
{
{ITEM_HEADER, 0, _menu, NULL},
//show logged in
{loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 101, _text, NULL},
{loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 102, _makeStartfolder, NULL},
{loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 103, _logout, NULL},
{loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 101, _syncFolder, NULL},
{loggedIn ? (short)ITEM_ACTIVE : (short)ITEM_HIDDEN, 102, _logout, NULL},
//show always
{ITEM_ACTIVE, 104, _info, NULL},
{ITEM_ACTIVE, 105, _exit, NULL},
{ITEM_ACTIVE, 103, _info, NULL},
{ITEM_ACTIVE, 104, _exit, NULL},
{0, 0, NULL, NULL}};
OpenMenu(mainMenu, 0, _panelMenuBeginX, _panelMenuBeginY, handler);
return 1;
}
}

View File

@ -17,25 +17,25 @@ class MainMenu
{
public:
/**
* Defines fonds, sets global Event Handler and starts new content
*
* Defines fonds, sets global Event Handler and starts new content
*
* @param name name of the application
*/
MainMenu(const std::string &name);
~MainMenu();
irect *getContentRect() { return &_contentRect; };
irect *getMenuButtonRect() { return &_menuButtonRect; };
irect &getContentRect() { return _contentRect; };
irect &getMenuButtonRect() { return _menuButtonRect; };
/**
* Shows the menu on the screen, lets the user choose menu options and then redirects the handler to the caller
*
*
* @param loogedIn the status if the user is logged in
* @param handler handles the clicks on the menu
* @param handler handles the clicks on the menu
* @return int returns if the event was handled
*/
int createMenu(bool loggedIn, bool workOffline, iv_menuhandler handler);
int createMenu(bool loggedIn, iv_menuhandler handler);
private:
ifont *_menuFont;
@ -49,9 +49,8 @@ private:
imenu _mainMenu;
irect _contentRect;
char *_text;
char *_menu = strdup("Menu");
char *_makeStartfolder = strdup("Make startfolder");
char *_syncFolder = strdup("Actualize folder");
char *_logout = strdup("Logout");
char *_info = strdup("Info");
char *_exit = strdup("Close App");
@ -61,4 +60,4 @@ private:
*/
static void panelHandlerStatic();
};
#endif
#endif

28
src/ui/fileModel.h 100644
View File

@ -0,0 +1,28 @@
//------------------------------------------------------------------
// fileModel.h
//
// Author: JuanJakobo
// Date: 23.04.2021
// Description:
//-------------------------------------------------------------------
#ifndef FILEMODEL
#define FILEMODEL
//#include "model.h"
#include <string>
enum Type
{
FOLDER,
FIL
};
struct File : Entry{
std::string name;
std::string path;
Type type;
};
#endif

View File

@ -0,0 +1,44 @@
//------------------------------------------------------------------
// fileView.cpp
//
// Author: JuanJakobo
// Date: 08.09.2021
//
//-------------------------------------------------------------------
#include "fileView.h"
#include "fileViewEntry.h"
#include "fileModel.h"
#include <string>
#include <vector>
using std::vector;
FileView::FileView(const irect &contentRect, const vector<File> &files, int page) : ListView(contentRect, page)
{
auto pageHeight = 0;
auto contentHeight = _contentRect->h - _footerHeight;
auto entrycount = files.size();
_entries.reserve(entrycount);
auto i = 0;
while (i < entrycount)
{
auto entrySize = TextRectHeight(contentRect->w, files.at(i).name.c_str(), 0) + 2.5 * _entryFontHeight;
if ((pageHeight + entrySize) > contentHeight)
{
pageHeight = 0;
_page++;
}
irect rect = iRect(_contentRect->x, _contentRect->y + pageHeight, _contentRect->w, entrySize, 0);
_entries.emplace_back(std::unique_ptr<FileViewEntry>(new FileViewEntry(_page, rect, files.at(i))));
i++;
pageHeight = pageHeight + entrySize;
}
draw();
}

View File

@ -0,0 +1,35 @@
//------------------------------------------------------------------
// fileView.h
//
// Author: JuanJakobo
// Date: 08.09.2021
// Description: An UI class to display items in a listview
//-------------------------------------------------------------------
#ifndef FILEVIEW
#define FILEVIEW
#include "fileModel.h"
#include "listView.h"
#include "fileViewEntry.h"
#include <vector>
#include <memory>
class FileView final : public ListView
{
public:
/**
* Displays a list view
*
* @param ContentRect area of the screen where the list view is placed
* @param Items items that shall be shown in the listview
* @param page page that is shown, default is 1
*/
FileView(const irect &contentRect, const std::vector<File> &files, int page = 1);
File &getCurrentEntry() { return getEntry(_selectedEntry); };
File &getEntry(int entryID) { return std::dynamic_pointer_cast<FileViewEntry>(_entries.at(entryID))->get(); };
};
#endif

View File

@ -0,0 +1,35 @@
//------------------------------------------------------------------
// fileViewEntry.cpp
//
// Author: JuanJakobo
// Date: 08.09.2021
//
//-------------------------------------------------------------------
#include "fileViewEntry.h"
#include "fileModel.h"
#include <string>
FileViewEntry::FileViewEntry(int page, const irect &position, const File &entry) : ListViewEntry(page, position), _entry(entry)
{
}
void FileViewEntry::draw(const ifont *entryFont, const ifont *entryFontBold, int fontHeight)
{
SetFont(entryFontBold, BLACK);
int heightOfTitle = TextRectHeight(_position.w, _entry.name.c_str(), 0);
DrawTextRect(_position.x, _position.y, _position.w, heightOfTitle, _entry.name.c_str(), ALIGN_LEFT);
SetFont(entryFont, BLACK);
//DrawTextRect(_position.x, _position.y + heightOfTitle, _position.w, fontHeight, _entry.name.c_str(), ALIGN_LEFT);
DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, _entry.path.c_str(), ALIGN_LEFT);
std::string type = "File";
if(_entry.type == Type::FOLDER)
type = "Folder";
DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, type.c_str(), ALIGN_RIGHT);
int line = (_position.y + _position.h) - 1;
DrawLine(0, line, ScreenWidth(), line, BLACK);
}

View File

@ -0,0 +1,41 @@
//------------------------------------------------------------------
// fileViewEntry.h
//
// Author: JuanJakobo
// Date: 08.09.2021
// Description:
//-------------------------------------------------------------------
#ifndef FILEVIEWENTRY
#define FILEVIEWENTRY
#include "listViewEntry.h"
#include "fileModel.h"
class FileViewEntry : public ListViewEntry
{
public:
/**
* Creates an FileViewEntry
*
* @param Page site of the listView the Entry is shown
* @param Rect area of the screen the item is positioned
* @param entry entry that shall be drawn
*/
FileViewEntry(int page, const irect &position, const File &entry);
/**
* draws the FileViewEntry to the screen
*
* @param entryFont font for the entry itself
* @param entryFontBold bold font for the header
* @param fontHeight height of the font
*/
void draw(const ifont *entryFont, const ifont *entryFontBold, int fontHeight) override;
File &get() { return _entry; };
private:
File _entry;
};
#endif

View File

@ -1,186 +0,0 @@
//------------------------------------------------------------------
// listView.cpp
//
// Author: JuanJakobo
// Date: 04.08.2020
//
//-------------------------------------------------------------------
#include "inkview.h"
#include "item.h"
#include "util.h"
#include "listView.h"
#include "listViewEntry.h"
#include <string>
#include <vector>
#include <memory>
using std::string;
using std::vector;
ListView::ListView(const irect *contentRect, const vector<Item> &items) : _contentRect(contentRect), _items(&items)
{
FillAreaRect(_contentRect, WHITE);
_entries.clear();
int entrySize = _contentRect->h / (_itemCount + 1);
_headerHeight = 0.25 * entrySize;
_footerHeight = 0.75 * entrySize;
_headerFontHeight = 0.8 * _headerHeight;
_footerFontHeight = 0.3 * _footerHeight;
_entryFontHeight = 0.2 * entrySize;
_headerFont = OpenFont("LiberationMono", _headerFontHeight, FONT_STD);
_footerFont = OpenFont("LiberationMono", _footerFontHeight, FONT_STD);
_entryFont = OpenFont("LiberationMono", _entryFontHeight, FONT_STD);
_entryFontBold = OpenFont("LiberationMono-Bold", _entryFontHeight, FONT_BOLD);
_page = 1;
_shownPage = _page;
auto i = _items->size();
auto z = 0;
_entries.reserve(i);
while (i > 0)
{
if (z >= _itemCount)
{
_page++;
z = 0;
}
irect rect = iRect(_contentRect->x, z * entrySize + _headerHeight + _contentRect->y, _contentRect->w, entrySize, 0);
this->_entries.emplace_back(_page, rect);
i--;
z++;
}
_pageIcon = iRect(_contentRect->w - 100, _contentRect->h + _contentRect->y - _footerHeight, 100, _footerHeight, ALIGN_CENTER);
_firstPageButton = iRect(_contentRect->x, _contentRect->h + _contentRect->y - _footerHeight, 130, _footerHeight, ALIGN_CENTER);
_prevPageButton = iRect(_contentRect->x + 150, _contentRect->h + _contentRect->y - _footerHeight, 130, _footerHeight, ALIGN_CENTER);
_nextPageButton = iRect(_contentRect->x + 300, _contentRect->h + _contentRect->y - _footerHeight, 130, _footerHeight, ALIGN_CENTER);
_lastPageButton = iRect(_contentRect->x + 450, _contentRect->h + _contentRect->y - _footerHeight, 130, _footerHeight, ALIGN_CENTER);
drawEntries();
drawFooter();
}
ListView::~ListView()
{
CloseFont(_entryFont);
CloseFont(_entryFontBold);
CloseFont(_headerFont);
CloseFont(_footerFont);
}
void ListView::drawHeader(string headerText)
{
SetFont(_headerFont, BLACK);
Util::decodeUrl(headerText);
DrawTextRect(_contentRect->x, _contentRect->y, _contentRect->w, _headerHeight - 1, headerText.c_str(), ALIGN_LEFT);
int line = (_contentRect->y + _headerHeight) - 2;
DrawLine(0, line, ScreenWidth(), line, BLACK);
}
void ListView::drawEntry(int itemID)
{
FillAreaRect(_entries[itemID].getPosition(), WHITE);
_entries[itemID].draw(_items->at(itemID), _entryFont, _entryFontBold, _entryFontHeight);
updateEntry(itemID);
}
void ListView::invertEntryColor(int itemID)
{
InvertAreaBW(_entries[itemID].getPosition()->x, _entries[itemID].getPosition()->y, _entries[itemID].getPosition()->w, _entries[itemID].getPosition()->h);
updateEntry(itemID);
}
void ListView::drawEntries()
{
for (unsigned int i = 0; i < _entries.size(); i++)
{
if (_entries[i].getPage() == _shownPage)
_entries[i].draw(_items->at(i), _entryFont, _entryFontBold, _entryFontHeight);
}
}
int ListView::listClicked(int x, int y)
{
if (IsInRect(x, y, &_firstPageButton))
{
firstPage();
}
else if (IsInRect(x, y, &_nextPageButton))
{
nextPage();
}
else if (IsInRect(x, y, &_prevPageButton))
{
prevPage();
}
else if (IsInRect(x, y, &_lastPageButton))
{
actualizePage(_page);
}
else
{
for (unsigned int i = 0; i < _entries.size(); i++)
{
if (_entries[i].getPage() == _shownPage && IsInRect(x, y, _entries[i].getPosition()) == 1)
{
return i;
}
}
}
return -1;
}
void ListView::drawFooter()
{
SetFont(_footerFont, WHITE);
string footer = std::to_string(_shownPage) + "/" + std::to_string(_page);
FillAreaRect(&_pageIcon, BLACK);
DrawTextRect2(&_pageIcon, footer.c_str());
FillAreaRect(&_firstPageButton, BLACK);
DrawTextRect2(&_firstPageButton, "First");
FillAreaRect(&_prevPageButton, BLACK);
DrawTextRect2(&_prevPageButton, "Prev");
FillAreaRect(&_nextPageButton, BLACK);
DrawTextRect2(&_nextPageButton, "Next");
FillAreaRect(&_lastPageButton, BLACK);
DrawTextRect2(&_lastPageButton, "Last");
}
void ListView::updateEntry(int itemID)
{
PartialUpdate(_entries[itemID].getPosition()->x, _entries[itemID].getPosition()->y, _entries[itemID].getPosition()->w, _entries[itemID].getPosition()->h);
}
void ListView::actualizePage(int pageToShown)
{
if (pageToShown > _page)
{
Message(ICON_INFORMATION, "Info", "You have reached the last page, to return to the first, please click \"first.\"", 1200);
}
else if (pageToShown < 1)
{
Message(ICON_INFORMATION, "Info", "You are already on the first page.", 1200);
}
else
{
_shownPage = pageToShown;
FillArea(_contentRect->x, _contentRect->y + _headerHeight, _contentRect->w, _contentRect->h, WHITE);
drawEntries();
drawFooter();
PartialUpdate(_contentRect->x, _contentRect->y + _headerHeight, _contentRect->w, _contentRect->h);
}
}

View File

@ -0,0 +1,155 @@
//------------------------------------------------------------------
// listView.cpp
//
// Author: JuanJakobo
// Date: 04.08.2020
//
//-------------------------------------------------------------------
#include "inkview.h"
#include "listView.h"
#include "listViewEntry.h"
#include <string>
#include <vector>
using std::string;
using std::vector;
ListView::ListView(const irect &contentRect, int page) : _contentRect(contentRect), _shownPage(page)
{
_entries.clear();
_footerHeight = _contentRect.h / 15;
_footerFontHeight = 0.3 * _footerHeight;
_entryFontHeight = contentRect.h/45;
_footerFont = OpenFont("LiberationMono", _footerFontHeight, 1);
_entryFont = OpenFont("LiberationMono", _entryFontHeight, 1);
_entryFontBold = OpenFont("LiberationMono-Bold", _entryFontHeight, 1);
SetFont(_entryFont, BLACK);
int footerWidth = contentRect.w/20;
_pageIcon = iRect(_contentRect.w - footerWidth*2, _contentRect.h + _contentRect.y - _footerHeight, contentRect.w/10, _footerHeight, ALIGN_CENTER);
_firstPageButton = iRect(_contentRect.x, _contentRect.h + _contentRect.y - _footerHeight, contentRect.w/8, _footerHeight, ALIGN_CENTER);
_prevPageButton = iRect(_contentRect.x + footerWidth*3, _contentRect.h + _contentRect.y - _footerHeight, contentRect.w/8, _footerHeight, ALIGN_CENTER);
_nextPageButton = iRect(_contentRect.x + footerWidth*6, _contentRect.h + _contentRect.y - _footerHeight, contentRect.w/8, _footerHeight, ALIGN_CENTER);
_lastPageButton = iRect(_contentRect.x + footerWidth*9, _contentRect.h + _contentRect.y - _footerHeight, contentRect.w/8, _footerHeight, ALIGN_CENTER);
}
ListView::~ListView()
{
CloseFont(_entryFont);
CloseFont(_entryFontBold);
CloseFont(_footerFont);
}
void ListView::draw()
{
FillAreaRect(&_contentRect, WHITE);
drawEntries();
drawFooter();
PartialUpdate(_contentRect.x, _contentRect.y, _contentRect.w, _contentRect.h);
}
void ListView::reDrawCurrentEntry()
{
FillAreaRect(&_entries.at(_selectedEntry)->getPosition(), WHITE);
_entries.at(_selectedEntry)->draw(_entryFont, _entryFontBold, _entryFontHeight);
updateEntry(_selectedEntry);
}
void ListView::invertCurrentEntryColor()
{
InvertAreaBW(_entries.at(_selectedEntry)->getPosition().x, _entries.at(_selectedEntry)->getPosition().y, _entries.at(_selectedEntry)->getPosition().w, _entries.at(_selectedEntry)->getPosition().h);
updateEntry(_selectedEntry);
}
void ListView::drawEntries()
{
for (unsigned int i = 0; i < _entries.size(); i++)
{
if (_entries.at(i)->getPage() == _shownPage)
_entries.at(i)->draw(_entryFont, _entryFontBold, _entryFontHeight);
}
}
bool ListView::checkIfEntryClicked(int x, int y)
{
if (IsInRect(x, y, &_firstPageButton))
{
firstPage();
}
else if (IsInRect(x, y, &_nextPageButton))
{
nextPage();
}
else if (IsInRect(x, y, &_prevPageButton))
{
prevPage();
}
else if (IsInRect(x, y, &_lastPageButton))
{
actualizePage(_page);
}
else
{
for (unsigned int i = 0; i < _entries.size(); i++)
{
if (_entries.at(i)->getPage() == _shownPage && IsInRect(x, y, &_entries.at(i)->getPosition()) == 1)
{
_selectedEntry = i;
return true;
}
}
}
return false;
}
void ListView::drawFooter()
{
SetFont(_footerFont, WHITE);
string footer = std::to_string(_shownPage) + "/" + std::to_string(_page);
FillAreaRect(&_pageIcon, BLACK);
DrawTextRect2(&_pageIcon, footer.c_str());
FillAreaRect(&_firstPageButton, BLACK);
DrawTextRect2(&_firstPageButton, "First");
FillAreaRect(&_prevPageButton, BLACK);
DrawTextRect2(&_prevPageButton, "Prev");
FillAreaRect(&_nextPageButton, BLACK);
DrawTextRect2(&_nextPageButton, "Next");
FillAreaRect(&_lastPageButton, BLACK);
DrawTextRect2(&_lastPageButton, "Last");
}
void ListView::updateEntry(int entryID)
{
PartialUpdate(_entries.at(entryID)->getPosition().x, _entries.at(entryID)->getPosition().y, _entries.at(entryID)->getPosition().w, _entries.at(entryID)->getPosition().h);
}
void ListView::actualizePage(int pageToShow)
{
if (pageToShow > _page)
{
Message(ICON_INFORMATION, "Info", "You have reached the last page, to return to the first, please click \"first.\"", 1200);
}
else if (pageToShow < 1)
{
Message(ICON_INFORMATION, "Info", "You are already on the first page.", 1200);
}
else
{
_shownPage = pageToShow;
FillArea(_contentRect.x, _contentRect.y, _contentRect.w, _contentRect.h, WHITE);
drawEntries();
drawFooter();
PartialUpdate(_contentRect.x, _contentRect.y, _contentRect.w, _contentRect.h);
}
}

View File

@ -10,10 +10,9 @@
#define LISTVIEW
#include "inkview.h"
#include "item.h"
#include "listViewEntry.h"
#include "model.h"
#include <string>
#include <vector>
#include <memory>
@ -21,41 +20,16 @@ class ListView
{
public:
/**
* Displays a list view
*
* Displays a list view
*
* @param ContentRect area of the screen where the list view is placed
* @param Items items that shall be shown in the listview
*/
ListView(const irect *contentRect, const std::vector<Item> &items);
ListView(const irect &contentRect, int page);
~ListView();
virtual ~ListView();
/**
* Draws the header
*
* @param headerText the text that shall be displayed in the header
*
*/
void drawHeader(std::string headerText);
/**
* Draws an single entry to the screen
*
* @param itemID the id of the item that shall be drawn
*/
void drawEntry(int itemID);
/**
* inverts the color of an entry
*
* @param itemID the id of the item that shall be inverted
*/
void invertEntryColor(int itemID);
/**
* Iterates through the items and sends them to the listViewEntry Class for drawing
*/
void drawEntries();
int getShownPage(){return _shownPage;};
/**
* Navigates to the next page
@ -72,54 +46,74 @@ public:
*/
void firstPage() { this->actualizePage(1); };
/**
* Draws an single entry to the screen
*/
void reDrawCurrentEntry();
/**
* inverts the color of the currently selected entry
*/
void invertCurrentEntryColor();
/**
* Checkes if the listview has been clicked and either changes the page or returns item ID
*
*
* @param x x-coordinate
* @param y y-coordinate
* @return int Item ID that has been clicked, -1 if no Item was clicked
* @return true if was clicked
*/
int listClicked(int x, int y);
bool checkIfEntryClicked(int x, int y);
private:
int getCurrentEntryItertator() const {return _selectedEntry;};
/**
* Clears the screen and draws entries and footer
*
*/
void draw();
protected:
int _footerHeight;
int _headerHeight;
int _headerFontHeight;
int _footerFontHeight;
int _entryFontHeight;
const irect *_contentRect;
std::unique_ptr<const std::vector<Item>> _items;
std::vector<ListViewEntry> _entries;
ifont *_headerFont;
const irect _contentRect;
std::vector<std::shared_ptr<ListViewEntry>> _entries;
ifont *_footerFont;
ifont *_entryFont;
ifont *_entryFontBold;
int _page;
int _page = 1;
int _shownPage;
irect _pageIcon;
irect _nextPageButton;
irect _prevPageButton;
irect _firstPageButton;
irect _lastPageButton;
int _itemCount = 7;
int _selectedEntry;
/**
* Draws the footer including a page changer
* Iterates through the items and sends them to the listViewEntry Class for drawing
*/
void drawEntries();
/**
* Draws the footer including a page changer
*/
void drawFooter();
/**
* updates an entry
*
* @param itemID the id of the item that shall be inverted
* updates an entry
*
* @param entryID the id of the item that shall be inverted
*/
void updateEntry(int itemID);
void updateEntry(int entryID);
/**
* Navigates to the selected page
*
* @param pageToShown page that shall be shown
*
* @param pageToShow page that shall be shown
*/
void actualizePage(int pageToShown);
void actualizePage(int pageToShow);
};
#endif
#endif

View File

@ -0,0 +1,14 @@
//------------------------------------------------------------------
// hnCommentViewEntry.cpp
//
// Author: JuanJakobo
// Date: 04.08.2020
//
//-------------------------------------------------------------------
#include "inkview.h"
#include "listViewEntry.h"
ListViewEntry::ListViewEntry(int page, const irect &rect) : _page(page), _position(rect)
{
}

View File

@ -10,34 +10,35 @@
#define LISTVIEWENTRY
#include "inkview.h"
#include "item.h"
#include "model.h"
class ListViewEntry
{
public:
/**
* Creates an ListViewEntry
*
* Creates an ListViewEntry
*
* @param Page site of the listView the Entry is shown
* @param Rect area of the screen the item is positioned
*/
ListViewEntry(int page, irect position);
ListViewEntry(int page, const irect &position);
irect *getPosition() { return &_position; }
virtual ~ListViewEntry(){};
irect &getPosition() { return _position; }
int getPage() const { return _page; }
/**
* draws the listViewEntry to the screen
*
* @param item item that shall be drawn
*
* @param entryFont font for the entry itself
* @param entryFontBold bold font for the header
* @param fontHeight height of the font
* @param fontHeight height of the font
*/
void draw(const Item &item, ifont *entryFont, ifont *entryFontBold, int fontHeight);
virtual void draw(const ifont *entryFont, const ifont *entryFontBold, int fontHeight) = 0;
private:
protected:
int _page;
irect _position;
};
#endif
#endif

View File

@ -1,62 +0,0 @@
//------------------------------------------------------------------
// listViewEntry.cpp
//
// Author: JuanJakobo
// Date: 04.08.2020
//
//-------------------------------------------------------------------
#include "inkview.h"
#include "item.h"
#include "listViewEntry.h"
#include "util.h"
ListViewEntry::ListViewEntry(int page, irect rect) : _page(page), _position(rect)
{
}
void ListViewEntry::draw(const Item &item, ifont *entryFont, ifont *entryFontBold, int fontHeight)
{
SetFont(entryFontBold, BLACK);
DrawTextRect(_position.x, _position.y, _position.w, fontHeight, item.getTitle().c_str(), ALIGN_LEFT);
SetFont(entryFont, BLACK);
if (item.getState() == FileState::ILOCAL)
{
DrawTextRect(_position.x, _position.y + 3 * fontHeight, _position.w, fontHeight, "Local", ALIGN_RIGHT);
}
else
{
if (item.getType() == IFILE)
{
DrawTextRect(_position.x, _position.y + fontHeight, _position.w, fontHeight, item.getFiletype().c_str(), ALIGN_LEFT);
if (item.getState() == FileState::ISYNCED)
{
DrawTextRect(_position.x, _position.y + 3 * fontHeight, _position.w, fontHeight, "Synced", ALIGN_RIGHT);
}
else if(item.getState() == FileState::IOUTSYNCED)
{
DrawTextRect(_position.x, _position.y + 3 * fontHeight, _position.w, fontHeight, "Out of sync", ALIGN_RIGHT);
}
else
{
DrawTextRect(_position.x, _position.y + 3 * fontHeight, _position.w, fontHeight, "Click to Download", ALIGN_RIGHT);
}
}
else
{
if (item.getState() == FileState::ISYNCED)
{
DrawTextRect(_position.x, _position.y + 3 * fontHeight, _position.w, fontHeight, "Folder synced", ALIGN_RIGHT);
}
}
DrawTextRect(_position.x, _position.y + 2 * fontHeight, _position.w, fontHeight, item.getLastEditDate().c_str(), ALIGN_LEFT);
DrawTextRect(_position.x, _position.y + 3 * fontHeight, _position.w, fontHeight, item.getSizeString().c_str(), ALIGN_LEFT);
}
int line = (_position.y + _position.h) - 1;
DrawLine(0, line, ScreenWidth(), line, BLACK);
}

View File

@ -14,17 +14,17 @@
using std::string;
LoginView *LoginView::_loginViewStatic;
std::unique_ptr<LoginView> LoginView::_loginViewStatic;
LoginView::LoginView(const irect *contentRect) : _contentRect(contentRect)
LoginView::LoginView(const irect &contentRect) : _contentRect(contentRect)
{
_loginViewStatic = this;
_loginViewStatic = std::unique_ptr<LoginView>(this);
int contentHeight = contentRect->h / 2;
int contentWidth = _contentRect->w * 0.9;
int contentHeight = contentRect.h / 2;
int contentWidth = _contentRect.w * 0.9;
int beginY = 0.4 * contentHeight;
int beginX = (_contentRect->w - contentWidth) / 2;
int beginX = (_contentRect.w - contentWidth) / 2;
int contents = contentHeight / 7;
@ -32,7 +32,7 @@ LoginView::LoginView(const irect *contentRect) : _contentRect(contentRect)
_loginFont = OpenFont("LiberationMono", _loginFontHeight, FONT_STD);
SetFont(_loginFont, BLACK);
FillAreaRect(_contentRect, WHITE);
FillAreaRect(&_contentRect, WHITE);
_urlButton = iRect(beginX, beginY, contentWidth, contents, ALIGN_CENTER);
DrawTextRect(_urlButton.x, _urlButton.y - _loginFontHeight - _loginFontHeight/2, _urlButton.w, _urlButton.h, "Server address:", ALIGN_LEFT);
@ -142,4 +142,4 @@ void LoginView::keyboardHandler(char *text)
DrawTextRect2(&_passwordButton, pass.c_str());
}
}
}

View File

@ -12,6 +12,7 @@
#include "inkview.h"
#include <string>
#include <memory>
enum KeyboardTarget
{
@ -26,17 +27,17 @@ class LoginView
{
public:
/**
* Draws the loginView includin URL, Username and Password buttons inside the contentRect
*
* Draws the loginView includin URL, Username and Password buttons inside the contentRect
*
* @param contentRect area where the loginscreen shall be drawn
*/
LoginView(const irect *contentRect);
LoginView(const irect &contentRect);
~LoginView();
/**
* Checks which part of the loginscreen is shown and reacts accordingly
*
*
* @param x x-coordinate
* @param y y-coordinate
* @return int if event has been handled. Returns 2 if login has been clicked and all items are set
@ -48,10 +49,10 @@ public:
std::string getURL() { return _url; };
private:
static LoginView *_loginViewStatic;
static std::unique_ptr<LoginView> _loginViewStatic;
int _loginFontHeight;
ifont *_loginFont;
const irect *_contentRect;
const irect _contentRect;
irect _urlButton;
irect _loginButton;
irect _usernameButton;
@ -64,17 +65,17 @@ private:
/**
* Functions needed to call C function, handles the keyboard
*
*
* @param text text that has been typed in by the user
*/
static void keyboardHandlerStatic(char *text);
/**
* Called by the static method and saves and writes the input from the user to the screen
*
* @param text text that has been typed in by the user
*/
*
* @param text text that has been typed in by the user
*/
void keyboardHandler(char *text);
};
#endif
#endif

View File

@ -0,0 +1,62 @@
//------------------------------------------------------------------
// webDAVView.cpp
//
// Author: JuanJakobo
// Date: 08.09.2021
//
//-------------------------------------------------------------------
#include "webDAVView.h"
#include "webDAVModel.h"
#include "webDAV.h"
#include <string>
#include <vector>
using std::vector;
WebDAVView::WebDAVView(const irect &contentRect, vector<WebDAVItem> &items, int page) : ListView(contentRect, page)
{
auto pageHeight = 0;
auto contentHeight = _contentRect.h - _footerHeight;
auto entrycount = items.size();
_entries.reserve(entrycount);
//resize item 1
std::string header = items.at(0).path;
header = header.substr(0, header.find_last_of("/"));
header = header.substr(0, header.find_last_of("/") + 1);
items.at(0).path = header;
items.at(0).title += "\nclick to go back";
items.at(0).lastEditDate = "";
if (items.at(0).path.compare(NEXTCLOUD_ROOT_PATH) == 0)
items.erase(items.begin());
for(auto item : items)
{
auto entrySize = TextRectHeight(contentRect.w, item.title.c_str(), 0);
if(item.type == IFILE)
entrySize += _entryFontHeight;
if(item.title.find("click to go back") != std::string::npos)
entrySize += 0.5 * _entryFontHeight;
else
entrySize += 2.5 * _entryFontHeight;
if ((pageHeight + entrySize) > contentHeight)
{
pageHeight = 0;
_page++;
}
irect rect = iRect(_contentRect.x, _contentRect.y + pageHeight, _contentRect.w, entrySize, 0);
_entries.emplace_back(std::unique_ptr<WebDAVViewEntry>(new WebDAVViewEntry(_page, rect, item)));
pageHeight = pageHeight + entrySize;
}
draw();
}

View File

@ -0,0 +1,35 @@
//------------------------------------------------------------------
// webDAVView.h
//
// Author: JuanJakobo
// Date: 08.09.2021
// Description: An UI class to display items in a listview
//-------------------------------------------------------------------
#ifndef WEBDAVVIEW
#define WEBDAVVIEW
#include "webDAVModel.h"
#include "listView.h"
#include "webDAVViewEntry.h"
#include <vector>
#include <memory>
class WebDAVView final : public ListView
{
public:
/**
* Displays a list view
*
* @param ContentRect area of the screen where the list view is placed
* @param Items items that shall be shown in the listview
* @param page page that is shown, default is 1
*/
WebDAVView(const irect &contentRect, std::vector<WebDAVItem> &items, int page = 1);
WebDAVItem &getCurrentEntry() { return getEntry(_selectedEntry); };
WebDAVItem &getEntry(int entryID) { return std::dynamic_pointer_cast<WebDAVViewEntry>(_entries.at(entryID))->get(); };
};
#endif

View File

@ -0,0 +1,72 @@
//------------------------------------------------------------------
// WebDAVViewEntry.cpp
//
// Author: JuanJakobo
// Date: 08.09.2021
//
//-------------------------------------------------------------------
#include "webDAVViewEntry.h"
#include "webDAVModel.h"
#include <string>
WebDAVViewEntry::WebDAVViewEntry(int page, const irect &position, const WebDAVItem &entry) : ListViewEntry(page, position), _entry(entry)
{
}
void WebDAVViewEntry::draw(const ifont *entryFont, const ifont *entryFontBold, int fontHeight)
{
SetFont(entryFontBold, BLACK);
int heightOfTitle = TextRectHeight(_position.w, _entry.title.c_str(), 0);
if(_entry.title.find("click to go back") != std::string::npos)
DrawTextRect(_position.x, _position.y, _position.w, heightOfTitle, _entry.title.c_str(), ALIGN_CENTER);
else
{
DrawTextRect(_position.x, _position.y, _position.w, heightOfTitle, _entry.title.c_str(), ALIGN_LEFT);
SetFont(entryFont, BLACK);
if (_entry.state == FileState::ILOCAL)
{
DrawTextRect(_position.x, _position.y + heightOfTitle + 2 * fontHeight, _position.w, fontHeight, "Local", ALIGN_RIGHT);
}
else
{
if (_entry.type == IFILE)
{
DrawTextRect(_position.x, _position.y + heightOfTitle, _position.w, fontHeight, _entry.fileType.c_str(), ALIGN_LEFT);
switch(_entry.state)
{
case FileState::ISYNCED:
DrawTextRect(_position.x, _position.y + heightOfTitle + 2 * fontHeight, _position.w, fontHeight, "Synced", ALIGN_RIGHT);
break;
case FileState::IOUTSYNCED:
DrawTextRect(_position.x, _position.y + heightOfTitle + 2 * fontHeight, _position.w, fontHeight, "Out of sync", ALIGN_RIGHT);
break;
default:
DrawTextRect(_position.x, _position.y + heightOfTitle + 2 * fontHeight, _position.w, fontHeight, "Click to Download", ALIGN_RIGHT);
}
DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, _entry.lastEditDate.c_str(), ALIGN_LEFT);
DrawTextRect(_position.x, _position.y + heightOfTitle + 2 * fontHeight, _position.w, fontHeight, _entry.size.c_str(), ALIGN_LEFT);
}
else
{
if (_entry.state == FileState::ISYNCED)
{
DrawTextRect(_position.x, _position.y + heightOfTitle + fontHeight, _position.w, fontHeight, "Folder synced", ALIGN_RIGHT);
}
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);
}
}
}
int line = (_position.y + _position.h) - 1;
DrawLine(0, line, ScreenWidth(), line, BLACK);
}

View File

@ -0,0 +1,41 @@
//------------------------------------------------------------------
// webDAVViewEntry.h
//
// Author: JuanJakobo
// Date: 08.09.2021
// Description:
//-------------------------------------------------------------------
#ifndef WEBDAVVIEWENTRY
#define WEBDAVVIEWENTRY
#include "listViewEntry.h"
#include "webDAVModel.h"
class WebDAVViewEntry : public ListViewEntry
{
public:
/**
* Creates an WebDAVViewEntry
*
* @param Page site of the listView the Entry is shown
* @param Rect area of the screen the item is positioned
* @param entry entry that shall be drawn
*/
WebDAVViewEntry(int page, const irect &position, const WebDAVItem &entry);
/**
* draws the WebDAVViewEntry to the screen
*
* @param entryFont font for the entry itself
* @param entryFontBold bold font for the header
* @param fontHeight height of the font
*/
void draw(const ifont *entryFont, const ifont *entryFontBold, int fontHeight) override;
WebDAVItem &get() { return _entry; };
private:
WebDAVItem _entry;
};
#endif

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

@ -12,9 +12,19 @@
#include <string>
#include <fstream>
void Log::writeInfoLog(const std::string &text)
{
writeLog("Info:" + text);
}
void Log::writeErrorLog(const std::string &text)
{
writeLog("Error:" + text);
}
void Log::writeLog(const std::string &text)
{
std::ofstream log(LOG_PATH + std::string("/logfile.txt"), std::ios_base::app | std::ios_base::out);
std::ofstream log(CONFIG_FOLDER + std::string("/logfile.txt"), std::ios_base::app | std::ios_base::out);
time_t rawtime;
struct tm *timeinfo;
@ -25,7 +35,7 @@ void Log::writeLog(const std::string &text)
strftime(buffer, sizeof(buffer), "%d/%b/%Y:%H:%M:%S %z", timeinfo);
log << buffer << ":" << text << "\n";
log << buffer << ':' << text << "\n";
log.close();
}
}

View File

@ -17,13 +17,27 @@ class Log
{
public:
/**
* Writes a log entry to the log file
*
* Writes a error log entry to the log file
*
* @param text that shall be written to the log
*/
static void writeLog(const std::string &text);
static void writeErrorLog(const std::string &text);
/**
* Writes a info log entry to the log file
*
* @param text that shall be written to the log
*/
static void writeInfoLog(const std::string &text);
private:
Log() {}
/**
* Writes a log entry to the log file
*
* @param text that shall be written to the log
*/
static void writeLog(const std::string &text);
};
#endif
#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

View File

@ -8,7 +8,6 @@
#include "util.h"
#include "inkview.h"
#include "log.h"
#include "nextcloud.h"
#include <string>
#include <math.h>
@ -54,12 +53,10 @@ bool Util::connectToNetwork()
return false;
}
//make template?
string Util::accessConfig(const Action &action, const string &name, const string &value)
string Util::accessConfig(const string &path, const Action &action, const string &name, const string &value)
{
iconfigedit *temp = nullptr;
iconfig *config = OpenConfig(NEXTCLOUD_CONFIG_PATH.c_str(), temp);
iconfig *config = OpenConfig(path.c_str(), temp);
string returnValue;
switch (action)
@ -157,4 +154,4 @@ void Util::updatePBLibrary(int seconds)
execlp(cmd.c_str(), cmd.c_str(), (char *)NULL);
exit(1);
}
}
}

View File

@ -26,28 +26,28 @@ class Util
public:
/**
* Handles the return of curl command
*
*
*/
static size_t writeCallback(void *contents, size_t size, size_t nmemb, void *userp);
/**
* Saves the return of curl command
*
*
*/
static size_t writeData(void *ptr, size_t size, size_t nmemb, FILE *stream);
/**
* Checks if a network connection can be established
*
*
* @return true - network access succeeded, false - network access failed
*/
static bool connectToNetwork();
//TODO Doku
static std::string accessConfig(const Action &action, const std::string &name, const std::string &value = std::string());
static std::string accessConfig(const std::string &path, const Action &action, const std::string &name, const std::string &value = std::string());
/**
* Returns an integer representing the download progress
*
*
*/
static int progress_callback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
@ -58,18 +58,18 @@ public:
/**
* Decodes an URL
*
*
* @param text text that shall be converted
*/
static void decodeUrl(std::string &text);
/**
* Updates the library of the Pocketbook
*
*/
/**
* Updates the library of the Pocketbook
*
*/
static void updatePBLibrary(int seconds);
private:
Util() {}
};
#endif
#endif