Преглед на файлове

Add internal file list to allow consistent status updates including removing progress on finished transfers

Sander, Paul преди 4 години
родител
ревизия
45882b89b3

+ 1 - 0
gui/include/climanager.h

@@ -12,6 +12,7 @@ void init();
 void writeToCli(QString s);
 void readPipeLoop();
 void notificationsLoop();
+void statusLoop();
 void onExit();
 void setProgramActive(bool active);
 } // namespace CliManager

+ 13 - 0
gui/include/cmdmanager.h

@@ -9,10 +9,22 @@
 #include <string>
 #include <vector>
 
+struct fileEntry {
+	bool dirty;
+	std::string type;
+	std::string method;
+	int progress;
+	float speed;
+};
+
 namespace CmdManager {
 void init();
 void setQmlHandler(QMLHandler *q);
 
+void updateInternalFile(std::string name, std::string type, std::string method, int progress, float speed);
+void emitFileList();
+void cleanInternalList();
+
 void executeCmd(std::string cmd, Json::Value root);
 
 void handleError(Json::Value root);
@@ -33,6 +45,7 @@ void handleDeleteFile(Json::Value root);
 void handleNotifications(Json::Value root);
 void handleQueue(Json::Value root);
 void handleDequeue(Json::Value root);
+void handleExtendedStatus(Json::Value root);
 } // namespace CmdManager
 
 #endif // CMDMANAGER_H

+ 1 - 1
gui/src/Forms/Main/FooterForm.ui.qml

@@ -12,7 +12,7 @@ Page {
         target: _qmlHandler
         onLog: {
             footerLog.append(new Date().toLocaleTimeString(
-                                 Qt.locale("de_DE"), "[hh:mm:ss]\n") + logText)
+                                 Qt.locale("C"), "[hh:mm:ss]\n") + logText)
             footerFlickable.contentY = footerLog.height - footerFlickable.height
         }
 

+ 7 - 0
gui/src/Forms/Receiving/ReceivingFileTemplate.ui.qml

@@ -20,6 +20,13 @@ Item {
             }
         }
 
+        onReceivingUpdateFile: {
+            if (fileNameText == fileName) {
+                fileProgressText = fileProgress
+                fileQueued = isQueued
+            }
+        }
+
         onReceivingCloseConfirmDeletePopup: {
             confirmDeletePopup.close()
         }

+ 1 - 1
gui/src/Forms/Receiving/ReceivingForm.ui.qml

@@ -14,7 +14,7 @@ Page {
         onReceivingListFile: {
             fileList.append({
                                 "fileName": fileName,
-                                "fileSize": fileSize + " kb",
+                                "fileSize": fileSize + " kB",
                                 "fileProgress": "",
                                 "fileDecryptable": fileDecryptable,
                                 "fileExistsLocally": existsLocally

+ 28 - 0
gui/src/climanager.cpp

@@ -27,6 +27,7 @@ using namespace std;
 
 thread readPipeLoopThread;
 thread notificationsLoopThread;
+thread statusLoopThread;
 
 namespace CliManager {
 
@@ -50,6 +51,7 @@ void CliManager::onExit() {
 	CliManager::setProgramActive(false);
 	readPipeLoopThread.join();
 	notificationsLoopThread.join();
+	statusLoopThread.join();
 
 	writeToCli("exit");
 }
@@ -81,6 +83,7 @@ void CliManager::init() {
 	// start threads
 	readPipeLoopThread = thread(&CliManager::readPipeLoop);
 	notificationsLoopThread = thread(&CliManager::notificationsLoop);
+	statusLoopThread = thread(&CliManager::statusLoop);
 }
 
 std::vector<std::string> tokenizeByNewlines(std::string in) {
@@ -139,6 +142,22 @@ void CliManager::readPipeLoop() {
 
 		if (readOffset >= 1024) {
 			pipeInput.append(buf);
+			inputs = tokenizeByNewlines(pipeInput);
+			for (unsigned i = 0; i < inputs.size() - 1; i++) { // process all lines except the last, potentially incomplete one
+				// do not log these lines, Qt/Quick has no simple way of limiting the number of lines in the logging window so we might go OoM real quick
+				//~ emit qmlHandler->log(QString::fromStdString(inputs[i]));
+				qInfo() << QString::fromStdString(inputs[i]);
+				// handleJSON(s);
+				JsonHandler::parseJSON(inputs[i]);
+			}
+			if (pipeInput.back() == '\n') { // process last line if it was complete
+				qInfo() << QString::fromStdString(inputs.back());
+				// handleJSON(s);
+				JsonHandler::parseJSON(inputs.back());
+				pipeInput = string();
+			} else {
+				pipeInput = inputs[inputs.size() - 1]; // set last, potentially incomplete line, as current buffer
+			}
 			readOffset = 0;
 			pollCount = 0;
 			memset(buf, 0, 1025);
@@ -150,6 +169,15 @@ void CliManager::notificationsLoop() {
 	while (programActive) {
 		std::this_thread::sleep_for(std::chrono::milliseconds(3000));
 		writeToCli("notifications");
+		writeToCli("extendedlist");
+	}
+}
+
+void CliManager::statusLoop() {
+	while (programActive) {
+		std::this_thread::sleep_for(std::chrono::seconds(1));
+		writeToCli("status");
+		writeToCli("extendedstatus");
 	}
 }
 

+ 94 - 0
gui/src/cmdmanager.cpp

@@ -16,6 +16,7 @@ using namespace std;
 namespace CmdManager {
 QMLHandler *qmlHandler;
 map<string, void (*)(Json::Value)> cmdmap;
+map<string, struct fileEntry> filemap;
 } // namespace CmdManager
 
 void CmdManager::init() {
@@ -37,10 +38,69 @@ void CmdManager::init() {
 	cmdmap["notifications"] = &CmdManager::handleNotifications;
 	cmdmap["queue"] = &CmdManager::handleQueue;
 	cmdmap["dequeue"] = &CmdManager::handleDequeue;
+	cmdmap["extendedstatus"] = &CmdManager::handleExtendedStatus;
+
+	filemap.clear();
 }
 
 void CmdManager::setQmlHandler(QMLHandler *q) { qmlHandler = q; }
 
+void CmdManager::updateInternalFile(string name, string type, string method, int progress, float speed) {
+	map<string, struct fileEntry>::iterator it = filemap.find(name);
+	if (it != filemap.end()) {
+		it->second.type = type;
+		it->second.method = method;
+		it->second.progress = progress;
+		it->second.speed = speed;
+		it->second.dirty = true;
+	}
+}
+
+void CmdManager::emitFileList() {
+	map<string, struct fileEntry>::iterator it;
+	char speedbuf[256];
+	string progstr;
+
+	for (it = filemap.begin(); it != filemap.end(); it++) {
+		// This is a transfer
+		if (it->second.type.size()) {
+			// using a covert channel
+			if (it->second.method.size()) {
+				snprintf(speedbuf, 256, "%.2f B/s", it->second.speed);
+				progstr = it->second.type + " via " + it->second.method + "\n" + std::to_string(it->second.progress) + "%" + " @ " + speedbuf;
+			}
+			else progstr = it->second.type + "\n" + std::to_string(it->second.progress) + "%";
+			emit qmlHandler->receivingUpdateFile(it->first.c_str(), progstr.c_str(), it->second.type == "Queued" || it->second.type == "Sending");
+		}
+		// else emit plain entry
+		else
+			emit qmlHandler->receivingUpdateFile(it->first.c_str(), "", false);
+	}
+}
+
+void CmdManager::cleanInternalList() {
+	map<string, struct fileEntry>::iterator it;
+
+	for (it = filemap.begin(); it != filemap.end(); it++) {
+		if (!it->second.dirty) {
+			it->second.type.clear();
+			it->second.method.clear();
+			it->second.progress = 0;
+			it->second.speed = 0;
+		} else
+			it->second.dirty = false;
+	}
+}
+
+void printInternalList() {
+	map<string, struct fileEntry>::iterator it;
+
+	for (it = CmdManager::filemap.begin(); it != CmdManager::filemap.end(); it++)
+		emit CmdManager::qmlHandler->log((string("IFL ENTRY ") + it->first + " " + it->second.type + " " + it->second.method + " " +
+		                                  std::to_string(it->second.progress) + " " + std::to_string(it->second.speed) + " " + std::to_string(it->second.dirty))
+		                                     .c_str());
+}
+
 void CmdManager::executeCmd(string cmd, Json::Value root) {
 	map<string, void (*)(Json::Value)>::iterator it = cmdmap.find(cmd);
 	if (it == cmdmap.end()) {
@@ -60,12 +120,14 @@ void CmdManager::handleList(Json::Value root) {
 	snprintf(sizebuf, 256, "%.2f", 4.2f);
 	if (root["accept"] == true) {
 		emit qmlHandler->receivingClearFileList();
+		filemap.clear();
 
 		// Get the array of file Names
 		auto fileNames = root["names"];
 		for (int i = 0; i < fileNames.size(); i++) {
 			emit qmlHandler->receivingListFile(QString::fromStdString(fileNames[i].asString()), QString::fromStdString(sizebuf), QString("Decryptable"),
 			                                   boost::filesystem::exists(fileNames[i].asString()));
+			filemap[fileNames[i].asString()] = {false, "", "", 0, 0};
 		}
 	} else {
 		emit qmlHandler->log(root["error"].asString().c_str());
@@ -76,6 +138,7 @@ void CmdManager::handleExtendedList(Json::Value root) {
 	char sizebuf[256];
 	if (root["accept"] == true) {
 		emit qmlHandler->receivingClearFileList();
+		filemap.clear();
 
 		// Get the array of file Names
 		auto files = root["files"];
@@ -83,6 +146,7 @@ void CmdManager::handleExtendedList(Json::Value root) {
 			snprintf(sizebuf, 256, "%.2f", f["size"].asFloat());
 			emit qmlHandler->receivingListFile(QString::fromStdString(f["name"].asString()), QString::fromStdString(sizebuf),
 			                                   QString::fromStdString(f["encrypted"].asString()), boost::filesystem::exists(f["name"].asString()));
+			filemap[f["name"].asString()] = {false, "", "", 0, 0};
 		}
 	} else {
 		emit qmlHandler->log(root["error"].asString().c_str());
@@ -208,3 +272,33 @@ void CmdManager::handleDequeue(Json::Value root) {
 		emit qmlHandler->log(errorMessage);
 	}
 }
+
+void CmdManager::handleExtendedStatus(Json::Value root) {
+	Json::Value cs, ss;
+	string typestring;
+	if (!root["accept"].asBool()) {
+		QString errorMessage = QString::fromStdString(string("Error when loading status " + root["error"].asString()));
+		emit qmlHandler->log(errorMessage);
+	} else {
+		cs = root["transfersclientserver"];
+		ss = root["transfersserverserver"];
+		if (!cs.isNull()) {
+			for (Json::Value t : cs) {
+				updateInternalFile(t["file"].asString(), t["upload"].asBool() ? "Upload" : "Download", "", t["progress"].asInt(), 0);
+			}
+		}
+		if (!ss.isNull()) {
+			for (Json::Value t : ss) {
+				if (t["type"] == "download")
+					typestring = "Receiving";
+				else if (t["type"] == "upload")
+					typestring = "Sending";
+				else if (t["type"] == "queued")
+					typestring = "Queued";
+				updateInternalFile(t["file"].asString(), typestring, t["method"].asString(), t["progress"].asInt(), t["speed"].asFloat());
+			}
+		}
+		cleanInternalList();
+		emitFileList();
+	}
+}

+ 2 - 0
gui/src/jsonhandler.cpp

@@ -15,6 +15,8 @@ void JsonHandler::parseJSON(string buffer) {
 	// boolean
 	bool parsingSuccessful = reader->parse(buffer.c_str(), buffer.c_str() + buffer.size(), &root, &jsonError);
 
+	delete (reader);
+
 	// If the string is not correct Json, return
 	if (!parsingSuccessful) {
 		return;

+ 2 - 0
gui/src/main.cpp

@@ -62,5 +62,7 @@ int main(int argc, char *argv[]) {
 		}
 	}
 
+	delete object;
+
 	return ret;
 }

+ 6 - 4
gui/src/qmlhandler.cpp

@@ -106,8 +106,9 @@ void QMLHandler::onSendingSelectFileButton(QUrl url) {
 }
 
 void QMLHandler::onSendingSendFileButton() {
-	QString command = "put " + sendFileUrl.toString();
+	QString command = "put \"" + sendFileUrl.toString() + "\"";
 	CliManager::writeToCli(command);
+	CliManager::writeToCli("extendedlist");
 }
 
 void QMLHandler::onSendingClearSelectionButton() {
@@ -121,13 +122,14 @@ void QMLHandler::onSendingClearSelectionButton() {
 void QMLHandler::onReceivingListFilesButton() { CliManager::writeToCli("extendedlist"); }
 
 void QMLHandler::onReceivingDownloadFileButton(QString fileName) {
-	QString command = "get " + fileName;
+	QString command = "get \"" + fileName + "\"";
 	CliManager::writeToCli(command);
 }
 
 void QMLHandler::onReceivingConfirmDeleteFileButton(QString fileName) {
-	QString command = "deletefile " + fileName;
+	QString command = "deletefile \"" + fileName + "\"";
 	CliManager::writeToCli(command);
+	CliManager::writeToCli("extendedlist");
 }
 
 // Messages
@@ -203,7 +205,7 @@ void QMLHandler::onSignupRegisterButton(QString username, QString passwordOne, Q
 }
 
 // Footer
-void QMLHandler::onFooterGetStatusButton() { CliManager::writeToCli("status"); }
+void QMLHandler::onFooterGetStatusButton() { CliManager::writeToCli("extendedstatus"); }
 
 // Notifications
 void QMLHandler::onDismissNotificationButton(int index) { emit dismissNotification(index); }