From cc8a81faff94bfb5b5f3a0e030f92df4d39025d2 Mon Sep 17 00:00:00 2001 From: Joan CiberSheep Date: Tue, 3 Dec 2019 01:10:45 +0100 Subject: [PATCH 1/7] Started BackUp System --- CMakeLists.txt | 2 +- qml/Components/ChooserTheme.qml | 3 +- qml/Components/ExpandableListItem.qml | 3 +- qml/Components/HeaderSettingsSpecial.qml | 8 + qml/Components/IndefinedProgressBar.qml | 17 ++ qml/Components/KeyToEdit.qml | 16 +- qml/Components/PageBackup.qml | 216 ++++++++++++++++++++ qml/Components/PageCheckdB.qml | 241 +++++++++++++++++++++++ qml/Components/PageHubExport.qml | 57 ++++++ qml/Components/PageHubImport.qml | 60 ++++++ qml/Components/PageImport.qml | 241 +++++++++++++++++++++++ qml/Components/PageSettings.qml | 51 ++++- qml/Components/RenderUriPopUp.qml | 14 +- qml/js/db.js | 4 +- tfamanager.apparmor | 5 +- 15 files changed, 915 insertions(+), 23 deletions(-) create mode 100644 qml/Components/HeaderSettingsSpecial.qml create mode 100644 qml/Components/IndefinedProgressBar.qml create mode 100644 qml/Components/PageBackup.qml create mode 100644 qml/Components/PageCheckdB.qml create mode 100644 qml/Components/PageHubExport.qml create mode 100644 qml/Components/PageHubImport.qml create mode 100644 qml/Components/PageImport.qml diff --git a/CMakeLists.txt b/CMakeLists.txt index 0db3669..98d5abc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ set(FULL_PROJECT_NAME "tfamanager.cibersheep") set(CMAKE_INSTALL_PREFIX /) set(DATA_DIR /) set(DESKTOP_FILE_NAME ${PROJECT_NAME}.desktop) -set(APP_VERSION "0.9.3") +set(APP_VERSION "0.9.3.1") # This command figures out the target architecture for use in the manifest file execute_process( diff --git a/qml/Components/ChooserTheme.qml b/qml/Components/ChooserTheme.qml index a082049..2a2d4bd 100644 --- a/qml/Components/ChooserTheme.qml +++ b/qml/Components/ChooserTheme.qml @@ -35,13 +35,14 @@ Item { ListModel { id: themeModel - Component.onCompleted: initialize() function initialize() { themeModel.append({"text": "Ambiance", "theme": "Ubuntu.Components.Themes.Ambiance"}); themeModel.append({"text": "Suru Dark", "theme": "Ubuntu.Components.Themes.SuruDark"}); themeModel.append({"text": "System Theme", "theme": ""}); } + + Component.onCompleted: initialize() } ExpandableListItem { diff --git a/qml/Components/ExpandableListItem.qml b/qml/Components/ExpandableListItem.qml index da71465..2f3749b 100644 --- a/qml/Components/ExpandableListItem.qml +++ b/qml/Components/ExpandableListItem.qml @@ -38,7 +38,7 @@ ListItem { // For autopilot readonly property double expansionHeight: expansion.height - //highlightColor: "Transparent" + highlightColor: root.highlightColor height: expandableHeader.height + divider.height divider.visible: false expansion.height: contentColumn.height @@ -60,6 +60,7 @@ ListItem { ListItem { height: expandableHeader.height + divider.height divider.visible: false + highlightColor: root.highlightColor ListItemLayout { id: expandableHeader diff --git a/qml/Components/HeaderSettingsSpecial.qml b/qml/Components/HeaderSettingsSpecial.qml new file mode 100644 index 0000000..9719314 --- /dev/null +++ b/qml/Components/HeaderSettingsSpecial.qml @@ -0,0 +1,8 @@ +/* + */ + +import QtQuick 2.9 +import Ubuntu.Components 1.3 + +HeaderBase { +} diff --git a/qml/Components/IndefinedProgressBar.qml b/qml/Components/IndefinedProgressBar.qml new file mode 100644 index 0000000..02dac97 --- /dev/null +++ b/qml/Components/IndefinedProgressBar.qml @@ -0,0 +1,17 @@ +/* + */ + +import QtQuick 2.9 +import Ubuntu.Components 1.3 + +Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.background + + ProgressBar { + visible: parent.visible + width: parent.width + anchors.verticalCenter: parent.verticalCenter + indeterminate: true + } +} diff --git a/qml/Components/KeyToEdit.qml b/qml/Components/KeyToEdit.qml index 91a6a9a..a1b1931 100644 --- a/qml/Components/KeyToEdit.qml +++ b/qml/Components/KeyToEdit.qml @@ -22,7 +22,7 @@ Column { anchors.top:parent.top anchors.topMargin: units.gu(2) id: typeTxt - text: i18n.tr("Type") + text: "" + i18n.tr("Type") + "" color: theme.palette.normal.overlayText } @@ -50,7 +50,7 @@ Column { Text { anchors.verticalCenter: parent.verticalCenter id: descTxt - text: i18n.tr("Description") + text: "" + i18n.tr("Description") + "" color: theme.palette.normal.overlayText } @@ -96,7 +96,7 @@ Column { Text { anchors.verticalCenter: parent.verticalCenter id: usrTxt - text: i18n.tr("User") + text: "" + i18n.tr("User") + "" color: theme.palette.normal.overlayText } @@ -123,7 +123,7 @@ Column { anchors.top: parent.top anchors.topMargin: units.gu(2) id: algTxt - text: i18n.tr("Algorithm") + text: "" + i18n.tr("Algorithm") + "" color: theme.palette.normal.overlayText } @@ -153,7 +153,7 @@ Column { anchors.top: parent.top anchors.topMargin: units.gu(2) id: passLenTxt - text: i18n.tr("Password Length") + text: "" + i18n.tr("Password Length") + "" color: theme.palette.normal.overlayText } @@ -184,7 +184,7 @@ Column { anchors.verticalCenter: parent.verticalCenter id: keyPeriod visible: true //uriJson.counter == undefined - text: i18n.tr("Interval") + text: "" + i18n.tr("Interval") + "" color: theme.palette.normal.overlayText } @@ -211,7 +211,7 @@ Column { anchors.verticalCenter: parent.verticalCenter id: countTxt //visible: !keyPeriod.visible - text: i18n.tr("Counter") + text: "" + i18n.tr("Counter") + "" color: theme.palette.normal.overlayText } @@ -229,7 +229,7 @@ Column { } Text { - text: i18n.tr("Secret Key") + text: "" + i18n.tr("Secret Key") + "" color: theme.palette.normal.overlayText } diff --git a/qml/Components/PageBackup.qml b/qml/Components/PageBackup.qml new file mode 100644 index 0000000..3c59d32 --- /dev/null +++ b/qml/Components/PageBackup.qml @@ -0,0 +1,216 @@ +/* + */ + +import QtQuick 2.9 +import Ubuntu.Components 1.3 +import Ubuntu.Components.Popups 1.3 + +import QtQuick.LocalStorage 2.0 +import "../js/otpauth.min.js" as OTPAuth +import "../js/db.js" as KeysDB + +Page { + id: backupPage + objectName: "backupPage" + anchors.fill: parent + + property string path + //TODO: Don't hardcode it + property string cachePath: "/home/phablet/.cache/tfamanager.cibersheep/" + + header: HeaderSettingsSpecial { + id: backupHeader + title: i18n.tr("Manage Backups") + flickable: mainFlickable + } + + ScrollView { + width: parent.width + height: parent.height + contentItem: mainFlickable + } + + Flickable { + id: mainFlickable + width: parent.width + height: parent.height + contentHeight: mainColumn.height + topMargin: units.gu(2) + bottomMargin: units.gu(6) + + Column { + id: mainColumn + width: parent.width + anchors.top: parent.top + anchors.topMargin: units.gu(1) + spacing: units.gu(4) + + Label { + width: parent.width + text: i18n.tr("Export and import keys from a file") + font.bold: true + horizontalAlignment: Text.AlignHCenter + anchors.horizontalCenter: parent.horizontalCenter + wrapMode: Text.WordWrap + } + + Column { + width: parent.width + spacing: units.gu(2) + + Label { + width: parent.width - units.gu(4) + text: i18n.tr("Export all keys in one json file. Keep in mind that keys are stored as plain text. It's advisable to encrypt that file. The Enigma app could help here.") + horizontalAlignment: Text.AlignJustify + anchors.horizontalCenter: parent.horizontalCenter + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + } + + Button { + id: exportButton + text: i18n.tr("Export json file") + width: Math.min(parent.width, units.gu(25)) + anchors.horizontalCenter: parent.horizontalCenter + + onClicked: { + visible = false; + requestExport(); + } + } + + IndefinedProgressBar { + id: exporting + visible: !exportButton.visible + width: exportButton.width + height: exportButton.height + } + } + + + Column { + width: parent.width + spacing: units.gu(2) + + Label { + width: parent.width - units.gu(4) + text: i18n.tr("Import keys from a json file. Select a plain text json file.") + horizontalAlignment: Text.AlignJustify + anchors.horizontalCenter: parent.horizontalCenter + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + } + + Button { + id: importButton + text: i18n.tr("Import json file") + width: Math.min(parent.width, units.gu(25)) + anchors.horizontalCenter: parent.horizontalCenter + + onClicked: { + visible = false; + requestImport(); + } + } + + IndefinedProgressBar { + id: importing + visible: !importButton.visible + width: importButton.width + height: importButton.height + } + } + + + Column { + width: parent.width + spacing: units.gu(2) + + Label { + width: parent.width - units.gu(4) + text: i18n.tr("Import keys from a text file containing a list of 'otpauth://' URIs. Select a plain text json file.") + horizontalAlignment: Text.AlignJustify + anchors.horizontalCenter: parent.horizontalCenter + wrapMode: Text.WordWrap + onLinkActivated: Qt.openUrlExternally(link) + } + + Button { + id: importTxtButton + text: i18n.tr("Import text file") + width: Math.min(parent.width, units.gu(25)) + anchors.horizontalCenter: parent.horizontalCenter + + onClicked: { + visible = false; + requestImportTxt(); + } + } + + IndefinedProgressBar { + id: importingTxt + visible: !importTxtButton.visible + width: importTxtButton.width + height: importTxtButton.height + } + } + } + } + + //TODO: Use c++ instead? + function saveFile(fileUrl, text) { + var request = new XMLHttpRequest(); + request.open("PUT", fileUrl, false); + request.send(text); + return request.status; + } + + function generateBackupJson(data) { + var jsonToReturn = '{"keys": [' + + if (data.length > 0) { + for (var i = 0; i < data.length; i++) { + jsonToReturn += '{"keyJSON": "' + data.item(i).keyJSON + '",'; + jsonToReturn += '"added": "' + data.item(i).added + '",' + jsonToReturn += '"description": "' + data.item(i).description + '"}' + + if (i !== data.length -1) { + jsonToReturn += ','; + } + } + } + + jsonToReturn += ']}' + + return jsonToReturn; + } + + function renderBackupJson() { + + } + + function requestExport() { + var date = new Date(); + var fileName = "export-" + date.getFullYear().toString() + date.getMonth().toString() + date.getDate().toString() + + date.getHours().toString() + date.getMinutes().toString() + date.getSeconds().toString() + ".json"; + var allKeys = KeysDB.getKeys(); + var saveJson = generateBackupJson(allKeys.rows) + var exportJson = saveFile(cachePath + fileName, saveJson) + var exportPage = mainStack.push(Qt.resolvedUrl("PageHubExport.qml")); + exportPage.path = cachePath + fileName; + + exportPage.close.connect(function() { + mainStack.pop(); + }); + + exportButton.visible = true; + } + + function requestImport() { + importButton.visible = true; + } + + function requestImportTxt() { + importTxtButton.visible = true; + } +} diff --git a/qml/Components/PageCheckdB.qml b/qml/Components/PageCheckdB.qml new file mode 100644 index 0000000..43fd3d3 --- /dev/null +++ b/qml/Components/PageCheckdB.qml @@ -0,0 +1,241 @@ +/* + */ + +import QtQuick 2.9 +import Ubuntu.Components 1.3 +import Ubuntu.Components.Popups 1.3 + +import QtQuick.LocalStorage 2.0 +//import "../js/otpauth.min.js" as OTPAuth +import "../js/db.js" as KeysDB + +Page { + id: keyInfoPage + objectName: "keyInfoPage" + anchors.fill: parent + + property var key + property int id + property int reminingTime + property string secret + property string url + property string desc + + property bool isTOTP + property string authType + + header: HeaderInfo { + id: pageHeader + title: i18n.tr("%1 Key Information").arg(desc) + flickable: mainFlickable + } + + ProgressBar { + id: bar + visible: isTOTP + width: parent.width + anchors.top: header.bottom + + maximumValue: key.period || 0 + minimumValue: 0 + } + + ScrollView { + width: parent.width + height: parent.height + contentItem: mainFlickable + } + + Flickable { + id: mainFlickable + width: parent.width + height: parent.height + contentHeight: mainColumn.height + topMargin: units.gu(2) + bottomMargin: units.gu(4) + + Column { + id: mainColumn + width: parent.width + anchors.top: parent.top + anchors.topMargin: units.gu(1) + spacing: units.gu(2) + + Label { + text: i18n.tr("%1 generator: %2 at %32").arg(authType).arg(key.label).arg(key.issuer) + textSize: Label.Small + anchors.horizontalCenter: parent.horizontalCenter + } + + UbuntuShape { + id: contentShape + anchors.horizontalCenter: parent.horizontalCenter + width: Math.min(parent.width - units.gu(8), units.gu(68)) + height: generatedKey.height + units.gu(8) + aspect: UbuntuShape.Flat + color: root.highlightColor + + onWidthChanged: generatedKey.adjustTextSize() + + Label { + id: generatedKey + anchors.centerIn: parent + + text: generateKey(isTOTP) + + //TODO: This might be too slow for what it does. Consider + //From Dialer App + property double maximumFontSize: units.dp(35) + readonly property double minimumFontSize: FontUtils.sizeToPixels("medium") + property bool __adjusting: false + + fontSizeMode: Text.HorizontalFit + + function adjustTextSize(){ + // avoid infinite recursion here + if (__adjusting) return; + + __adjusting = true; + + // start by resetting the font size to discover the scale that should be used + font.pixelSize = maximumFontSize + + // check if it really needs to be scaled + if (contentShape.width < generatedKey.width) { + var factor = contentShape.width / generatedKey.width; + font.pixelSize = Math.max(font.pixelSize * factor, minimumFontSize); + } + __adjusting = false + } + + Connections { + target: units + onGridUnitChanged: adjustTextSize() + } + + Component.onCompleted: adjustTextSize() + } + } + + Row { + width: parent.width - units.gu(8) + spacing: units.gu(4) + anchors.horizontalCenter: parent.horizontalCenter + + Button { + id: regenBut + visible: !keyPeriod.visible + width: parent.width/2 - units.gu(2) + text: i18n.tr("Generate password") + onClicked: { + var difference = 1; + url = KeysDB.updateCounter(id, difference); + keyInfoPage.key = OTPAuth.URI.parse(url); + generatedKey.text = generateKey(isTOTP); + counterHtop.text = i18n.tr("Counter: %1").arg(key.counter) + root.initDB(); + } + } + + Button { + width: regenBut.visible ? parent.width/2 - units.gu(2) : parent.width + text: i18n.tr("Copy to clipboard") + onClicked: Clipboard.push(generatedKey.text) + color: theme.palette.normal.activity + } + } + + Rectangle { + width: parent.width + height: units.gu(2) + color: "transparent" + } + + UbuntuShape { + anchors.horizontalCenter: parent.horizontalCenter + width: units.gu(21) + height: width + sourceFillMode: UbuntuShape.Pad + aspect: UbuntuShape.Flat + backgroundColor: "#ffffff" + + source: QRCode { + id: qr + width: parent.width - units.gu(4) + height: width + anchors.bottom: parent.bottom + value : url + } + } + + Text { + text: i18n.tr("Secret Key") + anchors.horizontalCenter: parent.horizontalCenter + height: units.gu(5) + verticalAlignment: Text.AlignBottom + color: theme.palette.normal.baseText + } + + Label { + text: secret + textSize: Label.XSmall + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + + Text { + text: i18n.tr("Algorithm: %1").arg(key.algorithm) + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + + Text { + text: i18n.tr("Password Length: %1").arg(key.digits) + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + + Text { + id: keyPeriod + visible: isTOTP + text: i18n.tr("Interval: %1").arg(key.period) + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + + Text { + id: counterHtop + text: i18n.tr("Counter: %1").arg(key.counter) + visible: !keyPeriod.visible + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + } + } + + //Let's connect with the main ticker + Connections { + target: timer + + onTriggered: { + generatedKey.text = generateKey(isTOTP) + + bar.value = timer.time % key.period + } + } + + Component.onCompleted: { + isTOTP = key.counter == undefined; + bar.value = reminingTime; + + authType = isTOTP + ? "TOTP" + : "HTOP" + } + + function generateKey(boolVar) { + return boolVar + ? OTPAuth.TOTP.generate(key) + : OTPAuth.HOTP.generate(key) + } +} diff --git a/qml/Components/PageHubExport.qml b/qml/Components/PageHubExport.qml new file mode 100644 index 0000000..d0ed973 --- /dev/null +++ b/qml/Components/PageHubExport.qml @@ -0,0 +1,57 @@ +import QtQuick 2.9 +import Ubuntu.Components 1.3 +import Ubuntu.Content 1.3 + +Page { + id: openDialog + objectName: "exportHubPage" + anchors.fill: parent + + property var activeTransfer + property var path + + signal close() + + header: HeaderBase { + title: i18n.tr("Export json file to") + } + + Rectangle { + anchors.fill: parent + anchors.topMargin: openDialog.header.height + + ContentItem { + id: exportItem + } + + ContentPeerPicker { + visible: openDialog.visible + showTitle: false + handler: ContentHandler.Destination + contentType: ContentType.Documents + + onPeerSelected: { + activeTransfer = peer.request(); + var items = []; + exportItem.url = path; + console.log("Export path:",path); + items.push(exportItem); + activeTransfer.items = items; + activeTransfer.state = ContentTransfer.Charged; + + close(); + } + + onCancelPressed: { + close(); + } + } + } + + onActiveTransferChanged: { + if (activeTransfer.state == ContentTransfer.Charged) { + console.log("use this path",path) + } + console.log("AT",activeTransfer.state) + } +} diff --git a/qml/Components/PageHubImport.qml b/qml/Components/PageHubImport.qml new file mode 100644 index 0000000..a54e05c --- /dev/null +++ b/qml/Components/PageHubImport.qml @@ -0,0 +1,60 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import Ubuntu.Content 1.3 + +Page { + id: importPage + objectName: "importHubPage" + + property var activeTransfer + property bool allowMultipleFiles + + signal accept(var files) + signal reject() + + Rectangle { + anchors.fill: parent + color: 'red' + + ContentTransferHint { + anchors.fill: parent + activeTransfer: importPage.activeTransfer + } + + ContentPeerPicker { + anchors.fill: parent + visible: true + contentType: ContentType.Pictures + handler: ContentHandler.Source + + onPeerSelected: { + if (importPage.allowMultipleFiles) { + peer.selectionType = ContentTransfer.Multiple + } else { + peer.selectionType = ContentTransfer.Single + } + importPage.activeTransfer = peer.request() + stateChangeConnection.target = importPage.activeTransfer + } + + onCancelPressed: { + reject(); + } + } + } + + Connections { + id: stateChangeConnection + target: null + onStateChanged: { + if (importPage.activeTransfer.state === ContentTransfer.Charged) { + var selectedItems = [] + for(var i in importPage.activeTransfer.items) { + selectedItems.push(String(importPage.activeTransfer.items[i].url).replace("file://", "")) + } + + accept(selectedItems); + } + } + } +} diff --git a/qml/Components/PageImport.qml b/qml/Components/PageImport.qml new file mode 100644 index 0000000..43fd3d3 --- /dev/null +++ b/qml/Components/PageImport.qml @@ -0,0 +1,241 @@ +/* + */ + +import QtQuick 2.9 +import Ubuntu.Components 1.3 +import Ubuntu.Components.Popups 1.3 + +import QtQuick.LocalStorage 2.0 +//import "../js/otpauth.min.js" as OTPAuth +import "../js/db.js" as KeysDB + +Page { + id: keyInfoPage + objectName: "keyInfoPage" + anchors.fill: parent + + property var key + property int id + property int reminingTime + property string secret + property string url + property string desc + + property bool isTOTP + property string authType + + header: HeaderInfo { + id: pageHeader + title: i18n.tr("%1 Key Information").arg(desc) + flickable: mainFlickable + } + + ProgressBar { + id: bar + visible: isTOTP + width: parent.width + anchors.top: header.bottom + + maximumValue: key.period || 0 + minimumValue: 0 + } + + ScrollView { + width: parent.width + height: parent.height + contentItem: mainFlickable + } + + Flickable { + id: mainFlickable + width: parent.width + height: parent.height + contentHeight: mainColumn.height + topMargin: units.gu(2) + bottomMargin: units.gu(4) + + Column { + id: mainColumn + width: parent.width + anchors.top: parent.top + anchors.topMargin: units.gu(1) + spacing: units.gu(2) + + Label { + text: i18n.tr("%1 generator: %2 at %32").arg(authType).arg(key.label).arg(key.issuer) + textSize: Label.Small + anchors.horizontalCenter: parent.horizontalCenter + } + + UbuntuShape { + id: contentShape + anchors.horizontalCenter: parent.horizontalCenter + width: Math.min(parent.width - units.gu(8), units.gu(68)) + height: generatedKey.height + units.gu(8) + aspect: UbuntuShape.Flat + color: root.highlightColor + + onWidthChanged: generatedKey.adjustTextSize() + + Label { + id: generatedKey + anchors.centerIn: parent + + text: generateKey(isTOTP) + + //TODO: This might be too slow for what it does. Consider + //From Dialer App + property double maximumFontSize: units.dp(35) + readonly property double minimumFontSize: FontUtils.sizeToPixels("medium") + property bool __adjusting: false + + fontSizeMode: Text.HorizontalFit + + function adjustTextSize(){ + // avoid infinite recursion here + if (__adjusting) return; + + __adjusting = true; + + // start by resetting the font size to discover the scale that should be used + font.pixelSize = maximumFontSize + + // check if it really needs to be scaled + if (contentShape.width < generatedKey.width) { + var factor = contentShape.width / generatedKey.width; + font.pixelSize = Math.max(font.pixelSize * factor, minimumFontSize); + } + __adjusting = false + } + + Connections { + target: units + onGridUnitChanged: adjustTextSize() + } + + Component.onCompleted: adjustTextSize() + } + } + + Row { + width: parent.width - units.gu(8) + spacing: units.gu(4) + anchors.horizontalCenter: parent.horizontalCenter + + Button { + id: regenBut + visible: !keyPeriod.visible + width: parent.width/2 - units.gu(2) + text: i18n.tr("Generate password") + onClicked: { + var difference = 1; + url = KeysDB.updateCounter(id, difference); + keyInfoPage.key = OTPAuth.URI.parse(url); + generatedKey.text = generateKey(isTOTP); + counterHtop.text = i18n.tr("Counter: %1").arg(key.counter) + root.initDB(); + } + } + + Button { + width: regenBut.visible ? parent.width/2 - units.gu(2) : parent.width + text: i18n.tr("Copy to clipboard") + onClicked: Clipboard.push(generatedKey.text) + color: theme.palette.normal.activity + } + } + + Rectangle { + width: parent.width + height: units.gu(2) + color: "transparent" + } + + UbuntuShape { + anchors.horizontalCenter: parent.horizontalCenter + width: units.gu(21) + height: width + sourceFillMode: UbuntuShape.Pad + aspect: UbuntuShape.Flat + backgroundColor: "#ffffff" + + source: QRCode { + id: qr + width: parent.width - units.gu(4) + height: width + anchors.bottom: parent.bottom + value : url + } + } + + Text { + text: i18n.tr("Secret Key") + anchors.horizontalCenter: parent.horizontalCenter + height: units.gu(5) + verticalAlignment: Text.AlignBottom + color: theme.palette.normal.baseText + } + + Label { + text: secret + textSize: Label.XSmall + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + + Text { + text: i18n.tr("Algorithm: %1").arg(key.algorithm) + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + + Text { + text: i18n.tr("Password Length: %1").arg(key.digits) + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + + Text { + id: keyPeriod + visible: isTOTP + text: i18n.tr("Interval: %1").arg(key.period) + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + + Text { + id: counterHtop + text: i18n.tr("Counter: %1").arg(key.counter) + visible: !keyPeriod.visible + anchors.horizontalCenter: parent.horizontalCenter + color: theme.palette.normal.baseText + } + } + } + + //Let's connect with the main ticker + Connections { + target: timer + + onTriggered: { + generatedKey.text = generateKey(isTOTP) + + bar.value = timer.time % key.period + } + } + + Component.onCompleted: { + isTOTP = key.counter == undefined; + bar.value = reminingTime; + + authType = isTOTP + ? "TOTP" + : "HTOP" + } + + function generateKey(boolVar) { + return boolVar + ? OTPAuth.TOTP.generate(key) + : OTPAuth.HOTP.generate(key) + } +} diff --git a/qml/Components/PageSettings.qml b/qml/Components/PageSettings.qml index 8448e7c..2a6771c 100644 --- a/qml/Components/PageSettings.qml +++ b/qml/Components/PageSettings.qml @@ -40,7 +40,7 @@ Page { spacing: units.gu(2) Text { - text: i18n.tr("Theme") + text: ""+ i18n.tr("Theme") + "" color: mainColor //theme.palette.normal.overlayText width: parent.width - units.gu(2) anchors.horizontalCenter: parent.horizontalCenter @@ -53,6 +53,7 @@ Page { ? cTheme.height : units.gu(7) divider.visible: false + highlightColor: root.highlightColor property string themeName: "TOTP" @@ -63,6 +64,54 @@ Page { onSelectedThemeChanged: themeTF.themeName = selectedTheme } } + + Text { + text: ""+ i18n.tr("Backup and import") + "" + color: mainColor //theme.palette.normal.overlayText + width: parent.width - units.gu(2) + anchors.horizontalCenter: parent.horizontalCenter + } + + ListView { + width: parent.width + height: units.gu(7) * settingsModel.count + interactive: false + + model: settingsModel + delegate: settingsDelegate + } + + ListModel { + id: settingsModel + + function initialize() { + settingsModel.append({"text": i18n.tr("Manage Backup"), "subText": i18n.tr("%1 at %2"), "page": "PageBackup.qml"}); + settingsModel.append({"text": i18n.tr("Import Keys"), "subText": i18n.tr("Import keys exported with an external app"), "page": "PageImport.qml"}); + settingsModel.append({"text": i18n.tr("Check Database"), "subText": i18n.tr("Try to solve issues with imported key"), "page": "PageCheckdB.qml"}); + } + + Component.onCompleted: initialize() + } + + Component { + id: settingsDelegate + + ListItem { + width: parent.width + divider.visible: false + highlightColor: root.highlightColor + + ListItemLayout { + width: parent.width + title.text: text + subtitle.text: subText + + ProgressionSlot {} + } + + onClicked: mainStack.push(Qt.resolvedUrl(page)); + } + } } } diff --git a/qml/Components/RenderUriPopUp.qml b/qml/Components/RenderUriPopUp.qml index ff3491a..f71f2f0 100644 --- a/qml/Components/RenderUriPopUp.qml +++ b/qml/Components/RenderUriPopUp.qml @@ -23,17 +23,17 @@ Dialog { } Text { - text: i18n.tr("Issuer: %1").arg(uriJson.issuer) + text: "" + i18n.tr("Issuer") + ": " + uriJson.issuer color: theme.palette.normal.overlayText } Text { - text: i18n.tr("User: %1").arg(uriJson.label) + text: "" + i18n.tr("User") + ": " + uriJson.label color: theme.palette.normal.overlayText } Text { - text: i18n.tr("Algorithm: %1").arg(uriJson.algorithm) + text: "" + i18n.tr("Algorithm") + ": " + uriJson.algorithm color: theme.palette.normal.overlayText } @@ -41,26 +41,26 @@ Dialog { spacing: units.gu(2) Text { - text: i18n.tr("Password Length: %1").arg(uriJson.digits) + text: "" + i18n.tr("Password Length") + ": " + uriJson.digits color: theme.palette.normal.overlayText } Text { id: keyPeriod visible: uriJson.counter == undefined - text: i18n.tr("Interval: %1").arg(uriJson.period) + text: "" + i18n.tr("Interval") + ": " + uriJson.period color: theme.palette.normal.overlayText } Text { visible: !keyPeriod.visible - text: i18n.tr("Counter: %1").arg(uriJson.counter) + text: "" + i18n.tr("Counter") + ": " + uriJson.counter color: theme.palette.normal.overlayText } } Text { - text: i18n.tr("Key: %1").arg(uriJson.secret.b32) + text: "" + i18n.tr("Key") + ": " + uriJson.secret.b32 color: theme.palette.normal.overlayText wrapMode: Text.WrapAnywhere } diff --git a/qml/js/db.js b/qml/js/db.js index f1d10f5..352fc34 100644 --- a/qml/js/db.js +++ b/qml/js/db.js @@ -17,7 +17,6 @@ function openDB() { - var db = LocalStorage.openDatabaseSync("storedkeys_db", "0.1", "Stored Authenticator Keys", 1000); try { db.transaction(function(tx){ @@ -28,11 +27,10 @@ function openDB() { } return db } -// Save Contact Info function storeKey(added, keyJSON, description) { var db = openDB(); db.transaction(function(tx){ - tx.executeSql('INSERT OR REPLACE INTO storedkeys (added, keyJSON, description) VALUES(?, ?, ?)', [added, keyJSON, description]); //null is the automatic id + tx.executeSql('INSERT OR REPLACE INTO storedkeys (added, keyJSON, description) VALUES(?, ?, ?)', [added, keyJSON, description]); }); } diff --git a/tfamanager.apparmor b/tfamanager.apparmor index 7a6cbd5..fe787c3 100644 --- a/tfamanager.apparmor +++ b/tfamanager.apparmor @@ -1,4 +1,7 @@ { - "policy_groups": [], + "policy_groups": [ + "content_exchange", + "content_exchange_source" + ], "policy_version": 16.04 } -- GitLab From 1e5cc5059f8c2c3ced483811d47db7fad51ebe7f Mon Sep 17 00:00:00 2001 From: Joan CiberSheep Date: Thu, 5 Dec 2019 18:14:03 +0100 Subject: [PATCH 2/7] importexport --- CMakeLists.txt | 2 +- qml/Components/EmptyState.qml | 1 + qml/Components/HeaderBase.qml | 1 - qml/Components/PageBackup.qml | 140 +++++++++++++++++++++++++----- qml/Components/PageHubExport.qml | 52 +++++++++-- qml/Components/PageHubImport.qml | 73 +++++++++++----- qml/Components/PageKeysResume.qml | 1 - qml/Components/PageSettings.qml | 1 - qml/Components/esborra.json | 1 + qml/js/db.js | 20 +++++ 10 files changed, 237 insertions(+), 55 deletions(-) create mode 100644 qml/Components/esborra.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 98d5abc..4f5b290 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ set(FULL_PROJECT_NAME "tfamanager.cibersheep") set(CMAKE_INSTALL_PREFIX /) set(DATA_DIR /) set(DESKTOP_FILE_NAME ${PROJECT_NAME}.desktop) -set(APP_VERSION "0.9.3.1") +set(APP_VERSION "0.9.3.4") # This command figures out the target architecture for use in the manifest file execute_process( diff --git a/qml/Components/EmptyState.qml b/qml/Components/EmptyState.qml index 9091c90..9692205 100644 --- a/qml/Components/EmptyState.qml +++ b/qml/Components/EmptyState.qml @@ -74,5 +74,6 @@ Item { horizontalAlignment: Text.AlignHCenter color: theme.palette.normal.backgroundSecondaryText onLinkActivated: Qt.openUrlExternally(link) + linkColor: theme.palette.normal.activity } } diff --git a/qml/Components/HeaderBase.qml b/qml/Components/HeaderBase.qml index c134a99..9a6a6e6 100644 --- a/qml/Components/HeaderBase.qml +++ b/qml/Components/HeaderBase.qml @@ -8,6 +8,5 @@ PageHeader { StyleHints { foregroundColor: UbuntuColors.porcelain backgroundColor: mainColor - //dividerColor: UbuntuColors.slate } } diff --git a/qml/Components/PageBackup.qml b/qml/Components/PageBackup.qml index 3c59d32..510f1d5 100644 --- a/qml/Components/PageBackup.qml +++ b/qml/Components/PageBackup.qml @@ -60,11 +60,20 @@ Page { Label { width: parent.width - units.gu(4) - text: i18n.tr("Export all keys in one json file. Keep in mind that keys are stored as plain text. It's advisable to encrypt that file. The Enigma app could help here.") + text: i18n.tr("Export all keys in one json file. Keep in mind that keys are stored as plain text.") + horizontalAlignment: Text.AlignJustify + anchors.horizontalCenter: parent.horizontalCenter + wrapMode: Text.WordWrap + } + + Label { + width: parent.width - units.gu(4) + text: i18n.tr("It's advisable to encrypt that file. The Enigma app could help here.") horizontalAlignment: Text.AlignJustify anchors.horizontalCenter: parent.horizontalCenter wrapMode: Text.WordWrap onLinkActivated: Qt.openUrlExternally(link) + linkColor: theme.palette.normal.activity } Button { @@ -108,7 +117,8 @@ Page { anchors.horizontalCenter: parent.horizontalCenter onClicked: { - visible = false; + //TODO: requestiImport finishes before visible changes (?) + importButton.visible = false; requestImport(); } } @@ -121,7 +131,6 @@ Page { } } - Column { width: parent.width spacing: units.gu(2) @@ -158,47 +167,116 @@ Page { } //TODO: Use c++ instead? - function saveFile(fileUrl, text) { + function saveFile(fileUrl, text, xhrAction) { + console.log("Requested:",xhrAction); var request = new XMLHttpRequest(); - request.open("PUT", fileUrl, false); + request.open(xhrAction, fileUrl, false); + request.setRequestHeader("Content-Type", 'application/json'); + + request.onreadystatechange = function() { + if (xhrAction == "GET" && request.readyState == XMLHttpRequest.DONE) { + console.log("Json is ready"); + + try { + renderBackupJson(JSON.stringify(JSON.parse(request.responseText))); + } catch(e) { + //TODO: We might want to try if it's a text file + if (e instanceof SyntaxError) { + console.log("Syntx Error: JSON parse error"); + } else { + console.log("Error:",e, currentFile); + } + } + } + } + + request.onerror = function () { + error(request, request.status); + }; + request.send(text); - return request.status; } function generateBackupJson(data) { - var jsonToReturn = '{"keys": [' + var jsonToReturn = {"keys": []} if (data.length > 0) { for (var i = 0; i < data.length; i++) { - jsonToReturn += '{"keyJSON": "' + data.item(i).keyJSON + '",'; - jsonToReturn += '"added": "' + data.item(i).added + '",' - jsonToReturn += '"description": "' + data.item(i).description + '"}' - - if (i !== data.length -1) { - jsonToReturn += ','; - } + jsonToReturn.keys.push({ + "keyJSON": data.item(i).keyJSON, + "added": data.item(i).added, + "description": data.item(i).description + }); } } - jsonToReturn += ']}' - - return jsonToReturn; + return JSON.stringify(jsonToReturn); } - function renderBackupJson() { + function renderBackupJson(stringFile) { + try { + var currentFile = JSON.parse(stringFile); + + if ("keys" in currentFile) { + console.log("Debug: Importing 2fa Manager json"); + for (var j=0; j 0) { + console.log(rs.rows.length) + console.log("TRUE") + return true; + } else { + console.log(rs.rows.length) + console.log("FALSE") + return false; + } +} + function deleteKey(id){ console.log("Deleting register id: " + id); var db = openDB(); -- GitLab From 75176bdf5b0ed394fc38893814fe78eb0a8ca7e4 Mon Sep 17 00:00:00 2001 From: Joan CiberSheep Date: Thu, 5 Dec 2019 18:42:21 +0100 Subject: [PATCH 3/7] more logs --- qml/Components/PageBackup.qml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/qml/Components/PageBackup.qml b/qml/Components/PageBackup.qml index 510f1d5..a855867 100644 --- a/qml/Components/PageBackup.qml +++ b/qml/Components/PageBackup.qml @@ -223,16 +223,20 @@ Page { for (var j=0; j Date: Thu, 5 Dec 2019 18:55:00 +0100 Subject: [PATCH 4/7] more logs in db --- qml/js/db.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/qml/js/db.js b/qml/js/db.js index 6d6dd68..ead463d 100644 --- a/qml/js/db.js +++ b/qml/js/db.js @@ -48,18 +48,20 @@ function findKey(key) { var db = openDB(); var rs; db.transaction(function(tx) { + console.log("db1 rs",rs) rs = tx.executeSql('SELECT * FROM storedkeys WHERE keyJSON like ?;', [key]); + console.log("db2 rs",rs) }); - console.log("wwwwwwwwwwwwwwwww",JSON.stringify(rs.rows)) + console.log("db3 wwwwwwwwwwwwwwwww",JSON.stringify(rs.rows)) if (rs.rows.length > 0) { console.log(rs.rows.length) - console.log("TRUE") + console.log("db4 TRUE") return true; } else { console.log(rs.rows.length) - console.log("FALSE") + console.log("db4 FALSE") return false; } } -- GitLab From c52a420b91d2ff77bdccf336dcef7929ec9a2bf1 Mon Sep 17 00:00:00 2001 From: Joan CiberSheep Date: Thu, 5 Dec 2019 19:31:41 +0100 Subject: [PATCH 5/7] Fix string boolean --- qml/Components/PageBackup.qml | 16 +++++----------- qml/js/db.js | 8 +++----- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/qml/Components/PageBackup.qml b/qml/Components/PageBackup.qml index a855867..2f66cdc 100644 --- a/qml/Components/PageBackup.qml +++ b/qml/Components/PageBackup.qml @@ -223,22 +223,18 @@ Page { for (var j=0; j 0) { console.log(rs.rows.length) - console.log("db4 TRUE") + console.log("TRUE") return true; } else { console.log(rs.rows.length) - console.log("db4 FALSE") + console.log("FALSE") return false; } } -- GitLab From b6ae0c1503721ab40c89c55f2b21eca9a7b887df Mon Sep 17 00:00:00 2001 From: Joan CiberSheep Date: Sat, 7 Dec 2019 23:38:46 +0100 Subject: [PATCH 6/7] Add import from other json --- qml/Components/PageBackup.qml | 116 +++++++++++++++++++++++++++++----- qml/Components/esborra.json | 1 - 2 files changed, 99 insertions(+), 18 deletions(-) delete mode 100644 qml/Components/esborra.json diff --git a/qml/Components/PageBackup.qml b/qml/Components/PageBackup.qml index 2f66cdc..a9da5d1 100644 --- a/qml/Components/PageBackup.qml +++ b/qml/Components/PageBackup.qml @@ -18,6 +18,11 @@ Page { //TODO: Don't hardcode it property string cachePath: "/home/phablet/.cache/tfamanager.cibersheep/" + signal isExporting() + signal doneExporting() + signal isImporting(bool isJson) + signal doneImporting(bool isJson) + header: HeaderSettingsSpecial { id: backupHeader title: i18n.tr("Manage Backups") @@ -82,9 +87,13 @@ Page { width: Math.min(parent.width, units.gu(25)) anchors.horizontalCenter: parent.horizontalCenter - onClicked: { - visible = false; - requestExport(); + onClicked: requestExport(); + + Connections { + target: backupPage + + onIsExporting: importButton.visible = false; + onDoneExporting:importButton.visible = true; } } @@ -116,10 +125,16 @@ Page { width: Math.min(parent.width, units.gu(25)) anchors.horizontalCenter: parent.horizontalCenter - onClicked: { - //TODO: requestiImport finishes before visible changes (?) - importButton.visible = false; - requestImport(); + onClicked: requestImport(); + + Connections { + target: backupPage + + onIsImporting: if (isJson) { + console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + importButton.visible = false; + } + onDoneImporting: if (isJson) importButton.visible = true; } } @@ -150,9 +165,13 @@ Page { width: Math.min(parent.width, units.gu(25)) anchors.horizontalCenter: parent.horizontalCenter - onClicked: { - visible = false; - requestImportTxt(); + onClicked: requestImportTxt(); + + Connections { + target: backupPage + + onIsImporting: if (!isJson) importTxtButton.visible = false; + onDoneImporting: if (!isJson) importTxtButton.visible = true; } } @@ -214,7 +233,9 @@ Page { } function renderBackupJson(stringFile) { - try { + var needToInitDB = false; + + try { var currentFile = JSON.parse(stringFile); if ("keys" in currentFile) { @@ -222,10 +243,9 @@ Page { for (var j=0; j isJson) + isImporting(true); /* var importPage = mainStack.push(Qt.resolvedUrl("PageHubImport.qml", {"allowMultipleFiles":false})); @@ -304,9 +381,14 @@ Page { console.log("#-------",i,"Try to parse",file[i]); saveFile(file[i], "", "GET"); } + + doneImporting(true); } function requestImportTxt() { - importTxtButton.visible = true; + //Send signal when import is started (false -> !isJson) + isImporting(false); + + doneImporting(false); } } diff --git a/qml/Components/esborra.json b/qml/Components/esborra.json deleted file mode 100644 index 895b414..0000000 --- a/qml/Components/esborra.json +++ /dev/null @@ -1 +0,0 @@ -{"keys":[{"added":"dt. de nov. 26 02:55:14 2019 GMT+0100","description":"d","keyJSON":"otpauth://totp/?&secret=ADASC&algorithm=SHA1&digits=6&period=30"},{"added":"dt. de nov. 26 02:58:35 2019 GMT+0100","description":"sas","keyJSON":"otpauth://totp/:jhg?&secret=HGJHG&algorithm=SHA1&digits=6&period=30&issuer=jhg"},{"added":"dt. de nov. 26 03:04:45 2019 GMT+0100","description":"hgjhg","keyJSON":"otpauth://totp/%20:hjh?issuer=%20&secret=JHGJHGI&algorithm=SHA1&digits=6&period=30"},{"added":"dl. de des. 2 12:39:57 2019 GMT+0100","description":"m","keyJSON":"otpauth://hotp/jh:jh?issuer=jh&secret=FGDA&algorithm=SHA1&digits=6&counter=467"},{"added":"dl. de des. 2 12:42:22 2019 GMT+0100","description":"m","keyJSON":"otpauth://hotp/aaa:aaa?issuer=aaa&secret=ASDAS&algorithm=SHA1&digits=6&counter=0"},{"added":"dl. de des. 2 12:42:51 2019 GMT+0100","description":"m","keyJSON":"otpauth://totp/aaa:aaa?issuer=aaa&secret=ASDAS&algorithm=SHA1&digits=6&period=30"},{"added":"dl. de des. 2 12:42:58 2019 GMT+0100","description":"m","keyJSON":"otpauth://totp/aaa:aaa?issuer=aaa&secret=32234&algorithm=SHA1&digits=6&period=30"}]} \ No newline at end of file -- GitLab From fff72811445465b997471b090416317ef48f3be6 Mon Sep 17 00:00:00 2001 From: Joan CiberSheep Date: Sun, 8 Dec 2019 03:41:45 +0100 Subject: [PATCH 7/7] Added dialog --- qml/Components/DialogImportResum.qml | 36 ++++++++ qml/Components/PageBackup.qml | 121 +++++++++++++++++++++------ qml/Components/PageHubImport.qml | 11 ++- qml/Components/PageSettings.qml | 19 ++++- qml/js/db.js | 6 -- 5 files changed, 155 insertions(+), 38 deletions(-) create mode 100644 qml/Components/DialogImportResum.qml diff --git a/qml/Components/DialogImportResum.qml b/qml/Components/DialogImportResum.qml new file mode 100644 index 0000000..1df1a0f --- /dev/null +++ b/qml/Components/DialogImportResum.qml @@ -0,0 +1,36 @@ +import QtQuick 2.9 +import Ubuntu.Components 1.3 +import Ubuntu.Components.Popups 1.3 + +Dialog { + id: infoDialog + title: i18n.tr("Import Summary") + + property int impSuccess: 0 + property int impSkipped: 0 + property int impError: 0 + + Text { + text: i18n.tr("Keys imported successfully: ") + impSuccess + wrapMode: Text.WordWrap + color: theme.palette.normal.overlayText + } + + Text { + text: i18n.tr("Keys skipped: ") + impSkipped + wrapMode: Text.WordWrap + color: theme.palette.normal.overlayText + } + + Text { + text: i18n.tr("Keys not imported: ") + impError + wrapMode: Text.WordWrap + color: theme.palette.normal.overlayText + } + + Button { + text: i18n.tr("Close") + + onClicked: hide() + } +} diff --git a/qml/Components/PageBackup.qml b/qml/Components/PageBackup.qml index a9da5d1..bd67934 100644 --- a/qml/Components/PageBackup.qml +++ b/qml/Components/PageBackup.qml @@ -18,6 +18,10 @@ Page { //TODO: Don't hardcode it property string cachePath: "/home/phablet/.cache/tfamanager.cibersheep/" + property int impSuccess + property int impSkipped + property int impError + signal isExporting() signal doneExporting() signal isImporting(bool isJson) @@ -130,11 +134,11 @@ Page { Connections { target: backupPage - onIsImporting: if (isJson) { - console.log("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") - importButton.visible = false; + onIsImporting: if (isJson) importButton.visible = false; + onDoneImporting: { + console.log("!!!!!!!!!!!!!!!!!!!!!!!!") + if (isJson) importButton.visible = true; } - onDoneImporting: if (isJson) importButton.visible = true; } } @@ -185,6 +189,12 @@ Page { } } + Component { + id: importFinishedDialog + + DialogImportResum {} + } + //TODO: Use c++ instead? function saveFile(fileUrl, text, xhrAction) { console.log("Requested:",xhrAction); @@ -202,8 +212,14 @@ Page { //TODO: We might want to try if it's a text file if (e instanceof SyntaxError) { console.log("Syntx Error: JSON parse error"); + + if (request.responseText.indexOf("otpauth://") !== -1) { + console.log("We received a text file with URI"); + renderUriFromTxt(request.responseText.split("\n")); + } } else { - console.log("Error:",e, currentFile); + console.log("Error:",e); + impError += 1 } } } @@ -232,6 +248,36 @@ Page { return JSON.stringify(jsonToReturn); } + function renderUriFromTxt(array) { + var needToInitDB = false; + + for (var j=0; j isJson) isImporting(true); - /* + impSuccess = 0 + impSkipped = 0 + impError = 0 var importPage = mainStack.push(Qt.resolvedUrl("PageHubImport.qml", {"allowMultipleFiles":false})); importPage.close.connect(function() { mainStack.pop(); + doneImporting(true); }); importPage.accept.connect(function(urlOfItems) { - renderBackupJson(urlOfItems); - }); - */ - - //DEBUG. DELETE THIS - var file = [Qt.resolvedUrl("esborra.json")]; - - for (var i=0; i !isJson) isImporting(false); + impSuccess = 0 + impSkipped = 0 + impError = 0 + var importPage = mainStack.push(Qt.resolvedUrl("PageHubImport.qml", {"allowMultipleFiles":false})); - doneImporting(false); + importPage.close.connect(function() { + mainStack.pop(); + doneImporting(false); + }); + + importPage.accept.connect(function(urlOfItems) { + for (var i=0; i"+ i18n.tr("Theme") + "" - color: mainColor //theme.palette.normal.overlayText + color: mainColor width: parent.width - units.gu(2) anchors.horizontalCenter: parent.horizontalCenter } @@ -67,7 +67,7 @@ Page { Text { text: ""+ i18n.tr("Backup and import") + "" - color: mainColor //theme.palette.normal.overlayText + color: mainColor width: parent.width - units.gu(2) anchors.horizontalCenter: parent.horizontalCenter } @@ -85,8 +85,19 @@ Page { id: settingsModel function initialize() { - settingsModel.append({"text": i18n.tr("Manage Backup"), "subText": i18n.tr("%1 at %2"), "page": "PageBackup.qml"}); - settingsModel.append({"text": i18n.tr("Check Database"), "subText": i18n.tr("Try to solve issues with imported key"), "page": "PageCheckdB.qml"}); + settingsModel.append({ + "text": i18n.tr("Manage Backup"), + "subText": i18n.tr("Export, import keys and create a back up file"), + "page": "PageBackup.qml" + }); + + /* + settingsModel.append({ + "text": i18n.tr("Check Database"), + "subText": i18n.tr("Try to solve issues with imported key"), + "page": "PageCheckdB.qml" + }); + */ } Component.onCompleted: initialize() diff --git a/qml/js/db.js b/qml/js/db.js index f7a30d0..356b6c1 100644 --- a/qml/js/db.js +++ b/qml/js/db.js @@ -51,15 +51,9 @@ function findKey(key) { rs = tx.executeSql('SELECT * FROM storedkeys WHERE keyJSON like ?;', [key]); }); - console.log("findKey rows",JSON.stringify(rs.rows)) - if (rs.rows.length > 0) { - console.log(rs.rows.length) - console.log("TRUE") return true; } else { - console.log(rs.rows.length) - console.log("FALSE") return false; } } -- GitLab