Browse Source

Merge branch 'us43-gui-notifications-rework' into 'develop'

US43: GUI Notifications Rework

See merge request tobias.wach/ccats!71
Sander, Paul 4 years ago
parent
commit
1a542cb297

+ 2 - 1
gui/include/climanager.h

@@ -8,9 +8,10 @@
 namespace CliManager {
 void setQmlHandler(QMLHandler *q);
 
-void startCli();
+void init();
 void writeToCli(QString s);
 void readPipeLoop();
+void notificationsLoop();
 void onExit();
 void setProgramActive(bool active);
 } // namespace CliManager

+ 2 - 0
gui/include/cmdmanager.h

@@ -15,6 +15,7 @@ void setQmlHandler(QMLHandler *q);
 
 void executeCmd(std::string cmd, Json::Value root);
 
+void handleError(Json::Value root);
 void handleStatus(Json::Value root);
 void handleClose(Json::Value root);
 void handleList(Json::Value root);
@@ -28,6 +29,7 @@ void handleGet(Json::Value root);
 void handleGetData(Json::Value root);
 void handleDeleteMe(Json::Value root);
 void handleDeleteFile(Json::Value root);
+void handleNotifications(Json::Value root);
 } // namespace CmdManager
 
 #endif // CMDMANAGER_H

+ 8 - 0
gui/include/qmlhandler.h

@@ -84,6 +84,11 @@ signals:
 	void log(QString logText);
 	void footerSetStatus(QString status);
 
+	// Notifications
+	void notification(QString message);
+	void dismissNotification(int index);
+	void showDesktopNotification(QString title, QString message);
+
 	// QML -> C++
 public slots:
 	void onStart();
@@ -129,6 +134,9 @@ public slots:
 
 	// Footer
 	void onFooterGetStatusButton();
+
+	// Notifications
+	void onDismissNotificationButton(int id);
 };
 
 #endif // QMLHANDLER_H

+ 6 - 2
gui/src/Forms/Connect/IpPopup.ui.qml

@@ -23,7 +23,7 @@ Popup {
             popup.close()
         }
         onIpPopupOpen: {
-            popup.open();
+            popup.open()
         }
         onIpPopupSetStatus: {
             ipPopupStatusText.text = status
@@ -86,6 +86,8 @@ Popup {
             verticalAlignment: Text.AlignVCenter
             Layout.alignment: Qt.AlignCenter
             font.pixelSize: 20
+            wrapMode: Text.WordWrap
+            Layout.preferredWidth: parent.width
         }
 
         Button {
@@ -107,7 +109,9 @@ Popup {
                 // @disable-check M223
                 if (ipPopupConnectButton.enabled) {
                     // @disable-check M222
-                    _qmlHandler.onIpPopupConnectButton(ipPopupIpInput.text, ipPopupSetDefaultCheckbox.checked)
+                    _qmlHandler.onIpPopupConnectButton(
+                                ipPopupIpInput.text,
+                                ipPopupSetDefaultCheckbox.checked)
                 }
             }
         }

+ 3 - 0
gui/src/Forms/Connect/LoginForm.ui.qml

@@ -5,6 +5,7 @@ import QtQuick.Layouts 1.3
 Page {
     width: 400
     height: 400
+    title: ""
 
     Connections {
         target: _qmlHandler
@@ -83,6 +84,8 @@ Page {
             id: loginStatusText
             color: "#df3f3f"
             text: qsTr("")
+            wrapMode: Text.WordWrap
+            Layout.preferredWidth: parent.width
             horizontalAlignment: Text.AlignHCenter
             verticalAlignment: Text.AlignVCenter
             Layout.alignment: Qt.AlignCenter

+ 2 - 0
gui/src/Forms/Connect/SignupForm.ui.qml

@@ -114,6 +114,8 @@ Page {
             horizontalAlignment: Text.AlignHCenter
             verticalAlignment: Text.AlignVCenter
             Layout.alignment: Qt.AlignCenter
+            wrapMode: Text.WordWrap
+            Layout.preferredWidth: parent.width
             font.pixelSize: 20
         }
 

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

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

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

@@ -121,7 +121,7 @@ Popup {
         onAccepted: {
             // @disable-check M222
             var path = invalidCliPathPopupDialog.fileUrl.toString()
-            path = path.replace(/^(file:\/{2})/,"");
+            path = path.replace(/^(file:\/{2})/, "")
             invalidCliPathPopupCliPath.text = path
         }
     }

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

@@ -101,7 +101,7 @@ Popup {
         onAccepted: {
             // @disable-check M222
             var path = noConfigFoundPopupCliDialog.fileUrl.toString()
-            path = path.replace(/^(file:\/{2})/,"");
+            path = path.replace(/^(file:\/{2})/, "")
             noConfigFoundPopupCliPath.text = path
         }
     }

+ 24 - 0
gui/src/Forms/Main/main.qml

@@ -6,6 +6,7 @@ import "../Messages"
 import "../Settings"
 import "../Help"
 import "../Connect"
+import "../Notifications"
 
 ApplicationWindow {
     id: window
@@ -20,6 +21,17 @@ ApplicationWindow {
     minimumWidth: width
     title: qsTr("Covert Channel - Control Panel")
 
+    property string notificationTabTitle: "Notifications"
+
+    Connections {
+        target: _qmlHandler
+
+        onNotification: {
+            if (swipeView.currentIndex != 5)
+              notificationTabTitle = "* Notifications"
+        }
+    }
+
     SwipeView {
         id: swipeView
         anchors.fill: parent
@@ -44,6 +56,10 @@ ApplicationWindow {
         HelpForm {
 
         }
+
+        NotificationsForm {
+
+        }
     }
 
     header: TabBar {
@@ -70,6 +86,14 @@ ApplicationWindow {
         TabButton {
             text: qsTr("Help")
         }
+
+        TabButton {
+            text: notificationTabTitle
+
+            onClicked: {
+                text = "Notifications"
+            }
+        }
     }
 
     footer: FooterForm {

+ 52 - 0
gui/src/Forms/Notifications/NotificationTemplate.ui.qml

@@ -0,0 +1,52 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.3
+
+Item {
+    width: 1250
+    height: 50
+    property string notificationDateText
+    property string notificationMessageText
+    property int myIndex
+
+    RowLayout {
+        id: rowLayout
+        anchors.fill: parent
+
+        Text {
+            id: notificationTemplateDateText
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: parent.height
+            Layout.preferredWidth: 200
+            verticalAlignment: Text.AlignVCenter
+            horizontalAlignment: Text.AlignHLeft
+            text: notificationDateText
+            color: "#ffffff"
+        }
+
+        Text {
+            id: notificationTemplateMessageText
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: parent.height
+            Layout.preferredWidth: 800
+            verticalAlignment: Text.AlignVCenter
+            horizontalAlignment: Text.AlignHLeft
+            text: notificationMessageText
+            color: "#ffffff"
+        }
+
+        Button {
+            id: notificationTemplateDismissButton
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: parent.height
+            Layout.preferredWidth: 50
+            text: qsTr("X")
+
+            // @disable-check M223
+            onClicked: {
+                // @disable-check M222
+                _qmlHandler.onDismissNotificationButton(myIndex)
+            }
+        }
+    }
+}

+ 93 - 0
gui/src/Forms/Notifications/NotificationsForm.ui.qml

@@ -0,0 +1,93 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.3
+import Qt.labs.platform 1.1
+
+Page {
+    width: 1280
+    height: 470
+    id: notificationsForm
+
+    font.capitalization: Font.MixedCase
+
+    Connections {
+        target: _qmlHandler
+        onNotification: {
+            notificationList.append({
+                                        "notificationDate": new Date().toLocaleString(
+                                                                Qt.locale(
+                                                                    "de_DE"),
+                                                                "[dd.MM.yyyy hh:mm:ss]"),
+                                        "notificationMessage": message
+                                    })
+        }
+
+        onDismissNotification: {
+            notificationList.remove(index)
+        }
+
+        onShowDesktopNotification: {
+            trayIcon.showMessage(title, message)
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        ScrollView {
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 370
+
+            ListView {
+                anchors.fill: parent
+                model: notificationList
+                clip: true
+
+                delegate: NotificationTemplate {
+                    notificationDateText: notificationDate
+                    notificationMessageText: notificationMessage
+                    myIndex: index
+                }
+            }
+        }
+
+        ListModel {
+            id: notificationList
+        }
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 30
+            id: loginTitle
+            color: "#ffffff"
+            text: qsTr("No new notifications!")
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            visible: notificationList.count == 0 ? true : false
+            font.pixelSize: 20
+        }
+
+        Button {
+            id: notificationsDismissAllButton
+            Layout.preferredWidth: 180
+            Layout.preferredHeight: 70
+            Layout.alignment: Qt.AlignCenter
+            text: qsTr("Dismiss all")
+            enabled: notificationList.count != 0 ? true : false
+            visible: enabled
+
+            // @disable-check M223
+            onClicked: {
+                // @disable-check M222
+                notificationList.clear()
+            }
+        }
+    }
+
+    SystemTrayIcon {
+        id: trayIcon
+        visible: true
+        icon.source: "qrc:/images/tray-icon.png"
+    }
+}

+ 6 - 4
gui/src/Forms/Receiving/ReceivingForm.ui.qml

@@ -12,10 +12,12 @@ Page {
     Connections {
         target: _qmlHandler
         onReceivingListFile: {
-            fileList.append({"fileName" : fileName,
-                           "fileSize" : "42 kb",
-                           "fileDecryptable" : "Decryptable?: Yes",
-                           "fileExistsLocally" : existsLocally})
+            fileList.append({
+                                "fileName": fileName,
+                                "fileSize": "42 kb",
+                                "fileDecryptable": "Decryptable?: Yes",
+                                "fileExistsLocally": existsLocally
+                            })
         }
 
         onReceivingClearFileList: {

+ 8 - 6
gui/src/Forms/Settings/SettingsForm.ui.qml

@@ -203,10 +203,12 @@ Page {
                 text: "Save Changes"
                 font.pixelSize: 20
                 onClicked: {
-                    _qmlHandler.onSettingsSaveButton(settingsCovertMethodPicker.currentIndex,
-                                                     settingsSaveIpSwitch.checked,
-                                                     settingsSaveUsernameSwitch.checked,
-                                                     settingsCliPath.text.replace("CLI-Path:     ", ""))
+                    _qmlHandler.onSettingsSaveButton(
+                                settingsCovertMethodPicker.currentIndex,
+                                settingsSaveIpSwitch.checked,
+                                settingsSaveUsernameSwitch.checked,
+                                settingsCliPath.text.replace("CLI-Path:     ",
+                                                             ""))
                 }
             }
 
@@ -233,11 +235,11 @@ Page {
         onAccepted: {
             // @disable-check M222
             var path = settingsCliDialog.fileUrl.toString()
-            path = path.replace(/^(file:\/{2})/,"");
+            path = path.replace(/^(file:\/{2})/, "")
             settingsCliPath.text = "CLI-Path:   " + path
         }
     }
-    
+
     DeleteMePopup {
         id: deleteMePopup
     }

+ 11 - 1
gui/src/climanager.cpp

@@ -1,6 +1,7 @@
 #include <QDebug>
 #include <QGuiApplication>
 
+#include <chrono>
 #include <csignal>
 #include <cstdio>
 #include <cstdlib>
@@ -44,7 +45,7 @@ void CliManager::onExit() { writeToCli("exit"); }
 
 void CliManager::setQmlHandler(QMLHandler *q) { qmlHandler = q; }
 
-void CliManager::startCli() {
+void CliManager::init() {
 	pipe(inpipefd);
 	pipe(outpipefd);
 
@@ -65,7 +66,9 @@ void CliManager::startCli() {
 
 	close(outpipefd[0]);
 	close(inpipefd[1]);
+
 	std::thread(&CliManager::readPipeLoop).detach();
+	std::thread(&CliManager::notificationsLoop).detach();
 }
 
 std::vector<std::string> tokenizeByNewlines(std::string in) {
@@ -131,4 +134,11 @@ void CliManager::readPipeLoop() {
 	}
 }
 
+void CliManager::notificationsLoop() {
+	while (programActive) {
+		std::this_thread::sleep_for(std::chrono::milliseconds(3000));
+		writeToCli("notifications");
+	}
+}
+
 void CliManager::setProgramActive(bool active) { programActive = active; }

+ 30 - 2
gui/src/cmdmanager.cpp

@@ -15,10 +15,11 @@ using namespace std;
 
 namespace CmdManager {
 QMLHandler *qmlHandler;
-map<string, void (*)(Json::Value root)> cmdmap;
+map<string, void (*)(Json::Value)> cmdmap;
 } // namespace CmdManager
 
 void CmdManager::init() {
+	cmdmap["error"] = &CmdManager::handleError;
 	cmdmap["status"] = &CmdManager::handleStatus;
 	cmdmap["close"] = &CmdManager::handleClose;
 	cmdmap["list"] = &CmdManager::handleList;
@@ -32,11 +33,20 @@ void CmdManager::init() {
 	cmdmap["getdata"] = &CmdManager::handleGetData;
 	cmdmap["deleteme"] = &CmdManager::handleDeleteMe;
 	cmdmap["deletefile"] = &CmdManager::handleDeleteFile;
+	cmdmap["notifications"] = &CmdManager::handleNotifications;
 }
 
 void CmdManager::setQmlHandler(QMLHandler *q) { qmlHandler = q; }
 
-void CmdManager::executeCmd(string cmd, Json::Value root) { cmdmap[cmd](root); }
+void CmdManager::executeCmd(string cmd, Json::Value root) {
+	map<string, void (*)(Json::Value)>::iterator it = cmdmap.find(cmd);
+	if (it == cmdmap.end()) {
+		return;
+	}
+	cmdmap[cmd](root);
+}
+
+void CmdManager::handleError(Json::Value root) { emit qmlHandler->log(root["error"].asString().c_str()); }
 
 void CmdManager::handleStatus(Json::Value root) { emit qmlHandler->footerSetStatus(root["response"].asString().c_str()); }
 
@@ -141,3 +151,21 @@ void CmdManager::handleDeleteFile(Json::Value root) {
 		emit qmlHandler->log(message);
 	}
 }
+
+void CmdManager::handleNotifications(Json::Value root) {
+	if (root["accept"] == true) {
+		// Get the array of notifications
+		auto notifications = root["messages"];
+		for (int i = 0; i < notifications.size(); i++) {
+			emit qmlHandler->notification(QString::fromStdString(notifications[i].asString().c_str()));
+		}
+
+		if (notifications.size() > 1)
+			emit qmlHandler->showDesktopNotification("Covert Channel - New Notifications",
+			                                         "You have multiple new notifications. Open the program to see them.");
+		else if (notifications.size() == 1)
+			emit qmlHandler->showDesktopNotification("Covert Channel - New Notification", QString::fromStdString(notifications[0].asString().c_str()));
+	} else {
+		emit qmlHandler->log(root["error"].asString().c_str());
+	}
+}

BIN
gui/src/images/tray-icon.png


+ 5 - 0
gui/src/qml.qrc

@@ -24,6 +24,11 @@
         <file>Forms/Settings/DeleteMePopup.ui.qml</file>
         <file>Forms/Settings/SettingsForm.ui.qml</file>
 
+        <file>Forms/Notifications/NotificationsForm.ui.qml</file>
+        <file>Forms/Notifications/NotificationTemplate.ui.qml</file>
+
         <file>Forms/Help/HelpForm.ui.qml</file>
+
+        <file>images/tray-icon.png</file>
     </qresource>
 </RCC>

+ 4 - 1
gui/src/qmlhandler.cpp

@@ -72,7 +72,7 @@ void QMLHandler::onStart() {
 		emit noConfigFoundPopupOpen();
 	}
 
-	CliManager::startCli();
+	CliManager::init();
 }
 
 // No Config Found Popup
@@ -205,4 +205,7 @@ void QMLHandler::onSignupRegisterButton(QString username, QString passwordOne, Q
 // Footer
 void QMLHandler::onFooterGetStatusButton() { CliManager::writeToCli("status"); }
 
+// Notifications
+void QMLHandler::onDismissNotificationButton(int index) { emit dismissNotification(index); }
+
 void QMLHandler::setRestart(bool restart) { _RESTART = restart; }

+ 1 - 1
gui/src/qtquickcontrols2.conf

@@ -7,4 +7,4 @@ Style=Material
 
 [Material]
 Theme=Dark
-Accent=Blue
+Accent=Cyan