From 9ab2922eb2f0853cd4ce4398ccfc50742ca0b62e Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Fri, 28 Jun 2019 06:48:54 +0200 Subject: [PATCH 01/20] Add vim generated files to gitignore --- .gitignore | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.gitignore b/.gitignore index 958bc75a..ecb0fb8c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,22 @@ monero-wallet-gui_plugin_import.cpp monero-wallet-gui_qml_plugin_import.cpp *.qmlc *.jsc + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ From 1b5796a0ee08ef1a0be4682aae59469b8e342a23 Mon Sep 17 00:00:00 2001 From: selsta Date: Sat, 29 Jun 2019 04:29:55 +0200 Subject: [PATCH 02/20] WizardSummary: remove duplicated entry --- wizard/WizardSummary.qml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/wizard/WizardSummary.qml b/wizard/WizardSummary.qml index 1698d28e..a94d921d 100644 --- a/wizard/WizardSummary.qml +++ b/wizard/WizardSummary.qml @@ -57,12 +57,6 @@ ColumnLayout { value: wizardController.language_language } - WizardSummaryItem { - Layout.fillWidth: true - header: qsTr("Wallet name") + translationManager.emptyString - value: walletOptionsName - } - WizardSummaryItem { Layout.fillWidth: true header: qsTr("Restore height") + translationManager.emptyString From e1b4bcb2efc1cb186953424c574d941676bc03b0 Mon Sep 17 00:00:00 2001 From: selsta Date: Mon, 1 Jul 2019 14:40:20 +0200 Subject: [PATCH 03/20] build: minimum required qt version is 5.9 --- monero-wallet-gui.pro | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index 8c4707c1..c4931240 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -1,6 +1,6 @@ -# qml components require at least QT 5.7.0 -lessThan (QT_MAJOR_VERSION, 5) | lessThan (QT_MINOR_VERSION, 7) { - error("Can't build with Qt $${QT_VERSION}. Use at least Qt 5.7.0") +# qml components require at least QT 5.9.0 +lessThan (QT_MAJOR_VERSION, 5) | lessThan (QT_MINOR_VERSION, 9) { + error("Can't build with Qt $${QT_VERSION}. Use at least Qt 5.9.0") } TEMPLATE = app From ab9e31e7cc5ab68a8291ff6ee1aa6d423484eea0 Mon Sep 17 00:00:00 2001 From: xiphon Date: Tue, 2 Jul 2019 10:57:37 +0000 Subject: [PATCH 04/20] WalletManager: async close with splash screen --- main.qml | 46 +++++++++++++++++-------------- pages/settings/SettingsInfo.qml | 9 +++--- src/libwalletqt/WalletManager.cpp | 6 ++-- src/libwalletqt/WalletManager.h | 3 +- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/main.qml b/main.qml index 743af497..c6300b52 100644 --- a/main.qml +++ b/main.qml @@ -283,7 +283,7 @@ ApplicationWindow { titleBar.visible = persistentSettings.customDecorations; } - function closeWallet() { + function closeWallet(callback) { // Disconnect all listeners if (typeof currentWallet !== "undefined" && currentWallet !== null) { @@ -306,8 +306,17 @@ ApplicationWindow { } currentWallet = undefined; - walletManager.closeWallet(); + appWindow.showProcessingSplash(qsTr("Closing wallet...")); + if (callback) { + walletManager.closeWalletAsync(function() { + hideProcessingSplash(); + callback(); + }); + } else { + walletManager.closeWallet(); + hideProcessingSplash(); + } } function connectWallet(wallet) { @@ -558,11 +567,6 @@ ApplicationWindow { } } - function onWalletClosed(walletAddress) { - hideProcessingSplash(); - console.log(">>> wallet closed: " + walletAddress) - } - function onWalletPassphraseNeeded(){ if(rootItem.state !== "normal") return; @@ -1115,17 +1119,18 @@ ApplicationWindow { function showWizard(){ clearMoneroCardLabelText(); walletInitialized = false; - closeWallet(); - currentWallet = undefined; - wizard.restart(); - wizard.wizardState = "wizardHome"; - rootItem.state = "wizard" - // reset balance - leftPanel.balanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(0); - fiatApiUpdateBalance(0, 0); - // disable timers - userInActivityTimer.running = false; - simpleModeConnectionTimer.running = false; + closeWallet(function() { + currentWallet = undefined; + wizard.restart(); + wizard.wizardState = "wizardHome"; + rootItem.state = "wizard" + // reset balance + leftPanel.balanceText = leftPanel.unlockedBalanceText = walletManager.displayAmount(0); + fiatApiUpdateBalance(0, 0); + // disable timers + userInActivityTimer.running = false; + simpleModeConnectionTimer.running = false; + }); } function hideMenu() { @@ -1289,7 +1294,6 @@ ApplicationWindow { y = (Screen.height - maxWindowHeight) / 2 // walletManager.walletOpened.connect(onWalletOpened); - walletManager.walletClosed.connect(onWalletClosed); walletManager.deviceButtonRequest.connect(onDeviceButtonRequest); walletManager.deviceButtonPressed.connect(onDeviceButtonPressed); walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete); @@ -2131,8 +2135,8 @@ ApplicationWindow { console.log("close accepted"); // Close wallet non async on exit daemonManager.exit(); - walletManager.closeWallet(); - Qt.quit(); + + closeWallet(Qt.quit); } function onWalletCheckUpdatesComplete(update) { diff --git a/pages/settings/SettingsInfo.qml b/pages/settings/SettingsInfo.qml index b329c068..938bd122 100644 --- a/pages/settings/SettingsInfo.qml +++ b/pages/settings/SettingsInfo.qml @@ -208,10 +208,11 @@ Rectangle { confirmationDialog.icon = StandardIcon.Question confirmationDialog.cancelText = qsTr("Cancel") confirmationDialog.onAcceptedCallback = function() { - walletManager.closeWallet(); - walletManager.clearWalletCache(persistentSettings.wallet_path); - walletManager.openWalletAsync(persistentSettings.wallet_path, appWindow.walletPassword, - persistentSettings.nettype, persistentSettings.kdfRounds); + appWindow.closeWallet(function() { + walletManager.clearWalletCache(persistentSettings.wallet_path); + walletManager.openWalletAsync(persistentSettings.wallet_path, appWindow.walletPassword, + persistentSettings.nettype, persistentSettings.kdfRounds); + }); } confirmationDialog.onRejectedCallback = null; diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index 22a810b9..9b53ad6e 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -228,11 +228,11 @@ QString WalletManager::closeWallet() return result; } -void WalletManager::closeWalletAsync() +void WalletManager::closeWalletAsync(const QJSValue& callback) { m_scheduler.run([this] { - emit walletClosed(closeWallet()); - }); + return QJSValueList({closeWallet()}); + }, callback); } bool WalletManager::walletExists(const QString &path) const diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index 18256b1c..54d2a29f 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -117,7 +117,7 @@ public: /*! * \brief closeWalletAsync - asynchronous version of "closeWallet" */ - Q_INVOKABLE void closeWalletAsync(); + Q_INVOKABLE void closeWalletAsync(const QJSValue& callback); //! checks is given filename is a wallet; Q_INVOKABLE bool walletExists(const QString &path) const; @@ -192,7 +192,6 @@ signals: void walletPassphraseNeeded(); void deviceButtonRequest(quint64 buttonCode); void deviceButtonPressed(); - void walletClosed(const QString &walletAddress); void checkUpdatesComplete(const QString &result) const; void miningStatus(bool isMining) const; From c34a2b43fc0be8ba9ca0a3019c0cb99e2cd26d5d Mon Sep 17 00:00:00 2001 From: selsta Date: Mon, 1 Jul 2019 23:19:29 +0200 Subject: [PATCH 05/20] QML: fix warnings --- js/TxUtils.js | 4 ++-- pages/Keys.qml | 3 ++- wizard/WizardCreateWallet1.qml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/js/TxUtils.js b/js/TxUtils.js index de4197d9..31f8553c 100644 --- a/js/TxUtils.js +++ b/js/TxUtils.js @@ -12,13 +12,13 @@ function destinationsToAddress(destinations){ } function addressTruncate(address, range){ - if(typeof(address) === "undefined") return; + if(typeof(address) === "undefined") return ""; if(typeof(range) === "undefined") range = 8; return address.substring(0, range) + "..." + address.substring(address.length-range); } function addressTruncatePretty(address, blocks){ - if(typeof(address) === "undefined") return; + if(typeof(address) === "undefined") return ""; if(typeof(blocks) === "undefined") blocks = 2; blocks = blocks <= 1 ? 1 : blocks >= 23 ? 23 : blocks; var ret = ""; diff --git a/pages/Keys.qml b/pages/Keys.qml index b913314b..a8f62a19 100644 --- a/pages/Keys.qml +++ b/pages/Keys.qml @@ -116,11 +116,11 @@ Rectangle { MoneroComponents.LineEdit { Layout.fillWidth: true + id: walletCreationHeight readOnly: true copyButton: true labelText: qsTr("Block #") + translationManager.emptyString fontSize: 16 - text: currentWallet.walletCreationHeight } } @@ -261,6 +261,7 @@ Rectangle { function onPageCompleted() { console.log("keys page loaded"); + walletCreationHeight.text = currentWallet.walletCreationHeight secretViewKey.text = currentWallet.secretViewKey publicViewKey.text = currentWallet.publicViewKey secretSpendKey.text = (!currentWallet.viewOnly) ? currentWallet.secretSpendKey : "" diff --git a/wizard/WizardCreateWallet1.qml b/wizard/WizardCreateWallet1.qml index 577243c3..f5090114 100644 --- a/wizard/WizardCreateWallet1.qml +++ b/wizard/WizardCreateWallet1.qml @@ -151,7 +151,7 @@ Rectangle { labelFontSize: 14 copyButton: false readOnly: true - text: Utils.roundDownToNearestThousand(wizardController.m_wallet.walletCreationHeight) + text: Utils.roundDownToNearestThousand(wizardController.m_wallet ? wizardController.m_wallet.walletCreationHeight : 0) } MoneroComponents.WarningBox { From 84865cdead8b8bdfe0ff6dfb4f7b5ac679eb59af Mon Sep 17 00:00:00 2001 From: selsta Date: Wed, 3 Jul 2019 13:54:01 +0200 Subject: [PATCH 06/20] History: allow to search for tx_note --- pages/History.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pages/History.qml b/pages/History.qml index 4ce053a7..c9a0d525 100644 --- a/pages/History.qml +++ b/pages/History.qml @@ -1387,6 +1387,8 @@ Rectangle { txs.push(item); } else if(item.blockheight.toString().startsWith(root.sortSearchString)) { txs.push(item); + } else if(item.tx_note.toLowerCase().indexOf(root.sortSearchString.toLowerCase()) !== -1) { + txs.push(item); } else if (item.hash.startsWith(root.sortSearchString)){ txs.push(item); } From 5f5f2103c0b8236c91ac127743e39697c24909bc Mon Sep 17 00:00:00 2001 From: selsta Date: Wed, 3 Jul 2019 15:24:39 +0200 Subject: [PATCH 07/20] History: dynamic number of tx rows --- pages/History.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pages/History.qml b/pages/History.qml index 4ce053a7..1471583f 100644 --- a/pages/History.qml +++ b/pages/History.qml @@ -49,9 +49,9 @@ Rectangle { property var model property int sideMargin: 50 property var initialized: false - property int txMax: 5 + property int txMax: Math.max(5, ((appWindow.height - 250) / 60)) property int txOffset: 0 - property int txPage: (txOffset / 5) + 1 + property int txPage: (txOffset / txMax) + 1 property int txCount: 0 property var sortSearchString: null property bool sortDirection: true // true = desc, false = asc @@ -67,6 +67,8 @@ Rectangle { color: "transparent" + onTxMaxChanged: root.updateDisplay(root.txOffset, root.txMax); + ColumnLayout { id: pageRoot anchors.topMargin: 40 @@ -1345,7 +1347,6 @@ Rectangle { function reset() { root.txOffset = 0; - root.txMax = 5; if (typeof root.model !== 'undefined' && root.model != null) { root.model.dateFromFilter = "2014-04-18" // genesis block From 817115fa47eefe604f1512342438cc466589c735 Mon Sep 17 00:00:00 2001 From: dsc Date: Thu, 4 Jul 2019 00:15:53 +0200 Subject: [PATCH 08/20] Start script for Tails Co-authored-by: thotbot --- linuxdeploy_helper.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/linuxdeploy_helper.sh b/linuxdeploy_helper.sh index db6ae8d1..59c1873c 100755 --- a/linuxdeploy_helper.sh +++ b/linuxdeploy_helper.sh @@ -51,4 +51,12 @@ SCRIPT_DIR="\$(dirname "\$(test -L "\${BASH_SOURCE[0]}" && readlink "\${BASH_SOU "\$SCRIPT_DIR"/$GUI_EXEC "\$@" EOL +# Create start script +cat > $TARGET/start-tails.AppImage < Date: Thu, 4 Jul 2019 15:17:41 +0200 Subject: [PATCH 09/20] History: don't reset date filtering when switching page --- pages/History.qml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pages/History.qml b/pages/History.qml index 4ce053a7..828b312f 100644 --- a/pages/History.qml +++ b/pages/History.qml @@ -1343,13 +1343,15 @@ Rectangle { root.updateDisplay(root.txOffset, root.txMax); } - function reset() { + function reset(keepDate) { root.txOffset = 0; root.txMax = 5; if (typeof root.model !== 'undefined' && root.model != null) { - root.model.dateFromFilter = "2014-04-18" // genesis block - root.model.dateToFilter = "9999-09-09" // fix before september 9999 + if (!keepDate) { + root.model.dateFromFilter = "2014-04-18" // genesis block + root.model.dateToFilter = "9999-09-09" // fix before september 9999 + } // negative values disable filters here; root.model.amountFromFilter = -1; root.model.amountToFilter = -1; @@ -1704,6 +1706,6 @@ Rectangle { function onPageClosed(){ root.initialized = false; - root.reset(); + root.reset(true); } } From 6baa96360f12eaaeb108a874ba166da57013d804 Mon Sep 17 00:00:00 2001 From: selsta Date: Wed, 3 Jul 2019 18:12:56 +0200 Subject: [PATCH 10/20] NetworkStatusItem: simple mode has no node settings page --- components/NetworkStatusItem.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/NetworkStatusItem.qml b/components/NetworkStatusItem.qml index 77ef4afa..30a7a867 100644 --- a/components/NetworkStatusItem.qml +++ b/components/NetworkStatusItem.qml @@ -88,6 +88,7 @@ Rectangle { } MouseArea { anchors.fill: parent + visible: appWindow.walletMode >= 2 cursorShape: Qt.PointingHandCursor onClicked: { if(!appWindow.isMining) { @@ -133,6 +134,7 @@ Rectangle { MouseArea { anchors.fill: parent + visible: appWindow.walletMode >= 2 cursorShape: Qt.PointingHandCursor onClicked: { if(!appWindow.isMining) { From 59550ec6739bb939d97fed86f99e96af36a7fe3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Brunner?= Date: Sun, 7 Jul 2019 17:23:08 +0200 Subject: [PATCH 11/20] Windows installer: Achieve reproducible builds --- installers/windows/Deterministic.md | 30 +++++++++++++++++++++++++++++ installers/windows/Monero.iss | 4 +++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 installers/windows/Deterministic.md diff --git a/installers/windows/Deterministic.md b/installers/windows/Deterministic.md new file mode 100644 index 00000000..bef6fc34 --- /dev/null +++ b/installers/windows/Deterministic.md @@ -0,0 +1,30 @@ +# Building the Installer Deterministically + +This file contains info about building the Windows installer deterministically, i.e. how different people on different Windows machines or VMs can build it and arrive at a result that is bit-for-bit identical. This approach is also known as *reproducible builds*, see e.g. [this Wikipedia article](https://en.wikipedia.org/wiki/Reproducible_builds). + +The steps to build the Windows installer deterministically by a group of people are the following (for some details about the build process in general see `README.md`): + +* Agree on a particular version of Inno Setup, and everybody install that +* Get the zip file for the Windows GUI wallet and unpack it, plus make sure / check that the file timestamps are preserved, i.e. upacked timestamp = timestamp in zip file +* Build using Inno Setup and the `Monero.iss` script file +* Success: All people arrive at a bit-for-bit identical installer .exe file, which they can verify by calculating and exchanging SHA256 hashes + +Some background info why this process is relatively simple: + +The tool used to build the Windows installer, Inno Setup, avoids many issues that make reproducible builds very challenging with many other compilers and similar tools: It does not store current date and time in the installer .exe file, and it does not seem to depend on the Windows version it runs on (tried with Windows 7 and two different editions of Windows 10), nor on the locale and display language. + +So fortunately no complicated things as faked current system time or use of VMs with exactly prescribed versions of Windows are necessary. + +The version of Inno Setup **is** important however: People wanting to reproducibly build the installer must agree on a particular version to use. This should not be hard to do however. + +Also important are the **timestamps** of the source files because they go into the installer file, to be restored at install time. + +You would think timestamp preservation is no problem when unpacking the zip archive with the files for the Windows GUI wallet from getmonero.org, but if you use the zip folder unpack functionality of the Windows 7 GUI, the files get the current date, **not** the file recorded in the zip file. (The Windows 10 GUI seems better here, and also the 7zip app.) + +In any case, after unpacking, check the file dates in the `bin` directory where the installer script looks for them with the dates of the files in the zip file: They must be identical. + +Note that the the following line in `Monero.iss` is also important regarding file timestamps: + + TimeStampsInUTC=yes + +Without this line the **timezone** of the machine used to build the installer would matter, with different timezones leading to different installer files. diff --git a/installers/windows/Monero.iss b/installers/windows/Monero.iss index 033c0440..bad675c2 100644 --- a/installers/windows/Monero.iss +++ b/installers/windows/Monero.iss @@ -21,6 +21,8 @@ DisableWelcomePage=no LicenseFile=LICENSE AppPublisher=The Monero Developer Community AppPublisherURL=https://getmonero.org +TimeStampsInUTC=yes +CompressionThreads=1 UsedUserAreasWarning=no ; The above directive silences the following compiler warning: @@ -68,7 +70,7 @@ Source: "FinishImage.bmp"; Flags: dontcopy ; Monero GUI wallet exe and guide Source: "bin\monero-wallet-gui.exe"; DestDir: "{app}"; Flags: ignoreversion -Source: "bin\monero-GUI-guide.pdf"; DestDir: "{app}"; Flags: ignoreversion +Source: "bin\monero-gui-wallet-guide.pdf"; DestDir: "{app}"; Flags: ignoreversion ; Monero CLI wallet Source: "bin\monero-wallet-cli.exe"; DestDir: "{app}"; Flags: ignoreversion From 35a0f25b5702a13696aec4bc679d0c25b576ca17 Mon Sep 17 00:00:00 2001 From: selsta Date: Mon, 1 Jul 2019 11:44:33 +0200 Subject: [PATCH 12/20] PasswordDialog: merge components and bug fixes This commit merges PasswordDialog, PassphraseDialog and NewPasswordDialog. Also the following bugs were fixed: - Wizard pages still being active when opening a wallet from wizard. - Capslock detection was buggy when copy pasting password, I replaced it with native implementations for each platform. - isAlpha could throw errors when using special characters. --- components/NewPasswordDialog.qml | 287 -------------------------- components/PassphraseDialog.qml | 321 ------------------------------ components/PasswordDialog.qml | 260 +++++++++++++++++++----- js/Utils.js | 19 -- main.qml | 49 ++--- monero-wallet-gui.pro | 9 +- oshelper.cpp | 35 ++++ oshelper.h | 1 + pages/settings/SettingsWallet.qml | 2 +- qml.qrc | 2 - src/qt/macoshelper.h | 40 ++++ src/qt/macoshelper.mm | 49 +++++ wizard/WizardController.qml | 8 +- 13 files changed, 369 insertions(+), 713 deletions(-) delete mode 100644 components/NewPasswordDialog.qml delete mode 100644 components/PassphraseDialog.qml create mode 100644 src/qt/macoshelper.h create mode 100644 src/qt/macoshelper.mm diff --git a/components/NewPasswordDialog.qml b/components/NewPasswordDialog.qml deleted file mode 100644 index 3f3c4f38..00000000 --- a/components/NewPasswordDialog.qml +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright (c) 2014-2019, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import QtQuick 2.9 -import QtQuick.Controls 2.0 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Window 2.0 -import FontAwesome 1.0 - -import "../components" as MoneroComponents - -Item { - id: root - visible: false - z: parent.z + 2 - - property bool isHidden: true - property alias password: passwordInput1.text - - // same signals as Dialog has - signal accepted() - signal rejected() - signal closeCallback() - - function open() { - isHidden = true - passwordInput1.echoMode = TextInput.Password; - passwordInput2.echoMode = TextInput.Password; - inactiveOverlay.visible = true - leftPanel.enabled = false - middlePanel.enabled = false - titleBar.state = "essentials" - root.visible = true; - passwordInput1.text = ""; - passwordInput2.text = ""; - passwordInput1.focus = true - } - - function close() { - inactiveOverlay.visible = false - leftPanel.enabled = true - middlePanel.enabled = true - titleBar.state = "default" - root.visible = false; - closeCallback(); - } - - function toggleIsHidden() { - passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password; - passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password; - isHidden = !isHidden; - } - - // TODO: implement without hardcoding sizes - width: 480 - height: 360 - - ColumnLayout { - z: inactiveOverlay.z + 1 - id: mainLayout - spacing: 10 - anchors { fill: parent; margins: 35 } - - ColumnLayout { - id: column - - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - Layout.maximumWidth: 400 - - Label { - text: qsTr("Please enter new password") + translationManager.emptyString - Layout.fillWidth: true - - font.pixelSize: 16 - font.family: MoneroComponents.Style.fontLight.name - - color: MoneroComponents.Style.defaultFontColor - } - - TextField { - id : passwordInput1 - Layout.topMargin: 6 - Layout.fillWidth: true - horizontalAlignment: TextInput.AlignLeft - verticalAlignment: TextInput.AlignVCenter - font.family: MoneroComponents.Style.fontLight.name - font.pixelSize: 24 - echoMode: TextInput.Password - bottomPadding: 10 - leftPadding: 10 - topPadding: 10 - color: MoneroComponents.Style.defaultFontColor - selectionColor: MoneroComponents.Style.textSelectionColor - selectedTextColor: MoneroComponents.Style.textSelectedColor - KeyNavigation.tab: passwordInput2 - - background: Rectangle { - radius: 2 - border.color: MoneroComponents.Style.inputBorderColorInActive - border.width: 1 - color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF" - - MoneroComponents.Label { - fontSize: 20 - text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash - opacity: 0.7 - fontFamily: FontAwesome.fontFamily - anchors.right: parent.right - anchors.rightMargin: 15 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onClicked: { - toggleIsHidden() - } - onEntered: { - parent.opacity = 0.9 - parent.fontSize = 24 - } - onExited: { - parent.opacity = 0.7 - parent.fontSize = 20 - } - } - } - } - - Keys.onEscapePressed: { - root.close() - root.rejected() - } - } - - // padding - Rectangle { - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - height: 10 - opacity: 0 - color: "black" - } - - Label { - text: qsTr("Please confirm new password") + translationManager.emptyString - Layout.fillWidth: true - - font.pixelSize: 16 - font.family: MoneroComponents.Style.fontLight.name - - color: MoneroComponents.Style.defaultFontColor - } - - TextField { - id : passwordInput2 - Layout.topMargin: 6 - Layout.fillWidth: true - horizontalAlignment: TextInput.AlignLeft - verticalAlignment: TextInput.AlignVCenter - font.family: MoneroComponents.Style.fontLight.name - font.pixelSize: 24 - echoMode: TextInput.Password - KeyNavigation.tab: okButton - bottomPadding: 10 - leftPadding: 10 - topPadding: 10 - color: MoneroComponents.Style.defaultFontColor - selectionColor: MoneroComponents.Style.textSelectionColor - selectedTextColor: MoneroComponents.Style.textSelectedColor - - background: Rectangle { - radius: 2 - border.color: MoneroComponents.Style.inputBorderColorInActive - border.width: 1 - color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF" - - MoneroComponents.Label { - fontSize: 20 - text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash - opacity: 0.7 - fontFamily: FontAwesome.fontFamily - anchors.right: parent.right - anchors.rightMargin: 15 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onClicked: { - toggleIsHidden() - } - onEntered: { - parent.opacity = 0.9 - parent.fontSize = 24 - } - onExited: { - parent.opacity = 0.7 - parent.fontSize = 20 - } - } - } - } - - Keys.onReturnPressed: { - if (passwordInput1.text === passwordInput2.text) { - root.close() - root.accepted() - } - } - Keys.onEscapePressed: { - root.close() - root.rejected() - } - } - - // padding - Rectangle { - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - height: 10 - opacity: 0 - color: "black" - } - - // Ok/Cancel buttons - RowLayout { - id: buttons - spacing: 16 - Layout.topMargin: 16 - Layout.alignment: Qt.AlignRight - - MoneroComponents.StandardButton { - id: cancelButton - text: qsTr("Cancel") + translationManager.emptyString - KeyNavigation.tab: passwordInput1 - onClicked: { - root.close() - root.rejected() - } - } - MoneroComponents.StandardButton { - id: okButton - text: qsTr("Continue") + translationManager.emptyString - KeyNavigation.tab: cancelButton - enabled: passwordInput1.text === passwordInput2.text - onClicked: { - root.close() - root.accepted() - } - } - } - } - } -} diff --git a/components/PassphraseDialog.qml b/components/PassphraseDialog.qml deleted file mode 100644 index 79dd2f19..00000000 --- a/components/PassphraseDialog.qml +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright (c) 2014-2019, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import QtQuick 2.7 -import QtQuick.Controls 2.0 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.1 -import QtQuick.Controls.Styles 1.4 -import QtQuick.Window 2.0 -import FontAwesome 1.0 - -import "../components" as MoneroComponents - -Item { - id: root - visible: false - z: parent.z + 2 - - property bool isHidden: true - property alias passphrase: passphaseInput1.text - property string walletName - property string errorText - - // same signals as Dialog has - signal accepted() - signal rejected() - signal closeCallback() - - function open(walletName, errorText) { - isHidden = true - passphaseInput1.echoMode = TextInput.Password; - passphaseInput2.echoMode = TextInput.Password; - - inactiveOverlay.visible = true - - root.walletName = walletName ? walletName : "" - root.errorText = errorText ? errorText : ""; - - leftPanel.enabled = false - middlePanel.enabled = false - titleBar.state = "essentials" - - root.visible = true; - passphaseInput1.text = ""; - passphaseInput2.text = ""; - passphaseInput1.focus = true - } - - function close() { - inactiveOverlay.visible = false - leftPanel.enabled = true - middlePanel.enabled = true - titleBar.state = "default" - root.visible = false; - closeCallback(); - } - - function toggleIsHidden() { - passphaseInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password; - passphaseInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password; - isHidden = !isHidden; - } - - function showError(errorText) { - open(root.walletName, errorText); - } - - // TODO: implement without hardcoding sizes - width: 480 - height: 360 - - ColumnLayout { - z: inactiveOverlay.z + 1 - id: mainLayout - spacing: 10 - anchors { fill: parent; margins: 35 } - - ColumnLayout { - id: column - - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - Layout.maximumWidth: 400 - - Label { - text: (root.walletName.length > 0 ? qsTr("Please enter wallet device passphrase for: ") + root.walletName : qsTr("Please enter wallet device passphrase")) + translationManager.emptyString - Layout.fillWidth: true - - font.pixelSize: 16 - font.family: MoneroComponents.Style.fontLight.name - - color: MoneroComponents.Style.defaultFontColor - } - - Label { - text: qsTr("Warning: passphrase entry on host is a security risk as it can be captured by malware. It is advised to prefer device-based passphrase entry.") + translationManager.emptyString - Layout.fillWidth: true - wrapMode: Text.Wrap - - font.pixelSize: 14 - font.family: MoneroComponents.Style.fontLight.name - - color: MoneroComponents.Style.warningColor - } - - Label { - text: root.errorText - visible: root.errorText - - color: MoneroComponents.Style.errorColor - font.pixelSize: 16 - font.family: MoneroComponents.Style.fontLight.name - Layout.fillWidth: true - wrapMode: Text.Wrap - } - - TextField { - id : passphaseInput1 - Layout.topMargin: 6 - Layout.fillWidth: true - horizontalAlignment: TextInput.AlignLeft - verticalAlignment: TextInput.AlignVCenter - font.family: MoneroComponents.Style.fontLight.name - font.pixelSize: 24 - echoMode: TextInput.Password - bottomPadding: 10 - leftPadding: 10 - topPadding: 10 - color: MoneroComponents.Style.defaultFontColor - selectionColor: MoneroComponents.Style.textSelectionColor - selectedTextColor: MoneroComponents.Style.textSelectedColor - KeyNavigation.tab: passphaseInput2 - - background: Rectangle { - radius: 2 - border.color: MoneroComponents.Style.inputBorderColorInActive - border.width: 1 - color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF" - - MoneroComponents.Label { - fontSize: 20 - text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash - opacity: 0.7 - fontFamily: FontAwesome.fontFamily - anchors.right: parent.right - anchors.rightMargin: 15 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onClicked: { - toggleIsHidden() - } - onEntered: { - parent.opacity = 0.9 - parent.fontSize = 24 - } - onExited: { - parent.opacity = 0.7 - parent.fontSize = 20 - } - } - } - } - - Keys.onEscapePressed: { - root.close() - root.rejected() - } - } - - // padding - Rectangle { - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - height: 10 - opacity: 0 - color: "transparent" - } - - Label { - text: qsTr("Please re-enter") + translationManager.emptyString - Layout.fillWidth: true - - font.pixelSize: 16 - font.family: MoneroComponents.Style.fontLight.name - - color: MoneroComponents.Style.defaultFontColor - } - - TextField { - id : passphaseInput2 - Layout.topMargin: 6 - Layout.fillWidth: true - horizontalAlignment: TextInput.AlignLeft - verticalAlignment: TextInput.AlignVCenter - font.family: MoneroComponents.Style.fontLight.name - font.pixelSize: 24 - echoMode: TextInput.Password - KeyNavigation.tab: okButton - bottomPadding: 10 - leftPadding: 10 - topPadding: 10 - color: MoneroComponents.Style.defaultFontColor - selectionColor: MoneroComponents.Style.dimmedFontColor - selectedTextColor: MoneroComponents.Style.defaultFontColor - - background: Rectangle { - radius: 2 - border.color: MoneroComponents.Style.inputBorderColorInActive - border.width: 1 - color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF" - - MoneroComponents.Label { - fontSize: 20 - text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash - opacity: 0.7 - fontFamily: FontAwesome.fontFamily - anchors.right: parent.right - anchors.rightMargin: 15 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: 1 - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onClicked: { - toggleIsHidden() - } - onEntered: { - parent.opacity = 0.9 - parent.fontSize = 24 - } - onExited: { - parent.opacity = 0.7 - parent.fontSize = 20 - } - } - } - } - - Keys.onReturnPressed: { - if (passphaseInput1.text === passphaseInput2.text) { - root.close() - root.accepted() - } - } - Keys.onEscapePressed: { - root.close() - root.rejected() - } - } - - // padding - Rectangle { - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - height: 10 - opacity: 0 - color: "black" - } - - // Ok/Cancel buttons - RowLayout { - id: buttons - spacing: 16 - Layout.topMargin: 16 - Layout.alignment: Qt.AlignRight - - MoneroComponents.StandardButton { - id: cancelButton - text: qsTr("Cancel") + translationManager.emptyString - KeyNavigation.tab: passphaseInput1 - onClicked: { - root.close() - root.rejected() - } - } - MoneroComponents.StandardButton { - id: okButton - text: qsTr("Continue") + translationManager.emptyString - KeyNavigation.tab: cancelButton - enabled: passphaseInput1.text === passphaseInput2.text - onClicked: { - root.close() - root.accepted() - } - } - } - } - } -} diff --git a/components/PasswordDialog.qml b/components/PasswordDialog.qml index 4d1c8467..67ffce96 100644 --- a/components/PasswordDialog.qml +++ b/components/PasswordDialog.qml @@ -44,34 +44,63 @@ Item { z: parent.z + 2 property bool isHidden: true - property alias password: passwordInput.text + property alias password: passwordInput1.text property string walletName property string errorText - property bool shiftIsPressed: false - property bool isCapsLocksActive: false - property bool backspaceIsPressed: false + property bool passwordDialogMode + property bool passphraseDialogMode + property bool newPasswordDialogMode // same signals as Dialog has signal accepted() + signal acceptedNewPassword() + signal acceptedPassphrase() signal rejected() + signal rejectedNewPassword() + signal rejectedPassphrase() signal closeCallback() - function open(walletName, errorText) { + function _openInit(walletName, errorText) { isHidden = true - passwordInput.echoMode = TextInput.Password - passwordInput.text = "" - passwordInput.forceActiveFocus(); + capsLockTextLabel.visible = oshelper.isCapsLock(); + passwordInput1.echoMode = TextInput.Password + passwordInput2.echoMode = TextInput.Password + passwordInput1.text = "" + passwordInput2.text = "" + passwordInput1.forceActiveFocus(); inactiveOverlay.visible = true // draw appwindow inactive root.walletName = walletName ? walletName : "" errorTextLabel.text = errorText ? errorText : ""; leftPanel.enabled = false middlePanel.enabled = false + wizard.enabled = false titleBar.state = "essentials" root.visible = true; appWindow.hideBalanceForced = true; appWindow.updateBalance(); } + function open(walletName, errorText) { + passwordDialogMode = true; + passphraseDialogMode = false; + newPasswordDialogMode = false; + _openInit(walletName, errorText); + } + + function openPassphraseDialog() { + passwordDialogMode = false; + passphraseDialogMode = true; + newPasswordDialogMode = false; + _openInit("", ""); + } + + function openNewPasswordDialog() { + passwordDialogMode = false; + passphraseDialogMode = false; + newPasswordDialogMode = true; + _openInit("", ""); + } + function showError(errorText) { open(root.walletName, errorText); } @@ -80,6 +109,7 @@ Item { inactiveOverlay.visible = false leftPanel.enabled = true middlePanel.enabled = true + wizard.enabled = true titleBar.state = "default" root.visible = false; @@ -88,6 +118,12 @@ Item { closeCallback(); } + function toggleIsHidden() { + passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password; + passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password; + isHidden = !isHidden; + } + ColumnLayout { z: inactiveOverlay.z + 1 id: mainLayout @@ -102,7 +138,11 @@ Item { Layout.maximumWidth: 400 Label { - text: (root.walletName.length > 0 ? qsTr("Please enter wallet password for: ") + root.walletName : qsTr("Please enter wallet password")) + translationManager.emptyString + text: { + var device = passwordDialogMode ? qsTr("wallet password") : qsTr("wallet device passphrase"); + (root.walletName.length > 0 ? qsTr("Please enter %1 for: ").arg(device) + root.walletName : qsTr("Please enter %1").arg(device)) + translationManager.emptyString; + } + visible: !newPasswordDialogMode Layout.fillWidth: true font.pixelSize: 16 @@ -111,19 +151,41 @@ Item { color: MoneroComponents.Style.defaultFontColor } + Label { + text: qsTr("Warning: passphrase entry on host is a security risk as it can be captured by malware. It is advised to prefer device-based passphrase entry.") + translationManager.emptyString + visible: passphraseDialogMode + Layout.fillWidth: true + wrapMode: Text.Wrap + + font.pixelSize: 14 + font.family: MoneroComponents.Style.fontLight.name + + color: MoneroComponents.Style.warningColor + } + Label { id: errorTextLabel visible: root.errorText || text !== "" - color: MoneroComponents.Style.errorColor font.pixelSize: 16 - font.family: MoneroComponents.Style.fontLight.name + font.family: MoneroComponents.Style.fontLight.name Layout.fillWidth: true wrapMode: Text.Wrap } + Label { + id: capsLockTextLabel + visible: false + color: MoneroComponents.Style.errorColor + font.pixelSize: 16 + font.family: MoneroComponents.Style.fontLight.name + Layout.fillWidth: true + wrapMode: Text.Wrap + text: qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString; + } + TextField { - id : passwordInput + id: passwordInput1 Layout.topMargin: 6 Layout.fillWidth: true horizontalAlignment: TextInput.AlignLeft @@ -131,24 +193,20 @@ Item { font.family: MoneroComponents.Style.fontLight.name font.pixelSize: 24 echoMode: TextInput.Password - KeyNavigation.tab: okButton + KeyNavigation.tab: { + if (passwordDialogMode) { + return okButton + } else { + return passwordInput2 + } + } bottomPadding: 10 leftPadding: 10 topPadding: 10 color: MoneroComponents.Style.defaultFontColor selectionColor: MoneroComponents.Style.textSelectionColor selectedTextColor: MoneroComponents.Style.textSelectedColor - - onTextChanged: { - var letter = text[passwordInput.text.length - 1]; - isCapsLocksActive = Utils.isUpperLock(shiftIsPressed, letter); - if(isCapsLocksActive && !backspaceIsPressed){ - errorTextLabel.text = qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString; - } - else{ - errorTextLabel.text = ""; - } - } + onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock(); background: Rectangle { radius: 2 @@ -177,8 +235,7 @@ Item { cursorShape: Qt.PointingHandCursor hoverEnabled: true onClicked: { - passwordInput.echoMode = isHidden ? TextInput.Normal : TextInput.Password; - isHidden = !isHidden; + toggleIsHidden(); } onEntered: { parent.opacity = 0.9 @@ -195,28 +252,129 @@ Item { Keys.enabled: root.visible Keys.onReturnPressed: { root.close() - root.accepted() + if (passwordDialogMode) { + root.accepted() + } else if (newPasswordDialogMode) { + root.acceptedNewPassword() + } else if (passphraseDialogMode) { + root.acceptedPassphrase() + } } Keys.onEscapePressed: { root.close() - root.rejected() - } - Keys.onPressed: { - if(event.key === Qt.Key_Shift){ - shiftIsPressed = true; - } - if(event.key === Qt.Key_Backspace){ - backspaceIsPressed = true; + if (passwordDialogMode) { + root.rejected() + } else if (newPasswordDialogMode) { + root.rejectedNewPassword() + } else if (passphraseDialogMode) { + root.rejectedPassphrase() } } - Keys.onReleased: { - if(event.key === Qt.Key_Shift){ - shiftIsPressed = false; - } - if(event.key === Qt.Key_Backspace){ - backspaceIsPressed =false; + } + + // padding + Rectangle { + visible: !passwordDialogMode + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + height: 10 + opacity: 0 + color: "black" + } + + Label { + visible: !passwordDialogMode + text: qsTr("Please confirm new password") + translationManager.emptyString + Layout.fillWidth: true + + font.pixelSize: 16 + font.family: MoneroComponents.Style.fontLight.name + + color: MoneroComponents.Style.defaultFontColor + } + + TextField { + id: passwordInput2 + visible: !passwordDialogMode + Layout.topMargin: 6 + Layout.fillWidth: true + horizontalAlignment: TextInput.AlignLeft + verticalAlignment: TextInput.AlignVCenter + font.family: MoneroComponents.Style.fontLight.name + font.pixelSize: 24 + echoMode: TextInput.Password + KeyNavigation.tab: okButton + bottomPadding: 10 + leftPadding: 10 + topPadding: 10 + color: MoneroComponents.Style.defaultFontColor + selectionColor: MoneroComponents.Style.textSelectionColor + selectedTextColor: MoneroComponents.Style.textSelectedColor + onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock(); + + background: Rectangle { + radius: 2 + border.color: MoneroComponents.Style.inputBorderColorInActive + border.width: 1 + color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF" + + MoneroComponents.Label { + fontSize: 20 + text: isHidden ? FontAwesome.eye : FontAwesome.eyeSlash + opacity: 0.7 + fontFamily: FontAwesome.fontFamily + anchors.right: parent.right + anchors.rightMargin: 15 + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: { + toggleIsHidden() + } + onEntered: { + parent.opacity = 0.9 + parent.fontSize = 24 + } + onExited: { + parent.opacity = 0.7 + parent.fontSize = 20 + } + } } } + + Keys.onReturnPressed: { + if (passwordInput1.text === passwordInput2.text) { + root.close() + if (newPasswordDialogMode) { + root.acceptedNewPassword() + } else if (passphraseDialogMode) { + root.acceptedPassphrase() + } + } + } + Keys.onEscapePressed: { + root.close() + if (newPasswordDialogMode) { + root.rejectedNewPassword() + } else if (passphraseDialogMode) { + root.rejectedPassphrase() + } + } + } + + // padding + Rectangle { + visible: !passwordDialogMode + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + height: 10 + opacity: 0 + color: "black" } // Ok/Cancel buttons @@ -230,10 +388,16 @@ Item { id: cancelButton small: true text: root.walletName.length > 0 ? qsTr("Change wallet") + translationManager.emptyString : qsTr("Cancel") + translationManager.emptyString - KeyNavigation.tab: passwordInput + KeyNavigation.tab: passwordInput1 onClicked: { root.close() - root.rejected() + if (passwordDialogMode) { + root.rejected() + } else if (newPasswordDialogMode) { + root.rejectedNewPassword() + } else if (passphraseDialogMode) { + root.rejectedPassphrase() + } } } @@ -242,13 +406,19 @@ Item { small: true text: qsTr("Continue") + translationManager.emptyString KeyNavigation.tab: cancelButton + enabled: (passwordDialogMode == true) ? true : passwordInput1.text === passwordInput2.text onClicked: { root.close() - root.accepted() + if (passwordDialogMode) { + root.accepted() + } else if (newPasswordDialogMode) { + root.acceptedNewPassword() + } else if (passphraseDialogMode) { + root.acceptedPassphrase() + } } } } - } } } diff --git a/js/Utils.js b/js/Utils.js index b709b2ce..4a9a5fed 100644 --- a/js/Utils.js +++ b/js/Utils.js @@ -112,25 +112,6 @@ function roundDownToNearestThousand(_num){ return Math.floor(_num/1000.0)*1000 } -function isAlpha(letter){ return letter.match(/^[A-Za-z0-9]+$/) !== null; } - -function isLowerCaseChar(letter){ return letter === letter.toLowerCase(); } - -function isUpperLock(shift, letter){ - if(!isAlpha((letter))) return false; - if(shift) { - if(isLowerCaseChar(letter)) - return true; - else - return false; - } else { - if(isLowerCaseChar(letter)) - return false; - else - return true; - } -} - function qmlEach(item, properties, ignoredObjectNames, arr){ // Traverse QML object tree and return components that match // via property names. Similar to jQuery("myclass").each(... diff --git a/main.qml b/main.qml index 743af497..af80ad4e 100644 --- a/main.qml +++ b/main.qml @@ -569,15 +569,15 @@ ApplicationWindow { hideProcessingSplash(); console.log(">>> wallet passphrase needed: ") - passphraseDialog.onAcceptedCallback = function() { - walletManager.onPassphraseEntered(passphraseDialog.passphrase); + passwordDialog.onAcceptedPassphraseCallback = function() { + walletManager.onPassphraseEntered(passwordDialog.password); this.onWalletOpening(); } - passphraseDialog.onRejectedCallback = function() { + passwordDialog.onRejectedPassphraseCallback = function() { walletManager.onPassphraseEntered("", true); this.onWalletOpening(); } - passphraseDialog.open() + passwordDialog.openPassphraseDialog() } function onWalletUpdate() { @@ -1494,23 +1494,6 @@ ApplicationWindow { } } - PassphraseDialog { - id: passphraseDialog - visible: false - z: parent.z + 1 - anchors.fill: parent - property var onAcceptedCallback - property var onRejectedCallback - onAccepted: { - if (onAcceptedCallback) - onAcceptedCallback(); - } - onRejected: { - if (onRejectedCallback) - onRejectedCallback(); - } - } - PasswordDialog { id: passwordDialog visible: false @@ -1518,6 +1501,8 @@ ApplicationWindow { anchors.fill: parent property var onAcceptedCallback property var onRejectedCallback + property var onAcceptedPassphraseCallback + property var onRejectedPassphraseCallback onAccepted: { if (onAcceptedCallback) onAcceptedCallback(); @@ -1526,16 +1511,9 @@ ApplicationWindow { if (onRejectedCallback) onRejectedCallback(); } - } - - NewPasswordDialog { - id: newPasswordDialog - z: parent.z + 1 - visible:false - anchors.fill: parent - onAccepted: { - if (currentWallet.setPassword(newPasswordDialog.password)) { - appWindow.walletPassword = newPasswordDialog.password; + onAcceptedNewPassword: { + if (currentWallet.setPassword(passwordDialog.password)) { + appWindow.walletPassword = passwordDialog.password; informationPopup.title = qsTr("Information") + translationManager.emptyString; informationPopup.text = qsTr("Password changed successfully") + translationManager.emptyString; informationPopup.icon = StandardIcon.Information; @@ -1547,7 +1525,14 @@ ApplicationWindow { informationPopup.onCloseCallback = null; informationPopup.open(); } - onRejected: { + onRejectedNewPassword: {} + onAcceptedPassphrase: { + if (onAcceptedPassphraseCallback) + onAcceptedPassphraseCallback(); + } + onRejectedPassphrase: { + if (onRejectedPassphraseCallback) + onRejectedPassphraseCallback(); } } diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index 8c4707c1..51c81a7d 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -67,7 +67,8 @@ HEADERS += \ src/qt/mime.h \ src/qt/KeysFiles.h \ src/qt/utils.h \ - src/qt/prices.h + src/qt/prices.h \ + src/qt/macoshelper.h SOURCES += main.cpp \ filter.cpp \ @@ -332,7 +333,8 @@ linux { -llmdb \ -lsodium \ -lhidapi-libusb \ - -lcrypto $$TREZOR_LINKER + -lcrypto $$TREZOR_LINKER \ + -lX11 if(!android) { LIBS+= \ @@ -357,6 +359,8 @@ macx { # message("using static libraries") # LIBS+= -Wl,-Bstatic # } + QT += macextras + OBJECTIVE_SOURCES += src/qt/macoshelper.mm LIBS+= \ -L/usr/local/lib \ -L/usr/local/opt/openssl/lib \ @@ -370,6 +374,7 @@ macx { -lboost_chrono \ -lboost_program_options \ -framework CoreFoundation \ + -framework AppKit \ -lhidapi \ -lssl \ -lsodium \ diff --git a/oshelper.cpp b/oshelper.cpp index 36a3c866..d3e4d063 100644 --- a/oshelper.cpp +++ b/oshelper.cpp @@ -31,6 +31,20 @@ #include #include #include +#ifdef Q_OS_MAC +#include "qt/macoshelper.h" +#endif +#ifdef Q_OS_WIN32 +#include +#endif +#ifdef Q_OS_LINUX +#include +#undef KeyPress +#undef KeyRelease +#undef FocusIn +#undef FocusOut +// #undef those Xlib #defines that conflict with QEvent::Type enum +#endif OSHelper::OSHelper(QObject *parent) : QObject(parent) { @@ -58,6 +72,27 @@ bool OSHelper::removeTemporaryWallet(const QString &fileName) const return cache_deleted && address_deleted && keys_deleted; } +// https://stackoverflow.com/a/3006934 +bool OSHelper::isCapsLock() const +{ + // platform dependent method of determining if CAPS LOCK is on +#if defined(Q_OS_WIN32) // MS Windows version + return GetKeyState(VK_CAPITAL) == 1; +#elif defined(Q_OS_LINUX) // X11 version + Display * d = XOpenDisplay((char*)0); + bool caps_state = false; + if (d) { + unsigned n; + XkbGetIndicatorState(d, XkbUseCoreKbd, &n); + caps_state = (n & 0x01) == 1; + } + return caps_state; +#elif defined(Q_OS_MAC) + return MacOSHelper::isCapsLock(); +#endif + return false; +} + QString OSHelper::temporaryPath() const { return QDir::tempPath(); diff --git a/oshelper.h b/oshelper.h index 1c11bdde..4d378a9d 100644 --- a/oshelper.h +++ b/oshelper.h @@ -42,6 +42,7 @@ public: Q_INVOKABLE QString temporaryFilename() const; Q_INVOKABLE QString temporaryPath() const; Q_INVOKABLE bool removeTemporaryWallet(const QString &walletName) const; + Q_INVOKABLE bool isCapsLock() const; signals: diff --git a/pages/settings/SettingsWallet.qml b/pages/settings/SettingsWallet.qml index 1b2c8463..ab80fec2 100644 --- a/pages/settings/SettingsWallet.qml +++ b/pages/settings/SettingsWallet.qml @@ -339,7 +339,7 @@ Rectangle { onClicked: { passwordDialog.onAcceptedCallback = function() { if(appWindow.walletPassword === passwordDialog.password){ - newPasswordDialog.open() + passwordDialog.openNewPasswordDialog() } else { informationPopup.title = qsTr("Error") + translationManager.emptyString; informationPopup.text = qsTr("Wrong password") + translationManager.emptyString; diff --git a/qml.qrc b/qml.qrc index 1200b285..3bced000 100644 --- a/qml.qrc +++ b/qml.qrc @@ -96,9 +96,7 @@ pages/SharedRingDB.qml components/effects/ImageMask.qml components/IconButton.qml - components/PassphraseDialog.qml components/PasswordDialog.qml - components/NewPasswordDialog.qml components/InputDialog.qml components/ProcessingSplash.qml components/ProgressBar.qml diff --git a/src/qt/macoshelper.h b/src/qt/macoshelper.h new file mode 100644 index 00000000..bddf70c6 --- /dev/null +++ b/src/qt/macoshelper.h @@ -0,0 +1,40 @@ +// Copyright (c) 2014-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef MACOSHELPER_H +#define MACOSHELPER_H + +class MacOSHelper +{ + MacOSHelper() {} + +public: + static bool isCapsLock(); +}; + +#endif //MACOSHELPER_H diff --git a/src/qt/macoshelper.mm b/src/qt/macoshelper.mm new file mode 100644 index 00000000..0b29f25f --- /dev/null +++ b/src/qt/macoshelper.mm @@ -0,0 +1,49 @@ +// Copyright (c) 2014-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include "macoshelper.h" + +#import +#import +#include +#include +#include + +bool MacOSHelper::isCapsLock() +{ +#ifdef __MAC_10_12 + NSUInteger flags = [NSEvent modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; + return (flags == NSEventModifierFlagCapsLock); +#else + NSUInteger flags = [NSEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask; + return (flags & NSAlphaShiftKeyMask); +#endif +} diff --git a/wizard/WizardController.qml b/wizard/WizardController.qml index f4728ac2..c209faf0 100644 --- a/wizard/WizardController.qml +++ b/wizard/WizardController.qml @@ -498,15 +498,15 @@ Rectangle { splash.close() console.log(">>> wallet passphrase needed: "); - passphraseDialog.onAcceptedCallback = function() { - walletManager.onPassphraseEntered(passphraseDialog.passphrase); + passwordDialog.onAcceptedPassphraseCallback = function() { + walletManager.onPassphraseEntered(passwordDialog.password); creatingWalletDeviceSplash(); } - passphraseDialog.onRejectedCallback = function() { + passwordDialog.onRejectedPassphraseCallback = function() { walletManager.onPassphraseEntered("", true); creatingWalletDeviceSplash(); } - passphraseDialog.open() + passwordDialog.openPassphraseDialog() } function onDeviceButtonRequest(code){ From 4b0d1dcfbf0d182cae901e4bfd97bf908fe00218 Mon Sep 17 00:00:00 2001 From: Dusan Klinec Date: Mon, 8 Jul 2019 16:09:03 +0200 Subject: [PATCH 13/20] Fixes #2275, transactionCommited txid list txid list was empty when emitted --- src/libwalletqt/Wallet.cpp | 3 ++- src/libwalletqt/Wallet.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libwalletqt/Wallet.cpp b/src/libwalletqt/Wallet.cpp index 46f843de..90172d15 100644 --- a/src/libwalletqt/Wallet.cpp +++ b/src/libwalletqt/Wallet.cpp @@ -524,7 +524,8 @@ bool Wallet::submitTxFile(const QString &fileName) const void Wallet::commitTransactionAsync(PendingTransaction *t) { m_scheduler.run([this, t] { - emit transactionCommitted(t->commit(), t, t->txid()); + auto txIdList = t->txid(); // retrieve before commit + emit transactionCommitted(t->commit(), t, txIdList); }); } diff --git a/src/libwalletqt/Wallet.h b/src/libwalletqt/Wallet.h index 95d4a954..15da7a80 100644 --- a/src/libwalletqt/Wallet.h +++ b/src/libwalletqt/Wallet.h @@ -351,7 +351,7 @@ signals: void walletCreationHeightChanged(); void deviceButtonRequest(quint64 buttonCode); void deviceButtonPressed(); - void transactionCommitted(bool status, PendingTransaction *t, QStringList txid); + void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid); void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const; // emitted when transaction is created async From d113bf1a6646a07bf2cc6dd807629246b50c816d Mon Sep 17 00:00:00 2001 From: selsta Date: Mon, 15 Jul 2019 21:56:57 +0200 Subject: [PATCH 14/20] main: fix password dialog overlay --- main.qml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/main.qml b/main.qml index c6300b52..6eb09373 100644 --- a/main.qml +++ b/main.qml @@ -1109,10 +1109,12 @@ ApplicationWindow { console.log("Hiding processing splash") splash.close(); - leftPanel.enabled = true - middlePanel.enabled = true - titleBar.enabled = true - inactiveOverlay.visible = false; + if (!passwordDialog.visible) { + leftPanel.enabled = true + middlePanel.enabled = true + titleBar.enabled = true + inactiveOverlay.visible = false; + } } // close wallet and show wizard From 96c99b250ed4557c1f396941614fb755a3487cb1 Mon Sep 17 00:00:00 2001 From: dsc Date: Wed, 3 Jul 2019 01:27:25 +0200 Subject: [PATCH 15/20] Replace Qt.labs.settings with MoneroSettings to allow for more customization --- main.cpp | 4 + main.qml | 4 +- monero-wallet-gui.pro | 6 +- src/qt/MoneroSettings.cpp | 207 ++++++++++++++++++++++++++++++++++++++ src/qt/MoneroSettings.h | 81 +++++++++++++++ 5 files changed, 298 insertions(+), 4 deletions(-) create mode 100644 src/qt/MoneroSettings.cpp create mode 100644 src/qt/MoneroSettings.h diff --git a/main.cpp b/main.cpp index a51568d2..a41b3c75 100644 --- a/main.cpp +++ b/main.cpp @@ -66,6 +66,7 @@ #include "qt/utils.h" #include "qt/mime.h" #include "src/qt/KeysFiles.h" +#include "src/qt/MoneroSettings.h" #include "qt/prices.h" // IOS exclusions @@ -224,6 +225,9 @@ int main(int argc, char *argv[]) // registering types for QML qmlRegisterType("moneroComponents.Clipboard", 1, 0, "Clipboard"); + // Temporary Qt.labs.settings replacement + qmlRegisterType("moneroComponents.Settings", 1, 0, "MoneroSettings"); + qmlRegisterUncreatableType("moneroComponents.Wallet", 1, 0, "Wallet", "Wallet can't be instantiated directly"); diff --git a/main.qml b/main.qml index fb003482..1655456c 100644 --- a/main.qml +++ b/main.qml @@ -31,11 +31,11 @@ import QtQuick.Window 2.0 import QtQuick.Controls 1.1 import QtQuick.Controls.Styles 1.1 import QtQuick.Dialogs 1.2 -import Qt.labs.settings 1.0 import moneroComponents.Wallet 1.0 import moneroComponents.PendingTransaction 1.0 import moneroComponents.NetworkType 1.0 +import moneroComponents.Settings 1.0 import "components" import "components" as MoneroComponents @@ -1347,7 +1347,7 @@ ApplicationWindow { } } - Settings { + MoneroSettings { id: persistentSettings property string language property string locale diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index 745fa1bd..d832ee12 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -69,7 +69,8 @@ HEADERS += \ src/qt/KeysFiles.h \ src/qt/utils.h \ src/qt/prices.h \ - src/qt/macoshelper.h + src/qt/macoshelper.h \ + src/qt/MoneroSettings.h SOURCES += main.cpp \ filter.cpp \ @@ -103,7 +104,8 @@ SOURCES += main.cpp \ src/qt/mime.cpp \ src/qt/KeysFiles.cpp \ src/qt/utils.cpp \ - src/qt/prices.cpp + src/qt/prices.cpp \ + src/qt/MoneroSettings.cpp CONFIG(DISABLE_PASS_STRENGTH_METER) { HEADERS -= src/zxcvbn-c/zxcvbn.h diff --git a/src/qt/MoneroSettings.cpp b/src/qt/MoneroSettings.cpp new file mode 100644 index 00000000..ba3e6876 --- /dev/null +++ b/src/qt/MoneroSettings.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +****************************************************************************/ +// Copyright (c) 2014-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include +#include +#include + +#include "src/qt/MoneroSettings.h" + +/*! + \qmlmodule moneroSettings 1.0 + \title Monero Settings QML Component + \ingroup qmlmodules + \brief Provides persistent platform-independent application settings. + + This component was introduced in order to have control over where the + configuration file is written. This is needed for Tails OS and + portable installations. + + For more information, see: https://doc.qt.io/qt-5/qml-qt-labs-settings-settings.html and + https://github.com/qt/qtdeclarative/blob/v5.12.0/src/imports/settings/qqmlsettings.cpp + + To use this module, import the module with the following line: + \code + import moneroComponents.Settings 1.0 + \endcode + + Usage: + \code + MoneroSettings { id: persistentSettings, property bool foo: true } + \endcode + + @TODO: Remove this QML component after migrating to Qt >= 5.12.0, as + `Qt.labs.settings` provides the fileName via a Q_PROPERTY +*/ + + +void MoneroSettings::load() +{ + const QMetaObject *mo = this->metaObject(); + const int offset = mo->propertyOffset(); + const int count = mo->propertyCount(); + + for (int i = offset; i < count; ++i) { + QMetaProperty property = mo->property(i); + const QVariant previousValue = readProperty(property); + const QVariant currentValue = this->m_settings->value(property.name(), previousValue); + + if (!currentValue.isNull() && (!previousValue.isValid() + || (currentValue.canConvert(previousValue.type()) && previousValue != currentValue))) { + property.write(this, currentValue); +#ifdef QT_DEBUG + qDebug() << "QQmlSettings: load" << property.name() << "setting:" << currentValue << "default:" << previousValue; +#endif + } + + // ensure that a non-existent setting gets written + // even if the property wouldn't change later + if (!this->m_settings->contains(property.name())) + this->_q_propertyChanged(); + + // setup change notifications on first load + if (!this->m_initialized && property.hasNotifySignal()) { + static const int propertyChangedIndex = mo->indexOfSlot("_q_propertyChanged()"); + int signalIndex = property.notifySignalIndex(); + QMetaObject::connect(this, signalIndex, this, propertyChangedIndex); + } + } +} + +void MoneroSettings::_q_propertyChanged() +{ + // Called on QML property change + const QMetaObject *mo = this->metaObject(); + const int offset = mo->propertyOffset(); + const int count = mo->propertyCount(); + for (int i = offset; i < count; ++i) { + const QMetaProperty &property = mo->property(i); + const QVariant value = readProperty(property); + this->m_changedProperties.insert(property.name(), value); +#ifdef QT_DEBUG + //qDebug() << "QQmlSettings: cache" << property.name() << ":" << value; +#endif + } + + if (this->m_timerId != 0) + this->killTimer(this->m_timerId); + this->m_timerId = this->startTimer(settingsWriteDelay); +} + +QVariant MoneroSettings::readProperty(const QMetaProperty &property) const +{ + QVariant var = property.read(this); + if (var.userType() == qMetaTypeId()) + var = var.value().toVariant(); + return var; +} + +void MoneroSettings::init() +{ + if (!this->m_initialized) { + this->m_settings = this->m_fileName.isEmpty() ? new QSettings() : new QSettings(this->m_fileName, QSettings::IniFormat); +#ifdef QT_DEBUG + qDebug() << "QQmlSettings: stored at" << this->m_settings->fileName(); +#endif + this->load(); + this->m_initialized = true; + } +} + +void MoneroSettings::reset() +{ + if (this->m_initialized && this->m_settings && !this->m_changedProperties.isEmpty()) + this->store(); + delete this->m_settings; +} + +void MoneroSettings::store() +{ + QHash::const_iterator it = this->m_changedProperties.constBegin(); + + while (it != this->m_changedProperties.constEnd()) { + this->m_settings->setValue(it.key(), it.value()); + +#ifdef QT_DEBUG + //qDebug() << "QQmlSettings: store" << it.key() << ":" << it.value(); +#endif + + ++it; + } + + this->m_changedProperties.clear(); +} + +void MoneroSettings::setFileName(const QString &fileName) +{ + if (fileName != this->m_fileName) { + this->reset(); + this->m_fileName = fileName; + if (this->m_initialized) + this->load(); + } +} + +QString MoneroSettings::fileName() const +{ + return this->m_fileName; +} + +void MoneroSettings::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == this->m_timerId) { + killTimer(this->m_timerId); + this->m_timerId = 0; + this->store(); + } + QObject::timerEvent(event); +} + +void MoneroSettings::componentComplete() +{ + this->init(); +} + +void MoneroSettings::classBegin() +{ +} + +MoneroSettings::MoneroSettings(QObject *parent) : + QObject(parent) +{ +} diff --git a/src/qt/MoneroSettings.h b/src/qt/MoneroSettings.h new file mode 100644 index 00000000..6de7f641 --- /dev/null +++ b/src/qt/MoneroSettings.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +****************************************************************************/ +// Copyright (c) 2014-2019, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#ifndef MONEROSETTINGS_H +#define MONEROSETTINGS_H + +#include +#include +#include +#include +#include +#include + +static const int settingsWriteDelay = 500; // ms + +class MoneroSettings : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QString fileName READ fileName WRITE setFileName FINAL) +public: + explicit MoneroSettings(QObject *parent = nullptr); + + QString fileName() const; + void setFileName(const QString &fileName); + +public slots: + void _q_propertyChanged(); + +protected: + void timerEvent(QTimerEvent *event) override; + void classBegin() override; + void componentComplete() override; + +private: + QVariant readProperty(const QMetaProperty &property) const; + void init(); + void reset(); + void load(); + void store(); + + QHash m_changedProperties; + QSettings *m_settings; + QString m_fileName = QString(""); + bool m_initialized = false; + int m_timerId = 0; +}; + +#endif // MONEROSETTINGS_H From e04d563eb4086c776aeea9f681a8aa72e52c8764 Mon Sep 17 00:00:00 2001 From: selsta Date: Tue, 16 Jul 2019 03:34:46 +0200 Subject: [PATCH 16/20] src: fix compiler warnings --- src/libwalletqt/WalletManager.cpp | 2 +- src/qt/ipc.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index 9b53ad6e..d4ec5d41 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -66,7 +66,7 @@ public: } auto tmpPass = m_mgr->m_passphrase.toStdString(); - m_mgr->m_passphrase = QString::null; + m_mgr->m_passphrase = QString(); return Monero::optional(tmpPass); } diff --git a/src/qt/ipc.cpp b/src/qt/ipc.cpp index 0753470e..ae6b061d 100644 --- a/src/qt/ipc.cpp +++ b/src/qt/ipc.cpp @@ -99,8 +99,8 @@ bool IPC::saveCommand(QString cmdString){ return true; } -bool IPC::saveCommand(const QUrl &url){; - this->saveCommand(url.toString()); +bool IPC::saveCommand(const QUrl &url){ + return this->saveCommand(url.toString()); } void IPC::handleConnection(){ From d2e7358db7ea5acd8a38aa005b450f173213d407 Mon Sep 17 00:00:00 2001 From: selsta Date: Tue, 16 Jul 2019 14:08:22 +0200 Subject: [PATCH 17/20] PasswordDialog: label improvements --- components/PasswordDialog.qml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/components/PasswordDialog.qml b/components/PasswordDialog.qml index 67ffce96..33942df9 100644 --- a/components/PasswordDialog.qml +++ b/components/PasswordDialog.qml @@ -139,10 +139,13 @@ Item { Label { text: { - var device = passwordDialogMode ? qsTr("wallet password") : qsTr("wallet device passphrase"); - (root.walletName.length > 0 ? qsTr("Please enter %1 for: ").arg(device) + root.walletName : qsTr("Please enter %1").arg(device)) + translationManager.emptyString; + if (newPasswordDialogMode) { + return qsTr("Please enter new wallet password") + translationManager.emptyString; + } else { + var device = passwordDialogMode ? qsTr("wallet password") : qsTr("wallet device passphrase"); + return (root.walletName.length > 0 ? qsTr("Please enter %1 for: ").arg(device) + root.walletName : qsTr("Please enter %1").arg(device)) + translationManager.emptyString; + } } - visible: !newPasswordDialogMode Layout.fillWidth: true font.pixelSize: 16 @@ -284,7 +287,7 @@ Item { Label { visible: !passwordDialogMode - text: qsTr("Please confirm new password") + translationManager.emptyString + text: newPasswordDialogMode ? qsTr("Please confirm new password") : qsTr("Please confirm wallet device passphrase") + translationManager.emptyString Layout.fillWidth: true font.pixelSize: 16 From b96004a101d45dc30114afb1c50bcfbc58c18454 Mon Sep 17 00:00:00 2001 From: selsta Date: Tue, 16 Jul 2019 14:17:22 +0200 Subject: [PATCH 18/20] main: don't lock on inactivity if passwordDialog is visible --- main.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/main.qml b/main.qml index 1655456c..5176afb3 100644 --- a/main.qml +++ b/main.qml @@ -2192,6 +2192,7 @@ ApplicationWindow { function checkInUserActivity() { if(rootItem.state !== "normal") return; if(!persistentSettings.lockOnUserInActivity) return; + if(passwordDialog.visible) return; // prompt password after X seconds of inactivity var epoch = Math.floor((new Date).getTime() / 1000); From 0b6132897101c4cd1618c79049d5685c5e9874fa Mon Sep 17 00:00:00 2001 From: dsc Date: Wed, 3 Jul 2019 05:13:51 +0200 Subject: [PATCH 19/20] Detect tails, support data persistence Co-authored-by: Thotbot Co-authored-by: Selsta --- Logger.cpp | 4 ++ images/tails-grey.png | Bin 0 -> 7873 bytes main.cpp | 41 +++++++++---- main.qml | 6 ++ monero-wallet-gui.pro | 6 +- pages/settings/SettingsInfo.qml | 35 +++++++++++ qml.qrc | 1 + src/qt/TailsOS.cpp | 100 ++++++++++++++++++++++++++++++++ src/qt/TailsOS.h | 23 ++++++++ src/qt/mime.cpp | 70 ---------------------- src/qt/mime.h | 36 ------------ src/qt/utils.cpp | 73 ++++++++++++++++++++++- src/qt/utils.h | 5 ++ 13 files changed, 277 insertions(+), 123 deletions(-) create mode 100644 images/tails-grey.png create mode 100644 src/qt/TailsOS.cpp create mode 100644 src/qt/TailsOS.h delete mode 100644 src/qt/mime.cpp delete mode 100644 src/qt/mime.h diff --git a/Logger.cpp b/Logger.cpp index f2e34552..ad356bcb 100644 --- a/Logger.cpp +++ b/Logger.cpp @@ -34,6 +34,7 @@ #include #include "Logger.h" +#include "src/qt/TailsOS.h" #include "wallet/api/wallet2_api.h" // default log path by OS (should be writable) @@ -66,6 +67,9 @@ const QString getLogPath(const QString logPath) { const QFileInfo fi(logPath); + if(TailsOS::detect() && TailsOS::usePersistence) + return QDir::homePath() + "/Persistent/Monero/logs/" + defaultLogName; + if(!logPath.isEmpty() && !fi.isDir()) return fi.absoluteFilePath(); else { diff --git a/images/tails-grey.png b/images/tails-grey.png new file mode 100644 index 0000000000000000000000000000000000000000..b2e18e42c5739f0336f3195634814ef389b0898b GIT binary patch literal 7873 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 Zr(dh{hEyLqu=h`96GL-@a=r6W{{=&6D!>2$ literal 0 HcmV?d00001 diff --git a/main.cpp b/main.cpp index a41b3c75..939b78f2 100644 --- a/main.cpp +++ b/main.cpp @@ -64,7 +64,7 @@ #include "MainApp.h" #include "qt/ipc.h" #include "qt/utils.h" -#include "qt/mime.h" +#include "src/qt/TailsOS.h" #include "src/qt/KeysFiles.h" #include "src/qt/MoneroSettings.h" #include "qt/prices.h" @@ -83,6 +83,7 @@ bool isAndroid = false; bool isWindows = false; bool isMac = false; bool isLinux = false; +bool isTails = false; bool isDesktop = false; bool isOpenGL = true; @@ -102,6 +103,7 @@ int main(int argc, char *argv[]) bool isWindows = true; #elif defined(Q_OS_LINUX) bool isLinux = true; + bool isTails = TailsOS::detect(); #elif defined(Q_OS_MAC) bool isMac = true; #endif @@ -123,25 +125,40 @@ int main(int argc, char *argv[]) // qDebug() << "High DPI auto scaling - enabled"; //#endif + MainApp app(argc, argv); + + app.setApplicationName("monero-core"); + app.setOrganizationDomain("getmonero.org"); + app.setOrganizationName("monero-project"); + + // Ask to enable Tails OS persistence mode, it affects: + // - Log file location + // - QML Settings file location (monero-core.conf) + // - Default wallets path + // Target directory is: ~/Persistent/Monero + if (isTails) { + if (!TailsOS::detectDataPersistence()) + TailsOS::showDataPersistenceDisabledWarning(); + else + TailsOS::askPersistence(); + } + QString moneroAccountsDir; #if defined(Q_OS_WIN) || defined(Q_OS_IOS) QStringList moneroAccountsRootDir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); #else QStringList moneroAccountsRootDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); #endif - if (!moneroAccountsRootDir.empty()) { + + if(isTails && TailsOS::usePersistence){ + moneroAccountsDir = QDir::homePath() + "/Persistent/Monero/wallets"; + } else if (!moneroAccountsRootDir.empty()) { moneroAccountsDir = moneroAccountsRootDir.at(0) + "/Monero/wallets"; } else { qCritical() << "Error: accounts root directory could not be set"; return 1; } - MainApp app(argc, argv); - - app.setApplicationName("monero-core"); - app.setOrganizationDomain("getmonero.org"); - app.setOrganizationName("monero-project"); - #if defined(Q_OS_LINUX) if (isDesktop) app.setWindowIcon(QIcon(":/images/appicon.ico")); #endif @@ -174,9 +191,8 @@ int main(int argc, char *argv[]) } qWarning().noquote() << "app startd" << "(log: " + logPath + ")"; -#ifdef Q_OS_LINUX + // Desktop entry registerXdgMime(app); -#endif IPC *ipc = new IPC(&app); QStringList posArgs = parser.positionalArguments(); @@ -317,6 +333,8 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty("walletLogPath", logPath); + engine.rootContext()->setContextProperty("tailsUsePersistence", TailsOS::usePersistence); + // Exclude daemon manager from IOS #ifndef Q_OS_IOS const QStringList arguments = (QStringList) QCoreApplication::arguments().at(0); @@ -330,7 +348,7 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty("isIOS", isIOS); engine.rootContext()->setContextProperty("isAndroid", isAndroid); engine.rootContext()->setContextProperty("isOpenGL", isOpenGL); - engine.rootContext()->setContextProperty("isLinux", isLinux); + engine.rootContext()->setContextProperty("isTails", isTails); engine.rootContext()->setContextProperty("screenWidth", geo.width()); engine.rootContext()->setContextProperty("screenHeight", geo.height()); @@ -354,6 +372,7 @@ int main(int argc, char *argv[]) accountName = "My monero Account"; engine.rootContext()->setContextProperty("defaultAccountName", accountName); + engine.rootContext()->setContextProperty("homePath", QDir::homePath()); engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath()); engine.rootContext()->setContextProperty("idealThreadCount", QThread::idealThreadCount()); diff --git a/main.qml b/main.qml index 1655456c..25536f79 100644 --- a/main.qml +++ b/main.qml @@ -1349,6 +1349,12 @@ ApplicationWindow { MoneroSettings { id: persistentSettings + fileName: { + if(isTails && tailsUsePersistence) + return homePath + "/Persistent/Monero/monero-core.conf"; + return ""; + } + property string language property string locale property string account_name diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index d832ee12..97286b05 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -65,12 +65,12 @@ HEADERS += \ MainApp.h \ src/qt/FutureScheduler.h \ src/qt/ipc.h \ - src/qt/mime.h \ src/qt/KeysFiles.h \ src/qt/utils.h \ src/qt/prices.h \ src/qt/macoshelper.h \ src/qt/MoneroSettings.h + src/qt/TailsOS.h SOURCES += main.cpp \ filter.cpp \ @@ -101,11 +101,11 @@ SOURCES += main.cpp \ MainApp.cpp \ src/qt/FutureScheduler.cpp \ src/qt/ipc.cpp \ - src/qt/mime.cpp \ src/qt/KeysFiles.cpp \ src/qt/utils.cpp \ src/qt/prices.cpp \ - src/qt/MoneroSettings.cpp + src/qt/MoneroSettings.cpp \ + src/qt/TailsOS.cpp CONFIG(DISABLE_PASS_STRENGTH_METER) { HEADERS -= src/zxcvbn-c/zxcvbn.h diff --git a/pages/settings/SettingsInfo.qml b/pages/settings/SettingsInfo.qml index 938bd122..80e6a129 100644 --- a/pages/settings/SettingsInfo.qml +++ b/pages/settings/SettingsInfo.qml @@ -326,6 +326,41 @@ Rectangle { font.pixelSize: 14 text: isOpenGL ? "OpenGL" : "Low graphics mode" } + + Rectangle { + visible: isTails + height: 1 + Layout.topMargin: 2 + Layout.bottomMargin: 2 + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + Rectangle { + visible: isTails + height: 1 + Layout.topMargin: 2 + Layout.bottomMargin: 2 + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + MoneroComponents.TextBlock { + visible: isTails + Layout.fillWidth: true + font.pixelSize: 14 + text: qsTr("Tails: ") + translationManager.emptyString + } + + MoneroComponents.TextBlock { + visible: isTails + Layout.fillWidth: true + color: MoneroComponents.Style.dimmedFontColor + font.pixelSize: 14 + text: tailsUsePersistence ? qsTr("persistent") + translationManager.emptyString : qsTr("persistence disabled") + translationManager.emptyString; + } } // Copy info to clipboard diff --git a/qml.qrc b/qml.qrc index 3bced000..29f68f1a 100644 --- a/qml.qrc +++ b/qml.qrc @@ -251,5 +251,6 @@ images/copy.svg images/edit.svg images/arrow-right-in-circle-outline-medium-white.svg + images/tails-grey.png diff --git a/src/qt/TailsOS.cpp b/src/qt/TailsOS.cpp new file mode 100644 index 00000000..df2fa92b --- /dev/null +++ b/src/qt/TailsOS.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#include "TailsOS.h" +#include "utils.h" + +bool TailsOS::usePersistence = false; +QString TailsOS::tailsPathData = QString("/live/persistence/TailsData_unlocked/"); + +bool TailsOS::detect() +{ + if (!fileExists("/etc/os-release")) + return false; + + QByteArray data = fileOpen("/etc/os-release"); + QRegularExpression re("TAILS_PRODUCT_NAME=\"Tails\""); + QRegularExpressionMatch os_match = re.match(data); + bool matched = os_match.hasMatch(); + +#ifdef QT_DEBUG + if (matched) + qDebug() << "Tails OS detected"; +#endif + + return matched; +} + +bool TailsOS::detectDataPersistence() +{ + return QDir(QDir::homePath() + "/Persistent").exists(); +} + +bool TailsOS::detectDotPersistence() +{ + return QDir(tailsPathData + "dotfiles").exists(); +} + +void TailsOS::showDataPersistenceDisabledWarning() +{ + QMessageBox msgBox; + msgBox.setText(QObject::tr("Warning: persistence disabled")); + msgBox.setWindowTitle(QObject::tr("Warning: persistence disabled")); + msgBox.setInformativeText( + QObject::tr("Monero GUI has detected that Tails persistence is " + "currently disabled. Any configurations you make inside " + "the Monero GUI will not be saved." + "\n\n" + "In addition, make sure to not save your wallet on the " + "filesystem, as it will be lost at shutdown." + "\n\n" + "To enable Tails persistence, setup an encrypted volume " + "and restart Tails. To gain a startup menu item, " + "enable the Tails \"dotfiles\" feature.")); + + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIconPixmap(QPixmap(":/images/tails-grey.png")); + msgBox.exec(); +} + +void TailsOS::askPersistence() +{ + QMessageBox msgBox; + msgBox.setWindowTitle(QObject::tr("Monero GUI")); + msgBox.setText(QObject::tr("Use Tails persistence?")); + msgBox.setInformativeText( + QObject::tr("Persist wallet files and configuration on the encrypted volume?" + "\n\n" + "In addition, you can enable Tails dotfiles persistence " + "to gain a start menu entry.\n")); + + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + msgBox.setIconPixmap(QPixmap(":/images/tails-grey.png")); + TailsOS::usePersistence = (msgBox.exec() == QMessageBox::Yes); +} + +void TailsOS::persistXdgMime(QString filePath, QString data) +{ + QFileInfo file(filePath); + QString tailsPath = tailsPathData + "dotfiles/.local/share/applications/"; + + // write to persistent volume +#ifdef QT_DEBUG + qDebug() << "Writing xdg mime: " << tailsPath + file.fileName(); +#endif + + QDir().mkpath(tailsPath); // ensure directory exists + fileWrite(tailsPath + file.fileName(), data); + + // write to current session +#ifdef QT_DEBUG + qDebug() << "Writing xdg mime: " << file.filePath(); +#endif + + QDir().mkpath(file.path()); // ensure directory exists + fileWrite(file.filePath(), data); +} diff --git a/src/qt/TailsOS.h b/src/qt/TailsOS.h new file mode 100644 index 00000000..05d5ebd1 --- /dev/null +++ b/src/qt/TailsOS.h @@ -0,0 +1,23 @@ +#ifndef TAILSOS_H +#define TAILSOS_H + +#include + + +class TailsOS +{ +public: + TailsOS(); + static bool detect(); + static bool detectDataPersistence(); + static bool detectDotPersistence(); + + static void showDataPersistenceDisabledWarning(); + static void askPersistence(); + static void persistXdgMime(QString filePath, QString data); + + static bool usePersistence; + static QString tailsPathData; +}; + +#endif // TAILSOS_H diff --git a/src/qt/mime.cpp b/src/qt/mime.cpp deleted file mode 100644 index 6daf4de6..00000000 --- a/src/qt/mime.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2014-2019, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include -#include - -#include "mime.h" -#include "utils.h" - -void registerXdgMime(QApplication &app){ - // MacOS handled via Info.plist - // Windows handled in the installer by rbrunner7 - - QString xdg = QString( - "[Desktop Entry]\n" - "Name=Monero GUI\n" - "GenericName=Monero-GUI\n" - "X-GNOME-FullName=Monero-GUI\n" - "Comment=Monero GUI\n" - "Keywords=Monero;\n" - "Exec=%1 %u\n" - "Terminal=false\n" - "Type=Application\n" - "Icon=monero\n" - "Categories=Network;GNOME;Qt;\n" - "MimeType=x-scheme-handler/monero;x-scheme-handler/moneroseed\n" - "StartupNotify=true\n" - "X-GNOME-Bugzilla-Bugzilla=GNOME\n" - "X-GNOME-UsesNotifications=true\n" - ).arg(app.applicationFilePath()); - - QString appPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); - QString filePath = QString("%1/monero-gui.desktop").arg(appPath); - - qDebug() << QString("Writing %1").arg(filePath); - QFile file(filePath); - if(file.open(QIODevice::WriteOnly)){ - QTextStream out(&file); out << xdg << endl; - file.close(); - } - else - file.close(); -} diff --git a/src/qt/mime.h b/src/qt/mime.h deleted file mode 100644 index b79d43a3..00000000 --- a/src/qt/mime.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2014-2019, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef MIME_H -#define MIME_H - -#include - -void registerXdgMime(QApplication &app); - -#endif // MIME_H diff --git a/src/qt/utils.cpp b/src/qt/utils.cpp index 2407a50e..4418a050 100644 --- a/src/qt/utils.cpp +++ b/src/qt/utils.cpp @@ -27,15 +27,35 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include "src/qt/TailsOS.h" #include "utils.h" bool fileExists(QString path) { QFileInfo check_file(path); - if (check_file.exists() && check_file.isFile()) + return check_file.exists() && check_file.isFile(); +} + +QByteArray fileOpen(QString path) { + QFile file(path); + if(!file.open(QFile::ReadOnly | QFile::Text)) + return QByteArray(); + + QByteArray data = file.readAll(); + file.close(); + return data; +} + +bool fileWrite(QString path, QString data) { + QFile file(path); + if(file.open(QIODevice::WriteOnly)){ + QTextStream out(&file); out << data << endl; + file.close(); return true; - else - return false; + } + + return false; } QString getAccountName(){ @@ -47,6 +67,53 @@ QString getAccountName(){ return accountName; } +QString xdgMime(QApplication &app){ + return QString( + "[Desktop Entry]\n" + "Name=Monero GUI\n" + "GenericName=Monero-GUI\n" + "X-GNOME-FullName=Monero-GUI\n" + "Comment=Monero GUI\n" + "Keywords=Monero;\n" + "Exec=%1 %u\n" + "Terminal=false\n" + "Type=Application\n" + "Icon=monero\n" + "Categories=Network;GNOME;Qt;\n" + "MimeType=x-scheme-handler/monero;x-scheme-handler/moneroseed\n" + "StartupNotify=true\n" + "X-GNOME-Bugzilla-Bugzilla=GNOME\n" + "X-GNOME-UsesNotifications=true\n" + ).arg(app.applicationFilePath()); +} + +void registerXdgMime(QApplication &app){ +#ifdef Q_OS_LINUX + // Register desktop entry + // - MacOS handled via Info.plist + // - Windows handled in the installer by rbrunner7 + // - Linux written to `QStandardPaths::ApplicationsLocation` + // - Tails written to persistent dotfiles + QString mime = xdgMime(app); + QString appPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); + QString filePath = QString("%1/monero-gui.desktop").arg(appPath); + + if (TailsOS::detect() && TailsOS::detectDotPersistence() && TailsOS::usePersistence) { + TailsOS::persistXdgMime(filePath, mime); + return; + } + + QFileInfo file(filePath); + QDir().mkpath(file.path()); // ensure directory exists + +#ifdef QT_DEBUG + qDebug() << "Writing xdg mime: " << filePath; +#endif + + fileWrite(filePath, mime); +#endif +} + QString randomUserAgent(){ QStringList urand; urand << "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1" diff --git a/src/qt/utils.h b/src/qt/utils.h index c18ffdc1..f48dff97 100644 --- a/src/qt/utils.h +++ b/src/qt/utils.h @@ -31,9 +31,14 @@ #include #include +#include bool fileExists(QString path); +QByteArray fileOpen(QString path); +bool fileWrite(QString path, QString data); QString getAccountName(); +QString xdgMime(QApplication &app); +void registerXdgMime(QApplication &app); const static QRegExp reURI = QRegExp("^\\w+:\\/\\/([\\w+\\-?\\-_\\-=\\-&]+)"); QString randomUserAgent(); From cddba5684880fb90f8c15cd030e0d1bab2853e2c Mon Sep 17 00:00:00 2001 From: dsc Date: Tue, 16 Jul 2019 18:47:12 +0200 Subject: [PATCH 20/20] Fix project file --- monero-wallet-gui.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index 97286b05..737a6875 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -69,7 +69,7 @@ HEADERS += \ src/qt/utils.h \ src/qt/prices.h \ src/qt/macoshelper.h \ - src/qt/MoneroSettings.h + src/qt/MoneroSettings.h \ src/qt/TailsOS.h SOURCES += main.cpp \