Browse Source

Merge branch 'us-31-configurable-gui' into 'develop'

US31: Configurable GUI

See merge request tobias.wach/ccats!49
Sander, Paul 4 năm trước cách đây
mục cha
commit
7e3478a9e1

+ 1 - 1
gui/CMakeLists.txt

@@ -16,7 +16,7 @@ pkg_check_modules(JSONCPP REQUIRED jsoncpp)
 
 add_definitions(-DQT_NO_DEBUG_OUTPUT)
 
-add_executable(${PROJECT_NAME} src/main.cpp src/qmlhandler.cpp src/qml.qrc)
+add_executable(${PROJECT_NAME} src/main.cpp src/qmlhandler.cpp src/qml.qrc src/config.cpp)
 
 include_directories(${Boost_INCLUDE_DIR} ${JSONCPP_INCLUDEDIR} include)
 target_link_libraries(${PROJECT_NAME} PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${JSONCPP_LIBRARIES} ${Boost_LIBRARIES} Qt5::Core Qt5::Quick)

+ 128 - 0
gui/src/InvalidCliPathPopup.ui.qml

@@ -0,0 +1,128 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.3
+import QtQuick.Dialogs 1.0
+
+Popup {
+    id: popup
+    height: 200
+    dim: true
+    clip: false
+    width: 800
+    modal: true
+    focus: true
+    closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
+
+    Connections {
+        target: _qmlHandler
+        onInvalidCliPathPopupClose: {
+            popup.close()
+        }
+        onInvalidCliPathPopupOpen: {
+            popup.open()
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            id: invalidCliPathPopupText
+            color: "#ffffff"
+            text: qsTr("Invalid CLI-Path: CLI could not be found on the location specified in the config file.\nPlease specify the new location of the CLI:")
+            wrapMode: Text.WordWrap
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Text {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 600
+                id: invalidCliPathPopupCliPath
+                color: "#ffffff"
+                text: qsTr("Select CLI Path >>>")
+                horizontalAlignment: Text.AlignLeft
+                font.italic: true
+                verticalAlignment: Text.AlignVCenter
+                font.pixelSize: 20
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignLeft
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 150
+                id: invalicCliPathPopupSelectCliButton
+                height: 50
+                text: qsTr("Select CLI")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    invalidCliPathPopupDialog.open()
+                }
+            }
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 200
+                id: invalidCliPathPopupQuitButton
+                text: qsTr("Quit")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onInvalidCliPathPopupQuitButton()
+                }
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 200
+                id: invalidCliPathPopupContinueButton
+                text: qsTr("Continue")
+                enabled: invalidCliPathPopupText.text == "Select CLI Path >>>" ? false : true
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onInvalidCliPathPopupContinueButton(
+                                invalidCliPathPopupCliPath.text)
+                    popup.close()
+                }
+            }
+        }
+    }
+
+    FileDialog {
+        id: invalidCliPathPopupDialog
+        nameFilters: ["CLI file (ccats-cli)"]
+        title: "Please select the CLI File"
+        folder: shortcuts.home
+        // @disable-check M223
+        onAccepted: {
+            // @disable-check M222
+            var path = invalidCliPathPopupDialog.fileUrl.toString()
+            path = path.replace(/^(file:\/{2})/,"");
+            invalidCliPathPopupCliPath.text = path
+        }
+    }
+}

+ 78 - 0
gui/src/InvalidConfigPopup.ui.qml

@@ -0,0 +1,78 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.3
+import QtQuick.Dialogs 1.0
+
+Popup {
+    id: popup
+    height: 150
+    dim: true
+    clip: false
+    width: 650
+    modal: true
+    focus: true
+    closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
+
+    Connections {
+        target: _qmlHandler
+        onInvalidConfigPopupClose: {
+            popup.close()
+        }
+        onInvalidConfigPopupOpen: {
+            popup.open()
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            id: invalidConfigPopupText
+            color: "#ffffff"
+            text: qsTr("Configuration file invalid!\nWarning: Creating the default config will overwrite your config file!")
+            wrapMode: Text.WordWrap
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 200
+                id: invalidConfigPopupQuitButton
+                text: qsTr("Quit")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onInvalidConfigPopupQuitButton()
+                }
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 200
+                id: invalidCliPathPopupContinueButton
+                text: qsTr("Create Default")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    _qmlHandler.onInvalidConfigPopupCreateDefaultButton()
+                    popup.close()
+                }
+            }
+        }
+    }
+}

+ 17 - 3
gui/src/IpPopup.ui.qml

@@ -4,7 +4,7 @@ import QtQuick.Layouts 1.3
 
 Popup {
     id: popup
-    height: 200
+    height: 250
     dim: true
     clip: false
     width: 400
@@ -15,11 +15,15 @@ Popup {
 
     Connections {
         target: _qmlHandler
+        onIpPopupSetIP: {
+            ipPopupIpInput.text = default_ip
+            ipPopupConnectButton.enabled = ipPopupIpInput.acceptableInput
+        }
         onIpPopupClose: {
             popup.close()
         }
         onIpPopupOpen: {
-          popup.open();
+            popup.open();
         }
         onIpPopupSetStatus: {
             ipPopupStatusText.text = status
@@ -30,6 +34,9 @@ Popup {
         onIpPopupDisableConnectButton: {
             ipPopupConnectButton.enabled = false
         }
+        onIpPopupCheckSaveCheckbox: {
+            ipPopupSetDefaultCheckbox.checked = true
+        }
     }
 
     ColumnLayout {
@@ -64,6 +71,13 @@ Popup {
             onTextEdited: ipPopupConnectButton.enabled = ipPopupIpInput.acceptableInput
         }
 
+        CheckBox {
+            id: ipPopupSetDefaultCheckbox
+            Layout.alignment: Qt.AlignCenter
+            checked: false
+            text: "Save as default IP"
+        }
+
         Text {
             id: ipPopupStatusText
             color: "#df3f3f"
@@ -93,7 +107,7 @@ Popup {
                 // @disable-check M223
                 if (ipPopupConnectButton.enabled) {
                     // @disable-check M222
-                    _qmlHandler.onIpPopupConnectButton(ipPopupIpInput.text)
+                    _qmlHandler.onIpPopupConnectButton(ipPopupIpInput.text, ipPopupSetDefaultCheckbox.checked)
                 }
             }
         }

+ 21 - 3
gui/src/LoginForm.ui.qml

@@ -8,6 +8,9 @@ Page {
 
     Connections {
         target: _qmlHandler
+        onLoginSetUsername: {
+            loginUsernameInput.text = username
+        }
         onLoginSetStatus: {
             loginStatusText.text = status
         }
@@ -17,6 +20,9 @@ Page {
         onLoginDisableLoginButton: {
             loginLoginButton.enabled = false
         }
+        onLoginSignupCheckSaveCheckbox: {
+            loginSetDefaultCheckbox.checked = true
+        }
     }
 
     ColumnLayout {
@@ -45,7 +51,8 @@ Page {
             // @disable-check M222
             Keys.onEnterPressed: loginLoginButton.activate()
 
-            onTextEdited: loginLoginButton.enabled = (loginUsernameInput.text != "" && loginPasswordInput.text != "")
+            onTextEdited: loginLoginButton.enabled = (loginUsernameInput.text != ""
+                                                      && loginPasswordInput.text != "")
         }
 
         TextField {
@@ -61,7 +68,15 @@ Page {
             Keys.onEnterPressed: loginLoginButton.activate()
             echoMode: TextInput.Password
 
-            onTextEdited: loginLoginButton.enabled = (loginUsernameInput.text != "" && loginPasswordInput.text != "")
+            onTextEdited: loginLoginButton.enabled = (loginUsernameInput.text != ""
+                                                      && loginPasswordInput.text != "")
+        }
+
+        CheckBox {
+            id: loginSetDefaultCheckbox
+            Layout.alignment: Qt.AlignCenter
+            checked: false
+            text: "Save as default user"
         }
 
         Text {
@@ -91,7 +106,10 @@ Page {
                 // @disable-check M223
                 if (loginLoginButton.enabled) {
                     // @disable-check M222
-                    _qmlHandler.onLoginLoginButton(loginUsernameInput.text, loginPasswordInput.text)
+                    _qmlHandler.onLoginLoginButton(
+                                loginUsernameInput.text,
+                                loginPasswordInput.text,
+                                loginSetDefaultCheckbox.checked)
                 }
             }
         }

+ 108 - 0
gui/src/NoConfigFoundPopup.ui.qml

@@ -0,0 +1,108 @@
+import QtQuick 2.4
+import QtQuick.Controls 2.3
+import QtQuick.Layouts 1.3
+import QtQuick.Dialogs 1.0
+
+Popup {
+    id: popup
+    height: 200
+    dim: true
+    clip: false
+    width: 800
+    modal: true
+    focus: true
+    closePolicy: Popup.NoAutoClose
+    anchors.centerIn: Overlay.overlay
+
+    Connections {
+        target: _qmlHandler
+        onNoConfigFoundPopupClose: {
+            popup.close()
+        }
+        onNoConfigFoundPopupOpen: {
+            popup.open()
+        }
+    }
+
+    ColumnLayout {
+        anchors.fill: parent
+
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            id: noConfigFoundPopupText
+            color: "#ffffff"
+            text: qsTr("No configuration file found. Default config will be created.\nPlease specify the location of the CLI:")
+            wrapMode: Text.WordWrap
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
+
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+
+            Text {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 600
+                id: noConfigFoundPopupCliPath
+                color: "#ffffff"
+                text: qsTr("Select CLI Path >>>")
+                horizontalAlignment: Text.AlignLeft
+                font.italic: true
+                verticalAlignment: Text.AlignVCenter
+                font.pixelSize: 20
+            }
+
+            Button {
+                Layout.alignment: Qt.AlignLeft
+                Layout.preferredHeight: parent.height
+                Layout.preferredWidth: 150
+                id: noConfigFoundPopupSelectCliButton
+                height: 50
+                text: qsTr("Select CLI")
+                font.pointSize: 16
+                // @disable-check M223
+                onClicked: {
+                    // @disable-check M222
+                    noConfigFoundPopupCliDialog.open()
+                }
+            }
+        }
+
+        Button {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredHeight: 50
+            Layout.preferredWidth: 200
+            id: noConfigFoundPopupContinueButton
+            text: qsTr("Continue")
+            enabled: noConfigFoundPopupCliPath.text == "Select CLI Path >>>" ? false : true
+            font.pointSize: 16
+            // @disable-check M223
+            onClicked: {
+                // @disable-check M222
+                _qmlHandler.onNoConfigFoundPopupContinueButton(
+                            noConfigFoundPopupCliPath.text)
+                popup.close()
+            }
+        }
+    }
+
+    FileDialog {
+        id: noConfigFoundPopupCliDialog
+        nameFilters: ["CLI file (ccats-cli)"]
+        title: "Please select the CLI File"
+        folder: shortcuts.home
+        // @disable-check M223
+        onAccepted: {
+            // @disable-check M222
+            var path = noConfigFoundPopupCliDialog.fileUrl.toString()
+            path = path.replace(/^(file:\/{2})/,"");
+            noConfigFoundPopupCliPath.text = path
+        }
+    }
+}

+ 211 - 51
gui/src/SettingsForm.ui.qml

@@ -1,5 +1,8 @@
 import QtQuick 2.12
+import QtQuick.Layouts 1.3
 import QtQuick.Controls 2.5
+import QtQuick.Controls.Material 2.3
+import QtQuick.Dialogs 1.0
 
 Page {
     width: 1280
@@ -13,68 +16,225 @@ Page {
         onCloseWindow: {
             window.close()
         }
+        onLoadSettings: {
+            settingsCovertMethodPicker.currentIndex = covertMethod
+            settingsSaveIpSwitch.checked = saveIP
+            settingsSaveUsernameSwitch.checked = saveUsername
+            settingsCliPath.text = "CLI-Path:     " + cliPath
+        }
     }
 
-    ComboBox {
-        id: settingsCovertMethodPicker
-        x: 328
-        y: 59
-        width: 285
-        height: 48
+    ColumnLayout {
+        anchors.fill: parent
 
-        model: ListModel {
-            ListElement {
-                text: "Method 1"
-            }
-            ListElement {
-                text: "Method 2"
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 400
+            Layout.bottomMargin: 20
+
+            ColumnLayout {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 500
+                Layout.preferredHeight: parent.height
+
+                Text {
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "Covert Channel Method:"
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+
+                Text {
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "Autofill default IP on start:"
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+
+                Text {
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "Autofill default username on start:"
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+
+                Text {
+                    id: settingsCliPath
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "CLI-Path:   "
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
+
+                Text {
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredWidth: 400
+                    Layout.preferredHeight: 50
+                    color: "#ffffff"
+                    text: "Delete my account:"
+                    verticalAlignment: Text.AlignVCenter
+                    horizontalAlignment: Text.AlignLeft
+                    font.pixelSize: 20
+                }
             }
-            ListElement {
-                text: "Method 3"
+
+            ColumnLayout {
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 500
+                Layout.preferredHeight: parent.height
+
+                ComboBox {
+                    id: settingsCovertMethodPicker
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 400
+
+                    model: ListModel {
+                        ListElement {
+                            text: "Method 1"
+                        }
+                        ListElement {
+                            text: "Method 2"
+                        }
+                        ListElement {
+                            text: "Method 3"
+                        }
+                    }
+                }
+
+                Switch {
+                    id: settingsSaveIpSwitch
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 400
+                    text: ""
+                    checked: false
+                    display: AbstractButton.IconOnly
+                }
+
+                Switch {
+                    id: settingsSaveUsernameSwitch
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 400
+                    text: ""
+                    checked: false
+                    display: AbstractButton.IconOnly
+                }
+
+                Button {
+                    id: settingsChangeCliPathButton
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 220
+                    text: "Change (req. restart)"
+                    font.pixelSize: 20
+                    // @disable-check M223
+                    onClicked: {
+                        // @disable-check M222
+                        settingsCliDialog.open()
+                    }
+                }
+
+                Button {
+                    id: settingsDeleteMeButton
+                    Layout.alignment: Qt.AlignCenter
+                    Layout.preferredHeight: 50
+                    Layout.preferredWidth: 150
+                    text: "Delete Me"
+                    font.pixelSize: 20
+                    // @disable-check M223
+                    onClicked: {
+                        // @disable-check M222
+                        _qmlHandler.onSettingsDeleteMeButton()
+                    }
+                }
             }
         }
-    }
 
-    Text {
-        id: settingsCovertMethodText
-        x: 56
-        y: 71
-        color: "#ffffff"
-        text: qsTr("Covert Channel Method:")
-        font.pixelSize: 20
-    }
+        Rectangle {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 2
+            Layout.bottomMargin: 30
+            color: Material.accent
+        }
 
-    Button {
-        id: settingsDeleteMeButton
-        x: 549
-        y: 343
-        width: 182
-        height: 71
-        text: qsTr("Delete Me")
-        font.pointSize: 15
-        // @disable-check M223
-        onClicked: {
-            // @disable-check M222
-            _qmlHandler.onSettingsDeleteMeButton()
+        RowLayout {
+            Layout.alignment: Qt.AlignCenter
+            Layout.preferredWidth: parent.width
+            Layout.preferredHeight: 50
+            Layout.bottomMargin: 20
 
-        }
-    }
+            Button {
+                id: settingsResetButton
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 200
+                Layout.preferredHeight: 50
+                text: "Select defaults"
+                font.pixelSize: 20
+                onClicked: {
+                    _qmlHandler.onSettingsResetButton()
+                }
+            }
 
-    Text {
-        id: settingsSaveIpText
-        x: 56
-        y: 154
-        color: "#ffffff"
-        text: qsTr("Save last IP:")
-        font.pixelSize: 20
+            Button {
+                id: settingsSaveButton
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 200
+                Layout.preferredHeight: 50
+                text: "Save Changes"
+                font.pixelSize: 20
+                onClicked: {
+                    _qmlHandler.onSettingsSaveButton(settingsCovertMethodPicker.currentIndex,
+                                                     settingsSaveIpSwitch.checked,
+                                                     settingsSaveUsernameSwitch.checked,
+                                                     settingsCliPath.text.replace("CLI-Path:     ", ""))
+                }
+            }
+
+            Button {
+                id: settingsRevertChangesButton
+                Layout.alignment: Qt.AlignCenter
+                Layout.preferredWidth: 200
+                Layout.preferredHeight: 50
+                text: "Revert Changes"
+                font.pixelSize: 20
+                onClicked: {
+                    _qmlHandler.onSettingsRevertChangesButton()
+                }
+            }
+        }
     }
 
-    Switch {
-        id: settingsSaveIpSwitch
-        x: 328
-        y: 142
-        text: qsTr("")
-        checked: false
-        display: AbstractButton.IconOnly
+    FileDialog {
+        id: settingsCliDialog
+        nameFilters: ["CLI file (ccats-cli)"]
+        title: "Please select the CLI File"
+        folder: shortcuts.home
+        // @disable-check M223
+        onAccepted: {
+            // @disable-check M222
+            var path = settingsCliDialog.fileUrl.toString()
+            path = path.replace(/^(file:\/{2})/,"");
+            settingsCliPath.text = "CLI-Path:   " + path
+        }
     }
 }

+ 128 - 114
gui/src/SignupForm.ui.qml

@@ -3,130 +3,144 @@ import QtQuick.Controls 2.5
 import QtQuick.Layouts 1.3
 
 Page {
-  width: 400
-  height: 400
+    width: 400
+    height: 400
 
-  Connections {
-      target: _qmlHandler
-      onSignupSetStatus: {
-          signupStatusText.text = status
-      }
-      onSignupEnableRegisterButton: {
-          signupRegisterButton.enabled = true
-      }
-      onSignupDisableRegisterButton: {
-          signupRegisterButton.enabled = false
-      }
-  }
+    Connections {
+        target: _qmlHandler
+        onSignupSetStatus: {
+            signupStatusText.text = status
+        }
+        onSignupEnableRegisterButton: {
+            signupRegisterButton.enabled = true
+        }
+        onSignupDisableRegisterButton: {
+            signupRegisterButton.enabled = false
+        }
+        onLoginSignupCheckSaveCheckbox: {
+            signupSetDefaultCheckbox.checked = true
+        }
+    }
 
-  ColumnLayout {
-      anchors.fill: parent
+    ColumnLayout {
+        anchors.fill: parent
 
-      Text {
-          Layout.alignment: Qt.AlignCenter
-          id: signupTitle
-          color: "#ffffff"
-          text: qsTr("Signup")
-          horizontalAlignment: Text.AlignHCenter
-          verticalAlignment: Text.AlignVCenter
-          font.pixelSize: 20
-      }
+        Text {
+            Layout.alignment: Qt.AlignCenter
+            id: signupTitle
+            color: "#ffffff"
+            text: qsTr("Signup")
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            font.pixelSize: 20
+        }
 
-      TextField {
-          Layout.alignment: Qt.AlignCenter
-          id: signupUsernameInput
-          selectByMouse: true
-          focus: true
-          text: qsTr("")
-          placeholderText: "Username"
-          horizontalAlignment: Text.AlignHCenter
-          // @disable-check M222
-          Keys.onReturnPressed: signupRegisterButton.activate()
-          // @disable-check M222
-          Keys.onEnterPressed: signupRegisterButton.activate()
+        TextField {
+            Layout.alignment: Qt.AlignCenter
+            id: signupUsernameInput
+            selectByMouse: true
+            focus: true
+            text: qsTr("")
+            placeholderText: "Username"
+            horizontalAlignment: Text.AlignHCenter
+            // @disable-check M222
+            Keys.onReturnPressed: signupRegisterButton.activate()
+            // @disable-check M222
+            Keys.onEnterPressed: signupRegisterButton.activate()
 
-          onTextEdited: {
-            signupStatusText.text = ""
-            signupRegisterButton.enabled = (signupUsernameInput.text != ""
-                      && signupPasswordOneInput.text != ""
-                      && signupPasswordTwoInput.text != "")
-          }
-      }
+            onTextEdited: {
+                signupStatusText.text = ""
+                signupRegisterButton.enabled = (signupUsernameInput.text != ""
+                                                && signupPasswordOneInput.text != ""
+                                                && signupPasswordTwoInput.text != "")
+            }
+        }
 
-      TextField {
-          Layout.alignment: Qt.AlignCenter
-          id: signupPasswordOneInput
-          selectByMouse: true
-          focus: true
-          text: qsTr("")
-          placeholderText: "Password"
-          horizontalAlignment: Text.AlignHCenter
-          // @disable-check M222
-          Keys.onReturnPressed: signupRegisterButton.activate()
-          // @disable-check M222
-          Keys.onEnterPressed: signupRegisterButton.activate()
-          echoMode: TextInput.Password
+        TextField {
+            Layout.alignment: Qt.AlignCenter
+            id: signupPasswordOneInput
+            selectByMouse: true
+            focus: true
+            text: qsTr("")
+            placeholderText: "Password"
+            horizontalAlignment: Text.AlignHCenter
+            // @disable-check M222
+            Keys.onReturnPressed: signupRegisterButton.activate()
+            // @disable-check M222
+            Keys.onEnterPressed: signupRegisterButton.activate()
+            echoMode: TextInput.Password
 
-          onTextEdited: {
-            signupStatusText.text = ""
-            signupRegisterButton.enabled = (signupUsernameInput.text != ""
-                      && signupPasswordOneInput.text != ""
-                      && signupPasswordTwoInput.text != "")
-          }
-      }
+            onTextEdited: {
+                signupStatusText.text = ""
+                signupRegisterButton.enabled = (signupUsernameInput.text != ""
+                                                && signupPasswordOneInput.text != ""
+                                                && signupPasswordTwoInput.text != "")
+            }
+        }
 
-      TextField {
-          Layout.alignment: Qt.AlignCenter
-          id: signupPasswordTwoInput
-          selectByMouse: true
-          focus: true
-          text: qsTr("")
-          placeholderText: "Repeat Passw."
-          horizontalAlignment: Text.AlignHCenter
-          // @disable-check M222
-          Keys.onReturnPressed: signupRegisterButton.activate()
-          // @disable-check M222
-          Keys.onEnterPressed: signupRegisterButton.activate()
-          echoMode: TextInput.Password
+        TextField {
+            Layout.alignment: Qt.AlignCenter
+            id: signupPasswordTwoInput
+            selectByMouse: true
+            focus: true
+            text: qsTr("")
+            placeholderText: "Repeat Passw."
+            horizontalAlignment: Text.AlignHCenter
+            // @disable-check M222
+            Keys.onReturnPressed: signupRegisterButton.activate()
+            // @disable-check M222
+            Keys.onEnterPressed: signupRegisterButton.activate()
+            echoMode: TextInput.Password
 
-          onTextEdited: {
-            signupStatusText.text = ""
-            signupRegisterButton.enabled = (signupUsernameInput.text != ""
-                      && signupPasswordOneInput.text != ""
-                      && signupPasswordTwoInput.text != "")
-          }
-      }
+            onTextEdited: {
+                signupStatusText.text = ""
+                signupRegisterButton.enabled = (signupUsernameInput.text != ""
+                                                && signupPasswordOneInput.text != ""
+                                                && signupPasswordTwoInput.text != "")
+            }
+        }
 
-      Text {
-          id: signupStatusText
-          color: "#df3f3f"
-          text: qsTr("")
-          horizontalAlignment: Text.AlignHCenter
-          verticalAlignment: Text.AlignVCenter
-          Layout.alignment: Qt.AlignCenter
-          font.pixelSize: 20
-      }
+        CheckBox {
+            id: signupSetDefaultCheckbox
+            Layout.alignment: Qt.AlignCenter
+            checked: false
+            text: "Save as default user"
+        }
 
-      Button {
-          Layout.alignment: Qt.AlignCenter
-          id: signupRegisterButton
-          text: qsTr("Register")
-          enabled: false
-          font.pointSize: 16
-          // @disable-check M223
-          onClicked: {
-              // @disable-check M222
-              signupRegisterButton.activate()
-          }
+        Text {
+            id: signupStatusText
+            color: "#df3f3f"
+            text: qsTr("")
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+            Layout.alignment: Qt.AlignCenter
+            font.pixelSize: 20
+        }
 
-          // @disable-check M222
-          function activate() {
-              // @disable-check M223
-              if (signupRegisterButton.enabled) {
-                  // @disable-check M222
-                  _qmlHandler.onSignupRegisterButton(signupUsernameInput.text, signupPasswordOneInput.text, signupPasswordTwoInput.text);
-              }
-          }
-      }
-   }
+        Button {
+            Layout.alignment: Qt.AlignCenter
+            id: signupRegisterButton
+            text: qsTr("Register")
+            enabled: false
+            font.pointSize: 16
+            // @disable-check M223
+            onClicked: {
+                // @disable-check M222
+                signupRegisterButton.activate()
+            }
+
+            // @disable-check M222
+            function activate() {
+                // @disable-check M223
+                if (signupRegisterButton.enabled) {
+                    // @disable-check M222
+                    _qmlHandler.onSignupRegisterButton(
+                                signupUsernameInput.text,
+                                signupPasswordOneInput.text,
+                                signupPasswordTwoInput.text,
+                                signupSetDefaultCheckbox.checked)
+                }
+            }
+        }
+    }
 }

+ 100 - 0
gui/src/config.cpp

@@ -0,0 +1,100 @@
+#include "config.h"
+#include <QDebug>
+#include <boost/lexical_cast.hpp>
+
+using boost::bad_lexical_cast;
+using boost::lexical_cast;
+
+namespace Config {
+std::map<std::string, std::string> configuration;
+bool configValid;
+std::string configPath = "config.txt";
+std::string configKeys[] = {"Autofill-IP", "Default-IP", "Autofill-Username", "Default-Username", "CLI-Path", "Covert-Channel-Method"};
+} // namespace Config
+
+void Config::setupDefaultConfig() {
+	configuration.clear();
+	setValue(configKeys[0], "0");
+	setValue(configKeys[1], "0.0.0.0");
+	setValue(configKeys[2], "0");
+	setValue(configKeys[3], "user");
+	setValue(configKeys[5], "0");
+}
+
+bool Config::checkConfig() {
+	if (!configValid || configuration.size() != (sizeof(configKeys) / sizeof(*configKeys)))
+		return false;
+	for (int i = 0; i < (sizeof(configKeys) / sizeof(*configKeys)); i++) {
+		if (getValue(configKeys[i]) == "")
+			return false;
+	}
+
+	std::string autofill_ip = getValue("Autofill-IP");
+	if (autofill_ip != "0" && autofill_ip != "1")
+		return false;
+
+	std::string autofill_user = getValue("Autofill-Username");
+	if (autofill_user != "0" && autofill_user != "1")
+		return false;
+
+	try {
+		int covert_method = lexical_cast<int>(getValue("Covert-Channel-Method"));
+	} catch (bad_lexical_cast &e) {
+		return false;
+	}
+
+	return true;
+}
+
+bool Config::loadFile() {
+	std::ifstream ifile(configPath);
+	std::string line;
+
+	if (ifile.is_open()) {
+		while (getline(ifile, line)) {
+			std::stringstream ss(line);
+			std::string segment;
+			std::vector<std::string> v;
+			while (getline(ss, segment, '=')) {
+				v.push_back(segment);
+			}
+			if (v.size() != 2) {
+				// One line doesn't have the format *=*
+				configValid = false;
+			} else {
+				configuration.insert(std::pair<std::string, std::string>(v.at(0), v.at(1)));
+			}
+		}
+		configValid = true;
+		return true;
+	}
+	return false;
+}
+
+void Config::saveFile() {
+	std::ofstream file;
+	file.open(configPath);
+	for (auto const &x : configuration) {
+		file << x.first << "=" << x.second << std::endl;
+	}
+	file.close();
+}
+
+std::string Config::getValue(const std::string &key) {
+	auto it = configuration.find(key);
+	if (it == configuration.end()) {
+		return "";
+	} else {
+		return it->second;
+	}
+}
+
+void Config::setValue(const std::string &key, const std::string &value) {
+	auto it = configuration.find(key);
+
+	if (it == configuration.end()) {
+		configuration.insert(std::pair<std::string, std::string>(key, value));
+	} else {
+		it->second = value;
+	}
+}

+ 18 - 0
gui/src/config.h

@@ -0,0 +1,18 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <fstream>
+#include <map>
+#include <sstream>
+#include <vector>
+
+namespace Config {
+  void setupDefaultConfig();
+  bool checkConfig();
+  bool loadFile();
+  void saveFile();
+  std::string getValue(const std::string &key);
+  void setValue(const std::string &key, const std::string &value);
+}
+
+#endif // CONFIG_H

+ 13 - 1
gui/src/main.qml

@@ -76,11 +76,23 @@ ApplicationWindow {
 
     IpPopup {
         id: ipPopup
-        onClosed: loginSignupPopup.open()
+    }
+
+    NoConfigFoundPopup {
+        id: noConfigFoundPopup
+    }
+
+    InvalidConfigPopup {
+        id: invalidConfigPopup
+    }
+
+    InvalidCliPathPopup {
+        id: invalidCliPathPopup
     }
 
     Component.onCompleted: {
       swipeView.interactive = false
       ipPopup.open()
+      _qmlHandler.onStart()
     }
 }

+ 3 - 0
gui/src/qml.qrc

@@ -14,5 +14,8 @@
         <file>SignupForm.ui.qml</file>
         <file>ReceivingFileTemplate.ui.qml</file>
         <file>ReceivingFileTemplateDeletePopup.ui.qml</file>
+        <file>NoConfigFoundPopup.ui.qml</file>
+        <file>InvalidCliPathPopup.ui.qml</file>
+        <file>InvalidConfigPopup.ui.qml</file>
     </qresource>
 </RCC>

+ 109 - 5
gui/src/qmlhandler.cpp

@@ -12,12 +12,15 @@
 #include <thread>
 #include <unistd.h>
 
+#include "config.h"
 #include "qmlhandler.h"
 #include <boost/asio.hpp>
 #include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
 #include <iostream>
 #include <json/json.h>
 
+using boost::lexical_cast;
 using boost::asio::buffer;
 
 using namespace std;
@@ -38,6 +41,14 @@ QMLHandler::QMLHandler(QObject *parent) : QObject(parent) {}
 
 void QMLHandler::closeCLI() { _CLI_RUNNING = false; }
 
+void QMLHandler::loadSettingsToGUI() {
+	int covertMethod = lexical_cast<int>(Config::getValue("Covert-Channel-Method"));
+	bool saveIP = lexical_cast<bool>(Config::getValue("Autofill-IP"));
+	bool saveUsername = lexical_cast<bool>(Config::getValue("Autofill-Username"));
+	QString cliPath = QString::fromStdString(Config::getValue("CLI-Path"));
+	emit loadSettings(covertMethod, saveIP, saveUsername, cliPath);
+}
+
 void QMLHandler::reopenCLI(QString ip) {
 	if (_CLI_RUNNING) {
 		waitpid(childpid, NULL, 0);
@@ -61,7 +72,8 @@ void QMLHandler::reopenCLI(QString ip) {
 
 		// Set the path to the CLI - pass argument h (help) for now
 		// TODO: Change hardcoded path
-		execl("../../cli/build/ccats-cli", "ccats-cli", ip.toUtf8().constData(), "--machine", (char *)NULL);
+		// execl("../../cli/build/ccats-cli", "ccats-cli", ip.toUtf8().constData(), "--machine", (char *)NULL);
+		execl(Config::getValue("CLI-Path").c_str(), "ccats-cli", ip.toUtf8().constData(), "--machine", (char *)NULL);
 
 		exit(1);
 	}
@@ -157,6 +169,7 @@ void QMLHandler::handleJSON(string buffer) {
 		if (root["accept"] == true) {
 			emit loginSignupPopupClose();
 			write(outpipefd[1], "list\n", strlen("list\n"));
+			loadSettingsToGUI();
 		} else {
 			emit loginSetStatus(root["error"].asString().c_str());
 			reopenCLI(_IP);
@@ -192,7 +205,7 @@ void QMLHandler::handleJSON(string buffer) {
 		} else {
 			string fileName = root["file"].asString();
 			// TODO: Only do this in getdata when remaining is 0 (when the file is fully downloaded) - maybe set text to "downloading.." in between
-			emit receivingDisableDownloadButton(QString::fromStdString(string(fileName)));
+			emit receivingDisableDownloadButton(QString::fromStdString(fileName));
 		}
 	}
 
@@ -274,6 +287,60 @@ void QMLHandler::readPipeLoop() {
 
 // ### QML Handlers ###
 
+void QMLHandler::onStart() {
+	bool configExists = Config::loadFile();
+
+	if (configExists == true) {
+		// Config exists
+		if (Config::checkConfig() == true) {
+			// Config is valid
+			if (!boost::filesystem::is_regular_file(Config::getValue("CLI-Path")) ||
+			    boost::filesystem::path(Config::getValue("CLI-Path")).filename().compare("ccats-cli")) {
+				// Invalid CLI Path
+				emit invalidCliPathPopupOpen();
+			}
+
+			if (Config::getValue("Autofill-IP") == "1") {
+				emit ipPopupSetIP(QString::fromStdString(Config::getValue("Default-IP")));
+				emit ipPopupCheckSaveCheckbox();
+			}
+			if (Config::getValue("Autofill-Username") == "1") {
+				emit loginSetUsername(QString::fromStdString(Config::getValue("Default-Username")));
+				emit loginSignupCheckSaveCheckbox();
+			}
+		} else {
+			// Config is invalid
+			emit invalidConfigPopupOpen();
+		}
+	} else {
+		// Config doesn't exist
+		Config::setupDefaultConfig();
+		emit noConfigFoundPopupOpen();
+	}
+}
+
+// No Config Found Popup
+void QMLHandler::onNoConfigFoundPopupContinueButton(QString cli_path) {
+	Config::setValue("CLI-Path", cli_path.toUtf8().constData());
+	Config::saveFile();
+}
+
+// Invalid Cli Path Popup
+void QMLHandler::onInvalidCliPathPopupContinueButton(QString cli_path) {
+	Config::setValue("CLI-Path", cli_path.toUtf8().constData());
+	Config::saveFile();
+}
+
+void QMLHandler::onInvalidCliPathPopupQuitButton() { emit closeWindow(); }
+
+// Invalid Config Popup
+void QMLHandler::onInvalidConfigPopupQuitButton() { emit closeWindow(); }
+
+void QMLHandler::onInvalidConfigPopupCreateDefaultButton() {
+	Config::setupDefaultConfig();
+	emit noConfigFoundPopupOpen();
+}
+
 // Sending
 void QMLHandler::onSendingSelectFileButton(QUrl url) {
 	sendFileUrl = url.toLocalFile();
@@ -313,21 +380,53 @@ void QMLHandler::onMessagesSendButton(QString msg) { emit message(msg); }
 // Settings
 void QMLHandler::onSettingsDeleteMeButton() { write(outpipefd[1], "deleteme\n", strlen("deleteme\n")); }
 
+void QMLHandler::onSettingsRevertChangesButton() {
+	loadSettingsToGUI();
+	emit log("Settings changes reverted.");
+}
+
+void QMLHandler::onSettingsResetButton() {
+	string cli_path = Config::getValue("CLI-Path");
+	Config::setupDefaultConfig();
+	Config::setValue("CLI-Path", cli_path);
+	loadSettingsToGUI();
+	emit log("Settings resetted to default.");
+}
+
+void QMLHandler::onSettingsSaveButton(int covertMethod, bool saveIP, bool saveUsername, QString cliPath) {
+	Config::setValue("Covert-Channel-Method", lexical_cast<string>(covertMethod));
+	Config::setValue("Autofill-IP", lexical_cast<string>(saveIP));
+	Config::setValue("Autofill-Username", lexical_cast<string>(saveUsername));
+	Config::setValue("CLI-Path", cliPath.toUtf8().constData());
+	Config::saveFile();
+	emit log("Settings saved.");
+}
+
 // Ip Popup
-void QMLHandler::onIpPopupConnectButton(QString ip) {
+void QMLHandler::onIpPopupConnectButton(QString ip, bool saveAsDefault) {
 	reopenCLI(ip);
 	emit ipPopupDisableConnectButton();
+	if (saveAsDefault) {
+		Config::setValue("Default-IP", ip.toUtf8().constData());
+		Config::setValue("Autofill-IP", "1");
+		Config::saveFile();
+	}
 }
 
 // Login
-void QMLHandler::onLoginLoginButton(QString username, QString password) {
+void QMLHandler::onLoginLoginButton(QString username, QString password, bool saveAsDefault) {
 	QString command = "login " + username + " " + password + "\n";
 	write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData()));
 	emit loginDisableLoginButton();
+	if (saveAsDefault) {
+		Config::setValue("Default-Username", username.toUtf8().constData());
+		Config::setValue("Autofill-Username", "1");
+		Config::saveFile();
+	}
 }
 
 // Signup
-void QMLHandler::onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo) {
+void QMLHandler::onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo, bool saveAsDefault) {
 	if (QString::compare(passwordOne, passwordTwo, Qt::CaseSensitive)) {
 		emit signupSetStatus("Passwords don't match");
 		return;
@@ -335,6 +434,11 @@ void QMLHandler::onSignupRegisterButton(QString username, QString passwordOne, Q
 	QString command = "signup " + username + " " + passwordOne + "\n";
 	write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData()));
 	emit signupDisableRegisterButton();
+	if (saveAsDefault) {
+		Config::setValue("Default-Username", username.toUtf8().constData());
+		Config::setValue("Autofill-Username", "1");
+		Config::saveFile();
+	}
 }
 
 // Footer

+ 37 - 3
gui/src/qmlhandler.h

@@ -15,6 +15,7 @@ private:
   void reopenCLI(QString ip);
   void closeCLI();
   void fileExists(std::string name);
+  void loadSettingsToGUI();
 
 public:
   explicit QMLHandler(QObject *parent = 0);
@@ -23,6 +24,18 @@ public:
 
 // C++ -> QML
 signals:
+  // No Config Found Popup
+  void noConfigFoundPopupOpen();
+  void noConfigFoundPopupClose();
+
+  // Invalid Cli Path Popup
+  void invalidCliPathPopupOpen();
+  void invalidCliPathPopupClose();
+
+  // Invalid Config Popup
+  void invalidConfigPopupOpen();
+  void invalidConfigPopupClose();
+
   // Sending
   void sendingSetFileUrlText(QString signalText);
   void sendingEnableSendButton();
@@ -39,22 +52,27 @@ signals:
 
   // Settings
   void closeWindow();
+  void loadSettings(int covertMethod, bool saveIP, bool saveUsername, QString cliPath);
 
   // Ip Popup
   void ipPopupSetStatus(QString status);
+  void ipPopupSetIP(QString default_ip);
   void ipPopupClose();
   void ipPopupOpen();
   void ipPopupEnableConnectButton();
   void ipPopupDisableConnectButton();
+  void ipPopupCheckSaveCheckbox();
 
   // Login Signup Popup
   void loginSignupPopupClose();
   void loginSignupPopupOpen();
+  void loginSignupCheckSaveCheckbox();
 
   // Login
   void loginSetStatus(QString status);
   void loginEnableLoginButton();
   void loginDisableLoginButton();
+  void loginSetUsername(QString username);
 
   // Signup
   void signupSetStatus(QString status);
@@ -68,6 +86,19 @@ signals:
 
 // QML -> C++
 public slots:
+  void onStart();
+
+  // No Config Found Popup
+  void onNoConfigFoundPopupContinueButton(QString cli_path);
+
+  // Invalid Cli Path Popup
+  void onInvalidCliPathPopupContinueButton(QString cli_path);
+  void onInvalidCliPathPopupQuitButton();
+
+  // Invalid Config Popup
+  void onInvalidConfigPopupCreateDefaultButton();
+  void onInvalidConfigPopupQuitButton();
+
   // Sending
   void onSendingSelectFileButton(QUrl url);
   void onSendingSendFileButton();
@@ -83,15 +114,18 @@ public slots:
 
   // Settings
   void onSettingsDeleteMeButton();
+  void onSettingsRevertChangesButton();
+  void onSettingsResetButton();
+  void onSettingsSaveButton(int covertMethod, bool saveIP, bool saveUsername, QString cliPath);
 
   // Ip Popup
-  void onIpPopupConnectButton(QString ip);
+  void onIpPopupConnectButton(QString ip, bool saveAsDefault);
 
   // Login
-  void onLoginLoginButton(QString username, QString password);
+  void onLoginLoginButton(QString username, QString password, bool saveAsDefault);
 
   // Signup
-  void onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo);
+  void onSignupRegisterButton(QString username, QString passwordOne, QString passwordTwo, bool saveAsDefault);
 
   // Footer
   void onFooterGetStatusButton();