Skip to content
Snippets Groups Projects
main.qml 15 KiB
Newer Older
  • Learn to ignore specific revisions
  • Kali Kaneko's avatar
    Kali Kaneko committed
    import QtQuick 2.9
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    import QtQuick.Window 2.2
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    import QtQuick.Dialogs 1.2
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    import QtQuick.Layouts 1.12
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    import QtQuick.Controls 2.12
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    import Qt.labs.platform 1.0
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    Window {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        id: app
    
        visible: true
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        width: 500
    
        height: 600
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        maximumWidth: 600
    
        minimumWidth: 300
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        maximumHeight: 500
        minimumHeight: 300
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
        flags: Qt.WindowsStaysOnTopHint
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        // TODO get a nice background color
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        property var ctx
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        property var loginDone
    
        property var allowEmptyPass
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        TabBar {
            id: bar
            width: parent.width
            TabButton {
                text: qsTr("Info")
            }
            TabButton {
                text: qsTr("Gateways")
            }
        }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        StackLayout {
    
            anchors.fill: parent
            currentIndex: bar.currentIndex
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                id: infoTab
                anchors.centerIn: parent
    
                Column {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    anchors.centerIn: parent
                    spacing: 5
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    Text {
    
                        id: mainStatus
                        text: "off"
                        font.pixelSize: 26
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        anchors.horizontalCenter: parent.horizontalCenter
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    Text {
    
                        id: mainCurrentGateway
                        text: ""
                        font.pixelSize: 20
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        anchors.horizontalCenter: parent.horizontalCenter
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    SwitchDelegate {
    
                        id: vpntoggle
    
                        text: qsTr("")
                        checked: false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        anchors.horizontalCenter: parent.horizontalCenter
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
                        Connections {
                            onCheckedChanged: {
                                if (vpntoggle.checked == true && ctx.status == "off") {
                                    backend.switchOn()
                                }
                                if (vpntoggle.checked === false && ctx.status == "on") {
                                    backend.switchOff()
                                }
                            }
                        }
    
                        contentItem: Text {
                            rightPadding: vpntoggle.indicator.width + control.spacing
                            text: vpntoggle.text
                            font: vpntoggle.font
                            opacity: enabled ? 1.0 : 0.3
                            color: vpntoggle.down ? "#17a81a" : "#21be2b"
                            elide: Text.ElideRight
                            verticalAlignment: Text.AlignVCenter
                        }
    
                        indicator: Rectangle {
                            implicitWidth: 48
                            implicitHeight: 26
                            x: vpntoggle.width - width - vpntoggle.rightPadding
                            y: parent.height / 2 - height / 2
                            radius: 13
                            color: vpntoggle.checked ? "#17a81a" : "transparent"
                            border.color: vpntoggle.checked ? "#17a81a" : "#cccccc"
    
                            Rectangle {
                                x: vpntoggle.checked ? parent.width - width : 0
                                width: 26
                                height: 26
                                radius: 13
                                color: vpntoggle.down ? "#cccccc" : "#ffffff"
                                border.color: vpntoggle.checked ? (vpntoggle.down ? "#17a81a" : "#21be2b") : "#999999"
                            }
                        }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        background: Rectangle {
                            implicitWidth: 100
                            implicitHeight: 40
                            visible: vpntoggle.down || vpntoggle.highlighted
                            color: vpntoggle.down ? "#bdbebf" : "#eeeeee"
                        }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    } // end switchdelegate
                }
            }
    
            Item {
    
                id: gatewayTab
                anchors.centerIn: parent
    
                Column {
    
                    anchors.centerIn: parent
                    spacing: 10
    
    
                    ComboBox {
                        id: gwSelector
                        editable: false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        anchors.horizontalCenter: parent.horizontalCenter
    
    
                        model: [qsTr("Automatic")]
                        onActivated: {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                            console.debug("Selected gateway:", currentText)
                            backend.useGateway(currentText.toString())
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                } // end columnlayout
            } // end item 
        } // end stacklayout
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        Connections {
            target: jsonModel
            onDataChanged: {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                ctx = JSON.parse(jsonModel.getJson())
    
                // TODO pass QML_DEBUG variable to be hyper-verbose
                //console.debug(jsonModel.getJson())
    
                gwSelector.model = Object.keys(ctx.gateways)
    
                if (ctx.donateDialog == 'true') {
    
                    console.debug(jsonModel.getJson())
                    donate.visible = true
    
                    backend.donateSeen()
    
                if (ctx.loginDialog == 'true') {
    
                    console.debug(jsonModel.getJson())
                    console.debug("DEBUG: should display login")
    
                    login.visible = true
                }
    
                if (ctx.loginOk == 'true') {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    loginOk.visible = true
                }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                if (ctx.errors) {
                    login.visible = false
                    if (ctx.errors == "nohelpers") {
                        showInitFailure(
                                    qsTr("Could not find helpers. Please check your installation"))
                    } else if (ctx.errors == "nopolkit") {
                        showInitFailure(qsTr("Could not find polkit agent."))
                    } else {
                        showInitFailure()
                    }
    
                if (ctx.donateURL) {
                    donateItem.visible = true
                }
    
        function showInitFailure(msg) {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            console.debug("ERRORS:", ctx.errors)
            if (msg == undefined) {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                if (ctx.errors == 'bad_auth_502'
                        || ctx.errors == 'bad_auth_timeout') {
                    msg = qsTr("Oops! The authentication service seems down. Please try again later")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    initFailure.title = qsTr("Service Error")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                } else if (ctx.errors == 'bad_auth') {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    if (allowEmptyPass) {
                        // For now, this is a libraryVPN, so we can be explicit about what credentials are here.
                        // Another option to consider is to customize the error strings while vendoring.
                        //: Incorrect library card number
                        msg = qsTr("Please check your Patron ID")
                    } else {
                        msg = qsTr("Could not log in with those credentials, please retry")
                    }
                    initFailure.title = qsTr("Login Error")
                } else {
                    //: %1 -> application name
                    //: %2 -> error string
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    msg = qsTr("Got an error starting %1: %2").arg(ctx.appName).arg(
                                ctx.errors)
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                }
            }
            initFailure.text = msg
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            initFailure.visible = true
    
        }
    
        function shouldAllowEmptyPass() {
            let obj = JSON.parse(providers.getJson())
            let active = obj['default']
            let allProviders = obj['providers']
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            for (var i = 0; i < allProviders.length; i++) {
    
                if (allProviders[i]['name'] === active) {
                    return (allProviders[i]['authEmptyPass'] === 'true')
                }
            }
            return false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        Component.onCompleted: {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            loginDone = false
            console.debug("Platform:", Qt.platform.os)
            console.debug("DEBUG: Pre-seeded providers:")
            console.debug(providers.getJson())
            allowEmptyPass = shouldAllowEmptyPass()
    
            /* TODO get appVisible flag from backend */
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            app.visible = true
            app.raise()
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        }
    
        function toHuman(st) {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            switch (st) {
            case "off":
                //: %1 -> application name
                return qsTr("%1 off").arg(ctx.appName)
            case "on":
                //: %1 -> application name
                return qsTr("%1 on").arg(ctx.appName)
            case "connecting":
                //: %1 -> application name
                return qsTr("Connecting to %1").arg(ctx.appName)
            case "stopping":
                //: %1 -> application name
                return qsTr("Stopping %1").arg(ctx.appName)
            case "failed":
                //: %1 -> application name
                return qsTr("%1 blocking internet").arg(
                            ctx.appName) // TODO failed is not handed yet
    
        /* TODO change this!!! automatic: Paris (FR) */
    
    
        function toHumanWithLocation(st) {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            switch (st) {
            case "off":
                //: %1 -> application name
                return qsTr("%1 off").arg(ctx.appName)
            case "on":
                //: %1 -> application name
                //: %2 -> current gateway
    
                return qsTr("%1 on - %2").arg(ctx.appName).arg(ctx.currentLocation)
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            case "connecting":
                //: %1 -> application name
                //: %2 -> current gateway
                return qsTr("Connecting to %1 - %2").arg(ctx.appName).arg(
    
                            ctx.currentLocation)
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            case "stopping":
                //: %1 -> application name
                return qsTr("Stopping %1").arg(ctx.appName)
            case "failed":
                //: %1 -> application name
                return qsTr("%1 blocking internet").arg(
                            ctx.appName) // TODO failed is not handed yet
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        property var icons: {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            "off": "qrc:/assets/icon/png/black/vpn_off.png",
            "on": "qrc:/assets/icon/png/black/vpn_on.png",
            "wait": "qrc:/assets/icon/png/black/vpn_wait_0.png",
            "blocked": "qrc:/assets/icon/png/black/vpn_blocked.png"
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
        VpnState {
            id: vpn
        }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        SystemTrayIcon {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
            id: systray
    
            visible: systrayVisible
    
            signal activatedSignal
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            onActivated: {
    
                systray.activatedSignal()
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
            /* the systray menu cannot be buried in a child qml file because
             * otherwise the ids are not available
             * from other components
             */
            menu: Menu {
    
                Connections {
                    target: systray
                    onActivatedSignal: {
                        if (Qt.platform.os === "windows" || desktop === "LXQt") {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                            console.debug("open systray menu")
                            systrayMenu.open()
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    id: statusItem
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("Checking status…")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    enabled: false
                }
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
                    id: autoSelectionItem
                    text: qsTr("automatic")
                    checkable: true
                    checked: true
                    enabled: false
                }
    
    
                /* a minimal segfault for submenu */
                // Menu {}
    
                MenuSeparator {}
    
                MenuItem {
                    id: manualSelectionItem
                    text: qsTr("Pick gateway…")
                    checkable: true
                    checked: false
                    enabled: true
                }
    
                MenuSeparator {}
    
    
                MenuItem {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    text: {
                        if (vpn.state == "failed")
                            qsTr("Reconnect")
                        else
                            qsTr("Turn on")
                    }
                    onTriggered: {
                        backend.switchOn()
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    visible: ctx ? (ctx.status == "off"
                                    || ctx.status == "failed") : false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    text: {
                        if (ctx && ctx.status == "starting")
                            qsTr("Cancel")
                        else
                            qsTr("Turn off")
                    }
                    onTriggered: {
                        backend.switchOff()
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    visible: ctx ? (ctx.status == "on" || ctx.status == "starting"
                                    || ctx.status == "failed") : false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuSeparator {}
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("About…")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    onTriggered: {
                        about.visible = true
    
                        app.focus = true
                        requestActivate()
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
                    id: donateItem
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("Donate…")
    
                    visible: ctx ? ctx.donateURL : false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    onTriggered: {
                        donate.visible = true
                    }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuSeparator {}
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("Help…")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        console.debug(Qt.resolvedUrl(ctx.helpURL))
    
                        Qt.openUrlExternally(Qt.resolvedUrl(ctx.helpURL))
                    }
                }
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    kwadronaut's avatar
    kwadronaut committed
                    text: qsTr("Report a bug…")
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                        Qt.openUrlExternally(
                                    Qt.resolvedUrl(
                                        "https://0xacab.org/leap/bitmask-vpn/issues"))
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuSeparator {}
    
    Kali Kaneko's avatar
    Kali Kaneko committed
    
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                MenuItem {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
                    text: qsTr("Quit")
                    onTriggered: backend.quit()
                }
            }
    
    
            Component.onCompleted: {
                icon.source = icons["off"]
                tooltip = qsTr("Checking status…")
                console.debug("systray init completed")
                hide()
                if (systrayVisible) {
                    console.log("show systray")
                    show()
                    if (Qt.platform.os === "windows") {
                        let appname = ctx ? ctx.appName : "VPN"
                        showNotification(
                                    appname
                                    + " is up and running. Please use system tray icon to control it.")
                    }
                }
            }
    
            // Helper to show notification messages
            function showNotification(msg) {
                console.log("Going to show notification message: ", msg)
                if (supportsMessages) {
                    let appname = ctx ? ctx.appName : "VPN"
                    showMessage(appname, msg, null, 15000)
                } else {
                    console.log("System doesn't support systray notifications")
                }
            }
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        }
    
        DonateDialog {
            id: donate
    
            visible: false
    
        AboutDialog {
            id: about
            visible: false
        }
    
        LoginDialog {
            id: login
    
            visible: false
    
    Kali Kaneko's avatar
    Kali Kaneko committed
        LoginOKDialog {
    
    Kali Kaneko's avatar
    Kali Kaneko committed
            id: loginOk
            visible: false
        }
    
    
        MessageDialog {
            id: errorStartingVPN
    
            //buttons: MessageDialog.Ok
    
            modality: Qt.NonModal
    
            title: qsTr("Error starting VPN")
            text: ""
            detailedText: ""
            visible: false
    
        MessageDialog {
            id: authAgent
    
            //buttons: MessageDialog.Ok
    
            modality: Qt.NonModal
    
            title: qsTr("Missing authentication agent")
            text: qsTr("Could not find a polkit authentication agent. Please run one and try again.")
            visible: false
    
            visible: false