Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0faddf964 | ||
|
|
b54127e997 | ||
|
|
ccd8eb1c3a | ||
|
|
d5b165bde2 | ||
|
|
c978027933 | ||
|
|
9deec4dad0 | ||
|
|
e306992ce8 | ||
|
|
19daa074ca | ||
|
|
1c62edeff4 | ||
|
|
34439af67e | ||
|
|
e1e862bce4 | ||
|
|
0b2e74cdb5 | ||
|
|
3fee17e564 | ||
|
|
1e7d8293cb | ||
|
|
a3fc6754e0 | ||
|
|
8354c251c5 | ||
|
|
8f5053bd61 | ||
|
|
92b0a115f4 | ||
|
|
6a3e1aaf40 | ||
|
|
c32e11d3e8 | ||
|
|
9580c19da3 | ||
|
|
27532dc1bf | ||
|
|
c0e0626b84 | ||
|
|
c6d5c5dc3a | ||
|
|
749460fb46 | ||
|
|
c9ee76c2ee | ||
|
|
94dbf179d5 | ||
|
|
7deecbfdf6 | ||
|
|
29a742ba26 | ||
|
|
47559e51be | ||
|
|
f64dcde600 | ||
|
|
52c090b82f | ||
|
|
d18af7da72 | ||
|
|
333c9ee311 | ||
|
|
ff4de8e8f7 | ||
|
|
ef5d855950 | ||
|
|
e6f30578c0 | ||
|
|
7f0c19950b | ||
|
|
1580c3a574 | ||
|
|
c8f4355e15 | ||
|
|
39561f8ead | ||
|
|
b15dbbb9b0 | ||
|
|
a73a4363ec | ||
|
|
55cbc562b6 | ||
|
|
e6c4c32d01 | ||
|
|
e36d4a918f | ||
|
|
0355ca2747 | ||
|
|
c946905907 | ||
|
|
a8bd2ab77e | ||
|
|
149e373367 | ||
|
|
fa3c8b5f89 | ||
|
|
e0f6577afd | ||
|
|
43f0854de9 | ||
|
|
f38e460842 | ||
|
|
0de1ba9f51 | ||
|
|
66e769603c | ||
|
|
15038850c2 | ||
|
|
efc9ad45e4 | ||
|
|
754a968706 | ||
|
|
c20a0ef928 | ||
|
|
eab98e3a48 | ||
|
|
2a8960bc2c | ||
|
|
fe68f59763 | ||
|
|
80e8dd6aef | ||
|
|
c82bd94bc3 | ||
|
|
28ca9503df | ||
|
|
73cc400ae8 | ||
|
|
8b7438ace2 | ||
|
|
210248e6ef | ||
|
|
f82948f4b0 | ||
|
|
da4e0dbf0f | ||
|
|
9dd3f4fecb | ||
|
|
abbe042c8a | ||
|
|
72a3b346bb | ||
|
|
d662e46146 | ||
|
|
bbc9ca8f08 | ||
|
|
f7271d1c7b | ||
|
|
afe1ae9b9c | ||
|
|
7f5b8ea0ad | ||
|
|
aa5000f556 | ||
|
|
8b6978b2a5 | ||
|
|
86d21a34ba | ||
|
|
4354a76df9 | ||
|
|
b616a14f88 | ||
|
|
7536e922e9 | ||
|
|
5bf0dd9684 | ||
|
|
cdf4ce2d6f | ||
|
|
37a8bcb525 | ||
|
|
2f8c0ca499 | ||
|
|
38612c1285 | ||
|
|
1d3a201077 | ||
|
|
a91a4f51ab | ||
|
|
52fbbae484 | ||
|
|
8f70fb4f79 | ||
|
|
c439d6814b | ||
|
|
f26f1469ca | ||
|
|
487e706f06 | ||
|
|
503c1af5f8 | ||
|
|
ca79525fdb | ||
|
|
7db5de082d | ||
|
|
ce6cc47afe | ||
|
|
a10a94f51c | ||
|
|
1833c16e39 | ||
|
|
d7207bfde3 | ||
|
|
5265e52b8b | ||
|
|
b6fdb709ba | ||
|
|
9aef3bab33 | ||
|
|
4b55197e18 | ||
|
|
11617d2f76 | ||
|
|
765e93cfd0 | ||
|
|
d31e661cd1 | ||
|
|
7f9b28c05f | ||
|
|
4a7ccd8d82 | ||
|
|
be954cc2c4 | ||
|
|
387e643ae9 | ||
|
|
6f71d47806 | ||
|
|
135c970ad9 | ||
|
|
fd5d1f584e | ||
|
|
2195c67f58 | ||
|
|
5c13624596 | ||
|
|
cfdba59584 | ||
|
|
4141832a4d | ||
|
|
5f183da6e3 | ||
|
|
30c54b1c6e | ||
|
|
c1da3d1c97 | ||
|
|
cbd03229dd | ||
|
|
2fc1b287dd | ||
|
|
7c8c6a116f | ||
|
|
ef93139a80 | ||
|
|
1b7844ec34 | ||
|
|
8f63e8870f | ||
|
|
286c75aa5b | ||
|
|
0a4d65dd99 | ||
|
|
83ccadb6a8 | ||
|
|
eae7eff9db | ||
|
|
2102e4be0d | ||
|
|
02eec351b9 | ||
|
|
ce4cb6512d | ||
|
|
3d24300963 | ||
|
|
9748974ce0 | ||
|
|
120b5285fb | ||
|
|
b022735506 | ||
|
|
33c1a6f4fc | ||
|
|
4b0dcb95bf | ||
|
|
fdb7f806fa | ||
|
|
a810bf3eb7 | ||
|
|
a99eef68f5 | ||
|
|
7ebdb884a1 | ||
|
|
c7f9ac926b | ||
|
|
9b98e0a2f5 | ||
|
|
3835387eea | ||
|
|
238b1b777f | ||
|
|
3af99e91e4 | ||
|
|
07ecca5af4 | ||
|
|
b245d0af7a | ||
|
|
585fb2810d | ||
|
|
0784532001 | ||
|
|
dcbdae0954 | ||
|
|
2bef74fe8a | ||
|
|
81d5dd1cae | ||
|
|
50767570f0 | ||
|
|
1d5b940349 | ||
|
|
3563d44d99 | ||
|
|
ffceda9159 | ||
|
|
a75a0fb8c5 | ||
|
|
5fa64c34ec | ||
|
|
376421667a | ||
|
|
8a73fd241e | ||
|
|
9760886eff | ||
|
|
34089599cd | ||
|
|
4a7a98034e | ||
|
|
79f78f48e2 | ||
|
|
fee81ba210 | ||
|
|
a078705ec6 | ||
|
|
de6a9b6779 | ||
|
|
3f13a5c9a4 | ||
|
|
fb7470a2a6 | ||
|
|
e2c6ae6472 | ||
|
|
ea25b71ca6 | ||
|
|
5ebe3f5092 | ||
|
|
8e4124f06a | ||
|
|
e47fd5f760 | ||
|
|
8767c71107 | ||
|
|
2156a6533b | ||
|
|
7eac690d44 | ||
|
|
31adc6cfd6 | ||
|
|
d582fd338d | ||
|
|
eed51e3ffa | ||
|
|
6a889bdaa1 | ||
|
|
5f27a45910 | ||
|
|
e1258e0ada | ||
|
|
925cced7d7 | ||
|
|
94083e746f | ||
|
|
a49d579bd3 | ||
|
|
6ed7fcec67 | ||
|
|
6d7db135e7 | ||
|
|
042400b83f | ||
|
|
df54439972 | ||
|
|
86252506f0 | ||
|
|
a9fea2462b | ||
|
|
58987b2ec6 | ||
|
|
aecd218d15 | ||
|
|
856843b52a | ||
|
|
fc740a89ab | ||
|
|
0643607ec3 | ||
|
|
5872bc8a2b | ||
|
|
d07da76383 | ||
|
|
dede10ea1a | ||
|
|
d21c22b444 | ||
|
|
b5fafb55c9 | ||
|
|
48aab5c6e5 |
59
.github/workflows/build.yml
vendored
@@ -6,47 +6,78 @@ jobs:
|
|||||||
build-macos:
|
build-macos:
|
||||||
|
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- name: update brew and install dependencies
|
- name: update brew and install dependencies
|
||||||
run: brew update && brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf qt5
|
run: brew update && brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf qt5 libgcrypt
|
||||||
- name: build
|
- name: build
|
||||||
run: export PATH=$PATH:/usr/local/opt/qt/bin && ./build.sh
|
run: export PATH=$PATH:/usr/local/opt/qt/bin && ./build.sh
|
||||||
- name: test qml
|
- name: test qml
|
||||||
run: build/release/bin/monero-wallet-gui.app/Contents/MacOS/monero-wallet-gui --test-qml
|
run: build/release/bin/monero-wallet-gui.app/Contents/MacOS/monero-wallet-gui --test-qml
|
||||||
|
|
||||||
build-ubuntu:
|
build-ubuntu:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- name: "qmake"
|
||||||
|
cmd: "./build.sh"
|
||||||
|
- name: "cmake"
|
||||||
|
cmd: "USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release -j3"
|
||||||
|
name: build-ubuntu-${{ matrix.toolchain.name }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
- name: remove bundled boost
|
- name: remove bundled boost
|
||||||
run: sudo rm -rf /usr/local/share/boost
|
run: sudo rm -rf /usr/local/share/boost
|
||||||
|
- name: set apt conf
|
||||||
|
run: |
|
||||||
|
echo "Acquire::Retries \"3\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||||
|
echo "Acquire::http::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||||
|
echo "Acquire::ftp::Timeout \"120\";" | sudo tee -a /etc/apt/apt.conf.d/80-custom
|
||||||
- name: update apt
|
- name: update apt
|
||||||
run: sudo apt update
|
run: sudo apt update
|
||||||
- name: install monero dependencies
|
- name: install monero dependencies
|
||||||
run: sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev
|
run: sudo apt -y install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler
|
||||||
- name: install monero gui dependencies
|
- name: install monero gui dependencies
|
||||||
run: sudo apt -y install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-folderlistmodel qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev xvfb
|
run: sudo apt -y install qtbase5-dev qt5-default qtdeclarative5-dev qml-module-qtquick-controls qml-module-qtquick-controls2 qml-module-qtquick-dialogs qml-module-qtquick-xmllistmodel qml-module-qt-labs-settings qml-module-qt-labs-folderlistmodel qttools5-dev-tools qml-module-qtquick-templates2 libqt5svg5-dev libgcrypt20-dev xvfb
|
||||||
- name: build
|
- name: build
|
||||||
run: ./build.sh
|
run: ${{ matrix.toolchain.cmd }}
|
||||||
- name: test qml
|
- name: test qml
|
||||||
run: xvfb-run -a build/release/bin/monero-wallet-gui --test-qml
|
run: xvfb-run -a build/release/bin/monero-wallet-gui --test-qml
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
|
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- name: "qmake"
|
||||||
|
cmd: "./build.sh release"
|
||||||
|
- name: "cmake"
|
||||||
|
cmd: "USE_SINGLE_BUILDDIR=ON DEV_MODE=ON make release-win64 -j2"
|
||||||
|
name: build-windows-${{ matrix.toolchain.name }}
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: numworks/setup-msys2@v1
|
with:
|
||||||
- name: update pacman
|
submodules: recursive
|
||||||
run: msys2do pacman -Syu --noconfirm
|
- uses: eine/setup-msys2@v1
|
||||||
- name: install monero dependencies
|
with:
|
||||||
run: msys2do pacman -S --noconfirm mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git mingw-w64-x86_64-qt5
|
update: true
|
||||||
|
install: mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb git mingw-w64-x86_64-qt5 mingw-w64-x86_64-libgcrypt
|
||||||
- name: build
|
- name: build
|
||||||
run: msys2do ./build.sh release
|
run: |
|
||||||
|
sed -i 's/CONFIG\ +=\ qtquickcompiler//' monero-wallet-gui.pro
|
||||||
|
${{ matrix.toolchain.cmd }}
|
||||||
- name: test qml
|
- name: test qml
|
||||||
run: msys2do build/release/bin/monero-wallet-gui --test-qml
|
run: build/release/bin/monero-wallet-gui --test-qml
|
||||||
|
|||||||
1
.gitignore
vendored
@@ -13,6 +13,7 @@ monero-wallet-gui_plugin_import.cpp
|
|||||||
monero-wallet-gui_qml_plugin_import.cpp
|
monero-wallet-gui_qml_plugin_import.cpp
|
||||||
*.qmlc
|
*.qmlc
|
||||||
*.jsc
|
*.jsc
|
||||||
|
qml_qmlcache.qrc
|
||||||
|
|
||||||
### Vim ###
|
### Vim ###
|
||||||
# Swap
|
# Swap
|
||||||
|
|||||||
168
CMakeLists.txt
@@ -8,15 +8,19 @@ set(VERSION_MINOR "0")
|
|||||||
set(VERSION_REVISION "3")
|
set(VERSION_REVISION "3")
|
||||||
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
|
set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}")
|
||||||
|
|
||||||
# libwallet requires a static build, so we only allow static compilation
|
option(STATIC "Link libraries statically, requires static Qt")
|
||||||
set(STATIC ON)
|
|
||||||
|
|
||||||
option(USE_DEVICE_TREZOR ON)
|
option(USE_DEVICE_TREZOR "Trezor support compilation" ON)
|
||||||
option(ENABLE_PASS_STRENGTH_METER "Disable zxcvbn" OFF)
|
option(ENABLE_PASS_STRENGTH_METER "Disable zxcvbn" OFF)
|
||||||
option(WITH_SCANNER "Enable webcam QR scanner" OFF)
|
option(WITH_SCANNER "Enable webcam QR scanner" OFF)
|
||||||
option(DEV_MODE "Checkout latest monero master on build" OFF)
|
option(DEV_MODE "Checkout latest monero master on build" OFF)
|
||||||
|
|
||||||
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
|
list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
include(CheckCCompilerFlag)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
include(CheckLinkerFlag)
|
||||||
|
include(FindCcache)
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
@@ -27,7 +31,7 @@ endif()
|
|||||||
set(BUILD_GUI_DEPS ON)
|
set(BUILD_GUI_DEPS ON)
|
||||||
set(ARCH "x86-64")
|
set(ARCH "x86-64")
|
||||||
set(BUILD_64 ON)
|
set(BUILD_64 ON)
|
||||||
set(INSTALL_VENDORED_LIBUNBOUND=ON)
|
set(INSTALL_VENDORED_LIBUNBOUND ${STATIC})
|
||||||
|
|
||||||
function (add_c_flag_if_supported flag var)
|
function (add_c_flag_if_supported flag var)
|
||||||
string(REPLACE "-" "_" supported ${flag}_c)
|
string(REPLACE "-" "_" supported ${flag}_c)
|
||||||
@@ -166,7 +170,7 @@ message(STATUS "libhidapi: libraries at ${HIDAPI_LIBRARIES}")
|
|||||||
if(DEBUG)
|
if(DEBUG)
|
||||||
set(Boost_DEBUG ON)
|
set(Boost_DEBUG ON)
|
||||||
endif()
|
endif()
|
||||||
find_package(Boost 1.62 REQUIRED COMPONENTS
|
find_package(Boost 1.58 REQUIRED COMPONENTS
|
||||||
system
|
system
|
||||||
filesystem
|
filesystem
|
||||||
thread
|
thread
|
||||||
@@ -194,21 +198,6 @@ if(MINGW)
|
|||||||
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
|
string(REGEX MATCH "^[^/]:/[^/]*" msys2_install_path "${CMAKE_C_COMPILER}")
|
||||||
message(STATUS "MSYS location: ${msys2_install_path}")
|
message(STATUS "MSYS location: ${msys2_install_path}")
|
||||||
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
|
set(CMAKE_INCLUDE_PATH "${msys2_install_path}/mingw${ARCH_WIDTH}/include")
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/Qt/labs/folderlistmodel")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/Qt/labs/settings")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtGraphicalEffects")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtGraphicalEffects/private")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtMultimedia")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick.2")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Controls")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Controls.2")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Dialogs")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Dialogs/Private")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Layouts")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/PrivateWidgets")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Templates.2")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/Window.2")
|
|
||||||
link_directories("${msys2_install_path}/mingw${ARCH_WIDTH}/qml/QtQuick/XmlListModel")
|
|
||||||
# This is necessary because otherwise CMake will make Boost libraries -lfoo
|
# This is necessary because otherwise CMake will make Boost libraries -lfoo
|
||||||
# rather than a full path. Unfortunately, this makes the shared libraries get
|
# rather than a full path. Unfortunately, this makes the shared libraries get
|
||||||
# linked due to a bug in CMake which misses putting -static flags around the
|
# linked due to a bug in CMake which misses putting -static flags around the
|
||||||
@@ -221,35 +210,46 @@ endif()
|
|||||||
set(QT5_LIBRARIES
|
set(QT5_LIBRARIES
|
||||||
Qt5Core
|
Qt5Core
|
||||||
Qt5Quick
|
Qt5Quick
|
||||||
Qt5QuickControls2
|
|
||||||
Qt5Widgets
|
Qt5Widgets
|
||||||
Qt5Gui
|
Qt5Gui
|
||||||
Qt5Network
|
Qt5Network
|
||||||
Qt5Qml
|
Qt5Qml
|
||||||
Qt5Multimedia
|
|
||||||
Qt5Xml
|
|
||||||
Qt5XmlPatterns
|
|
||||||
Qt5Svg
|
Qt5Svg
|
||||||
|
Qt5Xml
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(WITH_SCANNER)
|
||||||
|
list(APPEND QT5_LIBRARIES Qt5Multimedia)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO: drop this once we switch to Qt 5.14+
|
||||||
|
find_package(Qt5QmlModels QUIET)
|
||||||
|
if(Qt5QmlModels_FOUND)
|
||||||
|
list(APPEND QT5_LIBRARIES Qt5QmlModels)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# TODO: drop this once we switch to Qt 5.12+
|
||||||
|
find_package(Qt5XmlPatterns QUIET)
|
||||||
|
if(Qt5XmlPatterns_FOUND)
|
||||||
|
list(APPEND QT5_LIBRARIES Qt5XmlPatterns)
|
||||||
|
endif()
|
||||||
|
|
||||||
foreach(QT5_MODULE ${QT5_LIBRARIES})
|
foreach(QT5_MODULE ${QT5_LIBRARIES})
|
||||||
find_package(${QT5_MODULE} REQUIRED)
|
find_package(${QT5_MODULE} REQUIRED)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig REQUIRED)
|
||||||
if(PKGCONFIG_FOUND)
|
pkg_check_modules(QT5_PKG_CONFIG REQUIRED ${QT5_LIBRARIES})
|
||||||
pkg_check_modules(QT5_PKG_CONFIG ${QT5_LIBRARIES})
|
|
||||||
|
|
||||||
if(QT5_PKG_CONFIG_FOUND)
|
if(QT5_PKG_CONFIG_FOUND)
|
||||||
set(QT5_PKG_CONFIG "QT5_PKG_CONFIG")
|
set(QT5_PKG_CONFIG "QT5_PKG_CONFIG")
|
||||||
if(STATIC)
|
if(STATIC)
|
||||||
set(QT5_PKG_CONFIG "${QT5_PKG_CONFIG}_STATIC")
|
set(QT5_PKG_CONFIG "${QT5_PKG_CONFIG}_STATIC")
|
||||||
endif()
|
|
||||||
|
|
||||||
set(QT5_LIBRARIES ${${QT5_PKG_CONFIG}_LIBRARIES} ${${QT5_PKG_CONFIG}_LDFLAGS_OTHER})
|
|
||||||
include_directories(${${QT5_PKG_CONFIG}_INCLUDE_DIRS})
|
|
||||||
link_directories(${${QT5_PKG_CONFIG}_LIBRARY_DIRS})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(QT5_LIBRARIES ${${QT5_PKG_CONFIG}_LIBRARIES} ${${QT5_PKG_CONFIG}_LDFLAGS_OTHER})
|
||||||
|
include_directories(${${QT5_PKG_CONFIG}_INCLUDE_DIRS})
|
||||||
|
link_directories(${${QT5_PKG_CONFIG}_LIBRARY_DIRS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
list(APPEND QT5_LIBRARIES
|
list(APPEND QT5_LIBRARIES
|
||||||
@@ -260,37 +260,91 @@ list(APPEND QT5_LIBRARIES
|
|||||||
)
|
)
|
||||||
|
|
||||||
if(STATIC)
|
if(STATIC)
|
||||||
set(QT5_LIBRARIES
|
set(QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/folderlistmodel)
|
||||||
qtquickcontrols2plugin # has to be the first one, depends on Qt5QuickControls2
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/Qt/labs/settings)
|
||||||
${QT5_LIBRARIES}
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtGraphicalEffects)
|
||||||
declarative_multimedia
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtGraphicalEffects/private)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtMultimedia)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick.2)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Controls)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Controls.2)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Dialogs)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Dialogs/Private)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Layouts)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/PrivateWidgets)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Templates.2)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/Window.2)
|
||||||
|
list(APPEND QT5_EXTRA_PATHS ${QT5_PKG_CONFIG_Qt5Qml_PREFIX}/qml/QtQuick/XmlListModel)
|
||||||
|
|
||||||
|
set(QT5_EXTRA_LIBRARIES_LIST
|
||||||
|
qtquicktemplates2plugin
|
||||||
|
Qt5QuickTemplates2
|
||||||
|
qtquickcontrols2plugin
|
||||||
|
Qt5QuickControls2
|
||||||
dialogplugin
|
dialogplugin
|
||||||
dialogsprivateplugin
|
dialogsprivateplugin
|
||||||
qmlfolderlistmodelplugin
|
qmlfolderlistmodelplugin
|
||||||
qmlsettingsplugin
|
qmlsettingsplugin
|
||||||
qmlxmllistmodelplugin
|
qmlxmllistmodelplugin
|
||||||
qquicklayoutsplugin
|
qquicklayoutsplugin
|
||||||
|
)
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
list(APPEND QT5_EXTRA_LIBRARIES_LIST
|
||||||
|
Qt5XcbQpa
|
||||||
|
xcb-static
|
||||||
|
Qt5ServiceSupport
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WITH_SCANNER)
|
||||||
|
list(APPEND QT5_EXTRA_LIBRARIES_LIST
|
||||||
|
declarative_multimedia
|
||||||
|
Qt5MultimediaQuick_p
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND QT5_EXTRA_LIBRARIES_LIST
|
||||||
Qt5EventDispatcherSupport
|
Qt5EventDispatcherSupport
|
||||||
Qt5FontDatabaseSupport
|
Qt5FontDatabaseSupport
|
||||||
Qt5MultimediaQuick_p
|
|
||||||
Qt5PacketProtocol
|
Qt5PacketProtocol
|
||||||
Qt5ThemeSupport
|
Qt5ThemeSupport
|
||||||
qtgraphicaleffectsplugin
|
qtgraphicaleffectsplugin
|
||||||
qtgraphicaleffectsprivate
|
qtgraphicaleffectsprivate
|
||||||
qtquick2plugin
|
qtquick2plugin
|
||||||
qtquickcontrolsplugin
|
qtquickcontrolsplugin
|
||||||
qtquicktemplates2plugin
|
|
||||||
widgetsplugin
|
widgetsplugin
|
||||||
windowplugin
|
windowplugin
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(QT5_EXTRA_LIBRARIES)
|
||||||
|
foreach(LIBRARY ${QT5_EXTRA_LIBRARIES_LIST})
|
||||||
|
find_library(${LIBRARY}_LIBRARY ${LIBRARY} PATHS ${QT5_EXTRA_PATHS})
|
||||||
|
list(APPEND QT5_EXTRA_LIBRARIES ${${LIBRARY}_LIBRARY})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
list(APPEND QT5_LIBRARIES qtfreetype)
|
list(APPEND QT5_EXTRA_LIBRARIES qtfreetype)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
list(APPEND QT5_LIBRARIES D3D11 Dwrite D2d1)
|
list(APPEND QT5_EXTRA_LIBRARIES D3D11 Dwrite D2d1)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(QT5_LIBRARIES
|
||||||
|
${QT5_EXTRA_LIBRARIES}
|
||||||
|
${QT5_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
pkg_check_modules(X11_XCB REQUIRED x11-xcb)
|
||||||
|
pkg_check_modules(FONTCONFIG REQUIRED fontconfig)
|
||||||
|
|
||||||
|
list(APPEND QT5_LIBRARIES
|
||||||
|
${FONTCONFIG_STATIC_LIBRARIES}
|
||||||
|
${X11_XCB_STATIC_LIBRARIES}
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "Using Boost include dir at ${Boost_INCLUDE_DIRS}")
|
message(STATUS "Using Boost include dir at ${Boost_INCLUDE_DIRS}")
|
||||||
@@ -384,21 +438,35 @@ if (noexecheap_SUPPORTED)
|
|||||||
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap")
|
set(LD_SECURITY_FLAGS "${LD_SECURITY_FLAGS} -Wl,-z,noexecheap")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# some windows linker bits
|
||||||
|
if (WIN32)
|
||||||
|
add_linker_flag_if_supported(-Wl,--dynamicbase LD_SECURITY_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-Wl,--nxcompat LD_SECURITY_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-Wl,--high-entropy-va LD_SECURITY_FLAGS)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(STATIC)
|
if(STATIC)
|
||||||
|
add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
|
||||||
|
add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
add_linker_flag_if_supported(-static STATIC_FLAGS)
|
add_linker_flag_if_supported(-static STATIC_FLAGS)
|
||||||
else()
|
|
||||||
add_linker_flag_if_supported(-static-libgcc STATIC_FLAGS)
|
|
||||||
add_linker_flag_if_supported(-static-libstdc++ STATIC_FLAGS)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# With GCC 6.1.1 the compiled binary malfunctions due to aliasing. Until that
|
||||||
|
# is fixed in the code (Issue #847), force compiler to be conservative.
|
||||||
|
add_c_flag_if_supported(-fno-strict-aliasing C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-fno-strict-aliasing CXX_SECURITY_FLAGS)
|
||||||
|
|
||||||
|
add_c_flag_if_supported(-fPIC C_SECURITY_FLAGS)
|
||||||
|
add_cxx_flag_if_supported(-fPIC CXX_SECURITY_FLAGS)
|
||||||
|
|
||||||
message(STATUS "Using C security hardening flags: ${C_SECURITY_FLAGS}")
|
message(STATUS "Using C security hardening flags: ${C_SECURITY_FLAGS}")
|
||||||
message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}")
|
message(STATUS "Using C++ security hardening flags: ${CXX_SECURITY_FLAGS}")
|
||||||
message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}")
|
message(STATUS "Using linker security hardening flags: ${LD_SECURITY_FLAGS}")
|
||||||
|
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${C_SECURITY_FLAGS}")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 ${C_SECURITY_FLAGS}")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_SECURITY_FLAGS}")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${CXX_SECURITY_FLAGS}")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LD_SECURITY_FLAGS} ${STATIC_FLAGS}")
|
||||||
|
|
||||||
if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
|
if (HIDAPI_FOUND OR LibUSB_COMPILE_TEST_PASSED)
|
||||||
@@ -420,5 +488,5 @@ endif()
|
|||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
# Required to make wallet_merged build before the gui
|
# Required to make wallet_merged build before the gui
|
||||||
add_dependencies(monero-gui wallet_merged)
|
add_dependencies(monero-wallet-gui wallet_merged)
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ Rectangle {
|
|||||||
signal addressBookClicked()
|
signal addressBookClicked()
|
||||||
signal miningClicked()
|
signal miningClicked()
|
||||||
signal signClicked()
|
signal signClicked()
|
||||||
signal merchantClicked()
|
|
||||||
signal accountClicked()
|
signal accountClicked()
|
||||||
|
|
||||||
function selectItem(pos) {
|
function selectItem(pos) {
|
||||||
@@ -72,7 +71,6 @@ Rectangle {
|
|||||||
if(pos === "History") menuColumn.previousButton = historyButton
|
if(pos === "History") menuColumn.previousButton = historyButton
|
||||||
else if(pos === "Transfer") menuColumn.previousButton = transferButton
|
else if(pos === "Transfer") menuColumn.previousButton = transferButton
|
||||||
else if(pos === "Receive") menuColumn.previousButton = receiveButton
|
else if(pos === "Receive") menuColumn.previousButton = receiveButton
|
||||||
else if(pos === "Merchant") menuColumn.previousButton = merchantButton
|
|
||||||
else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton
|
else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton
|
||||||
else if(pos === "Mining") menuColumn.previousButton = miningButton
|
else if(pos === "Mining") menuColumn.previousButton = miningButton
|
||||||
else if(pos === "TxKey") menuColumn.previousButton = txkeyButton
|
else if(pos === "TxKey") menuColumn.previousButton = txkeyButton
|
||||||
@@ -265,6 +263,10 @@ Rectangle {
|
|||||||
anchors.leftMargin: 58
|
anchors.leftMargin: 58
|
||||||
anchors.baseline: currencyLabel.baseline
|
anchors.baseline: currencyLabel.baseline
|
||||||
color: MoneroComponents.Style.blackTheme ? "white" : "black"
|
color: MoneroComponents.Style.blackTheme ? "white" : "black"
|
||||||
|
Binding on color {
|
||||||
|
when: balancePart1MouseArea.containsMouse || balancePart2MouseArea.containsMouse
|
||||||
|
value: MoneroComponents.Style.orange
|
||||||
|
}
|
||||||
text: {
|
text: {
|
||||||
if (persistentSettings.fiatPriceEnabled && persistentSettings.fiatPriceToggle) {
|
if (persistentSettings.fiatPriceEnabled && persistentSettings.fiatPriceToggle) {
|
||||||
return balanceFiatString.split('.')[0] + "."
|
return balanceFiatString.split('.')[0] + "."
|
||||||
@@ -286,14 +288,6 @@ Rectangle {
|
|||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onEntered: {
|
|
||||||
balancePart1.color = MoneroComponents.Style.orange
|
|
||||||
balancePart2.color = MoneroComponents.Style.orange
|
|
||||||
}
|
|
||||||
onExited: {
|
|
||||||
balancePart1.color = Qt.binding(function() { return MoneroComponents.Style.blackTheme ? "white" : "black" })
|
|
||||||
balancePart2.color = Qt.binding(function() { return MoneroComponents.Style.blackTheme ? "white" : "black" })
|
|
||||||
}
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
console.log("Copied to clipboard");
|
console.log("Copied to clipboard");
|
||||||
clipboard.setText(balancePart1.text + balancePart2.text);
|
clipboard.setText(balancePart1.text + balancePart2.text);
|
||||||
@@ -307,7 +301,7 @@ Rectangle {
|
|||||||
anchors.left: balancePart1.right
|
anchors.left: balancePart1.right
|
||||||
anchors.leftMargin: 2
|
anchors.leftMargin: 2
|
||||||
anchors.baseline: currencyLabel.baseline
|
anchors.baseline: currencyLabel.baseline
|
||||||
color: MoneroComponents.Style.blackTheme ? "white" : "black"
|
color: balancePart1.color
|
||||||
text: {
|
text: {
|
||||||
if (persistentSettings.fiatPriceEnabled && persistentSettings.fiatPriceToggle) {
|
if (persistentSettings.fiatPriceEnabled && persistentSettings.fiatPriceToggle) {
|
||||||
return balanceFiatString.split('.')[1]
|
return balanceFiatString.split('.')[1]
|
||||||
@@ -317,11 +311,10 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
id: balancePart2MouseArea
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onEntered: balancePart1MouseArea.entered()
|
|
||||||
onExited: balancePart1MouseArea.exited()
|
|
||||||
onClicked: balancePart1MouseArea.clicked(mouse)
|
onClicked: balancePart1MouseArea.clicked(mouse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -454,30 +447,6 @@ Rectangle {
|
|||||||
anchors.leftMargin: 20
|
anchors.leftMargin: 20
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------- Merchant tab ---------------
|
|
||||||
|
|
||||||
MoneroComponents.MenuButton {
|
|
||||||
id: merchantButton
|
|
||||||
visible: appWindow.walletMode >= 2
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
text: qsTr("Merchant") + translationManager.emptyString
|
|
||||||
symbol: qsTr("U") + translationManager.emptyString
|
|
||||||
under: receiveButton
|
|
||||||
onClicked: {
|
|
||||||
parent.previousButton.checked = false
|
|
||||||
parent.previousButton = merchantButton
|
|
||||||
panel.merchantClicked()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MoneroComponents.MenuButtonDivider {
|
|
||||||
visible: merchantButton.present && appWindow.walletMode >= 2
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.leftMargin: 20
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------- History tab ---------------
|
// ------------- History tab ---------------
|
||||||
|
|
||||||
MoneroComponents.MenuButton {
|
MoneroComponents.MenuButton {
|
||||||
|
|||||||
12
Makefile
@@ -28,13 +28,21 @@ clean:
|
|||||||
scanner:
|
scanner:
|
||||||
mkdir -p build && cd build && cmake -D ARCH="x86-64" -D DEV_MODE=$(or ${DEV_MODE},ON) -D WITH_SCANNER=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
mkdir -p build && cd build && cmake -D ARCH="x86-64" -D DEV_MODE=$(or ${DEV_MODE},ON) -D WITH_SCANNER=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release .. && $(MAKE)
|
||||||
|
|
||||||
|
release:
|
||||||
|
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||||
|
|
||||||
|
release-static:
|
||||||
|
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release $(topdir) && $(MAKE)
|
||||||
|
|
||||||
debug-static-win64:
|
debug-static-win64:
|
||||||
mkdir -p $(builddir)/debug && cd $(builddir)/debug && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(cd $MINGW_PREFIX/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
mkdir -p $(builddir)/debug && cd $(builddir)/debug && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Debug -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
||||||
|
|
||||||
debug-static-mac64:
|
debug-static-mac64:
|
||||||
mkdir -p $(builddir)/debug
|
mkdir -p $(builddir)/debug
|
||||||
cd $(builddir)/debug && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" $(topdir) && $(MAKE)
|
cd $(builddir)/debug && cmake -D STATIC=ON -D DEV_MODE=$(or ${DEV_MODE},ON) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D BUILD_TAG="mac-x64" $(topdir) && $(MAKE)
|
||||||
|
|
||||||
release-static-win64:
|
release-static-win64:
|
||||||
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(cd $MINGW_PREFIX/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=ON -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
||||||
|
|
||||||
|
release-win64:
|
||||||
|
mkdir -p $(builddir)/release && cd $(builddir)/release && cmake -D STATIC=OFF -G "MSYS Makefiles" -D DEV_MODE=$(or ${DEV_MODE},OFF) -D ARCH="x86-64" -D BUILD_64=ON -D CMAKE_BUILD_TYPE=Release -D BUILD_TAG="win-x64" -D CMAKE_TOOLCHAIN_FILE=$(topdir)/cmake/64-bit-toolchain.cmake -D MSYS2_FOLDER=$(shell cd ${MINGW_PREFIX}/.. && pwd -W) -D MINGW=ON $(topdir) && $(MAKE)
|
||||||
|
|||||||
17
README.md
@@ -36,7 +36,7 @@ As with many development projects, the repository on Github is considered to be
|
|||||||
|
|
||||||
Monero is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Both Monero and Bitcoin donations can be made to **donate.getmonero.org** if using a client that supports the [OpenAlias](https://openalias.org) standard.
|
Monero is a 100% community-sponsored endeavor. If you want to join our efforts, the easiest thing you can do is support the project financially. Both Monero and Bitcoin donations can be made to **donate.getmonero.org** if using a client that supports the [OpenAlias](https://openalias.org) standard.
|
||||||
|
|
||||||
The Monero donation address is: `44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`)
|
The Monero donation address is: `888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H` (viewkey: `f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501`)
|
||||||
|
|
||||||
The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H`
|
The Bitcoin donation address is: `1KTexdemPdxSBcG55heUuTjDRYqbC5ZL8H`
|
||||||
|
|
||||||
@@ -62,6 +62,11 @@ Do you speak a second language and would like to help translate the Monero GUI?
|
|||||||
|
|
||||||
If you need help/support or any info you can contact the localization workgroup on the IRC channel #monero-translations (relayed on matrix/riot and MatterMost) or by email at translate[at]getmonero[dot]org. For more info about the Localization workgroup: [github.com/monero-ecosystem/monero-translations](https://github.com/monero-ecosystem/monero-translations)
|
If you need help/support or any info you can contact the localization workgroup on the IRC channel #monero-translations (relayed on matrix/riot and MatterMost) or by email at translate[at]getmonero[dot]org. For more info about the Localization workgroup: [github.com/monero-ecosystem/monero-translations](https://github.com/monero-ecosystem/monero-translations)
|
||||||
|
|
||||||
|
Status of the translations:
|
||||||
|
<a href="https://translate.getmonero.org/engage/monero/?utm_source=widget">
|
||||||
|
<img src="https://translate.getmonero.org/widgets/monero/-/gui-wallet/horizontal-auto.svg" alt="Translation status" />
|
||||||
|
</a>
|
||||||
|
|
||||||
## Installing the Monero GUI from a package
|
## Installing the Monero GUI from a package
|
||||||
|
|
||||||
Packages are available for
|
Packages are available for
|
||||||
@@ -84,15 +89,15 @@ Packaging for your favorite distribution would be a welcome contribution!
|
|||||||
|
|
||||||
- For Debian distributions (Debian, Ubuntu, Mint, Tails...)
|
- For Debian distributions (Debian, Ubuntu, Mint, Tails...)
|
||||||
|
|
||||||
`sudo apt install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler`
|
`sudo apt install build-essential cmake libboost-all-dev miniupnpc libunbound-dev graphviz doxygen libunwind8-dev pkg-config libssl-dev libzmq3-dev libsodium-dev libhidapi-dev libnorm-dev libusb-1.0-0-dev libpgm-dev libprotobuf-dev protobuf-compiler libgcrypt20-dev`
|
||||||
|
|
||||||
- For Gentoo
|
- For Gentoo
|
||||||
|
|
||||||
`sudo emerge app-arch/xz-utils app-doc/doxygen dev-cpp/gtest dev-libs/boost dev-libs/expat dev-libs/openssl dev-util/cmake media-gfx/graphviz net-dns/unbound net-libs/ldns net-libs/miniupnpc net-libs/zeromq sys-libs/libunwind dev-libs/libsodium dev-libs/hidapi`
|
`sudo emerge app-arch/xz-utils app-doc/doxygen dev-cpp/gtest dev-libs/boost dev-libs/expat dev-libs/openssl dev-util/cmake media-gfx/graphviz net-dns/unbound net-libs/ldns net-libs/miniupnpc net-libs/zeromq sys-libs/libunwind dev-libs/libsodium dev-libs/hidapi dev-libs/libgcrypt`
|
||||||
|
|
||||||
- For Fedora
|
- For Fedora
|
||||||
|
|
||||||
`sudo dnf install make automake cmake gcc-c++ boost-devel miniupnpc-devel graphviz doxygen unbound-devel libunwind-devel pkgconfig openssl-devel libcurl-devel hidapi-devel libusb-devel zeromq-devel`
|
`sudo dnf install make automake cmake gcc-c++ boost-devel miniupnpc-devel graphviz doxygen unbound-devel libunwind-devel pkgconfig openssl-devel libcurl-devel hidapi-devel libusb-devel zeromq-devel libgcrypt-devel`
|
||||||
|
|
||||||
2. Install Qt:
|
2. Install Qt:
|
||||||
|
|
||||||
@@ -144,7 +149,7 @@ The executable can be found in the build/release/bin folder.
|
|||||||
|
|
||||||
3. Install [monero](https://github.com/monero-project/monero) dependencies:
|
3. Install [monero](https://github.com/monero-project/monero) dependencies:
|
||||||
|
|
||||||
`brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf`
|
`brew install boost hidapi zmq libpgm miniupnpc ldns expat libunwind-headers protobuf libgcrypt`
|
||||||
|
|
||||||
4. Install Qt:
|
4. Install Qt:
|
||||||
|
|
||||||
@@ -180,7 +185,7 @@ The Monero GUI on Windows is 64 bits only; 32-bit Windows GUI builds are not off
|
|||||||
3. Install MSYS2 packages for Monero dependencies; the needed 64-bit packages have `x86_64` in their names
|
3. Install MSYS2 packages for Monero dependencies; the needed 64-bit packages have `x86_64` in their names
|
||||||
|
|
||||||
```
|
```
|
||||||
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb
|
pacman -S mingw-w64-x86_64-toolchain make mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-openssl mingw-w64-x86_64-zeromq mingw-w64-x86_64-libsodium mingw-w64-x86_64-hidapi mingw-w64-x86_64-protobuf-c mingw-w64-x86_64-libusb mingw-w64-x86_64-libgcrypt
|
||||||
```
|
```
|
||||||
|
|
||||||
Optional : To build the flag `WITH_SCANNER`
|
Optional : To build the flag `WITH_SCANNER`
|
||||||
|
|||||||
56
cmake/FindCcache.cmake
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Copyright (c) 2014-2020, 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.
|
||||||
|
# - Try to find readline include dirs and libraries
|
||||||
|
#
|
||||||
|
# Automatically finds ccache build accelerator, if it's found in PATH.
|
||||||
|
#
|
||||||
|
# Usage of this module as follows:
|
||||||
|
#
|
||||||
|
# project(monero)
|
||||||
|
# include(FindCcache) # Include AFTER the project() macro to be able to reach the CMAKE_CXX_COMPILER variable
|
||||||
|
#
|
||||||
|
# Properties modified by this module:
|
||||||
|
#
|
||||||
|
# GLOBAL PROPERTY RULE_LAUNCH_COMPILE set to ccache, when ccache found
|
||||||
|
# GLOBAL PROPERTY RULE_LAUNCH_LINK set to ccache, when ccache found
|
||||||
|
|
||||||
|
find_program(CCACHE_FOUND ccache)
|
||||||
|
if (CCACHE_FOUND)
|
||||||
|
set(TEMP_CPP_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/test-program.cpp")
|
||||||
|
file(WRITE "${TEMP_CPP_FILE}" "int main() { return 0; }")
|
||||||
|
execute_process(COMMAND "${CCACHE_FOUND}" "${CMAKE_CXX_COMPILER}" "${TEMP_CPP_FILE}" RESULT_VARIABLE RET)
|
||||||
|
if (${RET} EQUAL 0)
|
||||||
|
message("found usable ccache: ${CCACHE_FOUND}")
|
||||||
|
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_FOUND}")
|
||||||
|
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_FOUND}")
|
||||||
|
else()
|
||||||
|
message("found ccache ${CCACHE_FOUND}, but is UNUSABLE! Return code: ${RET}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message("ccache NOT found!")
|
||||||
|
endif()
|
||||||
106
components/AdvancedOptionsItem.qml
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import FontAwesome 1.0
|
||||||
|
|
||||||
|
import "../components" as MoneroComponents
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: advancedOptionsItem
|
||||||
|
|
||||||
|
property alias title: title.text
|
||||||
|
property alias button1: button1
|
||||||
|
property alias button2: button2
|
||||||
|
property alias button3: button3
|
||||||
|
property alias helpTextLarge: helpTextLarge
|
||||||
|
property alias helpTextSmall: helpTextSmall
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: titlecolumn
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
|
Layout.preferredWidth: 195
|
||||||
|
Layout.maximumWidth: 195
|
||||||
|
Layout.leftMargin: 10
|
||||||
|
|
||||||
|
MoneroComponents.Label {
|
||||||
|
id: title
|
||||||
|
fontSize: 14
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.Label {
|
||||||
|
id: iconLabel
|
||||||
|
fontSize: 12
|
||||||
|
text: FontAwesome.questionCircle
|
||||||
|
fontFamily: FontAwesome.fontFamily
|
||||||
|
opacity: 0.3
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: helpText.visible = !helpText.visible
|
||||||
|
onEntered: parent.opacity = 0.4
|
||||||
|
onExited: parent.opacity = 0.3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: separator
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 10
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: false
|
||||||
|
spacing: 12
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
|
|
||||||
|
StandardButton {
|
||||||
|
id: button1
|
||||||
|
small: true
|
||||||
|
visible: button1.text
|
||||||
|
}
|
||||||
|
|
||||||
|
StandardButton {
|
||||||
|
id: button2
|
||||||
|
small: true
|
||||||
|
visible: button2.text
|
||||||
|
}
|
||||||
|
|
||||||
|
StandardButton {
|
||||||
|
id: button3
|
||||||
|
small: true
|
||||||
|
visible: button3.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: helpText
|
||||||
|
visible: false
|
||||||
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
|
|
||||||
|
MoneroComponents.TextPlain {
|
||||||
|
id: helpTextLarge
|
||||||
|
visible: helpTextLarge.text
|
||||||
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
|
font.pixelSize: 13
|
||||||
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.TextPlain {
|
||||||
|
id: helpTextSmall
|
||||||
|
visible: helpTextSmall.text
|
||||||
|
Layout.leftMargin: 5
|
||||||
|
textFormat: Text.RichText
|
||||||
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
|
font.pixelSize: 12
|
||||||
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ Item {
|
|||||||
visible: checkBox.border
|
visible: checkBox.border
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
radius: 3
|
radius: 3
|
||||||
color: "transparent"
|
color: checkBox.enabled ? "transparent" : MoneroComponents.Style.inputBoxBackgroundDisabled
|
||||||
border.color:
|
border.color:
|
||||||
if(checkBox.checked){
|
if(checkBox.checked){
|
||||||
return MoneroComponents.Style.inputBorderColorActive;
|
return MoneroComponents.Style.inputBorderColorActive;
|
||||||
|
|||||||
104
components/DevicePassphraseDialog.qml
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Copyright (c) 2014-2020, 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 "." as MoneroComponents
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var onAcceptedCallback
|
||||||
|
property var onWalletEntryCallback
|
||||||
|
property var onRejectedCallback
|
||||||
|
|
||||||
|
function open(canEnterOnDevice_) {
|
||||||
|
var canEnterOnDevice = canEnterOnDevice_ !== null ? canEnterOnDevice_ : canEnterOnDevice
|
||||||
|
root.visible = true;
|
||||||
|
|
||||||
|
if (canEnterOnDevice) {
|
||||||
|
entryChooserDialog.okText = qsTr("Hardware wallet")
|
||||||
|
entryChooserDialog.cancelText = qsTr("Computer")
|
||||||
|
entryChooserDialog.open()
|
||||||
|
} else {
|
||||||
|
openPassphraseDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openPassphraseDialog() {
|
||||||
|
root.visible = true
|
||||||
|
passphraseDialog.openPassphraseDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
root.visible = false;
|
||||||
|
if (entryChooserDialog.visible)
|
||||||
|
entryChooserDialog.close()
|
||||||
|
if (passphraseDialog.visible)
|
||||||
|
passphraseDialog.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
StandardDialog {
|
||||||
|
id: entryChooserDialog
|
||||||
|
title: qsTr("Hardware wallet passphrase") + translationManager.emptyString
|
||||||
|
text: qsTr("Please select where you want to enter passphrase.\nIt is recommended to enter passphrase on the hardware wallet for better security.") + translationManager.emptyString
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
if (onWalletEntryCallback){
|
||||||
|
onWalletEntryCallback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRejected: {
|
||||||
|
openPassphraseDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloseCallback: {
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PasswordDialog {
|
||||||
|
id: passphraseDialog
|
||||||
|
anchors.fill: parent
|
||||||
|
passphraseDialogMode: true
|
||||||
|
|
||||||
|
onAcceptedPassphrase: {
|
||||||
|
if (onAcceptedCallback)
|
||||||
|
onAcceptedCallback(passphraseDialog.password);
|
||||||
|
}
|
||||||
|
|
||||||
|
onRejectedPassphrase: {
|
||||||
|
if (onRejectedCallback)
|
||||||
|
onRejectedCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
onCloseCallback: {
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -53,6 +53,7 @@ Item {
|
|||||||
property alias fontPixelSize: inlineText.font.pixelSize
|
property alias fontPixelSize: inlineText.font.pixelSize
|
||||||
property alias fontFamily: inlineText.font.family
|
property alias fontFamily: inlineText.font.family
|
||||||
property alias buttonColor: rect.color
|
property alias buttonColor: rect.color
|
||||||
|
property alias buttonHeight: rect.height
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
function doClick() {
|
function doClick() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2014-2019, The Monero Project
|
// Copyright (c) 2014-2020, The Monero Project
|
||||||
//
|
//
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
@@ -53,7 +53,7 @@ Drawer {
|
|||||||
y: titleBar.height
|
y: titleBar.height
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
color: "#0d0d0d"
|
color: MoneroComponents.Style.blackTheme ? "#0d0d0d" : "white"
|
||||||
width: parent.width
|
width: parent.width
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +79,24 @@ Drawer {
|
|||||||
width: sideBar.width
|
width: sideBar.width
|
||||||
height: 32
|
height: 32
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: flagRect
|
||||||
|
height: 24
|
||||||
|
width: 24
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 4
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: flag
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MoneroComponents.TextPlain {
|
MoneroComponents.TextPlain {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 30
|
||||||
font.bold: true
|
font.bold: true
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
color: MoneroComponents.Style.defaultFontColor
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
@@ -118,8 +133,10 @@ Drawer {
|
|||||||
// set wizard language settings
|
// set wizard language settings
|
||||||
wizard.language_locale = locale;
|
wizard.language_locale = locale;
|
||||||
wizard.language_wallet = wallet_language;
|
wizard.language_wallet = wallet_language;
|
||||||
wizard.language_language = display_name + " (" + locale_spl[1] + ") ";
|
wizard.language_language = display_name;
|
||||||
sideBar.close()
|
|
||||||
|
appWindow.showStatusMessage(qsTr("Language changed."), 3);
|
||||||
|
appWindow.toggleLanguageView();
|
||||||
}
|
}
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onEntered: {
|
onEntered: {
|
||||||
@@ -134,15 +151,8 @@ Drawer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollIndicator.vertical: ScrollIndicator {
|
ScrollBar.vertical: ScrollBar {
|
||||||
// @TODO: QT 5.9 introduces `policy: ScrollBar.AlwaysOn`
|
onActiveChanged: if (!active && !isMac) active = true
|
||||||
active: true
|
|
||||||
contentItem.opacity: 0.7
|
|
||||||
onActiveChanged: {
|
|
||||||
if (!active) {
|
|
||||||
active = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
// 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.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
import FontAwesome 1.0
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtGraphicalEffects 1.0
|
import QtGraphicalEffects 1.0
|
||||||
|
|
||||||
@@ -36,6 +37,10 @@ Item {
|
|||||||
property alias input: input
|
property alias input: input
|
||||||
property alias text: input.text
|
property alias text: input.text
|
||||||
|
|
||||||
|
property bool password: false
|
||||||
|
property bool passwordHidden: true
|
||||||
|
property var passwordLinked: null
|
||||||
|
|
||||||
property alias placeholderText: placeholderLabel.text
|
property alias placeholderText: placeholderLabel.text
|
||||||
property bool placeholderCenter: false
|
property bool placeholderCenter: false
|
||||||
property string placeholderFontFamily: MoneroComponents.Style.fontRegular.name
|
property string placeholderFontFamily: MoneroComponents.Style.fontRegular.name
|
||||||
@@ -48,7 +53,6 @@ Item {
|
|||||||
property alias validator: input.validator
|
property alias validator: input.validator
|
||||||
property alias readOnly : input.readOnly
|
property alias readOnly : input.readOnly
|
||||||
property alias cursorPosition: input.cursorPosition
|
property alias cursorPosition: input.cursorPosition
|
||||||
property alias echoMode: input.echoMode
|
|
||||||
property alias inlineButton: inlineButtonId
|
property alias inlineButton: inlineButtonId
|
||||||
property alias inlineButtonText: inlineButtonId.text
|
property alias inlineButtonText: inlineButtonId.text
|
||||||
property alias inlineIcon: inlineIcon.visible
|
property alias inlineIcon: inlineIcon.visible
|
||||||
@@ -109,6 +113,31 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isPasswordHidden() {
|
||||||
|
if (password) {
|
||||||
|
return passwordHidden;
|
||||||
|
}
|
||||||
|
if (passwordLinked) {
|
||||||
|
return passwordLinked.passwordHidden;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
text = "";
|
||||||
|
if (!passwordLinked) {
|
||||||
|
passwordHidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function passwordToggle() {
|
||||||
|
if (passwordLinked) {
|
||||||
|
passwordLinked.passwordHidden = !passwordLinked.passwordHidden;
|
||||||
|
} else {
|
||||||
|
passwordHidden = !passwordHidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MoneroComponents.TextPlain {
|
MoneroComponents.TextPlain {
|
||||||
id: inputLabel
|
id: inputLabel
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
@@ -210,6 +239,27 @@ Item {
|
|||||||
onTextChanged: item.textUpdated()
|
onTextChanged: item.textUpdated()
|
||||||
topPadding: 10
|
topPadding: 10
|
||||||
bottomPadding: 10
|
bottomPadding: 10
|
||||||
|
echoMode: isPasswordHidden() ? TextInput.Password : TextInput.Normal
|
||||||
|
|
||||||
|
MoneroComponents.Label {
|
||||||
|
visible: password || passwordLinked
|
||||||
|
fontSize: 20
|
||||||
|
text: isPasswordHidden() ? FontAwesome.eye : FontAwesome.eyeSlash
|
||||||
|
opacity: eyeMouseArea.containsMouse ? 0.9 : 0.7
|
||||||
|
fontFamily: FontAwesome.fontFamily
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 15
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.verticalCenterOffset: 1
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: eyeMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: passwordToggle()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.InlineButton {
|
MoneroComponents.InlineButton {
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ ColumnLayout {
|
|||||||
|
|
||||||
property alias inlineButton: inlineButtonId
|
property alias inlineButton: inlineButtonId
|
||||||
property bool inlineButtonVisible: false
|
property bool inlineButtonVisible: false
|
||||||
|
property alias inlineButton2: inlineButton2Id
|
||||||
|
property bool inlineButton2Visible: false
|
||||||
|
|
||||||
signal labelButtonClicked();
|
signal labelButtonClicked();
|
||||||
signal inputLabelLinkActivated();
|
signal inputLabelLinkActivated();
|
||||||
@@ -202,5 +204,12 @@ ColumnLayout {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: 8
|
anchors.rightMargin: 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MoneroComponents.InlineButton {
|
||||||
|
id: inlineButton2Id
|
||||||
|
visible: (inlineButton2Id.text || inlineButton2Id.icon) && inlineButton2Visible ? true : false
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: inlineButtonVisible ? 48 : 8
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ Rectangle {
|
|||||||
height: present ? ((appWindow.height >= 800) ? 44 : 38 ) : 0
|
height: present ? ((appWindow.height >= 800) ? 44 : 38 ) : 0
|
||||||
|
|
||||||
LinearGradient {
|
LinearGradient {
|
||||||
visible: isOpenGL && button.checked
|
visible: isOpenGL && (button.checked || buttonArea.containsMouse)
|
||||||
height: parent.height
|
height: parent.height
|
||||||
width: 260
|
width: 260
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
@@ -75,13 +75,15 @@ Rectangle {
|
|||||||
GradientStop { position: 0.0; color: MoneroComponents.Style.menuButtonGradientStart }
|
GradientStop { position: 0.0; color: MoneroComponents.Style.menuButtonGradientStart }
|
||||||
GradientStop { position: 1.0; color: MoneroComponents.Style.menuButtonGradientStop }
|
GradientStop { position: 1.0; color: MoneroComponents.Style.menuButtonGradientStop }
|
||||||
}
|
}
|
||||||
|
opacity: button.checked ? 1 : 0.3
|
||||||
}
|
}
|
||||||
|
|
||||||
// fallback hover effect when opengl is not available
|
// fallback hover effect when opengl is not available
|
||||||
Rectangle {
|
Rectangle {
|
||||||
visible: !isOpenGL && button.checked
|
visible: !isOpenGL && (button.checked || buttonArea.containsMouse)
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: MoneroComponents.Style.menuButtonFallbackBackgroundColor
|
color: MoneroComponents.Style.menuButtonFallbackBackgroundColor
|
||||||
|
opacity: button.checked ? 1 : 0.3
|
||||||
}
|
}
|
||||||
|
|
||||||
// button decorations that are subject to leftMargin offsets
|
// button decorations that are subject to leftMargin offsets
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ Item {
|
|||||||
visible: false
|
visible: false
|
||||||
z: parent.z + 2
|
z: parent.z + 2
|
||||||
|
|
||||||
property bool isHidden: true
|
|
||||||
property alias password: passwordInput1.text
|
property alias password: passwordInput1.text
|
||||||
property string walletName
|
property string walletName
|
||||||
property string errorText
|
property string errorText
|
||||||
@@ -61,13 +60,10 @@ Item {
|
|||||||
signal closeCallback()
|
signal closeCallback()
|
||||||
|
|
||||||
function _openInit(walletName, errorText) {
|
function _openInit(walletName, errorText) {
|
||||||
isHidden = true
|
|
||||||
capsLockTextLabel.visible = oshelper.isCapsLock();
|
capsLockTextLabel.visible = oshelper.isCapsLock();
|
||||||
passwordInput1.echoMode = TextInput.Password
|
passwordInput1.reset();
|
||||||
passwordInput2.echoMode = TextInput.Password
|
passwordInput2.reset();
|
||||||
passwordInput1.text = ""
|
passwordInput1.input.forceActiveFocus();
|
||||||
passwordInput2.text = ""
|
|
||||||
passwordInput1.forceActiveFocus();
|
|
||||||
root.walletName = walletName ? walletName : ""
|
root.walletName = walletName ? walletName : ""
|
||||||
errorTextLabel.text = errorText ? errorText : "";
|
errorTextLabel.text = errorText ? errorText : "";
|
||||||
leftPanel.enabled = false
|
leftPanel.enabled = false
|
||||||
@@ -116,10 +112,29 @@ Item {
|
|||||||
closeCallback();
|
closeCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleIsHidden() {
|
function onOk() {
|
||||||
passwordInput1.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
if (!passwordDialogMode && passwordInput1.text !== passwordInput2.text) {
|
||||||
passwordInput2.echoMode = isHidden ? TextInput.Normal : TextInput.Password;
|
return;
|
||||||
isHidden = !isHidden;
|
}
|
||||||
|
root.close()
|
||||||
|
if (passwordDialogMode) {
|
||||||
|
root.accepted()
|
||||||
|
} else if (newPasswordDialogMode) {
|
||||||
|
root.acceptedNewPassword()
|
||||||
|
} else if (passphraseDialogMode) {
|
||||||
|
root.acceptedPassphrase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
root.close()
|
||||||
|
if (passwordDialogMode) {
|
||||||
|
root.rejected()
|
||||||
|
} else if (newPasswordDialogMode) {
|
||||||
|
root.rejectedNewPassword()
|
||||||
|
} else if (passphraseDialogMode) {
|
||||||
|
root.rejectedPassphrase()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
@@ -184,15 +199,11 @@ Item {
|
|||||||
text: qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString;
|
text: qsTr("CAPSLOCKS IS ON.") + translationManager.emptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.Input {
|
MoneroComponents.LineEdit {
|
||||||
id: passwordInput1
|
id: passwordInput1
|
||||||
|
password: true
|
||||||
Layout.topMargin: 6
|
Layout.topMargin: 6
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
horizontalAlignment: TextInput.AlignLeft
|
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
|
||||||
font.family: MoneroComponents.Style.fontLight.name
|
|
||||||
font.pixelSize: 24
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
KeyNavigation.tab: {
|
KeyNavigation.tab: {
|
||||||
if (passwordDialogMode) {
|
if (passwordDialogMode) {
|
||||||
return okButton
|
return okButton
|
||||||
@@ -200,81 +211,12 @@ Item {
|
|||||||
return passwordInput2
|
return passwordInput2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
implicitHeight: 50
|
|
||||||
bottomPadding: 10
|
|
||||||
leftPadding: 10
|
|
||||||
topPadding: 10
|
|
||||||
color: MoneroComponents.Style.defaultFontColor
|
|
||||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
|
||||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
|
||||||
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
|
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
radius: 2
|
|
||||||
color: MoneroComponents.Style.blackTheme ? "black" : "#A9FFFFFF"
|
|
||||||
border.color: MoneroComponents.Style.inputBorderColorInActive
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
MoneroEffects.ColorTransition {
|
|
||||||
targetObj: parent
|
|
||||||
blackColor: "black"
|
|
||||||
whiteColor: "#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.enabled: root.visible
|
Keys.enabled: root.visible
|
||||||
Keys.onEnterPressed: Keys.onReturnPressed(event)
|
Keys.onEnterPressed: root.onOk()
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: root.onOk()
|
||||||
if (!passwordDialogMode && passwordInput1.text !== passwordInput2.text) {
|
Keys.onEscapePressed: root.onCancel()
|
||||||
return;
|
|
||||||
}
|
|
||||||
root.close()
|
|
||||||
if (passwordDialogMode) {
|
|
||||||
root.accepted()
|
|
||||||
} else if (newPasswordDialogMode) {
|
|
||||||
root.acceptedNewPassword()
|
|
||||||
} else if (passphraseDialogMode) {
|
|
||||||
root.acceptedPassphrase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Keys.onEscapePressed: {
|
|
||||||
root.close()
|
|
||||||
if (passwordDialogMode) {
|
|
||||||
root.rejected()
|
|
||||||
} else if (newPasswordDialogMode) {
|
|
||||||
root.rejectedNewPassword()
|
|
||||||
} else if (passphraseDialogMode) {
|
|
||||||
root.rejectedPassphrase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// padding
|
// padding
|
||||||
@@ -298,81 +240,19 @@ Item {
|
|||||||
color: MoneroComponents.Style.defaultFontColor
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.Input {
|
MoneroComponents.LineEdit {
|
||||||
id: passwordInput2
|
id: passwordInput2
|
||||||
|
passwordLinked: passwordInput1
|
||||||
visible: !passwordDialogMode
|
visible: !passwordDialogMode
|
||||||
Layout.topMargin: 6
|
Layout.topMargin: 6
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
horizontalAlignment: TextInput.AlignLeft
|
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
|
||||||
font.family: MoneroComponents.Style.fontLight.name
|
|
||||||
font.pixelSize: 24
|
|
||||||
echoMode: TextInput.Password
|
|
||||||
KeyNavigation.tab: okButton
|
KeyNavigation.tab: okButton
|
||||||
implicitHeight: 50
|
|
||||||
bottomPadding: 10
|
|
||||||
leftPadding: 10
|
|
||||||
topPadding: 10
|
|
||||||
color: MoneroComponents.Style.defaultFontColor
|
|
||||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
|
||||||
selectedTextColor: MoneroComponents.Style.textSelectedColor
|
|
||||||
onTextChanged: capsLockTextLabel.visible = oshelper.isCapsLock();
|
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.enabled: root.visible
|
Keys.enabled: root.visible
|
||||||
Keys.onEnterPressed: Keys.onReturnPressed(event)
|
Keys.onEnterPressed: root.onOk()
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: root.onOk()
|
||||||
if (passwordInput1.text === passwordInput2.text) {
|
Keys.onEscapePressed: root.onCancel()
|
||||||
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
|
// padding
|
||||||
@@ -397,16 +277,7 @@ Item {
|
|||||||
small: true
|
small: true
|
||||||
text: qsTr("Cancel") + translationManager.emptyString
|
text: qsTr("Cancel") + translationManager.emptyString
|
||||||
KeyNavigation.tab: passwordInput1
|
KeyNavigation.tab: passwordInput1
|
||||||
onClicked: {
|
onClicked: onCancel()
|
||||||
root.close()
|
|
||||||
if (passwordDialogMode) {
|
|
||||||
root.rejected()
|
|
||||||
} else if (newPasswordDialogMode) {
|
|
||||||
root.rejectedNewPassword()
|
|
||||||
} else if (passphraseDialogMode) {
|
|
||||||
root.rejectedPassphrase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.StandardButton {
|
MoneroComponents.StandardButton {
|
||||||
@@ -415,16 +286,7 @@ Item {
|
|||||||
text: qsTr("Ok") + translationManager.emptyString
|
text: qsTr("Ok") + translationManager.emptyString
|
||||||
KeyNavigation.tab: cancelButton
|
KeyNavigation.tab: cancelButton
|
||||||
enabled: (passwordDialogMode == true) ? true : passwordInput1.text === passwordInput2.text
|
enabled: (passwordDialogMode == true) ? true : passwordInput1.text === passwordInput2.text
|
||||||
onClicked: {
|
onClicked: onOk()
|
||||||
root.close()
|
|
||||||
if (passwordDialogMode) {
|
|
||||||
root.accepted()
|
|
||||||
} else if (newPasswordDialogMode) {
|
|
||||||
root.acceptedNewPassword()
|
|
||||||
} else if (passphraseDialogMode) {
|
|
||||||
root.acceptedPassphrase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,21 +29,23 @@
|
|||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Window 2.1
|
import QtQuick.Window 2.1
|
||||||
import QtQuick.Controls 1.4
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
import "../components" as MoneroComponents
|
import "../components" as MoneroComponents
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
color: MoneroComponents.Style.blackTheme ? "white" : "transparent"
|
color: MoneroComponents.Style.blackTheme ? "black" : "white"
|
||||||
visible: false
|
visible: false
|
||||||
|
radius: 10
|
||||||
|
border.color: MoneroComponents.Style.blackTheme ? Qt.rgba(255, 255, 255, 0.25) : Qt.rgba(0, 0, 0, 0.25)
|
||||||
|
border.width: 1
|
||||||
z: 11
|
z: 11
|
||||||
property alias messageText: messageTitle.text
|
property alias messageText: messageTitle.text
|
||||||
property alias heightProgressText : heightProgress.text
|
|
||||||
|
|
||||||
width: 200
|
width: 100
|
||||||
height: 100
|
height: 50
|
||||||
opacity: 0.7
|
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
root.visible = true;
|
root.visible = true;
|
||||||
@@ -56,44 +58,55 @@ Rectangle {
|
|||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: rootLayout
|
id: rootLayout
|
||||||
|
|
||||||
anchors.left: parent.left
|
anchors.centerIn: parent
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
anchors.leftMargin: 30
|
anchors.leftMargin: 30
|
||||||
anchors.rightMargin: 30
|
anchors.rightMargin: 30
|
||||||
|
|
||||||
spacing: 12
|
spacing: 21
|
||||||
|
|
||||||
BusyIndicator {
|
Item {
|
||||||
running: parent.visible
|
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
Layout.preferredHeight: 80
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: imgLogo
|
||||||
|
width: 60
|
||||||
|
height: 60
|
||||||
|
anchors.centerIn: parent
|
||||||
|
source: "qrc:///images/monero-vector.svg"
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
running: parent.visible
|
||||||
|
anchors.centerIn: imgLogo
|
||||||
|
style: BusyIndicatorStyle {
|
||||||
|
indicator: Image {
|
||||||
|
visible: control.running
|
||||||
|
source: "qrc:///images/busy-indicator.png"
|
||||||
|
RotationAnimator on rotation {
|
||||||
|
running: control.running
|
||||||
|
loops: Animation.Infinite
|
||||||
|
duration: 1000
|
||||||
|
from: 0
|
||||||
|
to: 360
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MoneroComponents.TextPlain {
|
MoneroComponents.TextPlain {
|
||||||
id: messageTitle
|
id: messageTitle
|
||||||
text: "Please wait..."
|
text: qsTr("Please wait...") + translationManager.emptyString
|
||||||
font {
|
font.pixelSize: 24
|
||||||
pixelSize: 22
|
|
||||||
}
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
themeTransition: false
|
themeTransition: false
|
||||||
color: "black"
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MoneroComponents.TextPlain {
|
|
||||||
id: heightProgress
|
|
||||||
font {
|
|
||||||
pixelSize: 18
|
|
||||||
}
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
|
||||||
Layout.fillWidth: true
|
|
||||||
themeTransition: false
|
|
||||||
color: "black"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Rectangle {
|
|||||||
function updateProgress(currentBlock,targetBlock, blocksToSync, statusTxt){
|
function updateProgress(currentBlock,targetBlock, blocksToSync, statusTxt){
|
||||||
if(targetBlock > 0) {
|
if(targetBlock > 0) {
|
||||||
var remaining = (currentBlock < targetBlock) ? targetBlock - currentBlock : 0
|
var remaining = (currentBlock < targetBlock) ? targetBlock - currentBlock : 0
|
||||||
var progressLevel = (blocksToSync > 0 && blocksToSync != remaining) ? (100*(blocksToSync - remaining)/blocksToSync).toFixed(0) : (100*(currentBlock / targetBlock)).toFixed(0)
|
var progressLevel = (blocksToSync > 0 ) ? (100*(blocksToSync - remaining)/blocksToSync).toFixed(0) : 100
|
||||||
fillLevel = progressLevel
|
fillLevel = progressLevel
|
||||||
if(typeof statusTxt != "undefined" && statusTxt != "") {
|
if(typeof statusTxt != "undefined" && statusTxt != "") {
|
||||||
progressText.text = statusTxt;
|
progressText.text = statusTxt;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2014-2018, The Monero Project
|
// Copyright (c) 2014-2020, The Monero Project
|
||||||
//
|
//
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
@@ -53,6 +53,7 @@ Rectangle {
|
|||||||
script: {
|
script: {
|
||||||
root.visible = true
|
root.visible = true
|
||||||
camera.captureMode = Camera.CaptureStillImage
|
camera.captureMode = Camera.CaptureStillImage
|
||||||
|
camera.cameraState = Camera.ActiveState
|
||||||
camera.start()
|
camera.start()
|
||||||
finder.enabled = true
|
finder.enabled = true
|
||||||
}
|
}
|
||||||
@@ -65,6 +66,7 @@ Rectangle {
|
|||||||
camera.stop()
|
camera.stop()
|
||||||
root.visible = false
|
root.visible = false
|
||||||
finder.enabled = false
|
finder.enabled = false
|
||||||
|
camera.cameraState = Camera.UnloadedState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,6 +76,7 @@ Rectangle {
|
|||||||
id: camera
|
id: camera
|
||||||
objectName: "qrCameraQML"
|
objectName: "qrCameraQML"
|
||||||
captureMode: Camera.CaptureStillImage
|
captureMode: Camera.CaptureStillImage
|
||||||
|
cameraState: Camera.UnloadedState
|
||||||
|
|
||||||
focus {
|
focus {
|
||||||
focusMode: Camera.FocusContinuous
|
focusMode: Camera.FocusContinuous
|
||||||
|
|||||||
@@ -1,41 +1,71 @@
|
|||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
|
import FontAwesome 1.0
|
||||||
|
|
||||||
import "../components" as MoneroComponents
|
import "../components" as MoneroComponents
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
property alias buttonText: button.text
|
id: settingsListItem
|
||||||
property alias description: description.text
|
property alias iconText: iconLabel.text
|
||||||
property alias title: title.text
|
property alias description: area.text
|
||||||
|
property alias title: header.text
|
||||||
|
property bool isLast: false
|
||||||
signal clicked()
|
signal clicked()
|
||||||
|
|
||||||
id: settingsListItem
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
// divider
|
id: root
|
||||||
Layout.preferredHeight: 1
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.bottomMargin: 8
|
Layout.minimumHeight: 75
|
||||||
color: MoneroComponents.Style.dividerColor
|
Layout.preferredHeight: rect.height + 15
|
||||||
opacity: MoneroComponents.Style.dividerOpacity
|
color: "transparent"
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
id: divider
|
||||||
spacing: 0
|
anchors.topMargin: 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: 1
|
||||||
|
color: MoneroComponents.Style.dividerColor
|
||||||
|
opacity: MoneroComponents.Style.dividerOpacity
|
||||||
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
id: rect
|
||||||
Layout.alignment: Qt.AlignVCenter
|
width: parent.width
|
||||||
spacing: 0
|
height: header.height + area.contentHeight
|
||||||
|
color: "transparent";
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottomMargin: 4
|
||||||
|
anchors.topMargin: 4
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: icon
|
||||||
|
color: "transparent"
|
||||||
|
height: 32
|
||||||
|
width: 32
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 16
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
MoneroComponents.Label {
|
||||||
|
id: iconLabel
|
||||||
|
fontSize: 32
|
||||||
|
fontFamily: FontAwesome.fontFamilySolid
|
||||||
|
anchors.centerIn: parent
|
||||||
|
fontColor: MoneroComponents.Style.defaultFontColor
|
||||||
|
styleName: "Solid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MoneroComponents.TextPlain {
|
MoneroComponents.TextPlain {
|
||||||
id: title
|
id: header
|
||||||
Layout.fillWidth: true
|
anchors.left: icon.right
|
||||||
Layout.preferredHeight: 20
|
anchors.leftMargin: 16
|
||||||
Layout.topMargin: 8
|
anchors.top: parent.top
|
||||||
color: MoneroComponents.Style.defaultFontColor
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
opacity: MoneroComponents.Style.blackTheme ? 1.0 : 0.8
|
opacity: MoneroComponents.Style.blackTheme ? 1.0 : 0.8
|
||||||
font.bold: true
|
font.bold: true
|
||||||
@@ -43,23 +73,43 @@ ColumnLayout {
|
|||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.TextPlainArea {
|
Text {
|
||||||
id: description
|
id: area
|
||||||
|
anchors.top: header.bottom
|
||||||
|
anchors.topMargin: 4
|
||||||
|
anchors.left: icon.right
|
||||||
|
anchors.leftMargin: 16
|
||||||
color: MoneroComponents.Style.dimmedFontColor
|
color: MoneroComponents.Style.dimmedFontColor
|
||||||
colorBlackTheme: MoneroComponents.Style._b_dimmedFontColor
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
colorWhiteTheme: MoneroComponents.Style._w_dimmedFontColor
|
font.pixelSize: 15
|
||||||
Layout.fillWidth: true
|
|
||||||
horizontalAlignment: TextInput.AlignLeft
|
horizontalAlignment: TextInput.AlignLeft
|
||||||
|
wrapMode: Text.WordWrap;
|
||||||
|
leftPadding: 0
|
||||||
|
topPadding: 0
|
||||||
|
width: parent.width - (icon.width + icon.anchors.leftMargin + anchors.leftMargin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.StandardButton {
|
Rectangle {
|
||||||
id: button
|
id: bottomDivider
|
||||||
small: true
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: 1
|
||||||
|
color: MoneroComponents.Style.dividerColor
|
||||||
|
opacity: MoneroComponents.Style.dividerOpacity
|
||||||
|
visible: settingsListItem.isLast
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: root.color = MoneroComponents.Style.titleBarButtonHoverColor
|
||||||
|
onExited: root.color = "transparent"
|
||||||
onClicked: {
|
onClicked: {
|
||||||
settingsListItem.clicked()
|
settingsListItem.clicked()
|
||||||
}
|
}
|
||||||
width: 135
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
68
components/Slider.qml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 2.0 as QtQuickControls
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import "../components" as MoneroComponents
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
property alias from: slider.from
|
||||||
|
property alias stepSize: slider.stepSize
|
||||||
|
property alias to: slider.to
|
||||||
|
property alias value: slider.value
|
||||||
|
|
||||||
|
property alias text: label.text
|
||||||
|
|
||||||
|
signal moved()
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
|
font.pixelSize: 14
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
QtQuickControls.Slider {
|
||||||
|
id: slider
|
||||||
|
leftPadding: 0
|
||||||
|
snapMode: QtQuickControls.Slider.SnapAlways
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
x: parent.leftPadding
|
||||||
|
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
||||||
|
implicitWidth: 200
|
||||||
|
implicitHeight: 4
|
||||||
|
width: parent.availableWidth
|
||||||
|
height: implicitHeight
|
||||||
|
radius: 2
|
||||||
|
color: MoneroComponents.Style.progressBarBackgroundColor
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.visualPosition * parent.width
|
||||||
|
height: parent.height
|
||||||
|
color: MoneroComponents.Style.green
|
||||||
|
radius: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle: Rectangle {
|
||||||
|
x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - width)
|
||||||
|
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
||||||
|
implicitWidth: 18
|
||||||
|
implicitHeight: 18
|
||||||
|
radius: 8
|
||||||
|
color: parent.pressed ? "#f0f0f0" : "#f6f6f6"
|
||||||
|
border.color: MoneroComponents.Style.grey
|
||||||
|
}
|
||||||
|
|
||||||
|
onMoved: parent.moved()
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.NoButton
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,11 +33,17 @@ import "../components" as MoneroComponents
|
|||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: button
|
id: button
|
||||||
|
property bool primary: true
|
||||||
property string rightIcon: ""
|
property string rightIcon: ""
|
||||||
property string rightIconInactive: ""
|
property string rightIconInactive: ""
|
||||||
property string textColor: button.enabled? MoneroComponents.Style.buttonTextColor: MoneroComponents.Style.buttonTextColorDisabled
|
property color textColor: !button.enabled
|
||||||
|
? MoneroComponents.Style.buttonTextColorDisabled
|
||||||
|
: primary
|
||||||
|
? MoneroComponents.Style.buttonTextColor
|
||||||
|
: MoneroComponents.Style.buttonSecondaryTextColor;
|
||||||
property bool small: false
|
property bool small: false
|
||||||
property alias text: label.text
|
property alias text: label.text
|
||||||
|
property alias fontBold: label.font.bold
|
||||||
property int fontSize: {
|
property int fontSize: {
|
||||||
if(small) return 14;
|
if(small) return 14;
|
||||||
else return 16;
|
else return 16;
|
||||||
@@ -70,7 +76,9 @@ Item {
|
|||||||
when: buttonArea.containsMouse || button.focus
|
when: buttonArea.containsMouse || button.focus
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonRect
|
target: buttonRect
|
||||||
color: MoneroComponents.Style.buttonBackgroundColorHover
|
color: primary
|
||||||
|
? MoneroComponents.Style.buttonBackgroundColorHover
|
||||||
|
: MoneroComponents.Style.buttonSecondaryBackgroundColorHover
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
@@ -78,7 +86,9 @@ Item {
|
|||||||
when: button.enabled
|
when: button.enabled
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: buttonRect
|
target: buttonRect
|
||||||
color: MoneroComponents.Style.buttonBackgroundColor
|
color: primary
|
||||||
|
? MoneroComponents.Style.buttonBackgroundColor
|
||||||
|
: MoneroComponents.Style.buttonSecondaryBackgroundColor
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
State {
|
State {
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ Rectangle {
|
|||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
root.visible = false;
|
root.visible = false;
|
||||||
|
// reset button text
|
||||||
|
okButton.text = qsTr("OK")
|
||||||
|
cancelButton.text = qsTr("Cancel")
|
||||||
|
|
||||||
closeCallback();
|
closeCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,11 +58,6 @@ Item {
|
|||||||
|
|
||||||
onExpandedChanged: if(expanded) appWindow.currentItem = dropdown
|
onExpandedChanged: if(expanded) appWindow.currentItem = dropdown
|
||||||
|
|
||||||
// Workaroud for suspected memory leak in 5.8 causing malloc crash on app exit
|
|
||||||
function update() {
|
|
||||||
firstColText.text = columnid.currentIndex < repeater.model.rowCount() ? qsTr(repeater.model.get(columnid.currentIndex).column1) + translationManager.emptyString : ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: head
|
id: head
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
@@ -80,15 +75,17 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.TextPlain {
|
MoneroComponents.TextPlain {
|
||||||
id: firstColText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 12
|
anchors.leftMargin: 12
|
||||||
|
anchors.right: dropIndicator.left
|
||||||
|
anchors.rightMargin: 12
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
font.family: MoneroComponents.Style.fontRegular.name
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
font.bold: dropdown.headerFontBold
|
font.bold: dropdown.headerFontBold
|
||||||
font.pixelSize: dropdown.fontHeaderSize
|
font.pixelSize: dropdown.fontHeaderSize
|
||||||
color: dropdown.textColor
|
color: dropdown.textColor
|
||||||
|
text: columnid.currentIndex < repeater.model.count ? qsTr(repeater.model.get(columnid.currentIndex).column1) + translationManager.emptyString : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -96,7 +93,8 @@ Item {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
width: 32
|
anchors.rightMargin: 12
|
||||||
|
width: dropdownIcon.width
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: dropdownIcon
|
id: dropdownIcon
|
||||||
@@ -214,7 +212,6 @@ Item {
|
|||||||
popup.close()
|
popup.close()
|
||||||
columnid.currentIndex = index
|
columnid.currentIndex = index
|
||||||
changed();
|
changed();
|
||||||
dropdown.update()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ QtObject {
|
|||||||
property string textSelectedColor: blackTheme ? _b_textSelectedColor : _w_textSelectedColor
|
property string textSelectedColor: blackTheme ? _b_textSelectedColor : _w_textSelectedColor
|
||||||
|
|
||||||
property string inputBoxBackground: blackTheme ? _b_inputBoxBackground : _w_inputBoxBackground
|
property string inputBoxBackground: blackTheme ? _b_inputBoxBackground : _w_inputBoxBackground
|
||||||
|
property string inputBoxBackgroundDisabled: blackTheme ? _b_inputBoxBackgroundDisabled : _w_inputBoxBackgroundDisabled
|
||||||
property string inputBoxBackgroundError: blackTheme ? _b_inputBoxBackgroundError : _w_inputBoxBackgroundError
|
property string inputBoxBackgroundError: blackTheme ? _b_inputBoxBackgroundError : _w_inputBoxBackgroundError
|
||||||
property string inputBoxColor: blackTheme ? _b_inputBoxColor : _w_inputBoxColor
|
property string inputBoxColor: blackTheme ? _b_inputBoxColor : _w_inputBoxColor
|
||||||
property string legacy_placeholderFontColor: blackTheme ? _b_legacy_placeholderFontColor : _w_legacy_placeholderFontColor
|
property string legacy_placeholderFontColor: blackTheme ? _b_legacy_placeholderFontColor : _w_legacy_placeholderFontColor
|
||||||
@@ -43,6 +44,9 @@ QtObject {
|
|||||||
property string buttonInlineBackgroundColor: blackTheme ? _b_buttonInlineBackgroundColor : _w_buttonInlineBackgroundColor
|
property string buttonInlineBackgroundColor: blackTheme ? _b_buttonInlineBackgroundColor : _w_buttonInlineBackgroundColor
|
||||||
property string buttonTextColor: blackTheme ? _b_buttonTextColor : _w_buttonTextColor
|
property string buttonTextColor: blackTheme ? _b_buttonTextColor : _w_buttonTextColor
|
||||||
property string buttonTextColorDisabled: blackTheme ? _b_buttonTextColorDisabled : _w_buttonTextColorDisabled
|
property string buttonTextColorDisabled: blackTheme ? _b_buttonTextColorDisabled : _w_buttonTextColorDisabled
|
||||||
|
property string buttonSecondaryBackgroundColor: "#d9d9d9"
|
||||||
|
property string buttonSecondaryBackgroundColorHover: "#a6a6a6"
|
||||||
|
property string buttonSecondaryTextColor: "#4d4d4d"
|
||||||
property string dividerColor: blackTheme ? _b_dividerColor : _w_dividerColor
|
property string dividerColor: blackTheme ? _b_dividerColor : _w_dividerColor
|
||||||
property real dividerOpacity: blackTheme ? _b_dividerOpacity : _w_dividerOpacity
|
property real dividerOpacity: blackTheme ? _b_dividerOpacity : _w_dividerOpacity
|
||||||
|
|
||||||
@@ -85,6 +89,7 @@ QtObject {
|
|||||||
property string _b_textSelectedColor: "white"
|
property string _b_textSelectedColor: "white"
|
||||||
|
|
||||||
property string _b_inputBoxBackground: "black"
|
property string _b_inputBoxBackground: "black"
|
||||||
|
property string _b_inputBoxBackgroundDisabled: Qt.rgba(255, 255, 255, 0.10)
|
||||||
property string _b_inputBoxBackgroundError: "#FFDDDD"
|
property string _b_inputBoxBackgroundError: "#FFDDDD"
|
||||||
property string _b_inputBoxColor: "white"
|
property string _b_inputBoxColor: "white"
|
||||||
property string _b_legacy_placeholderFontColor: "#BABABA"
|
property string _b_legacy_placeholderFontColor: "#BABABA"
|
||||||
@@ -141,6 +146,7 @@ QtObject {
|
|||||||
property string _w_textSelectedColor: "black"
|
property string _w_textSelectedColor: "black"
|
||||||
|
|
||||||
property string _w_inputBoxBackground: "white"
|
property string _w_inputBoxBackground: "white"
|
||||||
|
property string _w_inputBoxBackgroundDisabled: Qt.rgba(0, 0, 0, 0.20)
|
||||||
property string _w_inputBoxBackgroundError: "#FFDDDD"
|
property string _w_inputBoxBackgroundError: "#FFDDDD"
|
||||||
property string _w_inputBoxColor: "black"
|
property string _w_inputBoxColor: "black"
|
||||||
property string _w_legacy_placeholderFontColor: "#BABABA"
|
property string _w_legacy_placeholderFontColor: "#BABABA"
|
||||||
|
|||||||
@@ -309,8 +309,8 @@ Rectangle {
|
|||||||
width: 16
|
width: 16
|
||||||
image: MoneroComponents.Style.titleBarCloseSource
|
image: MoneroComponents.Style.titleBarCloseSource
|
||||||
color: MoneroComponents.Style.defaultFontColor
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
fontAwesomeFallbackIcon: FontAwesome.timesRectangle
|
fontAwesomeFallbackIcon: FontAwesome.times
|
||||||
fontAwesomeFallbackSize: 18
|
fontAwesomeFallbackSize: 21
|
||||||
fontAwesomeFallbackOpacity: MoneroComponents.Style.blackTheme ? 0.8 : 0.6
|
fontAwesomeFallbackOpacity: MoneroComponents.Style.blackTheme ? 0.8 : 0.6
|
||||||
opacity: 0.75
|
opacity: 0.75
|
||||||
}
|
}
|
||||||
|
|||||||
203
components/UpdateDialog.qml
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
// Copyright (c) 2020, 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.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import moneroComponents.Downloader 1.0
|
||||||
|
|
||||||
|
import "../components" as MoneroComponents
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: updateDialog
|
||||||
|
|
||||||
|
property bool active: false
|
||||||
|
property bool allowed: true
|
||||||
|
property string error: ""
|
||||||
|
property string filename: ""
|
||||||
|
property string hash: ""
|
||||||
|
property double progress: url && downloader.total > 0 ? downloader.loaded * 100 / downloader.total : 0
|
||||||
|
property string url: ""
|
||||||
|
property bool valid: false
|
||||||
|
property string version: ""
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
border.color: MoneroComponents.Style.appWindowBorderColor
|
||||||
|
border.width: 1
|
||||||
|
color: MoneroComponents.Style.middlePanelBackgroundColor
|
||||||
|
}
|
||||||
|
closePolicy: Popup.NoAutoClose
|
||||||
|
padding: 20
|
||||||
|
visible: active && allowed
|
||||||
|
|
||||||
|
function show(version, url, hash) {
|
||||||
|
updateDialog.error = "";
|
||||||
|
updateDialog.hash = hash;
|
||||||
|
updateDialog.url = url;
|
||||||
|
updateDialog.valid = false;
|
||||||
|
updateDialog.version = version;
|
||||||
|
updateDialog.active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: mainLayout
|
||||||
|
spacing: updateDialog.padding
|
||||||
|
|
||||||
|
Text {
|
||||||
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
|
font.bold: true
|
||||||
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
|
font.pixelSize: 18
|
||||||
|
text: qsTr("New Monero version v%1 is available.").arg(updateDialog.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: errorText
|
||||||
|
color: "red"
|
||||||
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
|
font.pixelSize: 18
|
||||||
|
text: updateDialog.error
|
||||||
|
visible: text
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: statusText
|
||||||
|
color: updateDialog.valid ? MoneroComponents.Style.green : MoneroComponents.Style.defaultFontColor
|
||||||
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
|
font.pixelSize: 18
|
||||||
|
visible: !errorText.visible
|
||||||
|
|
||||||
|
text: {
|
||||||
|
if (!updateDialog.url) {
|
||||||
|
return qsTr("Please visit getmonero.org for details") + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
if (downloader.active) {
|
||||||
|
return "%1 (%2%)"
|
||||||
|
.arg(qsTr("Downloading"))
|
||||||
|
.arg(updateDialog.progress.toFixed(1))
|
||||||
|
+ translationManager.emptyString;
|
||||||
|
}
|
||||||
|
if (updateDialog.valid) {
|
||||||
|
return qsTr("Update downloaded, signature verified") + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
return qsTr("Do you want to download and verify new version?") + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: progressBar
|
||||||
|
color: MoneroComponents.Style.lightGreyFontColor
|
||||||
|
height: 3
|
||||||
|
Layout.fillWidth: true
|
||||||
|
visible: updateDialog.valid || downloader.active
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: MoneroComponents.Style.buttonBackgroundColor
|
||||||
|
height: parent.height
|
||||||
|
width: parent.width * updateDialog.progress / 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
spacing: parent.spacing
|
||||||
|
|
||||||
|
MoneroComponents.StandardButton {
|
||||||
|
id: cancelButton
|
||||||
|
fontBold: false
|
||||||
|
primary: !updateDialog.url
|
||||||
|
text: {
|
||||||
|
if (!updateDialog.url) {
|
||||||
|
return qsTr("Ok") + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
if (updateDialog.valid || downloader.active || errorText.visible) {
|
||||||
|
return qsTr("Cancel") + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
return qsTr("Download later") + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
downloader.cancel();
|
||||||
|
updateDialog.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.StandardButton {
|
||||||
|
id: downloadButton
|
||||||
|
KeyNavigation.tab: cancelButton
|
||||||
|
fontBold: false
|
||||||
|
text: (updateDialog.error ? qsTr("Retry") : qsTr("Download")) + translationManager.emptyString
|
||||||
|
visible: updateDialog.url && !updateDialog.valid && !downloader.active
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
updateDialog.error = "";
|
||||||
|
updateDialog.filename = updateDialog.url.replace(/^.*\//, '');
|
||||||
|
const downloadingStarted = downloader.get(updateDialog.url, updateDialog.hash, function(error) {
|
||||||
|
if (error) {
|
||||||
|
console.error("Download failed", error);
|
||||||
|
updateDialog.error = qsTr("Download failed") + translationManager.emptyString;
|
||||||
|
} else {
|
||||||
|
updateDialog.valid = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!downloadingStarted) {
|
||||||
|
updateDialog.error = qsTr("Failed to start download") + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.StandardButton {
|
||||||
|
id: saveButton
|
||||||
|
KeyNavigation.tab: cancelButton
|
||||||
|
fontBold: false
|
||||||
|
onClicked: {
|
||||||
|
const fullPath = oshelper.openSaveFileDialog(
|
||||||
|
qsTr("Save as") + translationManager.emptyString,
|
||||||
|
oshelper.downloadLocation(),
|
||||||
|
updateDialog.filename);
|
||||||
|
if (!fullPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (downloader.saveToFile(fullPath)) {
|
||||||
|
cancelButton.clicked();
|
||||||
|
oshelper.openContainingFolder(fullPath);
|
||||||
|
} else {
|
||||||
|
updateDialog.error = qsTr("Save operation failed") + translationManager.emptyString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
text: qsTr("Save to file") + translationManager.emptyString
|
||||||
|
visible: updateDialog.valid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Downloader {
|
||||||
|
id: downloader
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -149,6 +149,7 @@ Object {
|
|||||||
property string caretUp : "\uf0d8"
|
property string caretUp : "\uf0d8"
|
||||||
property string cartArrowDown : "\uf218"
|
property string cartArrowDown : "\uf218"
|
||||||
property string cartPlus : "\uf217"
|
property string cartPlus : "\uf217"
|
||||||
|
property string cashRegister: "\uf788"
|
||||||
property string cc : "\uf20a"
|
property string cc : "\uf20a"
|
||||||
property string ccAmex : "\uf1f3"
|
property string ccAmex : "\uf1f3"
|
||||||
property string ccDinersClub : "\uf24c"
|
property string ccDinersClub : "\uf24c"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ if [ ! -d $MONERO_DIR/src ]; then
|
|||||||
fi
|
fi
|
||||||
git submodule update --remote
|
git submodule update --remote
|
||||||
git -C $MONERO_DIR fetch
|
git -C $MONERO_DIR fetch
|
||||||
git -C $MONERO_DIR checkout v0.15.0.5
|
git -C $MONERO_DIR checkout v0.16.0.1
|
||||||
|
|
||||||
# get monero core tag
|
# get monero core tag
|
||||||
pushd $MONERO_DIR
|
pushd $MONERO_DIR
|
||||||
|
|||||||
BIN
images/busy-indicator.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
images/busy-indicator@2x.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 493 B |
2
images/monero-vector.svg
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg version="1.1" viewBox="0 0 6000 6000" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/></cc:Work></rdf:RDF></metadata><defs><clipPath id="a"><path d="m0 4500h4500v-4500h-4500z"/></clipPath></defs><g transform="matrix(1.3333 0 0 -1.3333 0 6e3)"><g clip-path="url(#a)"><g transform="translate(4128 2250.2)"><path d="m0 0c0-1037.2-840.79-1878.1-1878.1-1878.1-1037.2 0-1878 840.88-1878 1878.1 0 1037.3 840.8 1878.1 1878 1878.1 1037.3 0 1878.1-840.79 1878.1-1878.1" fill="#fff"/></g><g transform="translate(2250 4128.2)"><path d="m0 0c-1036.9 0-1879.1-842.06-1877.8-1878 0.262-207.26 33.308-406.63 95.342-593.12h561.88v1579.9l1220.6-1220.6 1220.6 1220.6v-1579.9h561.96c62.117 186.48 95.008 385.85 95.369 593.12 1.809 1037-840.89 1877.8-1877.9 1877.8z" fill="#f36e36"/></g><g transform="translate(1969.3 1735.8)"><path d="m0 0-532.67 532.7v-994.14h-407.26l-384.29-0.07c329.63-540.8 925.35-902.56 1604.9-902.56 679.54 0 1275.3 361.85 1605 902.65l-384.44-0.013h-407.27v994.14l-813.3-813.31-280.62 280.61z" fill="#575757"/></g></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 596 B |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 440 B |
|
Before Width: | Height: | Size: 575 B |
@@ -1,8 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="18" viewBox="0 0 10 18">
|
|
||||||
<g fill="none" fill-rule="evenodd" opacity="1">
|
|
||||||
<path fill="none" d="M-13-9h36v36h-36z" opacity="1"/>
|
|
||||||
<g fill="#000" fill-rule="nonzero">
|
|
||||||
<path d="M5 0C3.75 0 2.571.468 1.643 1.296A5.057 5.057 0 0 0 0 5.04c0 .396.321.72.714.72h.715a.72.72 0 0 0 .714-.72c0-.828.357-1.584.964-2.16A2.823 2.823 0 0 1 5 2.16c.107 0 .214 0 .321.036 1.322.144 2.358 1.224 2.5 2.52.143 1.188-.464 2.304-1.5 2.88-1.5.792-2.428 2.304-2.428 3.96v2.124c0 .396.321.72.714.72h.714a.72.72 0 0 0 .715-.72v-2.124c0-.828.5-1.62 1.285-2.052A4.98 4.98 0 0 0 9.93 4.5C9.714 2.16 7.857.288 5.57.036 5.393 0 5.18 0 5 0zM5.714 18H4.286a.358.358 0 0 1-.357-.36V16.2c0-.2.16-.36.357-.36h1.428c.198 0 .357.16.357.36v1.44c0 .2-.16.36-.357.36z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 848 B |
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2014-2019, The Monero Project
|
Copyright (c) 2014-2020, The Monero Project
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
; Monero Carbon Chamaeleon GUI Wallet Installer for Windows
|
; Monero Nitrogen Nebula GUI Wallet Installer for Windows
|
||||||
; Copyright (c) 2017-2019, The Monero Project
|
; Copyright (c) 2017-2020, The Monero Project
|
||||||
; See LICENSE
|
; See LICENSE
|
||||||
#define GuiVersion GetFileVersion("bin\monero-wallet-gui.exe")
|
#define GuiVersion GetFileVersion("bin\monero-wallet-gui.exe")
|
||||||
|
|
||||||
@@ -62,7 +62,6 @@ Name: "en"; MessagesFile: "compiler:Default.isl"
|
|||||||
; .exe/.dll file possibly with version info).
|
; .exe/.dll file possibly with version info).
|
||||||
;
|
;
|
||||||
; This is far more robust than relying on version info or on file dates (flag "comparetimestamp").
|
; This is far more robust than relying on version info or on file dates (flag "comparetimestamp").
|
||||||
; As of version 0.15.0.0, the Monero .exe files do not carry version info anyway in their .exe headers.
|
|
||||||
; The only small drawback seems to be somewhat longer update times because each and every file is
|
; The only small drawback seems to be somewhat longer update times because each and every file is
|
||||||
; copied again, even if already present with correct file date and identical content.
|
; copied again, even if already present with correct file date and identical content.
|
||||||
;
|
;
|
||||||
@@ -71,17 +70,18 @@ Name: "en"; MessagesFile: "compiler:Default.isl"
|
|||||||
|
|
||||||
Source: {#file AddBackslash(SourcePath) + "ReadMe.htm"}; DestDir: "{app}"; DestName: "ReadMe.htm"; Flags: ignoreversion
|
Source: {#file AddBackslash(SourcePath) + "ReadMe.htm"}; DestDir: "{app}"; DestName: "ReadMe.htm"; Flags: ignoreversion
|
||||||
Source: "FinishImage.bmp"; Flags: dontcopy
|
Source: "FinishImage.bmp"; Flags: dontcopy
|
||||||
|
Source: "LICENSE"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|
||||||
; Monero GUI wallet exe and guide
|
; Monero GUI wallet exe and guide
|
||||||
Source: "bin\monero-wallet-gui.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\monero-wallet-gui.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-gui-wallet-guide.pdf"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\monero-gui-wallet-guide.pdf"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|
||||||
; Monero CLI wallet
|
; Monero CLI wallet
|
||||||
Source: "bin\monero-wallet-cli.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-wallet-cli.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-gen-trusted-multisig.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-gen-trusted-multisig.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|
||||||
; Monero wallet RPC interface implementation
|
; Monero wallet RPC interface implementation
|
||||||
Source: "bin\monero-wallet-rpc.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-wallet-rpc.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|
||||||
; Monero daemon
|
; Monero daemon
|
||||||
Source: "bin\monerod.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\monerod.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
@@ -90,16 +90,17 @@ Source: "bin\monerod.exe"; DestDir: "{app}"; Flags: ignoreversion
|
|||||||
Source: "monero-daemon.bat"; DestDir: "{app}"; Flags: ignoreversion;
|
Source: "monero-daemon.bat"; DestDir: "{app}"; Flags: ignoreversion;
|
||||||
|
|
||||||
; Monero blockchain utilities
|
; Monero blockchain utilities
|
||||||
Source: "bin\monero-blockchain-export.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-export.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-mark-spent-outputs.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-mark-spent-outputs.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-usage.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-usage.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-import.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-ancestry.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-ancestry.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-depth.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-depth.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-prune-known-spent-data.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-prune-known-spent-data.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-prune.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-prune.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
Source: "bin\monero-blockchain-stats.exe"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\extras\monero-blockchain-stats.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
Source: "bin\extras\monero-gen-ssl-cert.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
|
|
||||||
; Qt Quick 2D Renderer fallback for systems / environments with "low-level graphics" i.e. without 3D support
|
; Qt Quick 2D Renderer fallback for systems / environments with "low-level graphics" i.e. without 3D support
|
||||||
Source: "bin\start-low-graphics-mode.bat"; DestDir: "{app}"; Flags: ignoreversion
|
Source: "bin\start-low-graphics-mode.bat"; DestDir: "{app}"; Flags: ignoreversion
|
||||||
@@ -202,7 +203,7 @@ begin
|
|||||||
// Additional wizard page for entering a special blockchain location
|
// Additional wizard page for entering a special blockchain location
|
||||||
blockChainDefaultDir := ExpandConstant('{commonappdata}\bitmonero');
|
blockChainDefaultDir := ExpandConstant('{commonappdata}\bitmonero');
|
||||||
s := 'The default folder to store the Monero blockchain is ' + blockChainDefaultDir;
|
s := 'The default folder to store the Monero blockchain is ' + blockChainDefaultDir;
|
||||||
s := s + '. As this will need more than 74 GB of free space, you may want to use a folder on a different drive.';
|
s := s + '. As this will need more than 90 GB of free space, you may want to use a folder on a different drive.';
|
||||||
s := s + ' If yes, specify that folder here.';
|
s := s + ' If yes, specify that folder here.';
|
||||||
|
|
||||||
BlockChainDirPage := CreateInputDirPage(wpSelectDir,
|
BlockChainDirPage := CreateInputDirPage(wpSelectDir,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Monero GUI Wallet Windows Installer #
|
# Monero GUI Wallet Windows Installer #
|
||||||
|
|
||||||
Copyright (c) 2017-2019, The Monero Project
|
Copyright (c) 2017-2020, The Monero Project
|
||||||
|
|
||||||
## Introduction ##
|
## Introduction ##
|
||||||
|
|
||||||
This is a *Inno Setup* script `Monero.iss` plus some related files
|
This is a *Inno Setup* script `Monero.iss` plus some related files
|
||||||
that allows you to build a standalone Windows installer (.exe) for
|
that allows you to build a standalone Windows installer (.exe) for
|
||||||
the GUI wallet that comes with the Carbon Chamaeleon release of Monero.
|
the GUI wallet that comes with the Nitrogen Nebula release of Monero.
|
||||||
|
|
||||||
This turns the GUI wallet into a more or less standard Windows program,
|
This turns the GUI wallet into a more or less standard Windows program,
|
||||||
by default installed into a subdirectory of `C:\Program Files`, a
|
by default installed into a subdirectory of `C:\Program Files`, a
|
||||||
@@ -18,7 +18,7 @@ Monero.
|
|||||||
As the setup script in file [Monero.iss](Monero.iss) has to list many
|
As the setup script in file [Monero.iss](Monero.iss) has to list many
|
||||||
files and directories of the GUI wallet package to install by name,
|
files and directories of the GUI wallet package to install by name,
|
||||||
this version of the script only works with exactly the GUI wallet
|
this version of the script only works with exactly the GUI wallet
|
||||||
for Monero release *Carbon Chamaeleon* that you find on
|
for Monero release *Nitrogen Nebula* that you find on
|
||||||
[the official download page](https://getmonero.org/downloads/).
|
[the official download page](https://getmonero.org/downloads/).
|
||||||
|
|
||||||
It should however be easy to modify the script for future
|
It should however be easy to modify the script for future
|
||||||
@@ -32,15 +32,15 @@ See [LICENSE](LICENSE).
|
|||||||
|
|
||||||
You can only build on Windows, and the result is always a
|
You can only build on Windows, and the result is always a
|
||||||
Windows .exe file that can act as a standalone installer for the
|
Windows .exe file that can act as a standalone installer for the
|
||||||
Carbon Chamaeleon GUI wallet.
|
Nitrogen Nebula GUI wallet.
|
||||||
|
|
||||||
Note that the installer build process is now reproducible / deterministic. For details check the file [Deterministic.md](Deterministic.md).
|
Note that the installer build process is now reproducible / deterministic. For details check the file [Deterministic.md](Deterministic.md).
|
||||||
|
|
||||||
The build steps in detail:
|
The build steps in detail:
|
||||||
|
|
||||||
1. Install *Inno Setup*. You can get it from [here](http://www.jrsoftware.org/isdl.php)
|
1. Install *Inno Setup*. You can get it from [here](http://www.jrsoftware.org/isdl.php)
|
||||||
2. Get the Inno Setup script plus related files by cloning the whole [monero-gui GitHub repository](https://github.com/monero-project/monero-gui); you will only need the files in the installer directory `installers\windows` however. Depending on development state, additionally instead of simply using `master` you may have to checkout a specific branch, like `release-v0.15`.
|
2. Get the Inno Setup script plus related files by cloning the whole [monero-gui GitHub repository](https://github.com/monero-project/monero-gui); you will only need the files in the installer directory `installers\windows` however. Depending on development state, additionally instead of simply using `master` you may have to checkout a specific branch, like `release-v0.16`.
|
||||||
3. The setup script is written to take the GUI wallet files from a subdirectory named `bin`; so create `installers\windows\bin`, get the zip file of the GUI wallet from [here](https://getmonero.org/downloads/), unpack it somewhere, and copy all the files and subdirectories in the single subdirectory there (currently named `monero-gui-0.15.0.0`) to this `bin` subdirectory
|
3. The setup script is written to take the GUI wallet files from a subdirectory named `bin`; so create `installers\windows\bin`, get the zip file of the GUI wallet from [here](https://getmonero.org/downloads/), unpack it somewhere, and copy all the files and subdirectories in the single subdirectory there (currently named `monero-gui-0.16.0.0`) to this `bin` subdirectory
|
||||||
4. Start Inno Setup, load `Monero.iss` and compile it
|
4. Start Inno Setup, load `Monero.iss` and compile it
|
||||||
5. The result i.e. the finished installer will be the file `mysetup.exe` in the `installers\windows\Output` subdirectory
|
5. The result i.e. the finished installer will be the file `mysetup.exe` in the `installers\windows\Output` subdirectory
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Monero Carbon Chamaeleon GUI Wallet</title>
|
<title>Monero Nitrogen Nebula GUI Wallet</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body style="font-family: Arial, Helvetica, sans-serif">
|
<body style="font-family: Arial, Helvetica, sans-serif">
|
||||||
<h1>Monero Carbon Chamaeleon GUI Wallet</h1>
|
<h1>Monero Nitrogen Nebula GUI Wallet</h1>
|
||||||
|
|
||||||
<p>Copyright (c) 2014-2019, The Monero Project</p>
|
<p>Copyright (c) 2014-2020, The Monero Project</p>
|
||||||
|
|
||||||
<h2>Preface</h2>
|
<h2>Preface</h2>
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
<h2>Content of the Package</h2>
|
<h2>Content of the Package</h2>
|
||||||
|
|
||||||
<p>You just installed the <i>Monero GUI wallet</i> for Windows, release Carbon Chamaeleon, version {#GuiVersion}.
|
<p>You just installed the <i>Monero GUI wallet</i> for Windows, release Nitrogen Nebula, version {#GuiVersion}.
|
||||||
The wallet enables you to send and receive Moneroj in a secure and very private way.
|
The wallet enables you to send and receive Moneroj in a secure and very private way.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
provides the most security and privacy possible for you.</p>
|
provides the most security and privacy possible for you.</p>
|
||||||
|
|
||||||
<p>However if your Internet access makes it difficult to run a full node, or if you have simply no room to store
|
<p>However if your Internet access makes it difficult to run a full node, or if you have simply no room to store
|
||||||
the blockchain locally (somewhat over 74 GB in November 2019, and of course growing), you can compromise and try to connect
|
the blockchain locally (about 90 GB in May 2020, and of course growing), you can compromise and try to connect
|
||||||
to a remote node. One way of finding such a node is checking
|
to a remote node. One way of finding such a node is checking
|
||||||
<a href="https://moneroworld.com/#nodes">this page</a>.
|
<a href="https://moneroworld.com/#nodes">this page</a>.
|
||||||
</p>
|
</p>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
<p>The Monero software and especially the GUI wallet are "work in progress", and sometimes things go wrong.</p>
|
<p>The Monero software and especially the GUI wallet are "work in progress", and sometimes things go wrong.</p>
|
||||||
|
|
||||||
<p>Please note that despite any technical problems that you may encounter your moneroj are almost always safe: You may
|
<p>Please note that despite any technical problems that you may encounter your Moneroj are almost always safe: You may
|
||||||
not be able to move them or you even may not see how many you currently have, but you most probably won't loose any.
|
not be able to move them or you even may not see how many you currently have, but you most probably won't loose any.
|
||||||
But do remember that the seed needed to re-create the wallet <b>is</b> critical, however: <b>Never loose your
|
But do remember that the seed needed to re-create the wallet <b>is</b> critical, however: <b>Never loose your
|
||||||
seed!</b></p>
|
seed!</b></p>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 440 KiB After Width: | Height: | Size: 440 KiB |
@@ -133,3 +133,7 @@ function capitalize(s){
|
|||||||
if (typeof s !== 'string') return ''
|
if (typeof s !== 'string') return ''
|
||||||
return s.charAt(0).toUpperCase() + s.slice(1)
|
return s.charAt(0).toUpperCase() + s.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeTrailingZeros(value) {
|
||||||
|
return (value + '').replace(/(\.\d*[1-9])0+$/, '$1');
|
||||||
|
}
|
||||||
|
|||||||
11
js/Wizard.js
@@ -58,11 +58,6 @@ function switchPage(next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createWalletPath(isIOS, folder_path,account_name){
|
function createWalletPath(isIOS, folder_path,account_name){
|
||||||
// Remove trailing slash - (default on windows and mac)
|
|
||||||
if (folder_path.substring(folder_path.length -1) === "/"){
|
|
||||||
folder_path = folder_path.substring(0,folder_path.length -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store releative path on ios.
|
// Store releative path on ios.
|
||||||
if(isIOS)
|
if(isIOS)
|
||||||
folder_path = "";
|
folder_path = "";
|
||||||
@@ -102,10 +97,6 @@ function tr(text) {
|
|||||||
return qsTr(text) + translationManager.emptyString
|
return qsTr(text) + translationManager.emptyString
|
||||||
}
|
}
|
||||||
|
|
||||||
function lineBreaksToSpaces(text) {
|
|
||||||
return text.trim().replace(/(\r\n|\n|\r)/gm, " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
function usefulName(path) {
|
function usefulName(path) {
|
||||||
// arbitrary "short enough" limit
|
// arbitrary "short enough" limit
|
||||||
if (path.length < 32)
|
if (path.length < 32)
|
||||||
@@ -115,7 +106,7 @@ function usefulName(path) {
|
|||||||
|
|
||||||
function checkSeed(seed) {
|
function checkSeed(seed) {
|
||||||
console.log("Checking seed")
|
console.log("Checking seed")
|
||||||
var wordsArray = lineBreaksToSpaces(seed).split(" ");
|
var wordsArray = seed.split(/\s+/);
|
||||||
return wordsArray.length === 25 || wordsArray.length === 24
|
return wordsArray.length === 25 || wordsArray.length === 24
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
316
main.qml
@@ -45,6 +45,7 @@ import "pages/merchant" as MoneroMerchant
|
|||||||
import "wizard"
|
import "wizard"
|
||||||
import "js/Utils.js" as Utils
|
import "js/Utils.js" as Utils
|
||||||
import "js/Windows.js" as Windows
|
import "js/Windows.js" as Windows
|
||||||
|
import "version.js" as Version
|
||||||
|
|
||||||
ApplicationWindow {
|
ApplicationWindow {
|
||||||
id: appWindow
|
id: appWindow
|
||||||
@@ -66,12 +67,14 @@ ApplicationWindow {
|
|||||||
property bool walletSynced: false
|
property bool walletSynced: false
|
||||||
property int maxWindowHeight: (isAndroid || isIOS)? screenHeight : (screenHeight < 900)? 720 : 800;
|
property int maxWindowHeight: (isAndroid || isIOS)? screenHeight : (screenHeight < 900)? 720 : 800;
|
||||||
property bool daemonRunning: !persistentSettings.useRemoteNode && !disconnected
|
property bool daemonRunning: !persistentSettings.useRemoteNode && !disconnected
|
||||||
|
property bool daemonStartStopInProgress: false
|
||||||
property alias toolTip: toolTip
|
property alias toolTip: toolTip
|
||||||
property string walletName
|
property string walletName
|
||||||
property bool viewOnly: false
|
property bool viewOnly: false
|
||||||
property bool foundNewBlock: false
|
property bool foundNewBlock: false
|
||||||
property bool qrScannerEnabled: (typeof builtWithScanner != "undefined") && builtWithScanner
|
property bool qrScannerEnabled: (typeof builtWithScanner != "undefined") && builtWithScanner
|
||||||
property int blocksToSync: 1
|
property int blocksToSync: 1
|
||||||
|
property int firstBlockSeen
|
||||||
property bool isMining: false
|
property bool isMining: false
|
||||||
property int walletMode: persistentSettings.walletMode
|
property int walletMode: persistentSettings.walletMode
|
||||||
property var cameraUi
|
property var cameraUi
|
||||||
@@ -214,7 +217,7 @@ ApplicationWindow {
|
|||||||
appWindow.viewState = prevState;
|
appWindow.viewState = prevState;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
passwordDialog.open(usefulName(walletPath()));
|
passwordDialog.open(usefulName(persistentSettings.wallet_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
@@ -250,10 +253,9 @@ ApplicationWindow {
|
|||||||
|
|
||||||
// enable timers
|
// enable timers
|
||||||
userInActivityTimer.running = true;
|
userInActivityTimer.running = true;
|
||||||
simpleModeConnectionTimer.running = true;
|
|
||||||
|
|
||||||
// wallet already opened with wizard, we just need to initialize it
|
// wallet already opened with wizard, we just need to initialize it
|
||||||
var wallet_path = walletPath();
|
var wallet_path = persistentSettings.wallet_path;
|
||||||
if(isIOS)
|
if(isIOS)
|
||||||
wallet_path = moneroAccountsDir + wallet_path;
|
wallet_path = moneroAccountsDir + wallet_path;
|
||||||
// console.log("opening wallet at: ", wallet_path, "with password: ", appWindow.walletPassword);
|
// console.log("opening wallet at: ", wallet_path, "with password: ", appWindow.walletPassword);
|
||||||
@@ -291,6 +293,7 @@ ApplicationWindow {
|
|||||||
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
|
currentWallet.connectionStatusChanged.disconnect(onWalletConnectionStatusChanged)
|
||||||
currentWallet.deviceButtonRequest.disconnect(onDeviceButtonRequest);
|
currentWallet.deviceButtonRequest.disconnect(onDeviceButtonRequest);
|
||||||
currentWallet.deviceButtonPressed.disconnect(onDeviceButtonPressed);
|
currentWallet.deviceButtonPressed.disconnect(onDeviceButtonPressed);
|
||||||
|
currentWallet.walletPassphraseNeeded.disconnect(onWalletPassphraseNeededWallet);
|
||||||
currentWallet.transactionCommitted.disconnect(onTransactionCommitted);
|
currentWallet.transactionCommitted.disconnect(onTransactionCommitted);
|
||||||
middlePanel.paymentClicked.disconnect(handlePayment);
|
middlePanel.paymentClicked.disconnect(handlePayment);
|
||||||
middlePanel.sweepUnmixableClicked.disconnect(handleSweepUnmixable);
|
middlePanel.sweepUnmixableClicked.disconnect(handleSweepUnmixable);
|
||||||
@@ -358,6 +361,7 @@ ApplicationWindow {
|
|||||||
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
|
currentWallet.connectionStatusChanged.connect(onWalletConnectionStatusChanged)
|
||||||
currentWallet.deviceButtonRequest.connect(onDeviceButtonRequest);
|
currentWallet.deviceButtonRequest.connect(onDeviceButtonRequest);
|
||||||
currentWallet.deviceButtonPressed.connect(onDeviceButtonPressed);
|
currentWallet.deviceButtonPressed.connect(onDeviceButtonPressed);
|
||||||
|
currentWallet.walletPassphraseNeeded.connect(onWalletPassphraseNeededWallet);
|
||||||
currentWallet.transactionCommitted.connect(onTransactionCommitted);
|
currentWallet.transactionCommitted.connect(onTransactionCommitted);
|
||||||
middlePanel.paymentClicked.connect(handlePayment);
|
middlePanel.paymentClicked.connect(handlePayment);
|
||||||
middlePanel.sweepUnmixableClicked.connect(handleSweepUnmixable);
|
middlePanel.sweepUnmixableClicked.connect(handleSweepUnmixable);
|
||||||
@@ -392,11 +396,6 @@ ApplicationWindow {
|
|||||||
return !persistentSettings.useRemoteNode || persistentSettings.is_trusted_daemon;
|
return !persistentSettings.useRemoteNode || persistentSettings.is_trusted_daemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
function walletPath() {
|
|
||||||
var wallet_path = persistentSettings.wallet_path
|
|
||||||
return wallet_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function usefulName(path) {
|
function usefulName(path) {
|
||||||
// arbitrary "short enough" limit
|
// arbitrary "short enough" limit
|
||||||
if (path.length < 32)
|
if (path.length < 32)
|
||||||
@@ -474,9 +473,9 @@ ApplicationWindow {
|
|||||||
console.log("Wallet connection status changed " + status)
|
console.log("Wallet connection status changed " + status)
|
||||||
middlePanel.updateStatus();
|
middlePanel.updateStatus();
|
||||||
leftPanel.networkStatus.connected = status
|
leftPanel.networkStatus.connected = status
|
||||||
|
if (status == Wallet.ConnectionStatus_Disconnected) {
|
||||||
// Update fee multiplier dropdown on transfer page
|
firstBlockSeen = 0;
|
||||||
middlePanel.transferView.updatePriorityDropdown();
|
}
|
||||||
|
|
||||||
// If wallet isnt connected, advanced wallet mode and no daemon is running - Ask
|
// If wallet isnt connected, advanced wallet mode and no daemon is running - Ask
|
||||||
if (appWindow.walletMode >= 2 && !persistentSettings.useRemoteNode && !walletInitialized && disconnected) {
|
if (appWindow.walletMode >= 2 && !persistentSettings.useRemoteNode && !walletInitialized && disconnected) {
|
||||||
@@ -558,19 +557,32 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWalletPassphraseNeeded(){
|
function onWalletPassphraseNeededManager(on_device){
|
||||||
|
onWalletPassphraseNeeded(walletManager, on_device)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWalletPassphraseNeededWallet(on_device){
|
||||||
|
onWalletPassphraseNeeded(currentWallet, on_device)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onWalletPassphraseNeeded(handler, on_device){
|
||||||
hideProcessingSplash();
|
hideProcessingSplash();
|
||||||
|
|
||||||
console.log(">>> wallet passphrase needed: ")
|
console.log(">>> wallet passphrase needed: ")
|
||||||
passwordDialog.onAcceptedPassphraseCallback = function() {
|
devicePassphraseDialog.onAcceptedCallback = function(passphrase) {
|
||||||
walletManager.onPassphraseEntered(passwordDialog.password);
|
handler.onPassphraseEntered(passphrase, false, false);
|
||||||
appWindow.onWalletOpening();
|
appWindow.onWalletOpening();
|
||||||
}
|
}
|
||||||
passwordDialog.onRejectedPassphraseCallback = function() {
|
devicePassphraseDialog.onWalletEntryCallback = function() {
|
||||||
walletManager.onPassphraseEntered("", true);
|
handler.onPassphraseEntered("", true, false);
|
||||||
appWindow.onWalletOpening();
|
appWindow.onWalletOpening();
|
||||||
}
|
}
|
||||||
passwordDialog.openPassphraseDialog()
|
devicePassphraseDialog.onRejectedCallback = function() {
|
||||||
|
handler.onPassphraseEntered("", false, true);
|
||||||
|
appWindow.onWalletOpening();
|
||||||
|
}
|
||||||
|
|
||||||
|
devicePassphraseDialog.open(on_device)
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWalletUpdate() {
|
function onWalletUpdate() {
|
||||||
@@ -613,18 +625,22 @@ ApplicationWindow {
|
|||||||
currentDaemonAddress = localDaemonAddress
|
currentDaemonAddress = localDaemonAddress
|
||||||
currentWallet.initAsync(currentDaemonAddress, isTrustedDaemon());
|
currentWallet.initAsync(currentDaemonAddress, isTrustedDaemon());
|
||||||
walletManager.setDaemonAddressAsync(currentDaemonAddress);
|
walletManager.setDaemonAddressAsync(currentDaemonAddress);
|
||||||
|
firstBlockSeen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onHeightRefreshed(bcHeight, dCurrentBlock, dTargetBlock) {
|
function onHeightRefreshed(bcHeight, dCurrentBlock, dTargetBlock) {
|
||||||
// Daemon fully synced
|
// Daemon fully synced
|
||||||
// TODO: implement onDaemonSynced or similar in wallet API and don't start refresh thread before daemon is synced
|
// TODO: implement onDaemonSynced or similar in wallet API and don't start refresh thread before daemon is synced
|
||||||
// targetBlock = currentBlock = 1 before network connection is established.
|
// targetBlock = currentBlock = 1 before network connection is established.
|
||||||
|
if (firstBlockSeen == 0 && dTargetBlock != 1) {
|
||||||
|
firstBlockSeen = dCurrentBlock;
|
||||||
|
}
|
||||||
daemonSynced = dCurrentBlock >= dTargetBlock && dTargetBlock != 1
|
daemonSynced = dCurrentBlock >= dTargetBlock && dTargetBlock != 1
|
||||||
walletSynced = bcHeight >= dTargetBlock
|
walletSynced = bcHeight >= dTargetBlock
|
||||||
|
|
||||||
// Update progress bars
|
// Update progress bars
|
||||||
if(!daemonSynced) {
|
if(!daemonSynced) {
|
||||||
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, dTargetBlock-dCurrentBlock);
|
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, dTargetBlock-firstBlockSeen);
|
||||||
leftPanel.progressBar.updateProgress(0,dTargetBlock, dTargetBlock, qsTr("Waiting for daemon to sync"));
|
leftPanel.progressBar.updateProgress(0,dTargetBlock, dTargetBlock, qsTr("Waiting for daemon to sync"));
|
||||||
} else {
|
} else {
|
||||||
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, 0, qsTr("Daemon is synchronized (%1)").arg(dCurrentBlock.toFixed(0)));
|
leftPanel.daemonProgressBar.updateProgress(dCurrentBlock,dTargetBlock, 0, qsTr("Daemon is synchronized (%1)").arg(dCurrentBlock.toFixed(0)));
|
||||||
@@ -665,12 +681,11 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startDaemon(flags){
|
function startDaemon(flags){
|
||||||
|
daemonStartStopInProgress = true;
|
||||||
|
|
||||||
// Pause refresh while starting daemon
|
// Pause refresh while starting daemon
|
||||||
currentWallet.pauseRefresh();
|
currentWallet.pauseRefresh();
|
||||||
|
|
||||||
// Pause simplemode connection timer
|
|
||||||
simpleModeConnectionTimer.stop();
|
|
||||||
|
|
||||||
appWindow.showProcessingSplash(qsTr("Waiting for daemon to start..."))
|
appWindow.showProcessingSplash(qsTr("Waiting for daemon to start..."))
|
||||||
const noSync = appWindow.walletMode === 0;
|
const noSync = appWindow.walletMode === 0;
|
||||||
const bootstrapNodeAddress = persistentSettings.walletMode < 2 ? "auto" : persistentSettings.bootstrapNodeAddress
|
const bootstrapNodeAddress = persistentSettings.walletMode < 2 ? "auto" : persistentSettings.bootstrapNodeAddress
|
||||||
@@ -678,8 +693,10 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function stopDaemon(callback){
|
function stopDaemon(callback){
|
||||||
|
daemonStartStopInProgress = true;
|
||||||
appWindow.showProcessingSplash(qsTr("Waiting for daemon to stop..."))
|
appWindow.showProcessingSplash(qsTr("Waiting for daemon to stop..."))
|
||||||
daemonManager.stopAsync(persistentSettings.nettype, function(result) {
|
daemonManager.stopAsync(persistentSettings.nettype, function(result) {
|
||||||
|
daemonStartStopInProgress = false;
|
||||||
hideProcessingSplash();
|
hideProcessingSplash();
|
||||||
callback(result);
|
callback(result);
|
||||||
});
|
});
|
||||||
@@ -687,13 +704,13 @@ ApplicationWindow {
|
|||||||
|
|
||||||
function onDaemonStarted(){
|
function onDaemonStarted(){
|
||||||
console.log("daemon started");
|
console.log("daemon started");
|
||||||
|
daemonStartStopInProgress = false;
|
||||||
hideProcessingSplash();
|
hideProcessingSplash();
|
||||||
currentWallet.connected(true);
|
currentWallet.connected(true);
|
||||||
// resume refresh
|
// resume refresh
|
||||||
currentWallet.startRefresh();
|
currentWallet.startRefresh();
|
||||||
// resume simplemode connection timer
|
// resume simplemode connection timer
|
||||||
appWindow.disconnectedEpoch = Utils.epoch();
|
appWindow.disconnectedEpoch = Utils.epoch();
|
||||||
simpleModeConnectionTimer.start();
|
|
||||||
}
|
}
|
||||||
function onDaemonStopped(){
|
function onDaemonStopped(){
|
||||||
currentWallet.connected(true);
|
currentWallet.connected(true);
|
||||||
@@ -701,6 +718,7 @@ ApplicationWindow {
|
|||||||
|
|
||||||
function onDaemonStartFailure(error) {
|
function onDaemonStartFailure(error) {
|
||||||
console.log("daemon start failed");
|
console.log("daemon start failed");
|
||||||
|
daemonStartStopInProgress = false;
|
||||||
hideProcessingSplash();
|
hideProcessingSplash();
|
||||||
// resume refresh
|
// resume refresh
|
||||||
currentWallet.startRefresh();
|
currentWallet.startRefresh();
|
||||||
@@ -976,7 +994,11 @@ ApplicationWindow {
|
|||||||
informationPopup.open()
|
informationPopup.open()
|
||||||
currentWallet.refresh()
|
currentWallet.refresh()
|
||||||
currentWallet.disposeTransaction(transaction)
|
currentWallet.disposeTransaction(transaction)
|
||||||
currentWallet.store();
|
currentWallet.storeAsync(function(success) {
|
||||||
|
if (!success) {
|
||||||
|
appWindow.showStatusMessage(qsTr("Failed to store the wallet"), 3);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// called on "getProof"
|
// called on "getProof"
|
||||||
@@ -1076,7 +1098,6 @@ ApplicationWindow {
|
|||||||
console.log("Displaying processing splash")
|
console.log("Displaying processing splash")
|
||||||
if (typeof message != 'undefined') {
|
if (typeof message != 'undefined') {
|
||||||
splash.messageText = message
|
splash.messageText = message
|
||||||
splash.heightProgressText = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
leftPanel.enabled = false;
|
leftPanel.enabled = false;
|
||||||
@@ -1103,14 +1124,15 @@ ApplicationWindow {
|
|||||||
wizard.restart();
|
wizard.restart();
|
||||||
wizard.wizardState = "wizardHome";
|
wizard.wizardState = "wizardHome";
|
||||||
rootItem.state = "wizard"
|
rootItem.state = "wizard"
|
||||||
// reset balance
|
// reset balance, clear spendable funds message
|
||||||
clearMoneroCardLabelText();
|
clearMoneroCardLabelText();
|
||||||
|
leftPanel.minutesToUnlock = "";
|
||||||
|
// reset fields
|
||||||
middlePanel.addressBookView.clearFields();
|
middlePanel.addressBookView.clearFields();
|
||||||
middlePanel.transferView.clearFields();
|
middlePanel.transferView.clearFields();
|
||||||
middlePanel.receiveView.clearFields();
|
middlePanel.receiveView.clearFields();
|
||||||
// disable timers
|
// disable timers
|
||||||
userInActivityTimer.running = false;
|
userInActivityTimer.running = false;
|
||||||
simpleModeConnectionTimer.running = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1135,9 +1157,9 @@ ApplicationWindow {
|
|||||||
triggeredOnStart: false
|
triggeredOnStart: false
|
||||||
}
|
}
|
||||||
|
|
||||||
function fiatApiParseTicker(resp, currency){
|
function fiatApiParseTicker(url, resp, currency){
|
||||||
// parse & validate incoming JSON
|
// parse & validate incoming JSON
|
||||||
if(resp._url.startsWith("https://api.kraken.com/0/")){
|
if(url.startsWith("https://api.kraken.com/0/")){
|
||||||
if(resp.hasOwnProperty("error") && resp.error.length > 0 || !resp.hasOwnProperty("result")){
|
if(resp.hasOwnProperty("error") && resp.error.length > 0 || !resp.hasOwnProperty("result")){
|
||||||
appWindow.fiatApiError("Kraken API has error(s)");
|
appWindow.fiatApiError("Kraken API has error(s)");
|
||||||
return;
|
return;
|
||||||
@@ -1146,14 +1168,14 @@ ApplicationWindow {
|
|||||||
var key = currency === "xmreur" ? "XXMRZEUR" : "XXMRZUSD";
|
var key = currency === "xmreur" ? "XXMRZEUR" : "XXMRZUSD";
|
||||||
var ticker = resp.result[key]["o"];
|
var ticker = resp.result[key]["o"];
|
||||||
return ticker;
|
return ticker;
|
||||||
} else if(resp._url.startsWith("https://api.coingecko.com/api/v3/")){
|
} else if(url.startsWith("https://api.coingecko.com/api/v3/")){
|
||||||
var key = currency === "xmreur" ? "eur" : "usd";
|
var key = currency === "xmreur" ? "eur" : "usd";
|
||||||
if(!resp.hasOwnProperty("monero") || !resp["monero"].hasOwnProperty(key)){
|
if(!resp.hasOwnProperty("monero") || !resp["monero"].hasOwnProperty(key)){
|
||||||
appWindow.fiatApiError("Coingecko API has error(s)");
|
appWindow.fiatApiError("Coingecko API has error(s)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return resp["monero"][key];
|
return resp["monero"][key];
|
||||||
} else if(resp._url.startsWith("https://min-api.cryptocompare.com/data/")){
|
} else if(url.startsWith("https://min-api.cryptocompare.com/data/")){
|
||||||
var key = currency === "xmreur" ? "EUR" : "USD";
|
var key = currency === "xmreur" ? "EUR" : "USD";
|
||||||
if(!resp.hasOwnProperty(key)){
|
if(!resp.hasOwnProperty(key)){
|
||||||
appWindow.fiatApiError("cryptocompare API has error(s)");
|
appWindow.fiatApiError("cryptocompare API has error(s)");
|
||||||
@@ -1163,13 +1185,7 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fiatApiGetCurrency(resp){
|
function fiatApiGetCurrency(url) {
|
||||||
// map response to `appWindow.fiatPriceAPIs` object
|
|
||||||
if (!resp.hasOwnProperty('_url')){
|
|
||||||
appWindow.fiatApiError("invalid JSON");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var apis = appWindow.fiatPriceAPIs;
|
var apis = appWindow.fiatPriceAPIs;
|
||||||
for (var api in apis){
|
for (var api in apis){
|
||||||
if (!apis.hasOwnProperty(api))
|
if (!apis.hasOwnProperty(api))
|
||||||
@@ -1179,23 +1195,34 @@ ApplicationWindow {
|
|||||||
if(!apis[api].hasOwnProperty(cur))
|
if(!apis[api].hasOwnProperty(cur))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var url = apis[api][cur];
|
if (apis[api][cur] === url) {
|
||||||
if(url === resp._url){
|
|
||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fiatApiJsonReceived(resp){
|
function fiatApiJsonReceived(url, resp, error) {
|
||||||
|
if (error) {
|
||||||
|
appWindow.fiatApiError(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
resp = JSON.parse(resp);
|
||||||
|
} catch (e) {
|
||||||
|
appWindow.fiatApiError("bad JSON: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// handle incoming JSON, set ticker
|
// handle incoming JSON, set ticker
|
||||||
var currency = appWindow.fiatApiGetCurrency(resp);
|
var currency = appWindow.fiatApiGetCurrency(url);
|
||||||
if(typeof currency == "undefined"){
|
if(typeof currency == "undefined"){
|
||||||
appWindow.fiatApiError("could not get currency");
|
appWindow.fiatApiError("could not get currency");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ticker = appWindow.fiatApiParseTicker(resp, currency);
|
var ticker = appWindow.fiatApiParseTicker(url, resp, currency);
|
||||||
if(ticker <= 0){
|
if(ticker <= 0){
|
||||||
appWindow.fiatApiError("could not get ticker");
|
appWindow.fiatApiError("could not get ticker");
|
||||||
return;
|
return;
|
||||||
@@ -1227,7 +1254,7 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var url = provider[userCurrency];
|
var url = provider[userCurrency];
|
||||||
Prices.getJSON(url);
|
Network.getJSON(url, fiatApiJsonReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fiatApiCurrencySymbol() {
|
function fiatApiCurrencySymbol() {
|
||||||
@@ -1283,9 +1310,8 @@ ApplicationWindow {
|
|||||||
walletManager.deviceButtonRequest.connect(onDeviceButtonRequest);
|
walletManager.deviceButtonRequest.connect(onDeviceButtonRequest);
|
||||||
walletManager.deviceButtonPressed.connect(onDeviceButtonPressed);
|
walletManager.deviceButtonPressed.connect(onDeviceButtonPressed);
|
||||||
walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete);
|
walletManager.checkUpdatesComplete.connect(onWalletCheckUpdatesComplete);
|
||||||
walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeeded);
|
walletManager.walletPassphraseNeeded.connect(onWalletPassphraseNeededManager);
|
||||||
IPC.uriHandler.connect(onUriHandler);
|
IPC.uriHandler.connect(onUriHandler);
|
||||||
Prices.priceJsonReceived.connect(appWindow.fiatApiJsonReceived);
|
|
||||||
|
|
||||||
if(typeof daemonManager != "undefined") {
|
if(typeof daemonManager != "undefined") {
|
||||||
daemonManager.daemonStarted.connect(onDaemonStarted);
|
daemonManager.daemonStarted.connect(onDaemonStarted);
|
||||||
@@ -1317,8 +1343,6 @@ ApplicationWindow {
|
|||||||
openWallet("wizard");
|
openWallet("wizard");
|
||||||
}
|
}
|
||||||
|
|
||||||
checkUpdates();
|
|
||||||
|
|
||||||
if(persistentSettings.fiatPriceEnabled){
|
if(persistentSettings.fiatPriceEnabled){
|
||||||
appWindow.fiatApiRefresh();
|
appWindow.fiatApiRefresh();
|
||||||
appWindow.fiatTimerStart();
|
appWindow.fiatTimerStart();
|
||||||
@@ -1363,10 +1387,14 @@ ApplicationWindow {
|
|||||||
property int segregationHeight: 0
|
property int segregationHeight: 0
|
||||||
property int kdfRounds: 1
|
property int kdfRounds: 1
|
||||||
property bool hideBalance: false
|
property bool hideBalance: false
|
||||||
|
property bool askPasswordBeforeSending: true
|
||||||
property bool lockOnUserInActivity: true
|
property bool lockOnUserInActivity: true
|
||||||
property int walletMode: 2
|
property int walletMode: 2
|
||||||
property int lockOnUserInActivityInterval: 10 // minutes
|
property int lockOnUserInActivityInterval: 10 // minutes
|
||||||
property bool blackTheme: true
|
property bool blackTheme: true
|
||||||
|
property bool checkForUpdates: true
|
||||||
|
property bool autosave: true
|
||||||
|
property int autosaveMinutes: 10
|
||||||
|
|
||||||
property bool fiatPriceEnabled: false
|
property bool fiatPriceEnabled: false
|
||||||
property bool fiatPriceToggle: false
|
property bool fiatPriceToggle: false
|
||||||
@@ -1398,21 +1426,28 @@ ApplicationWindow {
|
|||||||
z: parent.z + 1
|
z: parent.z + 1
|
||||||
id: transactionConfirmationPopup
|
id: transactionConfirmationPopup
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
|
var handleAccepted = function() {
|
||||||
|
// Save transaction to file if view only wallet
|
||||||
|
if (viewOnly) {
|
||||||
|
saveTxDialog.open();
|
||||||
|
} else {
|
||||||
|
handleTransactionConfirmed()
|
||||||
|
}
|
||||||
|
}
|
||||||
close();
|
close();
|
||||||
passwordDialog.onAcceptedCallback = function() {
|
passwordDialog.onAcceptedCallback = function() {
|
||||||
if(walletPassword === passwordDialog.password){
|
if(walletPassword === passwordDialog.password){
|
||||||
// Save transaction to file if view only wallet
|
handleAccepted()
|
||||||
if(viewOnly) {
|
|
||||||
saveTxDialog.open();
|
|
||||||
} else {
|
|
||||||
handleTransactionConfirmed()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
passwordDialog.showError(qsTr("Wrong password") + translationManager.emptyString);
|
passwordDialog.showError(qsTr("Wrong password") + translationManager.emptyString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
passwordDialog.onRejectedCallback = null;
|
passwordDialog.onRejectedCallback = null;
|
||||||
passwordDialog.open()
|
if(!persistentSettings.askPasswordBeforeSending) {
|
||||||
|
handleAccepted()
|
||||||
|
} else {
|
||||||
|
passwordDialog.open()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1432,6 +1467,14 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MoneroComponents.UpdateDialog {
|
||||||
|
id: updateDialog
|
||||||
|
|
||||||
|
allowed: !passwordDialog.visible && !inputDialog.visible && !splash.visible
|
||||||
|
x: (parent.width - width) / 2
|
||||||
|
y: (parent.height - height) / 2
|
||||||
|
}
|
||||||
|
|
||||||
// Choose blockchain folder
|
// Choose blockchain folder
|
||||||
FileDialog {
|
FileDialog {
|
||||||
id: blockchainFileDialog
|
id: blockchainFileDialog
|
||||||
@@ -1461,7 +1504,6 @@ ApplicationWindow {
|
|||||||
confirmationDialog.text += qsTr("Note: lmdb folder not found. A new folder will be created.") + "\n\n"
|
confirmationDialog.text += qsTr("Note: lmdb folder not found. A new folder will be created.") + "\n\n"
|
||||||
|
|
||||||
confirmationDialog.icon = StandardIcon.Question
|
confirmationDialog.icon = StandardIcon.Question
|
||||||
confirmationDialog.cancelText = qsTr("Cancel")
|
|
||||||
|
|
||||||
// Continue
|
// Continue
|
||||||
confirmationDialog.onAcceptedCallback = function() {
|
confirmationDialog.onAcceptedCallback = function() {
|
||||||
@@ -1485,8 +1527,6 @@ ApplicationWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
property var onAcceptedCallback
|
property var onAcceptedCallback
|
||||||
property var onRejectedCallback
|
property var onRejectedCallback
|
||||||
property var onAcceptedPassphraseCallback
|
|
||||||
property var onRejectedPassphraseCallback
|
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
if (onAcceptedCallback)
|
if (onAcceptedCallback)
|
||||||
onAcceptedCallback();
|
onAcceptedCallback();
|
||||||
@@ -1510,14 +1550,13 @@ ApplicationWindow {
|
|||||||
informationPopup.open();
|
informationPopup.open();
|
||||||
}
|
}
|
||||||
onRejectedNewPassword: {}
|
onRejectedNewPassword: {}
|
||||||
onAcceptedPassphrase: {
|
}
|
||||||
if (onAcceptedPassphraseCallback)
|
|
||||||
onAcceptedPassphraseCallback();
|
DevicePassphraseDialog {
|
||||||
}
|
id: devicePassphraseDialog
|
||||||
onRejectedPassphrase: {
|
visible: false
|
||||||
if (onRejectedPassphraseCallback)
|
z: parent.z + 1
|
||||||
onRejectedPassphraseCallback();
|
anchors.fill: parent
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InputDialog {
|
InputDialog {
|
||||||
@@ -1548,8 +1587,8 @@ ApplicationWindow {
|
|||||||
|
|
||||||
ProcessingSplash {
|
ProcessingSplash {
|
||||||
id: splash
|
id: splash
|
||||||
width: appWindow.width / 1.5
|
width: appWindow.width / 2
|
||||||
height: appWindow.height / 2
|
height: appWindow.height / 2.66
|
||||||
x: (appWindow.width - width) / 2
|
x: (appWindow.width - width) / 2
|
||||||
y: (appWindow.height - height) / 2
|
y: (appWindow.height - height) / 2
|
||||||
messageText: qsTr("Please wait...") + translationManager.emptyString
|
messageText: qsTr("Please wait...") + translationManager.emptyString
|
||||||
@@ -1607,12 +1646,6 @@ ApplicationWindow {
|
|||||||
updateBalance();
|
updateBalance();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMerchantClicked: {
|
|
||||||
middlePanel.state = "Merchant";
|
|
||||||
middlePanel.flickable.contentY = 0;
|
|
||||||
updateBalance();
|
|
||||||
}
|
|
||||||
|
|
||||||
onTxkeyClicked: {
|
onTxkeyClicked: {
|
||||||
middlePanel.state = "TxKey";
|
middlePanel.state = "TxKey";
|
||||||
middlePanel.flickable.contentY = 0;
|
middlePanel.flickable.contentY = 0;
|
||||||
@@ -1687,16 +1720,10 @@ ApplicationWindow {
|
|||||||
anchors.fill: blurredArea
|
anchors.fill: blurredArea
|
||||||
source: blurredArea
|
source: blurredArea
|
||||||
radius: 64
|
radius: 64
|
||||||
visible: passwordDialog.visible || inputDialog.visible || splash.visible
|
visible: passwordDialog.visible || inputDialog.visible || splash.visible || updateDialog.visible || devicePassphraseDialog.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
WizardLang {
|
|
||||||
id: languageView
|
|
||||||
visible: false
|
|
||||||
anchors.fill: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
property int minWidth: 326
|
property int minWidth: 326
|
||||||
property int minHeight: 400
|
property int minHeight: 400
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -1794,16 +1821,10 @@ ApplicationWindow {
|
|||||||
color: "#FFFFFF"
|
color: "#FFFFFF"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Notifier {
|
|
||||||
visible:false
|
|
||||||
id: notifier
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleLanguageView(){
|
function toggleLanguageView(){
|
||||||
middlePanel.visible = !middlePanel.visible;
|
languageSidebar.isOpened ? languageSidebar.close() : languageSidebar.open();
|
||||||
languageView.visible = !languageView.visible
|
|
||||||
resetLanguageFields()
|
resetLanguageFields()
|
||||||
// update after changing language from settings page
|
// update after changing language from settings page
|
||||||
if (persistentSettings.language != wizard.language_language) {
|
if (persistentSettings.language != wizard.language_language) {
|
||||||
@@ -1812,6 +1833,24 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: autosaveTimer
|
||||||
|
interval: persistentSettings.autosaveMinutes * 60 * 1000
|
||||||
|
repeat: true
|
||||||
|
running: persistentSettings.autosave
|
||||||
|
onTriggered: {
|
||||||
|
if (currentWallet) {
|
||||||
|
currentWallet.storeAsync(function(success) {
|
||||||
|
if (success) {
|
||||||
|
appWindow.showStatusMessage(qsTr("Autosaved the wallet"), 3);
|
||||||
|
} else {
|
||||||
|
appWindow.showStatusMessage(qsTr("Failed to autosave the wallet"), 3);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Make the callback dynamic
|
// TODO: Make the callback dynamic
|
||||||
Timer {
|
Timer {
|
||||||
id: statusMessageTimer
|
id: statusMessageTimer
|
||||||
@@ -1851,9 +1890,6 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkSimpleModeConnection(){
|
function checkSimpleModeConnection(){
|
||||||
// auto-connection mechanism for simple mode
|
|
||||||
if(appWindow.walletMode >= 2) return;
|
|
||||||
|
|
||||||
const disconnectedTimeoutSec = 30;
|
const disconnectedTimeoutSec = 30;
|
||||||
const firstCheckDelaySec = 2;
|
const firstCheckDelaySec = 2;
|
||||||
|
|
||||||
@@ -1870,15 +1906,20 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (appWindow.daemonRunning) {
|
if (appWindow.daemonRunning) {
|
||||||
appWindow.stopDaemon();
|
appWindow.stopDaemon(function() {
|
||||||
|
appWindow.startDaemon("")
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
appWindow.startDaemon("");
|
||||||
}
|
}
|
||||||
appWindow.startDaemon("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
// Simple mode connection check timer
|
// Simple mode connection check timer
|
||||||
id: simpleModeConnectionTimer
|
id: simpleModeConnectionTimer
|
||||||
interval: 2000; running: false; repeat: true
|
interval: 2000
|
||||||
|
running: appWindow.walletMode < 2 && currentWallet != undefined && !daemonStartStopInProgress
|
||||||
|
repeat: true
|
||||||
onTriggered: appWindow.checkSimpleModeConnection()
|
onTriggered: appWindow.checkSimpleModeConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1952,14 +1993,26 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If daemon is running - prompt user before exiting
|
// If daemon is running - prompt user before exiting
|
||||||
if(typeof daemonManager != "undefined" && daemonRunning) {
|
if(daemonManager == undefined || persistentSettings.useRemoteNode) {
|
||||||
if (appWindow.walletMode == 0) {
|
|
||||||
stopDaemon(closeAccepted);
|
|
||||||
} else {
|
|
||||||
showDaemonIsRunningDialog(closeAccepted);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
closeAccepted();
|
closeAccepted();
|
||||||
|
} else if (appWindow.walletMode == 0) {
|
||||||
|
stopDaemon(closeAccepted);
|
||||||
|
} else {
|
||||||
|
showProcessingSplash(qsTr("Checking local node status..."));
|
||||||
|
const handler = function(running) {
|
||||||
|
hideProcessingSplash();
|
||||||
|
if (running) {
|
||||||
|
showDaemonIsRunningDialog(closeAccepted);
|
||||||
|
} else {
|
||||||
|
closeAccepted();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (currentWallet) {
|
||||||
|
handler(!currentWallet.disconnected);
|
||||||
|
} else {
|
||||||
|
daemonManager.runningAsync(persistentSettings.nettype, handler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1971,34 +2024,42 @@ ApplicationWindow {
|
|||||||
closeWallet(Qt.quit);
|
closeWallet(Qt.quit);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onWalletCheckUpdatesComplete(update) {
|
function onWalletCheckUpdatesComplete(version, downloadUrl, hash, firstSigner, secondSigner) {
|
||||||
if (update === "")
|
const alreadyAsked = updateDialog.url == downloadUrl && updateDialog.hash == hash;
|
||||||
return
|
if (!alreadyAsked)
|
||||||
print("Update found: " + update)
|
{
|
||||||
var parts = update.split("|")
|
updateDialog.show(version, isMac || isWindows || isLinux ? downloadUrl : "", hash);
|
||||||
if (parts.length == 4) {
|
|
||||||
var version = parts[0]
|
|
||||||
var hash = parts[1]
|
|
||||||
var user_url = parts[2]
|
|
||||||
var msg = qsTr("New version of Monero v%1 is available.").arg(version)
|
|
||||||
if (isMac || isWindows || isLinux) {
|
|
||||||
msg += "<br><br>%1:<br>%2<br><br>%3:<br>%4".arg(qsTr("Download")).arg(user_url).arg(qsTr("SHA256 Hash")).arg(hash) + translationManager.emptyString
|
|
||||||
} else {
|
|
||||||
msg += " " + qsTr("Check out getmonero.org") + translationManager.emptyString
|
|
||||||
}
|
|
||||||
notifier.show(msg)
|
|
||||||
} else {
|
|
||||||
print("Failed to parse update spec")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBuildTag() {
|
||||||
|
if (isMac) {
|
||||||
|
return "mac-x64";
|
||||||
|
}
|
||||||
|
if (isWindows) {
|
||||||
|
return oshelper.installed ? "install-win-x64" : "win-x64";
|
||||||
|
}
|
||||||
|
if (isLinux) {
|
||||||
|
return "linux-x64";
|
||||||
|
}
|
||||||
|
return "source";
|
||||||
|
}
|
||||||
|
|
||||||
function checkUpdates() {
|
function checkUpdates() {
|
||||||
walletManager.checkUpdatesAsync("monero-gui", "gui")
|
const version = Version.GUI_VERSION.match(/\d+\.\d+\.\d+\.\d+/);
|
||||||
|
if (version) {
|
||||||
|
walletManager.checkUpdatesAsync("monero-gui", "gui", getBuildTag(), version[0]);
|
||||||
|
} else {
|
||||||
|
console.error("failed to parse version number", Version.GUI_VERSION);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: updatesTimer
|
id: updatesTimer
|
||||||
interval: 3600*1000; running: true; repeat: true
|
interval: 3600 * 1000
|
||||||
|
repeat: true
|
||||||
|
running: !disableCheckUpdatesFlag && persistentSettings.checkForUpdates
|
||||||
|
triggeredOnStart: true
|
||||||
onTriggered: checkUpdates()
|
onTriggered: checkUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2078,7 +2139,7 @@ ApplicationWindow {
|
|||||||
if (mode < 2) {
|
if (mode < 2) {
|
||||||
persistentSettings.useRemoteNode = false;
|
persistentSettings.useRemoteNode = false;
|
||||||
|
|
||||||
if (middlePanel.settingsView.settingsStateViewState === "Node" || middlePanel.settingsView.settingsStateViewState === "Log") {
|
if (middlePanel.settingsView.settingsStateViewState === "Node") {
|
||||||
middlePanel.settingsView.settingsStateViewState = "Wallet"
|
middlePanel.settingsView.settingsStateViewState = "Wallet"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2098,6 +2159,11 @@ ApplicationWindow {
|
|||||||
blackColor: "black"
|
blackColor: "black"
|
||||||
whiteColor: "white"
|
whiteColor: "white"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// borders on white theme + linux
|
// borders on white theme + linux
|
||||||
@@ -2165,8 +2231,8 @@ ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @TODO: QML type 'Drawer' has issues with buildbot; debug after Qt 5.9 migration
|
MoneroComponents.LanguageSidebar {
|
||||||
// MoneroComponents.LanguageSidebar {
|
id: languageSidebar
|
||||||
// id: languageSidebar
|
dragMargin: 0
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
monero
@@ -53,6 +53,7 @@ HEADERS += \
|
|||||||
src/main/oscursor.h \
|
src/main/oscursor.h \
|
||||||
src/libwalletqt/WalletManager.h \
|
src/libwalletqt/WalletManager.h \
|
||||||
src/libwalletqt/Wallet.h \
|
src/libwalletqt/Wallet.h \
|
||||||
|
src/libwalletqt/PassphraseHelper.h \
|
||||||
src/libwalletqt/PendingTransaction.h \
|
src/libwalletqt/PendingTransaction.h \
|
||||||
src/libwalletqt/TransactionHistory.h \
|
src/libwalletqt/TransactionHistory.h \
|
||||||
src/libwalletqt/TransactionInfo.h \
|
src/libwalletqt/TransactionInfo.h \
|
||||||
@@ -76,11 +77,12 @@ HEADERS += \
|
|||||||
src/libwalletqt/UnsignedTransaction.h \
|
src/libwalletqt/UnsignedTransaction.h \
|
||||||
src/main/Logger.h \
|
src/main/Logger.h \
|
||||||
src/main/MainApp.h \
|
src/main/MainApp.h \
|
||||||
|
src/qt/downloader.h \
|
||||||
src/qt/FutureScheduler.h \
|
src/qt/FutureScheduler.h \
|
||||||
src/qt/ipc.h \
|
src/qt/ipc.h \
|
||||||
src/qt/KeysFiles.h \
|
src/qt/KeysFiles.h \
|
||||||
|
src/qt/network.h \
|
||||||
src/qt/utils.h \
|
src/qt/utils.h \
|
||||||
src/qt/prices.h \
|
|
||||||
src/qt/macoshelper.h \
|
src/qt/macoshelper.h \
|
||||||
src/qt/MoneroSettings.h \
|
src/qt/MoneroSettings.h \
|
||||||
src/qt/TailsOS.h
|
src/qt/TailsOS.h
|
||||||
@@ -90,12 +92,15 @@ SOURCES += src/main/main.cpp \
|
|||||||
src/main/clipboardAdapter.cpp \
|
src/main/clipboardAdapter.cpp \
|
||||||
src/main/oscursor.cpp \
|
src/main/oscursor.cpp \
|
||||||
src/libwalletqt/WalletManager.cpp \
|
src/libwalletqt/WalletManager.cpp \
|
||||||
|
src/libwalletqt/WalletListenerImpl.cpp \
|
||||||
src/libwalletqt/Wallet.cpp \
|
src/libwalletqt/Wallet.cpp \
|
||||||
|
src/libwalletqt/PassphraseHelper.cpp \
|
||||||
src/libwalletqt/PendingTransaction.cpp \
|
src/libwalletqt/PendingTransaction.cpp \
|
||||||
src/libwalletqt/TransactionHistory.cpp \
|
src/libwalletqt/TransactionHistory.cpp \
|
||||||
src/libwalletqt/TransactionInfo.cpp \
|
src/libwalletqt/TransactionInfo.cpp \
|
||||||
src/libwalletqt/QRCodeImageProvider.cpp \
|
src/libwalletqt/QRCodeImageProvider.cpp \
|
||||||
src/main/oshelper.cpp \
|
src/main/oshelper.cpp \
|
||||||
|
src/openpgp/openpgp.cpp \
|
||||||
src/TranslationManager.cpp \
|
src/TranslationManager.cpp \
|
||||||
src/model/TransactionHistoryModel.cpp \
|
src/model/TransactionHistoryModel.cpp \
|
||||||
src/model/TransactionHistorySortFilterModel.cpp \
|
src/model/TransactionHistorySortFilterModel.cpp \
|
||||||
@@ -112,11 +117,13 @@ SOURCES += src/main/main.cpp \
|
|||||||
src/libwalletqt/UnsignedTransaction.cpp \
|
src/libwalletqt/UnsignedTransaction.cpp \
|
||||||
src/main/Logger.cpp \
|
src/main/Logger.cpp \
|
||||||
src/main/MainApp.cpp \
|
src/main/MainApp.cpp \
|
||||||
|
src/qt/downloader.cpp \
|
||||||
src/qt/FutureScheduler.cpp \
|
src/qt/FutureScheduler.cpp \
|
||||||
src/qt/ipc.cpp \
|
src/qt/ipc.cpp \
|
||||||
src/qt/KeysFiles.cpp \
|
src/qt/KeysFiles.cpp \
|
||||||
|
src/qt/network.cpp \
|
||||||
|
src/qt/updater.cpp \
|
||||||
src/qt/utils.cpp \
|
src/qt/utils.cpp \
|
||||||
src/qt/prices.cpp \
|
|
||||||
src/qt/MoneroSettings.cpp \
|
src/qt/MoneroSettings.cpp \
|
||||||
src/qt/TailsOS.cpp
|
src/qt/TailsOS.cpp
|
||||||
|
|
||||||
@@ -157,6 +164,8 @@ ios:arm64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LIBS_COMMON = \
|
LIBS_COMMON = \
|
||||||
|
-lgcrypt \
|
||||||
|
-lgpg-error \
|
||||||
-lwallet_merged \
|
-lwallet_merged \
|
||||||
-llmdb \
|
-llmdb \
|
||||||
-lepee \
|
-lepee \
|
||||||
@@ -178,8 +187,10 @@ android {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
QMAKE_CXXFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -Werror -Wformat -Wformat-security
|
QMAKE_CXXFLAGS += -Werror -Wformat -Wformat-security
|
||||||
QMAKE_CFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -Werror -Wformat -Wformat-security
|
QMAKE_CFLAGS += -Werror -Wformat -Wformat-security
|
||||||
|
QMAKE_CXXFLAGS_RELEASE += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2
|
||||||
|
QMAKE_CFLAGS_RELEASE += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2
|
||||||
|
|
||||||
ios {
|
ios {
|
||||||
message("Host is IOS")
|
message("Host is IOS")
|
||||||
@@ -223,6 +234,14 @@ CONFIG(WITH_SCANNER) {
|
|||||||
LIBS += -lzbarjni -liconv
|
LIBS += -lzbarjni -liconv
|
||||||
} else {
|
} else {
|
||||||
LIBS += -lzbar
|
LIBS += -lzbar
|
||||||
|
macx {
|
||||||
|
ZBAR_DIR = $$system(brew --prefix zbar, lines, EXIT_CODE)
|
||||||
|
equals(EXIT_CODE, 0) {
|
||||||
|
INCLUDEPATH += $$ZBAR_DIR/include
|
||||||
|
} else {
|
||||||
|
INCLUDEPATH += /usr/local/include
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message("Skipping camera scanner because of Incompatible Qt Version !")
|
message("Skipping camera scanner because of Incompatible Qt Version !")
|
||||||
@@ -388,6 +407,27 @@ macx {
|
|||||||
INCLUDEPATH += /usr/local/include
|
INCLUDEPATH += /usr/local/include
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GCRYPT_DIR = $$system(brew --prefix libgcrypt, lines, EXIT_CODE)
|
||||||
|
equals(EXIT_CODE, 0) {
|
||||||
|
INCLUDEPATH += $$GCRYPT_DIR/include
|
||||||
|
} else {
|
||||||
|
INCLUDEPATH += /usr/local/include
|
||||||
|
}
|
||||||
|
|
||||||
|
GPGP_ERROR_DIR = $$system(brew --prefix libgpg-error, lines, EXIT_CODE)
|
||||||
|
equals(EXIT_CODE, 0) {
|
||||||
|
INCLUDEPATH += $$GPGP_ERROR_DIR/include
|
||||||
|
} else {
|
||||||
|
INCLUDEPATH += /usr/local/include
|
||||||
|
}
|
||||||
|
|
||||||
|
SODIUM_DIR = $$system(brew --prefix libsodium, lines, EXIT_CODE)
|
||||||
|
equals(EXIT_CODE, 0) {
|
||||||
|
INCLUDEPATH += $$SODIUM_DIR/include
|
||||||
|
} else {
|
||||||
|
INCLUDEPATH += /usr/local/include
|
||||||
|
}
|
||||||
|
|
||||||
QT += macextras
|
QT += macextras
|
||||||
OBJECTIVE_SOURCES += src/qt/macoshelper.mm
|
OBJECTIVE_SOURCES += src/qt/macoshelper.mm
|
||||||
LIBS+= -Wl,-dead_strip
|
LIBS+= -Wl,-dead_strip
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ Rectangle {
|
|||||||
topPadding: 0
|
topPadding: 0
|
||||||
text: qsTr("Save your most used addresses here") + translationManager.emptyString
|
text: qsTr("Save your most used addresses here") + translationManager.emptyString
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
// @TODO: Legacy. Remove after Qt 5.8.
|
|
||||||
// https://stackoverflow.com/questions/41990013
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
@@ -99,13 +92,6 @@ Rectangle {
|
|||||||
topPadding: 0
|
topPadding: 0
|
||||||
text: qsTr("This makes it easier to send or receive Monero and reduces errors when typing in addresses manually.") + translationManager.emptyString
|
text: qsTr("This makes it easier to send or receive Monero and reduces errors when typing in addresses manually.") + translationManager.emptyString
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
// @TODO: Legacy. Remove after Qt 5.8.
|
|
||||||
// https://stackoverflow.com/questions/41990013
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.StandardButton {
|
MoneroComponents.StandardButton {
|
||||||
@@ -325,8 +311,6 @@ Rectangle {
|
|||||||
if (!parsed.error) {
|
if (!parsed.error) {
|
||||||
addressLine.text = parsed.address;
|
addressLine.text = parsed.address;
|
||||||
descriptionLine.text = parsed.tx_description;
|
descriptionLine.text = parsed.tx_description;
|
||||||
} else {
|
|
||||||
addressLine.text = clipboardText;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtQuick.Dialogs 1.2
|
import QtQuick.Dialogs 1.2
|
||||||
import moneroComponents.Clipboard 1.0
|
import moneroComponents.Clipboard 1.0
|
||||||
@@ -38,6 +39,7 @@ import "../components"
|
|||||||
import "../components" as MoneroComponents
|
import "../components" as MoneroComponents
|
||||||
import "." 1.0
|
import "." 1.0
|
||||||
import "../js/TxUtils.js" as TxUtils
|
import "../js/TxUtils.js" as TxUtils
|
||||||
|
import "../js/Utils.js" as Utils
|
||||||
|
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -54,8 +56,8 @@ Rectangle {
|
|||||||
property string sendButtonWarning: {
|
property string sendButtonWarning: {
|
||||||
// Currently opened wallet is not view-only
|
// Currently opened wallet is not view-only
|
||||||
if (appWindow.viewOnly) {
|
if (appWindow.viewOnly) {
|
||||||
return qsTr("Wallet is view-only and sends are not possible. Unless key images are imported, " +
|
return qsTr("Wallet is view-only and sends are only possible by using offline transaction signing. " +
|
||||||
"the balance reflects only incoming but not outgoing transactions.") + translationManager.emptyString;
|
"Unless key images are imported, the balance reflects only incoming but not outgoing transactions.") + translationManager.emptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are sufficient unlocked funds available
|
// There are sufficient unlocked funds available
|
||||||
@@ -116,7 +118,6 @@ Rectangle {
|
|||||||
amountLine.text = ""
|
amountLine.text = ""
|
||||||
setDescription("");
|
setDescription("");
|
||||||
priorityDropdown.currentIndex = 0
|
priorityDropdown.currentIndex = 0
|
||||||
updatePriorityDropdown()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Information dialog
|
// Information dialog
|
||||||
@@ -162,96 +163,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
|
||||||
columns: appWindow.walletMode < 2 ? 1 : 2
|
|
||||||
Layout.fillWidth: true
|
|
||||||
columnSpacing: 32
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.minimumWidth: 200
|
|
||||||
|
|
||||||
// Amount input
|
|
||||||
LineEdit {
|
|
||||||
id: amountLine
|
|
||||||
Layout.fillWidth: true
|
|
||||||
inlineIcon: true
|
|
||||||
labelText: "<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
|
|
||||||
%1 <a href='#'>(%2)</a>".arg(qsTr("Amount")).arg(qsTr("Change account"))
|
|
||||||
+ translationManager.emptyString
|
|
||||||
copyButton: !isNaN(amountLine.text) && persistentSettings.fiatPriceEnabled
|
|
||||||
copyButtonText: fiatApiCurrencySymbol() + " ~" + fiatApiConvertToFiat(amountLine.text)
|
|
||||||
copyButtonEnabled: false
|
|
||||||
|
|
||||||
onLabelLinkActivated: {
|
|
||||||
middlePanel.accountView.selectAndSend = true;
|
|
||||||
appWindow.showPageRequest("Account")
|
|
||||||
}
|
|
||||||
placeholderText: "0.00"
|
|
||||||
width: 100
|
|
||||||
fontBold: true
|
|
||||||
inlineButtonText: qsTr("All") + translationManager.emptyString
|
|
||||||
inlineButton.onClicked: amountLine.text = "(all)"
|
|
||||||
onTextChanged: {
|
|
||||||
const match = amountLine.text.match(/^0+(\d.*)/);
|
|
||||||
if (match) {
|
|
||||||
const cursorPosition = amountLine.cursorPosition;
|
|
||||||
amountLine.text = match[1];
|
|
||||||
amountLine.cursorPosition = Math.max(cursorPosition, 1) - 1;
|
|
||||||
} else if(amountLine.text.indexOf('.') === 0){
|
|
||||||
amountLine.text = '0' + amountLine.text;
|
|
||||||
if (amountLine.text.length > 2) {
|
|
||||||
amountLine.cursorPosition = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
amountLine.error = walletManager.amountFromString(amountLine.text) > appWindow.getUnlockedBalance()
|
|
||||||
}
|
|
||||||
|
|
||||||
validator: RegExpValidator {
|
|
||||||
regExp: /^(\d{1,8})?([\.]\d{1,12})?$/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
visible: appWindow.walletMode >= 2
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Label {
|
|
||||||
id: transactionPriority
|
|
||||||
Layout.topMargin: 12
|
|
||||||
text: qsTr("Transaction priority") + translationManager.emptyString
|
|
||||||
fontBold: false
|
|
||||||
fontSize: 16
|
|
||||||
}
|
|
||||||
// Note: workaround for translations in listElements
|
|
||||||
// ListElement: cannot use script for property value, so
|
|
||||||
// code like this wont work:
|
|
||||||
// ListElement { column1: qsTr("LOW") + translationManager.emptyString ; column2: ""; priority: PendingTransaction.Priority_Low }
|
|
||||||
// For translations to work, the strings need to be listed in
|
|
||||||
// the file components/StandardDropdown.qml too.
|
|
||||||
|
|
||||||
// Priorites after v5
|
|
||||||
ListModel {
|
|
||||||
id: priorityModelV5
|
|
||||||
|
|
||||||
ListElement { column1: qsTr("Automatic") ; column2: ""; priority: 0}
|
|
||||||
ListElement { column1: qsTr("Slow (x0.2 fee)") ; column2: ""; priority: 1}
|
|
||||||
ListElement { column1: qsTr("Normal (x1 fee)") ; column2: ""; priority: 2 }
|
|
||||||
ListElement { column1: qsTr("Fast (x5 fee)") ; column2: ""; priority: 3 }
|
|
||||||
ListElement { column1: qsTr("Fastest (x200 fee)") ; column2: ""; priority: 4 }
|
|
||||||
}
|
|
||||||
|
|
||||||
StandardDropdown {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
id: priorityDropdown
|
|
||||||
Layout.topMargin: 5
|
|
||||||
currentIndex: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make sure dropdown is on top
|
|
||||||
z: parent.z + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// recipient address input
|
// recipient address input
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: addressLineRow
|
id: addressLineRow
|
||||||
@@ -260,10 +171,9 @@ Rectangle {
|
|||||||
LineEditMulti {
|
LineEditMulti {
|
||||||
id: addressLine
|
id: addressLine
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
inputPaddingRight: inlineButtonVisible && inlineButton2Visible ? 100 : 60
|
||||||
fontBold: true
|
fontBold: true
|
||||||
labelText: qsTr("<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
|
labelText: qsTr("Address") + translationManager.emptyString
|
||||||
%1 <a href='#'>(%2)</a>").arg(qsTr("Address")).arg(qsTr("Address book"))
|
|
||||||
+ translationManager.emptyString
|
|
||||||
labelButtonText: qsTr("Resolve") + translationManager.emptyString
|
labelButtonText: qsTr("Resolve") + translationManager.emptyString
|
||||||
placeholderText: {
|
placeholderText: {
|
||||||
if(persistentSettings.nettype == NetworkType.MAINNET){
|
if(persistentSettings.nettype == NetworkType.MAINNET){
|
||||||
@@ -276,10 +186,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
wrapMode: Text.WrapAnywhere
|
wrapMode: Text.WrapAnywhere
|
||||||
addressValidation: true
|
addressValidation: true
|
||||||
onInputLabelLinkActivated: {
|
|
||||||
middlePanel.addressBookView.selectAndSend = true;
|
|
||||||
appWindow.showPageRequest("AddressBook");
|
|
||||||
}
|
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
const parsed = walletManager.parse_uri_to_object(text);
|
const parsed = walletManager.parse_uri_to_object(text);
|
||||||
if (!parsed.error) {
|
if (!parsed.error) {
|
||||||
@@ -289,16 +195,27 @@ Rectangle {
|
|||||||
setDescription(parsed.tx_description);
|
setDescription(parsed.tx_description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inlineButton.text: FontAwesome.qrcode
|
inlineButton.text: FontAwesome.addressBook
|
||||||
|
inlineButton.buttonHeight: 30
|
||||||
inlineButton.fontPixelSize: 22
|
inlineButton.fontPixelSize: 22
|
||||||
inlineButton.fontFamily: FontAwesome.fontFamily
|
inlineButton.fontFamily: FontAwesome.fontFamily
|
||||||
inlineButton.textColor: MoneroComponents.Style.defaultFontColor
|
inlineButton.textColor: MoneroComponents.Style.defaultFontColor
|
||||||
inlineButton.buttonColor: MoneroComponents.Style.orange
|
|
||||||
inlineButton.onClicked: {
|
inlineButton.onClicked: {
|
||||||
cameraUi.state = "Capture"
|
middlePanel.addressBookView.selectAndSend = true;
|
||||||
cameraUi.qrcode_decoded.connect(updateFromQrCode)
|
appWindow.showPageRequest("AddressBook");
|
||||||
}
|
}
|
||||||
inlineButtonVisible : appWindow.qrScannerEnabled && !addressLine.text
|
inlineButtonVisible: true
|
||||||
|
|
||||||
|
inlineButton2.text: FontAwesome.qrcode
|
||||||
|
inlineButton2.buttonHeight: 30
|
||||||
|
inlineButton2.fontPixelSize: 22
|
||||||
|
inlineButton2.fontFamily: FontAwesome.fontFamily
|
||||||
|
inlineButton2.textColor: MoneroComponents.Style.defaultFontColor
|
||||||
|
inlineButton2.onClicked: {
|
||||||
|
cameraUi.state = "Capture"
|
||||||
|
cameraUi.qrcode_decoded.connect(updateFromQrCode)
|
||||||
|
}
|
||||||
|
inlineButton2Visible: appWindow.qrScannerEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,6 +265,142 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
columns: appWindow.walletMode < 2 ? 1 : 2
|
||||||
|
Layout.fillWidth: true
|
||||||
|
columnSpacing: 32
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 200
|
||||||
|
|
||||||
|
// Amount input
|
||||||
|
LineEdit {
|
||||||
|
id: amountLine
|
||||||
|
Layout.fillWidth: true
|
||||||
|
inlineIcon: true
|
||||||
|
labelText: "<style type='text/css'>a {text-decoration: none; color: #858585; font-size: 14px;}</style>\
|
||||||
|
%1 <a href='#'>(%2)</a>".arg(qsTr("Amount")).arg(qsTr("Change account"))
|
||||||
|
+ translationManager.emptyString
|
||||||
|
copyButton: !isNaN(amountLine.text) && persistentSettings.fiatPriceEnabled
|
||||||
|
copyButtonText: "~%1 %2".arg(fiatApiConvertToFiat(amountLine.text)).arg(fiatApiCurrencySymbol())
|
||||||
|
copyButtonEnabled: false
|
||||||
|
|
||||||
|
onLabelLinkActivated: {
|
||||||
|
middlePanel.accountView.selectAndSend = true;
|
||||||
|
appWindow.showPageRequest("Account")
|
||||||
|
}
|
||||||
|
placeholderText: "0.00"
|
||||||
|
width: 100
|
||||||
|
fontBold: true
|
||||||
|
inlineButtonText: qsTr("All") + translationManager.emptyString
|
||||||
|
inlineButton.onClicked: amountLine.text = "(all)"
|
||||||
|
onTextChanged: {
|
||||||
|
const match = amountLine.text.match(/^0+(\d.*)/);
|
||||||
|
if (match) {
|
||||||
|
const cursorPosition = amountLine.cursorPosition;
|
||||||
|
amountLine.text = match[1];
|
||||||
|
amountLine.cursorPosition = Math.max(cursorPosition, 1) - 1;
|
||||||
|
} else if(amountLine.text.indexOf('.') === 0){
|
||||||
|
amountLine.text = '0' + amountLine.text;
|
||||||
|
if (amountLine.text.length > 2) {
|
||||||
|
amountLine.cursorPosition = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amountLine.error = walletManager.amountFromString(amountLine.text) > appWindow.getUnlockedBalance()
|
||||||
|
}
|
||||||
|
|
||||||
|
validator: RegExpValidator {
|
||||||
|
regExp: /^(\d{1,8})?([\.]\d{1,12})?$/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.TextPlain {
|
||||||
|
id: feeLabel
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
Layout.topMargin: 12
|
||||||
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
|
font.pixelSize: 14
|
||||||
|
color: MoneroComponents.Style.defaultFontColor
|
||||||
|
property bool estimating: false
|
||||||
|
property var estimatedFee: null
|
||||||
|
property string estimatedFeeFiat: {
|
||||||
|
if (!persistentSettings.fiatPriceEnabled || estimatedFee == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const fiatFee = fiatApiConvertToFiat(estimatedFee);
|
||||||
|
return " (%1 %3)".arg(fiatFee < 0.01 ? "<0.01" : "~" + fiatFee).arg(fiatApiCurrencySymbol());
|
||||||
|
}
|
||||||
|
property var fee: {
|
||||||
|
estimatedFee = null;
|
||||||
|
estimating = sendButton.enabled;
|
||||||
|
if (!sendButton.enabled || !currentWallet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentWallet.estimateTransactionFeeAsync(
|
||||||
|
addressLine.text,
|
||||||
|
walletManager.amountFromString(amountLine.text),
|
||||||
|
priorityModelV5.get(priorityDropdown.currentIndex).priority,
|
||||||
|
function (amount) {
|
||||||
|
estimatedFee = Utils.removeTrailingZeros(amount);
|
||||||
|
estimating = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
text: {
|
||||||
|
if (!sendButton.enabled || estimatedFee == null) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "%1: ~%2 XMR".arg(qsTr("Fee")).arg(estimatedFee) +
|
||||||
|
estimatedFeeFiat +
|
||||||
|
translationManager.emptyString;
|
||||||
|
}
|
||||||
|
|
||||||
|
BusyIndicator {
|
||||||
|
anchors.right: parent.right
|
||||||
|
running: feeLabel.estimating
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
visible: appWindow.walletMode >= 2
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
Label {
|
||||||
|
id: transactionPriority
|
||||||
|
Layout.topMargin: 0
|
||||||
|
text: qsTr("Transaction priority") + translationManager.emptyString
|
||||||
|
fontBold: false
|
||||||
|
fontSize: 16
|
||||||
|
}
|
||||||
|
// Note: workaround for translations in listElements
|
||||||
|
// ListElement: cannot use script for property value, so
|
||||||
|
// code like this wont work:
|
||||||
|
// ListElement { column1: qsTr("LOW") + translationManager.emptyString ; column2: ""; priority: PendingTransaction.Priority_Low }
|
||||||
|
// For translations to work, the strings need to be listed in
|
||||||
|
// the file components/StandardDropdown.qml too.
|
||||||
|
|
||||||
|
// Priorites after v5
|
||||||
|
ListModel {
|
||||||
|
id: priorityModelV5
|
||||||
|
|
||||||
|
ListElement { column1: qsTr("Automatic") ; column2: ""; priority: 0}
|
||||||
|
ListElement { column1: qsTr("Slow (x0.2 fee)") ; column2: ""; priority: 1}
|
||||||
|
ListElement { column1: qsTr("Normal (x1 fee)") ; column2: ""; priority: 2 }
|
||||||
|
ListElement { column1: qsTr("Fast (x5 fee)") ; column2: ""; priority: 3 }
|
||||||
|
ListElement { column1: qsTr("Fastest (x200 fee)") ; column2: ""; priority: 4 }
|
||||||
|
}
|
||||||
|
|
||||||
|
StandardDropdown {
|
||||||
|
Layout.preferredWidth: 200
|
||||||
|
id: priorityDropdown
|
||||||
|
Layout.topMargin: 5
|
||||||
|
currentIndex: 0
|
||||||
|
dataModel: priorityModelV5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MoneroComponents.WarningBox {
|
MoneroComponents.WarningBox {
|
||||||
text: qsTr("Description field contents match long payment ID format. \
|
text: qsTr("Description field contents match long payment ID format. \
|
||||||
Please don't paste long payment ID into description field, your funds might be lost.") + translationManager.emptyString;
|
Please don't paste long payment ID into description field, your funds might be lost.") + translationManager.emptyString;
|
||||||
@@ -461,10 +514,9 @@ Rectangle {
|
|||||||
id: advancedLayout
|
id: advancedLayout
|
||||||
anchors.top: pageRoot.bottom
|
anchors.top: pageRoot.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.margins: 20
|
anchors.margins: 20
|
||||||
anchors.topMargin: 32
|
anchors.topMargin: 32
|
||||||
spacing: 26
|
spacing: 10
|
||||||
enabled: !viewOnly || pageRoot.enabled
|
enabled: !viewOnly || pageRoot.enabled
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -479,84 +531,88 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GridLayout {
|
AdvancedOptionsItem {
|
||||||
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
||||||
columns: 6
|
title: qsTr("Key images") + translationManager.emptyString
|
||||||
|
button1.text: qsTr("Export") + translationManager.emptyString
|
||||||
StandardButton {
|
button1.enabled: !appWindow.viewOnly
|
||||||
id: sweepUnmixableButton
|
button1.onClicked: {
|
||||||
text: qsTr("Sweep Unmixable") + translationManager.emptyString
|
console.log("Transfer: export key images clicked")
|
||||||
enabled : pageRoot.enabled
|
exportKeyImagesDialog.open();
|
||||||
small: true
|
|
||||||
onClicked: {
|
|
||||||
console.log("Transfer: sweepUnmixableClicked")
|
|
||||||
root.sweepUnmixableClicked()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
button2.text: qsTr("Import") + translationManager.emptyString
|
||||||
StandardButton {
|
button2.enabled: appWindow.viewOnly && appWindow.isTrustedDaemon()
|
||||||
id: saveTxButton
|
button2.onClicked: {
|
||||||
text: qsTr("Create tx file") + translationManager.emptyString
|
console.log("Transfer: import key images clicked")
|
||||||
visible: appWindow.viewOnly
|
importKeyImagesDialog.open();
|
||||||
enabled: pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)
|
|
||||||
small: true
|
|
||||||
onClicked: {
|
|
||||||
console.log("Transfer: saveTx Clicked")
|
|
||||||
var priority = priorityModelV5.get(priorityDropdown.currentIndex).priority
|
|
||||||
console.log("priority: " + priority)
|
|
||||||
console.log("amount: " + amountLine.text)
|
|
||||||
addressLine.text = addressLine.text.trim()
|
|
||||||
setPaymentId(paymentIdLine.text.trim());
|
|
||||||
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, root.mixin, priority, descriptionLine.text)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
helpTextLarge.text: qsTr("Required for view-only wallets to display the real balance") + translationManager.emptyString
|
||||||
|
helpTextSmall.text: {
|
||||||
|
var errorMessage = "";
|
||||||
|
if (appWindow.viewOnly && !appWindow.isTrustedDaemon()){
|
||||||
|
errorMessage = "<p class='orange'>" + qsTr("* To import, you must connect to a local node or a trusted remote node") + "</p>";
|
||||||
|
}
|
||||||
|
return "<style type='text/css'>p{line-height:20px; margin-top:0px; margin-bottom:0px; color:#ffffff;} p.orange{color:#ff9323;}</style>" +
|
||||||
|
"<p>" + qsTr("1. Using cold wallet, export the key images into a file") + "</p>" +
|
||||||
|
"<p>" + qsTr("2. Using view-only wallet, import the key images file") + "</p>" +
|
||||||
|
errorMessage + translationManager.emptyString
|
||||||
|
}
|
||||||
|
helpTextSmall.themeTransition: false
|
||||||
|
}
|
||||||
|
|
||||||
|
AdvancedOptionsItem {
|
||||||
|
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
||||||
|
title: qsTr("Offline transaction signing") + translationManager.emptyString
|
||||||
|
button1.text: qsTr("Create") + translationManager.emptyString
|
||||||
|
button1.enabled: appWindow.viewOnly && pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)
|
||||||
|
button1.onClicked: {
|
||||||
|
console.log("Transfer: saveTx Clicked")
|
||||||
|
var priority = priorityModelV5.get(priorityDropdown.currentIndex).priority
|
||||||
|
console.log("priority: " + priority)
|
||||||
|
console.log("amount: " + amountLine.text)
|
||||||
|
addressLine.text = addressLine.text.trim()
|
||||||
|
setPaymentId(paymentIdLine.text.trim());
|
||||||
|
root.paymentClicked(addressLine.text, paymentIdLine.text, amountLine.text, root.mixin, priority, descriptionLine.text)
|
||||||
|
}
|
||||||
|
button2.text: qsTr("Sign (offline)") + translationManager.emptyString
|
||||||
|
button2.enabled: !appWindow.viewOnly
|
||||||
|
button2.onClicked: {
|
||||||
|
console.log("Transfer: sign tx clicked")
|
||||||
|
signTxDialog.open();
|
||||||
|
}
|
||||||
|
button3.text: qsTr("Submit") + translationManager.emptyString
|
||||||
|
button3.enabled: appWindow.viewOnly
|
||||||
|
button3.onClicked: {
|
||||||
|
console.log("Transfer: submit tx clicked")
|
||||||
|
submitTxDialog.open();
|
||||||
|
}
|
||||||
|
helpTextLarge.text: qsTr("Spend XMR from a cold (offline) wallet") + translationManager.emptyString
|
||||||
|
helpTextSmall.text: {
|
||||||
|
var errorMessage = "";
|
||||||
|
if (appWindow.viewOnly && !pageRoot.checkInformation(amountLine.text, addressLine.text, appWindow.persistentSettings.nettype)){
|
||||||
|
errorMessage = "<p class='orange'>" + qsTr("* To create a transaction file, please enter address and amount above") + "</p>";
|
||||||
|
}
|
||||||
|
return "<style type='text/css'>p{line-height:20px; margin-top:0px; margin-bottom:0px; color:#ffffff;} p.orange{color:#ff9323;}</style>" +
|
||||||
|
"<p>" + qsTr("1. Using view-only wallet, export the outputs into a file") + "</p>" +
|
||||||
|
"<p>" + qsTr("2. Using cold wallet, import the outputs file and export the key images") + "</p>" +
|
||||||
|
"<p>" + qsTr("3. Using view-only wallet, import the key images file and create a transaction file") + "</p>" +
|
||||||
|
errorMessage +
|
||||||
|
"<p>" + qsTr("4. Using cold wallet, sign your transaction file") + "</p>" +
|
||||||
|
"<p>" + qsTr("5. Using view-only wallet, submit your signed transaction") + "</p>" + translationManager.emptyString
|
||||||
|
}
|
||||||
|
helpTextSmall.themeTransition: false
|
||||||
|
}
|
||||||
|
|
||||||
StandardButton {
|
AdvancedOptionsItem {
|
||||||
id: signTxButton
|
visible: persistentSettings.transferShowAdvanced && appWindow.walletMode >= 2
|
||||||
text: qsTr("Sign tx file") + translationManager.emptyString
|
title: qsTr("Unmixable outputs") + translationManager.emptyString
|
||||||
small: true
|
button1.text: qsTr("Sweep") + translationManager.emptyString
|
||||||
visible: !appWindow.viewOnly
|
button1.enabled : pageRoot.enabled
|
||||||
onClicked: {
|
button1.onClicked: {
|
||||||
console.log("Transfer: sign tx clicked")
|
console.log("Transfer: sweepUnmixableClicked")
|
||||||
signTxDialog.open();
|
root.sweepUnmixableClicked()
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StandardButton {
|
|
||||||
id: submitTxButton
|
|
||||||
text: qsTr("Submit tx file") + translationManager.emptyString
|
|
||||||
small: true
|
|
||||||
visible: appWindow.viewOnly
|
|
||||||
enabled: pageRoot.enabled
|
|
||||||
onClicked: {
|
|
||||||
console.log("Transfer: submit tx clicked")
|
|
||||||
submitTxDialog.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StandardButton {
|
|
||||||
id: exportKeyImagesButton
|
|
||||||
text: qsTr("Export key images") + translationManager.emptyString
|
|
||||||
small: true
|
|
||||||
visible: !appWindow.viewOnly
|
|
||||||
enabled: pageRoot.enabled
|
|
||||||
onClicked: {
|
|
||||||
console.log("Transfer: export key images clicked")
|
|
||||||
exportKeyImagesDialog.open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StandardButton {
|
|
||||||
id: importKeyImagesButton
|
|
||||||
text: qsTr("Import key images") + translationManager.emptyString
|
|
||||||
small: true
|
|
||||||
enabled: appWindow.viewOnly && appWindow.isTrustedDaemon()
|
|
||||||
onClicked: {
|
|
||||||
console.log("Transfer: import key images clicked")
|
|
||||||
importKeyImagesDialog.open();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
helpTextLarge.text: qsTr("Create a transaction that spends old unmovable outputs") + translationManager.emptyString
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,12 +745,6 @@ Rectangle {
|
|||||||
function onPageCompleted() {
|
function onPageCompleted() {
|
||||||
console.log("transfer page loaded")
|
console.log("transfer page loaded")
|
||||||
updateStatus();
|
updateStatus();
|
||||||
updatePriorityDropdown()
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePriorityDropdown() {
|
|
||||||
priorityDropdown.dataModel = priorityModelV5;
|
|
||||||
priorityDropdown.update()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Add daemon sync status
|
//TODO: Add daemon sync status
|
||||||
@@ -717,6 +767,8 @@ Rectangle {
|
|||||||
|
|
||||||
switch (currentWallet.connected()) {
|
switch (currentWallet.connected()) {
|
||||||
case Wallet.ConnectionStatus_Connecting:
|
case Wallet.ConnectionStatus_Connecting:
|
||||||
|
root.warningContent = qsTr("Wallet is connecting to daemon.")
|
||||||
|
break
|
||||||
case Wallet.ConnectionStatus_Disconnected:
|
case Wallet.ConnectionStatus_Disconnected:
|
||||||
root.warningContent = messageNotConnected;
|
root.warningContent = messageNotConnected;
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ Item {
|
|||||||
anchors.margins: 0
|
anchors.margins: 0
|
||||||
|
|
||||||
property int minWidth: 900
|
property int minWidth: 900
|
||||||
|
property int minHeight: 600
|
||||||
property int qrCodeSize: 220
|
property int qrCodeSize: 220
|
||||||
property bool enableTracking: false
|
property bool enableTracking: false
|
||||||
property string trackingError: "" // setting this will show a message @ tracking table
|
property string trackingError: "" // setting this will show a message @ tracking table
|
||||||
@@ -33,6 +34,9 @@ Item {
|
|||||||
property var hiddenAmounts: []
|
property var hiddenAmounts: []
|
||||||
|
|
||||||
function onPageCompleted() {
|
function onPageCompleted() {
|
||||||
|
if (appWindow.currentWallet) {
|
||||||
|
appWindow.current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, 0)
|
||||||
|
}
|
||||||
// prepare tracking
|
// prepare tracking
|
||||||
trackingCheckbox.checked = root.enableTracking
|
trackingCheckbox.checked = root.enableTracking
|
||||||
root.update();
|
root.update();
|
||||||
@@ -67,7 +71,7 @@ Item {
|
|||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
id: mainLayout
|
id: mainLayout
|
||||||
visible: parent.width >= root.minWidth
|
visible: parent.width >= root.minWidth && appWindow.height >= root.minHeight
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
// emulates max-width + center for container
|
// emulates max-width + center for container
|
||||||
@@ -544,7 +548,7 @@ Item {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: appWindow.showPageRequest("Receive")
|
onClicked: appWindow.showPageRequest("Settings")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -553,7 +557,7 @@ Item {
|
|||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
// Shows when the window is too small
|
// Shows when the window is too small
|
||||||
visible: parent.width < root.minWidth
|
visible: parent.width < root.minWidth || appWindow.height < root.minHeight
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 100;
|
anchors.topMargin: 100;
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -570,6 +574,13 @@ Item {
|
|||||||
text: qsTr("The merchant page requires a larger window") + translationManager.emptyString
|
text: qsTr("The merchant page requires a larger window") + translationManager.emptyString
|
||||||
themeTransition: false
|
themeTransition: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: appWindow.showPageRequest("Settings")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
@@ -589,9 +600,7 @@ Item {
|
|||||||
|
|
||||||
var model = appWindow.currentWallet.historyModel
|
var model = appWindow.currentWallet.historyModel
|
||||||
var count = model.rowCount()
|
var count = model.rowCount()
|
||||||
var totalAmount = 0
|
|
||||||
var nTransactions = 0
|
var nTransactions = 0
|
||||||
var blockchainHeight = null
|
|
||||||
var txs = []
|
var txs = []
|
||||||
|
|
||||||
// Currently selected subaddress as per Receive page
|
// Currently selected subaddress as per Receive page
|
||||||
@@ -607,8 +616,6 @@ Item {
|
|||||||
var subaddrIndex = model.data(idx, TransactionHistoryModel.TransactionSubaddrIndexRole);
|
var subaddrIndex = model.data(idx, TransactionHistoryModel.TransactionSubaddrIndexRole);
|
||||||
|
|
||||||
if (!isout && subaddrAccount == appWindow.currentWallet.currentSubaddressAccount && subaddrIndex == current_subaddress_table_index) {
|
if (!isout && subaddrAccount == appWindow.currentWallet.currentSubaddressAccount && subaddrIndex == current_subaddress_table_index) {
|
||||||
var amount = model.data(idx, TransactionHistoryModel.TransactionAtomicAmountRole);
|
|
||||||
totalAmount = walletManager.addi(totalAmount, amount)
|
|
||||||
nTransactions += 1
|
nTransactions += 1
|
||||||
|
|
||||||
var txid = model.data(idx, TransactionHistoryModel.TransactionHashRole);
|
var txid = model.data(idx, TransactionHistoryModel.TransactionHashRole);
|
||||||
@@ -616,21 +623,17 @@ Item {
|
|||||||
|
|
||||||
var in_txpool = false;
|
var in_txpool = false;
|
||||||
var confirmations = 0;
|
var confirmations = 0;
|
||||||
var displayAmount = 0;
|
var displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
|
||||||
|
|
||||||
if (blockHeight == 0) {
|
if (blockHeight === undefined) {
|
||||||
in_txpool = true;
|
in_txpool = true;
|
||||||
} else {
|
} else {
|
||||||
if (blockchainHeight == null)
|
confirmations = model.data(idx, TransactionHistoryModel.TransactionConfirmationsRole);
|
||||||
blockchainHeight = walletManager.blockchainHeight()
|
|
||||||
confirmations = blockchainHeight - blockHeight - 1
|
|
||||||
displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
txs.push({
|
txs.push({
|
||||||
"amount": displayAmount,
|
"amount": displayAmount,
|
||||||
"confirmations": confirmations,
|
"confirmations": confirmations,
|
||||||
"blockheight": blockHeight,
|
|
||||||
"in_txpool": in_txpool,
|
"in_txpool": in_txpool,
|
||||||
"txid": txid,
|
"txid": txid,
|
||||||
"time_epoch": timeEpoch,
|
"time_epoch": timeEpoch,
|
||||||
@@ -650,9 +653,7 @@ Item {
|
|||||||
txs.forEach(function(tx){
|
txs.forEach(function(tx){
|
||||||
trackingModel.append({
|
trackingModel.append({
|
||||||
"amount": tx.amount,
|
"amount": tx.amount,
|
||||||
"blockheight": tx.blockheight,
|
|
||||||
"confirmations": tx.confirmations,
|
"confirmations": tx.confirmations,
|
||||||
"blockheight": tx.blockHeight,
|
|
||||||
"in_txpool": tx.in_txpool,
|
"in_txpool": tx.in_txpool,
|
||||||
"txid": tx.txid,
|
"txid": tx.txid,
|
||||||
"time_epoch": tx.time_epoch,
|
"time_epoch": tx.time_epoch,
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ ListView {
|
|||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
font.bold: true
|
font.bold: true
|
||||||
color: hide_amount ? "#707070" : "#009F1E"
|
color: hide_amount ? "#707070" : "#009F1E"
|
||||||
text: hide_amount ? '-' : '+' + amount
|
text: hide_amount ? '-' : '+' + amount + (in_txpool ? ' (%1)'.arg(qsTr('unconfirmed')) : '')
|
||||||
selectionColor: MoneroComponents.Style.textSelectionColor
|
selectionColor: MoneroComponents.Style.textSelectionColor
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
|||||||
@@ -273,7 +273,6 @@ Rectangle {
|
|||||||
// LOG
|
// LOG
|
||||||
id: navLog
|
id: navLog
|
||||||
property bool isActive: settingsStateView.state === "Log"
|
property bool isActive: settingsStateView.state === "Log"
|
||||||
visible: appWindow.walletMode >= 2
|
|
||||||
Layout.preferredWidth: navLogText.width + grid.textMargin
|
Layout.preferredWidth: navLogText.width + grid.textMargin
|
||||||
Layout.preferredHeight: 32
|
Layout.preferredHeight: 32
|
||||||
Layout.minimumWidth: 72
|
Layout.minimumWidth: 72
|
||||||
|
|||||||
@@ -131,10 +131,11 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.TextBlock {
|
MoneroComponents.TextBlock {
|
||||||
|
id: walletLocation
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
color: MoneroComponents.Style.dimmedFontColor
|
color: MoneroComponents.Style.dimmedFontColor
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
property string walletPath: (isIOS ? moneroAccountsDir : "") + appWindow.walletPath()
|
property string walletPath: (isIOS ? moneroAccountsDir : "") + persistentSettings.wallet_path
|
||||||
text: "\
|
text: "\
|
||||||
<style type='text/css'>\
|
<style type='text/css'>\
|
||||||
a {cursor:pointer;text-decoration: none; color: #FF6C3C}\
|
a {cursor:pointer;text-decoration: none; color: #FF6C3C}\
|
||||||
@@ -212,7 +213,6 @@ Rectangle {
|
|||||||
+ "The old wallet cache file will be renamed and can be restored later.\n"
|
+ "The old wallet cache file will be renamed and can be restored later.\n"
|
||||||
);
|
);
|
||||||
confirmationDialog.icon = StandardIcon.Question
|
confirmationDialog.icon = StandardIcon.Question
|
||||||
confirmationDialog.cancelText = qsTr("Cancel")
|
|
||||||
confirmationDialog.onAcceptedCallback = function() {
|
confirmationDialog.onAcceptedCallback = function() {
|
||||||
appWindow.closeWallet(function() {
|
appWindow.closeWallet(function() {
|
||||||
walletManager.clearWalletCache(persistentSettings.wallet_path);
|
walletManager.clearWalletCache(persistentSettings.wallet_path);
|
||||||
@@ -230,7 +230,7 @@ Rectangle {
|
|||||||
appWindow.showStatusMessage(qsTr("Invalid restore height specified. Must be a number or a date formatted YYYY-MM-DD"),3);
|
appWindow.showStatusMessage(qsTr("Invalid restore height specified. Must be a number or a date formatted YYYY-MM-DD"),3);
|
||||||
}
|
}
|
||||||
inputDialog.onRejectedCallback = null;
|
inputDialog.onRejectedCallback = null;
|
||||||
inputDialog.open(currentWallet ? currentWallet.walletCreationHeight : "0")
|
inputDialog.open(currentWallet ? currentWallet.walletCreationHeight.toFixed(0) : "0")
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -389,12 +389,7 @@ Rectangle {
|
|||||||
var data = "";
|
var data = "";
|
||||||
data += "GUI version: " + Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")";
|
data += "GUI version: " + Version.GUI_VERSION + " (Qt " + qtRuntimeVersion + ")";
|
||||||
data += "\nEmbedded Monero version: " + Version.GUI_MONERO_VERSION;
|
data += "\nEmbedded Monero version: " + Version.GUI_MONERO_VERSION;
|
||||||
data += "\nWallet path: ";
|
data += "\nWallet path: " + walletLocation.walletPath;
|
||||||
|
|
||||||
var wallet_path = walletPath();
|
|
||||||
if(isIOS)
|
|
||||||
wallet_path = moneroAccountsDir + wallet_path;
|
|
||||||
data += wallet_path;
|
|
||||||
|
|
||||||
data += "\nWallet creation height: ";
|
data += "\nWallet creation height: ";
|
||||||
if(currentWallet)
|
if(currentWallet)
|
||||||
|
|||||||
@@ -58,6 +58,14 @@ Rectangle {
|
|||||||
text: qsTr("Custom decorations") + translationManager.emptyString
|
text: qsTr("Custom decorations") + translationManager.emptyString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MoneroComponents.CheckBox {
|
||||||
|
id: checkForUpdatesCheckBox
|
||||||
|
enabled: !disableCheckUpdatesFlag
|
||||||
|
checked: persistentSettings.checkForUpdates && !disableCheckUpdatesFlag
|
||||||
|
onClicked: persistentSettings.checkForUpdates = !persistentSettings.checkForUpdates
|
||||||
|
text: qsTr("Check for updates periodically") + translationManager.emptyString
|
||||||
|
}
|
||||||
|
|
||||||
MoneroComponents.CheckBox {
|
MoneroComponents.CheckBox {
|
||||||
id: hideBalanceCheckBox
|
id: hideBalanceCheckBox
|
||||||
checked: persistentSettings.hideBalance
|
checked: persistentSettings.hideBalance
|
||||||
@@ -78,6 +86,46 @@ Rectangle {
|
|||||||
persistentSettings.blackTheme = MoneroComponents.Style.blackTheme;
|
persistentSettings.blackTheme = MoneroComponents.Style.blackTheme;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MoneroComponents.CheckBox {
|
||||||
|
checked: persistentSettings.askPasswordBeforeSending
|
||||||
|
text: qsTr("Ask for password before sending a transaction") + translationManager.emptyString
|
||||||
|
toggleOnClick: false
|
||||||
|
onClicked: {
|
||||||
|
if (persistentSettings.askPasswordBeforeSending) {
|
||||||
|
passwordDialog.onAcceptedCallback = function() {
|
||||||
|
if (appWindow.walletPassword === passwordDialog.password){
|
||||||
|
persistentSettings.askPasswordBeforeSending = false;
|
||||||
|
} else {
|
||||||
|
passwordDialog.showError(qsTr("Wrong password"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
passwordDialog.onRejectedCallback = null;
|
||||||
|
passwordDialog.open()
|
||||||
|
} else {
|
||||||
|
persistentSettings.askPasswordBeforeSending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.CheckBox {
|
||||||
|
checked: persistentSettings.autosave
|
||||||
|
onClicked: persistentSettings.autosave = !persistentSettings.autosave
|
||||||
|
text: qsTr("Autosave") + translationManager.emptyString
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneroComponents.Slider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.leftMargin: 35
|
||||||
|
Layout.topMargin: 6
|
||||||
|
visible: persistentSettings.autosave
|
||||||
|
from: 1
|
||||||
|
stepSize: 1
|
||||||
|
to: 60
|
||||||
|
value: persistentSettings.autosaveMinutes
|
||||||
|
text: "%1 %2 %3".arg(qsTr("Every")).arg(value).arg(qsTr("minute(s)")) + translationManager.emptyString
|
||||||
|
onMoved: persistentSettings.autosaveMinutes = value
|
||||||
|
}
|
||||||
|
|
||||||
MoneroComponents.CheckBox {
|
MoneroComponents.CheckBox {
|
||||||
id: userInActivityCheckbox
|
id: userInActivityCheckbox
|
||||||
@@ -86,70 +134,20 @@ Rectangle {
|
|||||||
text: qsTr("Lock wallet on inactivity") + translationManager.emptyString
|
text: qsTr("Lock wallet on inactivity") + translationManager.emptyString
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
MoneroComponents.Slider {
|
||||||
visible: userInActivityCheckbox.checked
|
visible: userInActivityCheckbox.checked
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.topMargin: 6
|
Layout.topMargin: 6
|
||||||
Layout.leftMargin: 42
|
Layout.leftMargin: 35
|
||||||
spacing: 0
|
from: 1
|
||||||
|
stepSize: 1
|
||||||
Text {
|
to: 60
|
||||||
color: MoneroComponents.Style.defaultFontColor
|
value: persistentSettings.lockOnUserInActivityInterval
|
||||||
font.pixelSize: 14
|
text: {
|
||||||
Layout.fillWidth: true
|
var minutes = value > 1 ? qsTr("minutes") : qsTr("minute");
|
||||||
text: {
|
return qsTr("After ") + value + " " + minutes + translationManager.emptyString;
|
||||||
var val = userInactivitySlider.value;
|
|
||||||
var minutes = val > 1 ? qsTr("minutes") : qsTr("minute");
|
|
||||||
|
|
||||||
qsTr("After ") + val + " " + minutes + translationManager.emptyString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Slider {
|
|
||||||
id: userInactivitySlider
|
|
||||||
from: 1
|
|
||||||
value: persistentSettings.lockOnUserInActivityInterval
|
|
||||||
to: 60
|
|
||||||
leftPadding: 0
|
|
||||||
stepSize: 1
|
|
||||||
snapMode: Slider.SnapAlways
|
|
||||||
|
|
||||||
background: Rectangle {
|
|
||||||
x: parent.leftPadding
|
|
||||||
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
|
||||||
implicitWidth: 200
|
|
||||||
implicitHeight: 4
|
|
||||||
width: parent.availableWidth
|
|
||||||
height: implicitHeight
|
|
||||||
radius: 2
|
|
||||||
color: MoneroComponents.Style.progressBarBackgroundColor
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.visualPosition * parent.width
|
|
||||||
height: parent.height
|
|
||||||
color: MoneroComponents.Style.green
|
|
||||||
radius: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handle: Rectangle {
|
|
||||||
x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - width)
|
|
||||||
y: parent.topPadding + parent.availableHeight / 2 - height / 2
|
|
||||||
implicitWidth: 18
|
|
||||||
implicitHeight: 18
|
|
||||||
radius: 8
|
|
||||||
color: parent.pressed ? "#f0f0f0" : "#f6f6f6"
|
|
||||||
border.color: MoneroComponents.Style.grey
|
|
||||||
}
|
|
||||||
|
|
||||||
onMoved: persistentSettings.lockOnUserInActivityInterval = userInactivitySlider.value;
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
acceptedButtons: Qt.NoButton
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
onMoved: persistentSettings.lockOnUserInActivityInterval = value
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Manage pricing
|
//! Manage pricing
|
||||||
@@ -212,6 +210,7 @@ Rectangle {
|
|||||||
MoneroComponents.StandardDropdown {
|
MoneroComponents.StandardDropdown {
|
||||||
id: fiatPriceCurrencyDropdown
|
id: fiatPriceCurrencyDropdown
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
currentIndex: persistentSettings.fiatPriceCurrency === "xmrusd" ? 0 : 1
|
||||||
dataModel: fiatPriceCurrencyModel
|
dataModel: fiatPriceCurrencyModel
|
||||||
onChanged: {
|
onChanged: {
|
||||||
var obj = dataModel.get(currentIndex);
|
var obj = dataModel.get(currentIndex);
|
||||||
@@ -297,10 +296,6 @@ Rectangle {
|
|||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fiatPriceProviderDropDown.update();
|
|
||||||
fiatPriceCurrencyDropdown.currentIndex = persistentSettings.fiatPriceCurrency === "xmrusd" ? 0 : 1;
|
|
||||||
fiatPriceCurrencyDropdown.update();
|
|
||||||
|
|
||||||
console.log('SettingsLayout loaded');
|
console.log('SettingsLayout loaded');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ Rectangle {
|
|||||||
textFormat: TextEdit.RichText
|
textFormat: TextEdit.RichText
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
selectByKeyboard: true
|
selectByKeyboard: true
|
||||||
font.family: MoneroComponents.Style.defaultFontColor
|
font.family: MoneroComponents.Style.fontRegular.name
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
readOnly: true
|
readOnly: true
|
||||||
@@ -231,9 +231,6 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
logLevelDropdown.currentIndex = appWindow.persistentSettings.logLevel;
|
|
||||||
logLevelDropdown.update();
|
|
||||||
|
|
||||||
if(typeof daemonManager != "undefined")
|
if(typeof daemonManager != "undefined")
|
||||||
daemonManager.daemonConsoleUpdated.connect(onDaemonConsoleUpdated)
|
daemonManager.daemonConsoleUpdated.connect(onDaemonConsoleUpdated)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,13 +130,6 @@ Rectangle{
|
|||||||
topPadding: 0
|
topPadding: 0
|
||||||
text: qsTr("The blockchain is downloaded to your computer. Provides higher security and requires more local storage.") + translationManager.emptyString
|
text: qsTr("The blockchain is downloaded to your computer. Provides higher security and requires more local storage.") + translationManager.emptyString
|
||||||
width: parent.width - (localNodeIcon.width + localNodeIcon.anchors.leftMargin + anchors.leftMargin)
|
width: parent.width - (localNodeIcon.width + localNodeIcon.anchors.leftMargin + anchors.leftMargin)
|
||||||
|
|
||||||
// @TODO: Legacy. Remove after Qt 5.8.
|
|
||||||
// https://stackoverflow.com/questions/41990013
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,13 +222,6 @@ Rectangle{
|
|||||||
topPadding: 0
|
topPadding: 0
|
||||||
text: qsTr("Uses a third-party server to connect to the Monero network. Less secure, but easier on your computer.") + translationManager.emptyString
|
text: qsTr("Uses a third-party server to connect to the Monero network. Less secure, but easier on your computer.") + translationManager.emptyString
|
||||||
width: parent.width - (remoteNodeIcon.width + remoteNodeIcon.anchors.leftMargin + anchors.leftMargin)
|
width: parent.width - (remoteNodeIcon.width + remoteNodeIcon.anchors.leftMargin + anchors.leftMargin)
|
||||||
|
|
||||||
// @TODO: Legacy. Remove after Qt 5.8.
|
|
||||||
// https://stackoverflow.com/questions/41990013
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
enabled: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
@@ -279,8 +265,8 @@ Rectangle{
|
|||||||
Layout.minimumWidth: 100
|
Layout.minimumWidth: 100
|
||||||
placeholderFontSize: 15
|
placeholderFontSize: 15
|
||||||
|
|
||||||
daemonAddrLabelText: qsTr("Address")
|
daemonAddrLabelText: qsTr("Address") + translationManager.emptyString
|
||||||
daemonPortLabelText: qsTr("Port")
|
daemonPortLabelText: qsTr("Port") + translationManager.emptyString
|
||||||
|
|
||||||
property var rna: persistentSettings.remoteNodeAddress
|
property var rna: persistentSettings.remoteNodeAddress
|
||||||
daemonAddrText: rna.search(":") != -1 ? rna.split(":")[0].trim() : ""
|
daemonAddrText: rna.search(":") != -1 ? rna.split(":")[0].trim() : ""
|
||||||
@@ -318,7 +304,7 @@ Rectangle{
|
|||||||
labelText: qsTr("Daemon password") + translationManager.emptyString
|
labelText: qsTr("Daemon password") + translationManager.emptyString
|
||||||
text: persistentSettings.daemonPassword
|
text: persistentSettings.daemonPassword
|
||||||
placeholderText: qsTr("Password") + translationManager.emptyString
|
placeholderText: qsTr("Password") + translationManager.emptyString
|
||||||
echoMode: TextInput.Password
|
password: true
|
||||||
placeholderFontSize: 15
|
placeholderFontSize: 15
|
||||||
labelFontSize: 14
|
labelFontSize: 14
|
||||||
fontSize: 15
|
fontSize: 15
|
||||||
@@ -382,14 +368,12 @@ Rectangle{
|
|||||||
labelFontSize: 14
|
labelFontSize: 14
|
||||||
property string style: "<style type='text/css'>a {cursor:pointer;text-decoration: none; color: #FF6C3C}</style>"
|
property string style: "<style type='text/css'>a {cursor:pointer;text-decoration: none; color: #FF6C3C}</style>"
|
||||||
labelText: qsTr("Blockchain location") + style + " <a href='#'> (%1)</a>".arg(qsTr("Change")) + translationManager.emptyString
|
labelText: qsTr("Blockchain location") + style + " <a href='#'> (%1)</a>".arg(qsTr("Change")) + translationManager.emptyString
|
||||||
|
labelButtonText: qsTr("Reset") + translationManager.emptyString
|
||||||
|
labelButtonVisible: text
|
||||||
placeholderText: qsTr("(default)") + translationManager.emptyString
|
placeholderText: qsTr("(default)") + translationManager.emptyString
|
||||||
placeholderFontSize: 15
|
placeholderFontSize: 15
|
||||||
readOnly: true
|
readOnly: true
|
||||||
text: {
|
text: persistentSettings.blockchainDataDir
|
||||||
if(persistentSettings.blockchainDataDir.length > 0){
|
|
||||||
return persistentSettings.blockchainDataDir;
|
|
||||||
} else { return "" }
|
|
||||||
}
|
|
||||||
addressValidation: false
|
addressValidation: false
|
||||||
onInputLabelLinkActivated: {
|
onInputLabelLinkActivated: {
|
||||||
//mouse.accepted = false
|
//mouse.accepted = false
|
||||||
@@ -399,6 +383,7 @@ Rectangle{
|
|||||||
blockchainFileDialog.open();
|
blockchainFileDialog.open();
|
||||||
blockchainFolder.focus = true;
|
blockchainFolder.focus = true;
|
||||||
}
|
}
|
||||||
|
onLabelButtonClicked: persistentSettings.blockchainDataDir = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,7 +398,12 @@ Rectangle{
|
|||||||
placeholderFontSize: 15
|
placeholderFontSize: 15
|
||||||
text: persistentSettings.daemonFlags
|
text: persistentSettings.daemonFlags
|
||||||
addressValidation: false
|
addressValidation: false
|
||||||
onEditingFinished: persistentSettings.daemonFlags = daemonFlags.text;
|
error: text.match(/(^|\s)--(data-dir|bootstrap-daemon-address)/)
|
||||||
|
onEditingFinished: {
|
||||||
|
if (!daemonFlags.error) {
|
||||||
|
persistentSettings.daemonFlags = daemonFlags.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
@@ -427,8 +417,8 @@ Rectangle{
|
|||||||
Layout.minimumWidth: 100
|
Layout.minimumWidth: 100
|
||||||
Layout.bottomMargin: 20
|
Layout.bottomMargin: 20
|
||||||
|
|
||||||
daemonAddrLabelText: qsTr("Bootstrap Address")
|
daemonAddrLabelText: qsTr("Bootstrap Address") + translationManager.emptyString
|
||||||
daemonPortLabelText: qsTr("Bootstrap Port")
|
daemonPortLabelText: qsTr("Bootstrap Port") + translationManager.emptyString
|
||||||
daemonAddrText: persistentSettings.bootstrapNodeAddress.split(":")[0].trim()
|
daemonAddrText: persistentSettings.bootstrapNodeAddress.split(":")[0].trim()
|
||||||
daemonPortText: {
|
daemonPortText: {
|
||||||
var node_split = persistentSettings.bootstrapNodeAddress.split(":");
|
var node_split = persistentSettings.bootstrapNodeAddress.split(":");
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import QtQuick 2.9
|
|||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
import QtQuick.Dialogs 1.2
|
import QtQuick.Dialogs 1.2
|
||||||
|
import FontAwesome 1.0
|
||||||
|
|
||||||
import "../../js/Utils.js" as Utils
|
import "../../js/Utils.js" as Utils
|
||||||
import "../../components" as MoneroComponents
|
import "../../components" as MoneroComponents
|
||||||
@@ -47,10 +48,10 @@ Rectangle {
|
|||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.margins: 20
|
anchors.margins: 20
|
||||||
anchors.topMargin: 0
|
anchors.topMargin: 0
|
||||||
spacing: 8
|
spacing: 0
|
||||||
|
|
||||||
MoneroComponents.SettingsListItem {
|
MoneroComponents.SettingsListItem {
|
||||||
buttonText: qsTr("Close wallet") + translationManager.emptyString
|
iconText: FontAwesome.signOutAlt
|
||||||
description: qsTr("Logs out of this wallet.") + translationManager.emptyString
|
description: qsTr("Logs out of this wallet.") + translationManager.emptyString
|
||||||
title: qsTr("Close this wallet") + translationManager.emptyString
|
title: qsTr("Close this wallet") + translationManager.emptyString
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.SettingsListItem {
|
MoneroComponents.SettingsListItem {
|
||||||
buttonText: qsTr("Create wallet") + translationManager.emptyString
|
iconText: FontAwesome.eye
|
||||||
description: qsTr("Creates a new wallet that can only view and initiate transactions, but requires a spendable wallet to sign transactions before sending.") + translationManager.emptyString
|
description: qsTr("Creates a new wallet that can only view and initiate transactions, but requires a spendable wallet to sign transactions before sending.") + translationManager.emptyString
|
||||||
title: qsTr("Create a view-only wallet") + translationManager.emptyString
|
title: qsTr("Create a view-only wallet") + translationManager.emptyString
|
||||||
visible: !appWindow.viewOnly
|
visible: !appWindow.viewOnly
|
||||||
@@ -80,7 +81,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.SettingsListItem {
|
MoneroComponents.SettingsListItem {
|
||||||
buttonText: qsTr("Show seed") + translationManager.emptyString
|
iconText: FontAwesome.key
|
||||||
description: qsTr("Store this information safely to recover your wallet in the future.") + translationManager.emptyString
|
description: qsTr("Store this information safely to recover your wallet in the future.") + translationManager.emptyString
|
||||||
title: qsTr("Show seed & keys") + translationManager.emptyString
|
title: qsTr("Show seed & keys") + translationManager.emptyString
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.SettingsListItem {
|
MoneroComponents.SettingsListItem {
|
||||||
buttonText: qsTr("Rescan") + translationManager.emptyString
|
iconText: FontAwesome.repeat
|
||||||
description: qsTr("Use this feature if you think the shown balance is not accurate.") + translationManager.emptyString
|
description: qsTr("Use this feature if you think the shown balance is not accurate.") + translationManager.emptyString
|
||||||
title: qsTr("Rescan wallet balance") + translationManager.emptyString
|
title: qsTr("Rescan wallet balance") + translationManager.emptyString
|
||||||
visible: appWindow.walletMode >= 2
|
visible: appWindow.walletMode >= 2
|
||||||
@@ -114,7 +115,7 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MoneroComponents.SettingsListItem {
|
MoneroComponents.SettingsListItem {
|
||||||
buttonText: qsTr("Change password") + translationManager.emptyString
|
iconText: FontAwesome.ellipsisH
|
||||||
description: qsTr("Change the password of your wallet.") + translationManager.emptyString
|
description: qsTr("Change the password of your wallet.") + translationManager.emptyString
|
||||||
title: qsTr("Change wallet password") + translationManager.emptyString
|
title: qsTr("Change wallet password") + translationManager.emptyString
|
||||||
|
|
||||||
@@ -135,6 +136,19 @@ Rectangle {
|
|||||||
passwordDialog.open()
|
passwordDialog.open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MoneroComponents.SettingsListItem {
|
||||||
|
iconText: FontAwesome.cashRegister
|
||||||
|
isLast: true
|
||||||
|
description: qsTr("Receive Monero for your business, easily.") + translationManager.emptyString
|
||||||
|
title: qsTr("Enter merchant mode") + translationManager.emptyString
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
middlePanel.state = "Merchant";
|
||||||
|
middlePanel.flickable.contentY = 0;
|
||||||
|
updateBalance();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
|||||||
20
qml.qrc
@@ -5,10 +5,14 @@
|
|||||||
<file>MiddlePanel.qml</file>
|
<file>MiddlePanel.qml</file>
|
||||||
<file>components/Label.qml</file>
|
<file>components/Label.qml</file>
|
||||||
<file>components/SettingsListItem.qml</file>
|
<file>components/SettingsListItem.qml</file>
|
||||||
|
<file>components/Slider.qml</file>
|
||||||
|
<file>components/UpdateDialog.qml</file>
|
||||||
<file>images/whatIsIcon.png</file>
|
<file>images/whatIsIcon.png</file>
|
||||||
<file>images/whatIsIcon@2x.png</file>
|
<file>images/whatIsIcon@2x.png</file>
|
||||||
<file>images/lockIcon.png</file>
|
|
||||||
<file>components/MenuButton.qml</file>
|
<file>components/MenuButton.qml</file>
|
||||||
|
<file>monero/utils/gpg_keys/binaryfate.asc</file>
|
||||||
|
<file>monero/utils/gpg_keys/fluffypony.asc</file>
|
||||||
|
<file>monero/utils/gpg_keys/luigi1111.asc</file>
|
||||||
<file>pages/Account.qml</file>
|
<file>pages/Account.qml</file>
|
||||||
<file>pages/Transfer.qml</file>
|
<file>pages/Transfer.qml</file>
|
||||||
<file>pages/History.qml</file>
|
<file>pages/History.qml</file>
|
||||||
@@ -23,7 +27,7 @@
|
|||||||
<file>components/TipItem.qml</file>
|
<file>components/TipItem.qml</file>
|
||||||
<file>images/tip.png</file>
|
<file>images/tip.png</file>
|
||||||
<file>components/MenuButtonDivider.qml</file>
|
<file>components/MenuButtonDivider.qml</file>
|
||||||
<file>images/moneroIcon.png</file>
|
<file>images/monero-vector.svg</file>
|
||||||
<file>components/StandardDropdown.qml</file>
|
<file>components/StandardDropdown.qml</file>
|
||||||
<file>images/whiteDropIndicator.png</file>
|
<file>images/whiteDropIndicator.png</file>
|
||||||
<file>images/whiteDropIndicator@2x.png</file>
|
<file>images/whiteDropIndicator@2x.png</file>
|
||||||
@@ -34,7 +38,6 @@
|
|||||||
<file>images/prevMonth.png</file>
|
<file>images/prevMonth.png</file>
|
||||||
<file>images/prevMonth@2x.png</file>
|
<file>images/prevMonth@2x.png</file>
|
||||||
<file>components/TitleBar.qml</file>
|
<file>components/TitleBar.qml</file>
|
||||||
<file>images/moneroLogo2.png</file>
|
|
||||||
<file>images/resize.png</file>
|
<file>images/resize.png</file>
|
||||||
<file>images/resize@2x.png</file>
|
<file>images/resize@2x.png</file>
|
||||||
<file>images/resizeHovered.png</file>
|
<file>images/resizeHovered.png</file>
|
||||||
@@ -96,11 +99,11 @@
|
|||||||
<file>components/ProcessingSplash.qml</file>
|
<file>components/ProcessingSplash.qml</file>
|
||||||
<file>components/ProgressBar.qml</file>
|
<file>components/ProgressBar.qml</file>
|
||||||
<file>components/StandardDialog.qml</file>
|
<file>components/StandardDialog.qml</file>
|
||||||
|
<file>components/DevicePassphraseDialog.qml</file>
|
||||||
<file>pages/Sign.qml</file>
|
<file>pages/Sign.qml</file>
|
||||||
<file>components/DaemonManagerDialog.qml</file>
|
<file>components/DaemonManagerDialog.qml</file>
|
||||||
<file>version.js</file>
|
<file>version.js</file>
|
||||||
<file>components/QRCodeScanner.qml</file>
|
<file>components/QRCodeScanner.qml</file>
|
||||||
<file>components/Notifier.qml</file>
|
|
||||||
<file>components/TextBlock.qml</file>
|
<file>components/TextBlock.qml</file>
|
||||||
<file>components/RemoteNodeEdit.qml</file>
|
<file>components/RemoteNodeEdit.qml</file>
|
||||||
<file>pages/Keys.qml</file>
|
<file>pages/Keys.qml</file>
|
||||||
@@ -110,8 +113,6 @@
|
|||||||
<file>images/card-background-white.png</file>
|
<file>images/card-background-white.png</file>
|
||||||
<file>images/card-background-white@2x.png</file>
|
<file>images/card-background-white@2x.png</file>
|
||||||
<file>images/moneroLogo_white.png</file>
|
<file>images/moneroLogo_white.png</file>
|
||||||
<file>images/question.png</file>
|
|
||||||
<file>images/question@2x.png</file>
|
|
||||||
<file>images/titlebarLogo.png</file>
|
<file>images/titlebarLogo.png</file>
|
||||||
<file>images/titlebarLogo@2x.png</file>
|
<file>images/titlebarLogo@2x.png</file>
|
||||||
<file>pages/merchant/MerchantTitlebar.qml</file>
|
<file>pages/merchant/MerchantTitlebar.qml</file>
|
||||||
@@ -188,7 +189,6 @@
|
|||||||
<file>wizard/WizardHeader.qml</file>
|
<file>wizard/WizardHeader.qml</file>
|
||||||
<file>wizard/WizardHome.qml</file>
|
<file>wizard/WizardHome.qml</file>
|
||||||
<file>wizard/WizardLanguage.qml</file>
|
<file>wizard/WizardLanguage.qml</file>
|
||||||
<file>wizard/WizardLang.qml</file>
|
|
||||||
<file>wizard/WizardNav.qml</file>
|
<file>wizard/WizardNav.qml</file>
|
||||||
<file>wizard/WizardWalletInput.qml</file>
|
<file>wizard/WizardWalletInput.qml</file>
|
||||||
<file>wizard/WizardRestoreWallet1.qml</file>
|
<file>wizard/WizardRestoreWallet1.qml</file>
|
||||||
@@ -204,7 +204,6 @@
|
|||||||
<file>js/Wizard.js</file>
|
<file>js/Wizard.js</file>
|
||||||
<file>components/LanguageSidebar.qml</file>
|
<file>components/LanguageSidebar.qml</file>
|
||||||
<file>images/world-flags-globe.png</file>
|
<file>images/world-flags-globe.png</file>
|
||||||
<file>images/langFlagGrey.png</file>
|
|
||||||
<file>images/restore-wallet-from-hardware@2x.png</file>
|
<file>images/restore-wallet-from-hardware@2x.png</file>
|
||||||
<file>images/restore-wallet-from-hardware.png</file>
|
<file>images/restore-wallet-from-hardware.png</file>
|
||||||
<file>images/open-wallet-from-file@2x.png</file>
|
<file>images/open-wallet-from-file@2x.png</file>
|
||||||
@@ -219,7 +218,6 @@
|
|||||||
<file>images/local-node@2x.png</file>
|
<file>images/local-node@2x.png</file>
|
||||||
<file>images/local-node-full.png</file>
|
<file>images/local-node-full.png</file>
|
||||||
<file>images/local-node-full@2x.png</file>
|
<file>images/local-node-full@2x.png</file>
|
||||||
<file>wizard/WizardNavProgressDot.qml</file>
|
|
||||||
<file>wizard/WizardOpenWallet1.qml</file>
|
<file>wizard/WizardOpenWallet1.qml</file>
|
||||||
<file>images/arrow-right-in-circle.png</file>
|
<file>images/arrow-right-in-circle.png</file>
|
||||||
<file>images/arrow-right-in-circle@2x.png</file>
|
<file>images/arrow-right-in-circle@2x.png</file>
|
||||||
@@ -233,7 +231,6 @@
|
|||||||
<file>images/themes/white/close.svg</file>
|
<file>images/themes/white/close.svg</file>
|
||||||
<file>images/themes/white/fullscreen.svg</file>
|
<file>images/themes/white/fullscreen.svg</file>
|
||||||
<file>images/themes/white/minimize.svg</file>
|
<file>images/themes/white/minimize.svg</file>
|
||||||
<file>images/themes/white/question.svg</file>
|
|
||||||
<file>components/effects/ColorTransition.qml</file>
|
<file>components/effects/ColorTransition.qml</file>
|
||||||
<file>components/effects/GradientBackground.qml</file>
|
<file>components/effects/GradientBackground.qml</file>
|
||||||
<file>images/check-white.svg</file>
|
<file>images/check-white.svg</file>
|
||||||
@@ -241,5 +238,8 @@
|
|||||||
<file>images/edit.svg</file>
|
<file>images/edit.svg</file>
|
||||||
<file>images/arrow-right-in-circle-outline-medium-white.svg</file>
|
<file>images/arrow-right-in-circle-outline-medium-white.svg</file>
|
||||||
<file>images/tails-grey.png</file>
|
<file>images/tails-grey.png</file>
|
||||||
|
<file>components/AdvancedOptionsItem.qml</file>
|
||||||
|
<file>images/busy-indicator.png</file>
|
||||||
|
<file>images/busy-indicator@2x.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ add_subdirectory(QR-Code-scanner)
|
|||||||
add_subdirectory(daemon)
|
add_subdirectory(daemon)
|
||||||
add_subdirectory(libwalletqt)
|
add_subdirectory(libwalletqt)
|
||||||
add_subdirectory(model)
|
add_subdirectory(model)
|
||||||
|
add_subdirectory(openpgp)
|
||||||
add_subdirectory(zxcvbn-c)
|
add_subdirectory(zxcvbn-c)
|
||||||
|
|
||||||
qt5_add_resources(RESOURCES ../qml.qrc)
|
qt5_add_resources(RESOURCES ../qml.qrc)
|
||||||
@@ -14,7 +15,9 @@ file(GLOB SOURCE_FILES
|
|||||||
"main/*.h"
|
"main/*.h"
|
||||||
"main/*.cpp"
|
"main/*.cpp"
|
||||||
"libwalletqt/WalletManager.cpp"
|
"libwalletqt/WalletManager.cpp"
|
||||||
|
"libwalletqt/WalletListenerImpl.cpp"
|
||||||
"libwalletqt/Wallet.cpp"
|
"libwalletqt/Wallet.cpp"
|
||||||
|
"libwalletqt/PassphraseHelper.cpp"
|
||||||
"libwalletqt/PendingTransaction.cpp"
|
"libwalletqt/PendingTransaction.cpp"
|
||||||
"libwalletqt/TransactionHistory.cpp"
|
"libwalletqt/TransactionHistory.cpp"
|
||||||
"libwalletqt/TransactionInfo.cpp"
|
"libwalletqt/TransactionInfo.cpp"
|
||||||
@@ -28,6 +31,7 @@ file(GLOB SOURCE_FILES
|
|||||||
"libwalletqt/UnsignedTransaction.cpp"
|
"libwalletqt/UnsignedTransaction.cpp"
|
||||||
"libwalletqt/WalletManager.h"
|
"libwalletqt/WalletManager.h"
|
||||||
"libwalletqt/Wallet.h"
|
"libwalletqt/Wallet.h"
|
||||||
|
"libwalletqt/PassphraseHelper.h"
|
||||||
"libwalletqt/PendingTransaction.h"
|
"libwalletqt/PendingTransaction.h"
|
||||||
"libwalletqt/TransactionHistory.h"
|
"libwalletqt/TransactionHistory.h"
|
||||||
"libwalletqt/TransactionInfo.h"
|
"libwalletqt/TransactionInfo.h"
|
||||||
@@ -76,25 +80,25 @@ if(MINGW)
|
|||||||
list(APPEND RESOURCES ${ICON_RES})
|
list(APPEND RESOURCES ${ICON_RES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(monero-gui ${EXECUTABLE_FLAG} main/main.cpp
|
add_executable(monero-wallet-gui ${EXECUTABLE_FLAG} main/main.cpp
|
||||||
${SOURCE_FILES}
|
${SOURCE_FILES}
|
||||||
${PASS_STRENGTH_FILES}
|
${PASS_STRENGTH_FILES}
|
||||||
${QR_CODE_FILES}
|
${QR_CODE_FILES}
|
||||||
${RESOURCES}
|
${RESOURCES}
|
||||||
)
|
)
|
||||||
set_property(TARGET monero-gui PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
set_property(TARGET monero-wallet-gui PROPERTY RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||||
|
|
||||||
# OpenGL
|
# OpenGL
|
||||||
target_include_directories(monero-gui PUBLIC ${OPENGL_INCLUDE_DIR})
|
target_include_directories(monero-wallet-gui PUBLIC ${OPENGL_INCLUDE_DIR})
|
||||||
message(STATUS "OpenGL: include dir at ${OPENGL_INCLUDE_DIR}")
|
message(STATUS "OpenGL: include dir at ${OPENGL_INCLUDE_DIR}")
|
||||||
message(STATUS "OpenGL: libraries at ${OPENGL_LIBRARIES}")
|
message(STATUS "OpenGL: libraries at ${OPENGL_LIBRARIES}")
|
||||||
|
|
||||||
target_include_directories(monero-gui PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
target_include_directories(monero-wallet-gui PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
|
|
||||||
file(GLOB_RECURSE SRC_SOURCES *.cpp)
|
file(GLOB_RECURSE SRC_SOURCES *.cpp)
|
||||||
file(GLOB_RECURSE SRC_HEADERS *.h)
|
file(GLOB_RECURSE SRC_HEADERS *.h)
|
||||||
|
|
||||||
target_include_directories(monero-gui PUBLIC
|
target_include_directories(monero-wallet-gui PUBLIC
|
||||||
${CMAKE_SOURCE_DIR}/monero/include
|
${CMAKE_SOURCE_DIR}/monero/include
|
||||||
${CMAKE_SOURCE_DIR}/monero/src
|
${CMAKE_SOURCE_DIR}/monero/src
|
||||||
${CMAKE_SOURCE_DIR}/monero/external/easylogging++
|
${CMAKE_SOURCE_DIR}/monero/external/easylogging++
|
||||||
@@ -114,28 +118,25 @@ target_include_directories(monero-gui PUBLIC
|
|||||||
${ZBAR_INCLUDE_DIR}
|
${ZBAR_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_definitions(monero-gui
|
target_compile_definitions(monero-wallet-gui
|
||||||
PUBLIC
|
PUBLIC
|
||||||
${Qt5Widgets_DEFINITIONS}
|
${Qt5Widgets_DEFINITIONS}
|
||||||
${Qt5Qml_DEFINITIONS}
|
${Qt5Qml_DEFINITIONS}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
|
||||||
|
|
||||||
if(X11_FOUND)
|
if(INSTALL_VENDORED_LIBUNBOUND)
|
||||||
target_link_libraries(monero-gui ${X11_LIBRARIES} pthread dl Xt xcb X11)
|
set(UNBOUND_LIBRARY ${CMAKE_BINARY_DIR}/monero/external/unbound/libunbound.a)
|
||||||
|
else()
|
||||||
|
set(UNBOUND_LIBRARY unbound)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(DEVICE_TREZOR_READY)
|
target_link_libraries(monero-wallet-gui
|
||||||
target_link_libraries(monero-gui ${TREZOR_DEP_LIBS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(monero-gui
|
|
||||||
${CMAKE_BINARY_DIR}/lib/libwallet_merged.a
|
${CMAKE_BINARY_DIR}/lib/libwallet_merged.a
|
||||||
${LMDB_LIBRARY}
|
${LMDB_LIBRARY}
|
||||||
${CMAKE_BINARY_DIR}/monero/contrib/epee/src/libepee.a
|
${CMAKE_BINARY_DIR}/monero/contrib/epee/src/libepee.a
|
||||||
${CMAKE_BINARY_DIR}/monero/external/unbound/libunbound.a
|
${UNBOUND_LIBRARY}
|
||||||
${SODIUM_LIBRARY}
|
${SODIUM_LIBRARY}
|
||||||
${CMAKE_BINARY_DIR}/monero/external/easylogging++/libeasylogging.a
|
${CMAKE_BINARY_DIR}/monero/external/easylogging++/libeasylogging.a
|
||||||
${CMAKE_BINARY_DIR}/monero/src/blockchain_db/libblockchain_db.a
|
${CMAKE_BINARY_DIR}/monero/src/blockchain_db/libblockchain_db.a
|
||||||
@@ -149,10 +150,19 @@ target_link_libraries(monero-gui
|
|||||||
${QT5_LIBRARIES}
|
${QT5_LIBRARIES}
|
||||||
${EXTRA_LIBRARIES}
|
${EXTRA_LIBRARIES}
|
||||||
${ICU_LIBRARIES}
|
${ICU_LIBRARIES}
|
||||||
|
openpgp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(DEVICE_TREZOR_READY)
|
||||||
|
target_link_libraries(monero-wallet-gui ${TREZOR_DEP_LIBS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(X11_FOUND)
|
||||||
|
target_link_libraries(monero-wallet-gui ${X11_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WITH_SCANNER)
|
if(WITH_SCANNER)
|
||||||
target_link_libraries(monero-gui
|
target_link_libraries(monero-wallet-gui
|
||||||
${ZBAR_LIBRARIES}
|
${ZBAR_LIBRARIES}
|
||||||
jpeg
|
jpeg
|
||||||
v4l2
|
v4l2
|
||||||
@@ -161,6 +171,6 @@ if(WITH_SCANNER)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
install(TARGETS monero-gui
|
install(TARGETS monero-wallet-gui
|
||||||
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -80,7 +80,11 @@ bool QrScanThread::zimageFromQImage(const QImage &qimg, zbar::Image &dst)
|
|||||||
unsigned int bpl( qimg.bytesPerLine() ), width( bpl / 4), height( qimg.height());
|
unsigned int bpl( qimg.bytesPerLine() ), width( bpl / 4), height( qimg.height());
|
||||||
dst.set_size(width, height);
|
dst.set_size(width, height);
|
||||||
dst.set_format("BGR4");
|
dst.set_format("BGR4");
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||||
|
unsigned long datalen = qimg.sizeInBytes();
|
||||||
|
#else
|
||||||
unsigned long datalen = qimg.byteCount();
|
unsigned long datalen = qimg.byteCount();
|
||||||
|
#endif
|
||||||
dst.set_data(qimg.bits(), datalen);
|
dst.set_data(qimg.bits(), datalen);
|
||||||
if((width * 4 != bpl) || (width * height * 4 > datalen)){
|
if((width * 4 != bpl) || (width * height * 4 > datalen)){
|
||||||
emit notifyError(QString("QImage to Zbar::Image failed !"));
|
emit notifyError(QString("QImage to Zbar::Image failed !"));
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "DaemonManager.h"
|
#include "DaemonManager.h"
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QMutexLocker>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@@ -113,8 +114,8 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const
|
|||||||
|
|
||||||
arguments << "--check-updates" << "disabled";
|
arguments << "--check-updates" << "disabled";
|
||||||
|
|
||||||
// --max-concurrency based on threads available. max: 6
|
// --max-concurrency based on threads available.
|
||||||
int32_t concurrency = qBound(1, QThread::idealThreadCount() / 2, 6);
|
int32_t concurrency = qMax(1, QThread::idealThreadCount() / 2);
|
||||||
|
|
||||||
if(!flags.contains("--max-concurrency", Qt::CaseSensitive)){
|
if(!flags.contains("--max-concurrency", Qt::CaseSensitive)){
|
||||||
arguments << "--max-concurrency" << QString::number(concurrency);
|
arguments << "--max-concurrency" << QString::number(concurrency);
|
||||||
@@ -123,18 +124,19 @@ bool DaemonManager::start(const QString &flags, NetworkType::Type nettype, const
|
|||||||
qDebug() << "starting monerod " + m_monerod;
|
qDebug() << "starting monerod " + m_monerod;
|
||||||
qDebug() << "With command line arguments " << arguments;
|
qDebug() << "With command line arguments " << arguments;
|
||||||
|
|
||||||
m_daemon = new QProcess();
|
QMutexLocker locker(&m_daemonMutex);
|
||||||
initialized = true;
|
|
||||||
|
m_daemon.reset(new QProcess());
|
||||||
|
|
||||||
// Connect output slots
|
// Connect output slots
|
||||||
connect (m_daemon, SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput()));
|
connect(m_daemon.get(), SIGNAL(readyReadStandardOutput()), this, SLOT(printOutput()));
|
||||||
connect (m_daemon, SIGNAL(readyReadStandardError()), this, SLOT(printError()));
|
connect(m_daemon.get(), SIGNAL(readyReadStandardError()), this, SLOT(printError()));
|
||||||
|
|
||||||
// Start monerod
|
// Start monerod
|
||||||
bool started = m_daemon->startDetached(m_monerod, arguments);
|
bool started = m_daemon->startDetached(m_monerod, arguments);
|
||||||
|
|
||||||
// add state changed listener
|
// add state changed listener
|
||||||
connect(m_daemon,SIGNAL(stateChanged(QProcess::ProcessState)),this,SLOT(stateChanged(QProcess::ProcessState)));
|
connect(m_daemon.get(), SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(stateChanged(QProcess::ProcessState)));
|
||||||
|
|
||||||
if (!started) {
|
if (!started) {
|
||||||
qDebug() << "Daemon start error: " + m_daemon->errorString();
|
qDebug() << "Daemon start error: " + m_daemon->errorString();
|
||||||
@@ -200,9 +202,9 @@ bool DaemonManager::stopWatcher(NetworkType::Type nettype) const
|
|||||||
if(counter >= 5) {
|
if(counter >= 5) {
|
||||||
qDebug() << "Killing it! ";
|
qDebug() << "Killing it! ";
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
QProcess::execute("taskkill /F /IM monerod.exe");
|
QProcess::execute("taskkill", {"/F", "/IM", "monerod.exe"});
|
||||||
#else
|
#else
|
||||||
QProcess::execute("pkill monerod");
|
QProcess::execute("pkill", {"monerod"});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +225,10 @@ void DaemonManager::stateChanged(QProcess::ProcessState state)
|
|||||||
|
|
||||||
void DaemonManager::printOutput()
|
void DaemonManager::printOutput()
|
||||||
{
|
{
|
||||||
QByteArray byteArray = m_daemon->readAllStandardOutput();
|
QByteArray byteArray = [this]() {
|
||||||
|
QMutexLocker locker(&m_daemonMutex);
|
||||||
|
return m_daemon->readAllStandardOutput();
|
||||||
|
}();
|
||||||
QStringList strLines = QString(byteArray).split("\n");
|
QStringList strLines = QString(byteArray).split("\n");
|
||||||
|
|
||||||
foreach (QString line, strLines) {
|
foreach (QString line, strLines) {
|
||||||
@@ -234,7 +239,10 @@ void DaemonManager::printOutput()
|
|||||||
|
|
||||||
void DaemonManager::printError()
|
void DaemonManager::printError()
|
||||||
{
|
{
|
||||||
QByteArray byteArray = m_daemon->readAllStandardError();
|
QByteArray byteArray = [this]() {
|
||||||
|
QMutexLocker locker(&m_daemonMutex);
|
||||||
|
return m_daemon->readAllStandardError();
|
||||||
|
}();
|
||||||
QStringList strLines = QString(byteArray).split("\n");
|
QStringList strLines = QString(byteArray).split("\n");
|
||||||
|
|
||||||
foreach (QString line, strLines) {
|
foreach (QString line, strLines) {
|
||||||
@@ -351,7 +359,6 @@ DaemonManager::DaemonManager(QObject *parent)
|
|||||||
|
|
||||||
if (m_monerod.length() == 0) {
|
if (m_monerod.length() == 0) {
|
||||||
qCritical() << "no daemon binary defined for current platform";
|
qCritical() << "no daemon binary defined for current platform";
|
||||||
m_has_daemon = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@
|
|||||||
#ifndef DAEMONMANAGER_H
|
#ifndef DAEMONMANAGER_H
|
||||||
#define DAEMONMANAGER_H
|
#define DAEMONMANAGER_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QMutex>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
@@ -78,10 +81,9 @@ private:
|
|||||||
|
|
||||||
static DaemonManager * m_instance;
|
static DaemonManager * m_instance;
|
||||||
static QStringList m_clArgs;
|
static QStringList m_clArgs;
|
||||||
QProcess *m_daemon;
|
std::unique_ptr<QProcess> m_daemon;
|
||||||
bool initialized = false;
|
QMutex m_daemonMutex;
|
||||||
QString m_monerod;
|
QString m_monerod;
|
||||||
bool m_has_daemon = true;
|
|
||||||
bool m_app_exit = false;
|
bool m_app_exit = false;
|
||||||
bool m_noSync = false;
|
bool m_noSync = false;
|
||||||
|
|
||||||
|
|||||||
70
src/libwalletqt/PassphraseHelper.cpp
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// Copyright (c) 2014-2020, 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 "PassphraseHelper.h"
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
Monero::optional<std::string> PassphraseHelper::onDevicePassphraseRequest(bool & on_device)
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
QMutexLocker locker(&m_mutex_pass);
|
||||||
|
m_passphrase_on_device = true;
|
||||||
|
m_passphrase_abort = false;
|
||||||
|
|
||||||
|
if (m_prompter != nullptr){
|
||||||
|
m_prompter->onWalletPassphraseNeeded(on_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cond_pass.wait(&m_mutex_pass);
|
||||||
|
|
||||||
|
if (m_passphrase_abort)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Passphrase entry abort");
|
||||||
|
}
|
||||||
|
|
||||||
|
on_device = m_passphrase_on_device;
|
||||||
|
if (!on_device) {
|
||||||
|
auto tmpPass = m_passphrase.toStdString();
|
||||||
|
m_passphrase = QString();
|
||||||
|
return Monero::optional<std::string>(tmpPass);
|
||||||
|
} else {
|
||||||
|
return Monero::optional<std::string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PassphraseHelper::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
QMutexLocker locker(&m_mutex_pass);
|
||||||
|
m_passphrase = passphrase;
|
||||||
|
m_passphrase_abort = entry_abort;
|
||||||
|
m_passphrase_on_device = enter_on_device;
|
||||||
|
|
||||||
|
m_cond_pass.wakeAll();
|
||||||
|
}
|
||||||
74
src/libwalletqt/PassphraseHelper.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright (c) 2014-2020, 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 MONERO_GUI_PASSPHRASEHELPER_H
|
||||||
|
#define MONERO_GUI_PASSPHRASEHELPER_H
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <wallet/api/wallet2_api.h>
|
||||||
|
#include <QMutex>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements component responsible for showing entry prompt to the user,
|
||||||
|
* typically Wallet / Wallet manager.
|
||||||
|
*/
|
||||||
|
class PassprasePrompter {
|
||||||
|
public:
|
||||||
|
virtual void onWalletPassphraseNeeded(bool onDevice) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements receiver of the passphrase responsible for passing it back to the wallet,
|
||||||
|
* typically wallet listener.
|
||||||
|
*/
|
||||||
|
class PassphraseReceiver {
|
||||||
|
public:
|
||||||
|
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PassphraseHelper {
|
||||||
|
public:
|
||||||
|
PassphraseHelper(PassprasePrompter * prompter=nullptr): m_prompter(prompter) {};
|
||||||
|
PassphraseHelper(const PassphraseHelper & h): PassphraseHelper(h.m_prompter) {};
|
||||||
|
Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device);
|
||||||
|
void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PassprasePrompter * m_prompter;
|
||||||
|
QWaitCondition m_cond_pass;
|
||||||
|
QMutex m_mutex_pass;
|
||||||
|
QString m_passphrase;
|
||||||
|
bool m_passphrase_abort;
|
||||||
|
bool m_passphrase_on_device;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //MONERO_GUI_PASSPHRASEHELPER_H
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QReadLocker>
|
#include <QReadLocker>
|
||||||
#include <QWriteLocker>
|
#include <QWriteLocker>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
|
||||||
bool TransactionHistory::transaction(int index, std::function<void (TransactionInfo &)> callback)
|
bool TransactionHistory::transaction(int index, std::function<void (TransactionInfo &)> callback)
|
||||||
@@ -58,7 +59,11 @@ bool TransactionHistory::transaction(int index, std::function<void (TransactionI
|
|||||||
|
|
||||||
void TransactionHistory::refresh(quint32 accountIndex)
|
void TransactionHistory::refresh(quint32 accountIndex)
|
||||||
{
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
QDateTime firstDateTime = QDate(2014, 4, 18).startOfDay();
|
||||||
|
#else
|
||||||
QDateTime firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
|
QDateTime firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
|
||||||
|
#endif
|
||||||
QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
|
QDateTime lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
|
||||||
|
|
||||||
emit refreshStarted();
|
emit refreshStarted();
|
||||||
@@ -143,7 +148,11 @@ bool TransactionHistory::TransactionHistory::locked() const
|
|||||||
TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent)
|
TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObject *parent)
|
||||||
: QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false)
|
: QObject(parent), m_pimpl(pimpl), m_minutesToUnlock(0), m_locked(false)
|
||||||
{
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
m_firstDateTime = QDate(2014, 4, 18).startOfDay();
|
||||||
|
#else
|
||||||
m_firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
|
m_firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block
|
||||||
|
#endif
|
||||||
m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
|
m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,6 @@
|
|||||||
#include <QtConcurrent/QtConcurrent>
|
#include <QtConcurrent/QtConcurrent>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QMutex>
|
|
||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -59,66 +58,6 @@ namespace {
|
|||||||
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] ="gui.subaddress_account";
|
static constexpr char ATTRIBUTE_SUBADDRESS_ACCOUNT[] ="gui.subaddress_account";
|
||||||
}
|
}
|
||||||
|
|
||||||
class WalletListenerImpl : public Monero::WalletListener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WalletListenerImpl(Wallet * w)
|
|
||||||
: m_wallet(w)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void moneySpent(const std::string &txId, uint64_t amount) override
|
|
||||||
{
|
|
||||||
qDebug() << __FUNCTION__;
|
|
||||||
emit m_wallet->moneySpent(QString::fromStdString(txId), amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
virtual void moneyReceived(const std::string &txId, uint64_t amount) override
|
|
||||||
{
|
|
||||||
qDebug() << __FUNCTION__;
|
|
||||||
emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override
|
|
||||||
{
|
|
||||||
qDebug() << __FUNCTION__;
|
|
||||||
emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void newBlock(uint64_t height) override
|
|
||||||
{
|
|
||||||
// qDebug() << __FUNCTION__;
|
|
||||||
emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void updated() override
|
|
||||||
{
|
|
||||||
emit m_wallet->updated();
|
|
||||||
}
|
|
||||||
|
|
||||||
// called when wallet refreshed by background thread or explicitly
|
|
||||||
virtual void refreshed() override
|
|
||||||
{
|
|
||||||
qDebug() << __FUNCTION__;
|
|
||||||
emit m_wallet->refreshed();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void onDeviceButtonRequest(uint64_t code) override
|
|
||||||
{
|
|
||||||
emit m_wallet->deviceButtonRequest(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void onDeviceButtonPressed() override
|
|
||||||
{
|
|
||||||
emit m_wallet->deviceButtonPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Wallet * m_wallet;
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet::Wallet(QObject * parent)
|
Wallet::Wallet(QObject * parent)
|
||||||
: Wallet(nullptr, parent)
|
: Wallet(nullptr, parent)
|
||||||
{
|
{
|
||||||
@@ -227,12 +166,22 @@ QString Wallet::address(quint32 accountIndex, quint32 addressIndex) const
|
|||||||
|
|
||||||
QString Wallet::path() const
|
QString Wallet::path() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(m_walletImpl->path());
|
return QDir::toNativeSeparators(QString::fromStdString(m_walletImpl->path()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wallet::store(const QString &path)
|
void Wallet::storeAsync(const QJSValue &callback, const QString &path /* = "" */)
|
||||||
{
|
{
|
||||||
return m_walletImpl->store(path.toStdString());
|
const auto future = m_scheduler.run(
|
||||||
|
[this, path] {
|
||||||
|
QMutexLocker locker(&m_storeMutex);
|
||||||
|
|
||||||
|
return QJSValueList({m_walletImpl->store(path.toStdString())});
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
if (!future.first)
|
||||||
|
{
|
||||||
|
QJSValue(callback).call(QJSValueList({false}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
|
bool Wallet::init(const QString &daemonAddress, bool trustedDaemon, quint64 upperTransactionLimit, bool isRecovering, bool isRecoveringFromDevice, quint64 restoreHeight)
|
||||||
@@ -290,6 +239,11 @@ bool Wallet::isLedger() const
|
|||||||
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Ledger;
|
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Ledger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Wallet::isTrezor() const
|
||||||
|
{
|
||||||
|
return m_walletImpl->getDeviceType() == Monero::Wallet::Device_Trezor;
|
||||||
|
}
|
||||||
|
|
||||||
//! create a view only wallet
|
//! create a view only wallet
|
||||||
bool Wallet::createViewOnly(const QString &path, const QString &password) const
|
bool Wallet::createViewOnly(const QString &path, const QString &password) const
|
||||||
{
|
{
|
||||||
@@ -600,6 +554,19 @@ void Wallet::disposeTransaction(UnsignedTransaction *t)
|
|||||||
delete t;
|
delete t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Wallet::estimateTransactionFeeAsync(const QString &destination,
|
||||||
|
quint64 amount,
|
||||||
|
PendingTransaction::Priority priority,
|
||||||
|
const QJSValue &callback)
|
||||||
|
{
|
||||||
|
m_scheduler.run([this, destination, amount, priority] {
|
||||||
|
const uint64_t fee = m_walletImpl->estimateTransactionFee(
|
||||||
|
{std::make_pair(destination.toStdString(), amount)},
|
||||||
|
static_cast<Monero::PendingTransaction::Priority>(priority));
|
||||||
|
return QJSValueList({QString::fromStdString(Monero::Wallet::displayAmount(fee))});
|
||||||
|
}, callback);
|
||||||
|
}
|
||||||
|
|
||||||
TransactionHistory *Wallet::history() const
|
TransactionHistory *Wallet::history() const
|
||||||
{
|
{
|
||||||
return m_history;
|
return m_history;
|
||||||
@@ -999,6 +966,19 @@ void Wallet::keyReuseMitigation2(bool mitigation)
|
|||||||
m_walletImpl->keyReuseMitigation2(mitigation);
|
m_walletImpl->keyReuseMitigation2(mitigation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Wallet::onWalletPassphraseNeeded(bool on_device)
|
||||||
|
{
|
||||||
|
emit this->walletPassphraseNeeded(on_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Wallet::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||||
|
{
|
||||||
|
if (m_walletListener != nullptr)
|
||||||
|
{
|
||||||
|
m_walletListener->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
Wallet::Wallet(Monero::Wallet *w, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_walletImpl(w)
|
, m_walletImpl(w)
|
||||||
|
|||||||
@@ -41,6 +41,8 @@
|
|||||||
#include "PendingTransaction.h" // we need to have an access to the PendingTransaction::Priority enum here;
|
#include "PendingTransaction.h" // we need to have an access to the PendingTransaction::Priority enum here;
|
||||||
#include "UnsignedTransaction.h"
|
#include "UnsignedTransaction.h"
|
||||||
#include "NetworkType.h"
|
#include "NetworkType.h"
|
||||||
|
#include "PassphraseHelper.h"
|
||||||
|
#include "WalletListenerImpl.h"
|
||||||
|
|
||||||
namespace Monero {
|
namespace Monero {
|
||||||
struct Wallet; // forward declaration
|
struct Wallet; // forward declaration
|
||||||
@@ -57,7 +59,7 @@ class SubaddressModel;
|
|||||||
class SubaddressAccount;
|
class SubaddressAccount;
|
||||||
class SubaddressAccountModel;
|
class SubaddressAccountModel;
|
||||||
|
|
||||||
class Wallet : public QObject
|
class Wallet : public QObject, public PassprasePrompter
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged)
|
Q_PROPERTY(bool disconnected READ disconnected NOTIFY disconnectedChanged)
|
||||||
@@ -144,7 +146,7 @@ public:
|
|||||||
|
|
||||||
//! saves wallet to the file by given path
|
//! saves wallet to the file by given path
|
||||||
//! empty path stores in current location
|
//! empty path stores in current location
|
||||||
Q_INVOKABLE bool store(const QString &path = "");
|
Q_INVOKABLE void storeAsync(const QJSValue &callback, const QString &path = "");
|
||||||
|
|
||||||
//! initializes wallet asynchronously
|
//! initializes wallet asynchronously
|
||||||
Q_INVOKABLE void initAsync(const QString &daemonAddress, bool trustedDaemon = false, quint64 upperTransactionLimit = 0, bool isRecovering = false, bool isRecoveringFromDevice = false, quint64 restoreHeight = 0);
|
Q_INVOKABLE void initAsync(const QString &daemonAddress, bool trustedDaemon = false, quint64 upperTransactionLimit = 0, bool isRecovering = false, bool isRecoveringFromDevice = false, quint64 restoreHeight = 0);
|
||||||
@@ -185,6 +187,7 @@ public:
|
|||||||
//! hw-device backed wallets
|
//! hw-device backed wallets
|
||||||
Q_INVOKABLE bool isHwBacked() const;
|
Q_INVOKABLE bool isHwBacked() const;
|
||||||
Q_INVOKABLE bool isLedger() const;
|
Q_INVOKABLE bool isLedger() const;
|
||||||
|
Q_INVOKABLE bool isTrezor() const;
|
||||||
|
|
||||||
//! returns if view only wallet
|
//! returns if view only wallet
|
||||||
Q_INVOKABLE bool viewOnly() const;
|
Q_INVOKABLE bool viewOnly() const;
|
||||||
@@ -250,6 +253,11 @@ public:
|
|||||||
//! deletes unsigned transaction and frees memory
|
//! deletes unsigned transaction and frees memory
|
||||||
Q_INVOKABLE void disposeTransaction(UnsignedTransaction * t);
|
Q_INVOKABLE void disposeTransaction(UnsignedTransaction * t);
|
||||||
|
|
||||||
|
Q_INVOKABLE void estimateTransactionFeeAsync(const QString &destination,
|
||||||
|
quint64 amount,
|
||||||
|
PendingTransaction::Priority priority,
|
||||||
|
const QJSValue &callback);
|
||||||
|
|
||||||
//! returns transaction history
|
//! returns transaction history
|
||||||
TransactionHistory * history() const;
|
TransactionHistory * history() const;
|
||||||
|
|
||||||
@@ -343,6 +351,10 @@ public:
|
|||||||
Q_INVOKABLE void segregationHeight(quint64 height);
|
Q_INVOKABLE void segregationHeight(quint64 height);
|
||||||
Q_INVOKABLE void keyReuseMitigation2(bool mitigation);
|
Q_INVOKABLE void keyReuseMitigation2(bool mitigation);
|
||||||
|
|
||||||
|
// Passphrase entry for hardware wallets
|
||||||
|
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
|
||||||
|
virtual void onWalletPassphraseNeeded(bool on_device) override;
|
||||||
|
|
||||||
// TODO: setListenter() when it implemented in API
|
// TODO: setListenter() when it implemented in API
|
||||||
signals:
|
signals:
|
||||||
// emitted on every event happened with wallet
|
// emitted on every event happened with wallet
|
||||||
@@ -362,6 +374,7 @@ signals:
|
|||||||
void walletCreationHeightChanged();
|
void walletCreationHeightChanged();
|
||||||
void deviceButtonRequest(quint64 buttonCode);
|
void deviceButtonRequest(quint64 buttonCode);
|
||||||
void deviceButtonPressed();
|
void deviceButtonPressed();
|
||||||
|
void walletPassphraseNeeded(bool onDevice);
|
||||||
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
|
void transactionCommitted(bool status, PendingTransaction *t, const QStringList& txid);
|
||||||
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
|
void heightRefreshed(quint64 walletHeight, quint64 daemonHeight, quint64 targetHeight) const;
|
||||||
void deviceShowAddressShowed();
|
void deviceShowAddressShowed();
|
||||||
@@ -427,8 +440,9 @@ private:
|
|||||||
bool m_connectionStatusRunning;
|
bool m_connectionStatusRunning;
|
||||||
QString m_daemonUsername;
|
QString m_daemonUsername;
|
||||||
QString m_daemonPassword;
|
QString m_daemonPassword;
|
||||||
Monero::WalletListener *m_walletListener;
|
WalletListenerImpl *m_walletListener;
|
||||||
FutureScheduler m_scheduler;
|
FutureScheduler m_scheduler;
|
||||||
|
QMutex m_storeMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
97
src/libwalletqt/WalletListenerImpl.cpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// Copyright (c) 2014-2020, 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 "WalletListenerImpl.h"
|
||||||
|
#include "Wallet.h"
|
||||||
|
|
||||||
|
WalletListenerImpl::WalletListenerImpl(Wallet * w)
|
||||||
|
: m_wallet(w)
|
||||||
|
, m_phelper(w)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletListenerImpl::moneySpent(const std::string &txId, uint64_t amount)
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
emit m_wallet->moneySpent(QString::fromStdString(txId), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletListenerImpl::moneyReceived(const std::string &txId, uint64_t amount)
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
emit m_wallet->moneyReceived(QString::fromStdString(txId), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletListenerImpl::unconfirmedMoneyReceived(const std::string &txId, uint64_t amount)
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
emit m_wallet->unconfirmedMoneyReceived(QString::fromStdString(txId), amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletListenerImpl::newBlock(uint64_t height)
|
||||||
|
{
|
||||||
|
// qDebug() << __FUNCTION__;
|
||||||
|
emit m_wallet->newBlock(height, m_wallet->daemonBlockChainTargetHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletListenerImpl::updated()
|
||||||
|
{
|
||||||
|
emit m_wallet->updated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// called when wallet refreshed by background thread or explicitly
|
||||||
|
void WalletListenerImpl::refreshed()
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
emit m_wallet->refreshed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletListenerImpl::onDeviceButtonRequest(uint64_t code)
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
emit m_wallet->deviceButtonRequest(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletListenerImpl::onDeviceButtonPressed()
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
emit m_wallet->deviceButtonPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WalletListenerImpl::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
Monero::optional<std::string> WalletListenerImpl::onDevicePassphraseRequest(bool & on_device)
|
||||||
|
{
|
||||||
|
qDebug() << __FUNCTION__;
|
||||||
|
return m_phelper.onDevicePassphraseRequest(on_device);
|
||||||
|
}
|
||||||
68
src/libwalletqt/WalletListenerImpl.h
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) 2014-2020, 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 MONERO_GUI_WALLETLISTENERIMPL_H
|
||||||
|
#define MONERO_GUI_WALLETLISTENERIMPL_H
|
||||||
|
|
||||||
|
#include "wallet/api/wallet2_api.h"
|
||||||
|
#include "PassphraseHelper.h"
|
||||||
|
|
||||||
|
class Wallet;
|
||||||
|
|
||||||
|
class WalletListenerImpl : public Monero::WalletListener, public PassphraseReceiver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WalletListenerImpl(Wallet * w);
|
||||||
|
|
||||||
|
virtual void moneySpent(const std::string &txId, uint64_t amount) override;
|
||||||
|
|
||||||
|
virtual void moneyReceived(const std::string &txId, uint64_t amount) override;
|
||||||
|
|
||||||
|
virtual void unconfirmedMoneyReceived(const std::string &txId, uint64_t amount) override;
|
||||||
|
|
||||||
|
virtual void newBlock(uint64_t height) override;
|
||||||
|
|
||||||
|
virtual void updated() override;
|
||||||
|
|
||||||
|
// called when wallet refreshed by background thread or explicitly
|
||||||
|
virtual void refreshed() override;
|
||||||
|
|
||||||
|
virtual void onDeviceButtonRequest(uint64_t code) override;
|
||||||
|
|
||||||
|
virtual void onDeviceButtonPressed() override;
|
||||||
|
|
||||||
|
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override;
|
||||||
|
|
||||||
|
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Wallet * m_wallet;
|
||||||
|
PassphraseHelper m_phelper;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //MONERO_GUI_WALLETLISTENERIMPL_H
|
||||||
@@ -41,10 +41,13 @@
|
|||||||
#include <QMutexLocker>
|
#include <QMutexLocker>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
class WalletPassphraseListenerImpl : public Monero::WalletListener
|
#include "qt/updater.h"
|
||||||
|
#include "qt/ScopeGuard.h"
|
||||||
|
|
||||||
|
class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_wallet(nullptr) {}
|
WalletPassphraseListenerImpl(WalletManager * mgr): m_mgr(mgr), m_phelper(mgr) {}
|
||||||
|
|
||||||
virtual void moneySpent(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
|
virtual void moneySpent(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
|
||||||
virtual void moneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
|
virtual void moneyReceived(const std::string &txId, uint64_t amount) override { (void)txId; (void)amount; };
|
||||||
@@ -53,43 +56,33 @@ public:
|
|||||||
virtual void updated() override {};
|
virtual void updated() override {};
|
||||||
virtual void refreshed() override {};
|
virtual void refreshed() override {};
|
||||||
|
|
||||||
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool on_device) override
|
virtual void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort) override
|
||||||
{
|
{
|
||||||
qDebug() << __FUNCTION__;
|
qDebug() << __FUNCTION__;
|
||||||
if (on_device) return Monero::optional<std::string>();
|
m_phelper.onPassphraseEntered(passphrase, enter_on_device, entry_abort);
|
||||||
|
}
|
||||||
|
|
||||||
m_mgr->onWalletPassphraseNeeded(m_wallet);
|
virtual Monero::optional<std::string> onDevicePassphraseRequest(bool & on_device) override
|
||||||
|
{
|
||||||
if (m_mgr->m_passphrase_abort)
|
qDebug() << __FUNCTION__;
|
||||||
{
|
return m_phelper.onDevicePassphraseRequest(on_device);
|
||||||
throw std::runtime_error("Passphrase entry abort");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto tmpPass = m_mgr->m_passphrase.toStdString();
|
|
||||||
m_mgr->m_passphrase = QString();
|
|
||||||
|
|
||||||
return Monero::optional<std::string>(tmpPass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void onDeviceButtonRequest(uint64_t code) override
|
virtual void onDeviceButtonRequest(uint64_t code) override
|
||||||
{
|
{
|
||||||
emit m_mgr->deviceButtonRequest(code);
|
qDebug() << __FUNCTION__;
|
||||||
|
emit m_mgr->deviceButtonRequest(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void onDeviceButtonPressed() override
|
virtual void onDeviceButtonPressed() override
|
||||||
{
|
|
||||||
emit m_mgr->deviceButtonPressed();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void onSetWallet(Monero::Wallet * wallet) override
|
|
||||||
{
|
{
|
||||||
qDebug() << __FUNCTION__;
|
qDebug() << __FUNCTION__;
|
||||||
m_wallet = wallet;
|
emit m_mgr->deviceButtonPressed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WalletManager * m_mgr;
|
WalletManager * m_mgr;
|
||||||
Monero::Wallet * m_wallet;
|
PassphraseHelper m_phelper;
|
||||||
};
|
};
|
||||||
|
|
||||||
WalletManager * WalletManager::m_instance = nullptr;
|
WalletManager * WalletManager::m_instance = nullptr;
|
||||||
@@ -121,6 +114,13 @@ Wallet *WalletManager::openWallet(const QString &path, const QString &password,
|
|||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
WalletPassphraseListenerImpl tmpListener(this);
|
WalletPassphraseListenerImpl tmpListener(this);
|
||||||
|
m_mutex_passphraseReceiver.lock();
|
||||||
|
m_passphraseReceiver = &tmpListener;
|
||||||
|
m_mutex_passphraseReceiver.unlock();
|
||||||
|
const auto cleanup = sg::make_scope_guard([this]() noexcept {
|
||||||
|
QMutexLocker passphrase_locker(&m_mutex_passphraseReceiver);
|
||||||
|
this->m_passphraseReceiver = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
if (m_currentWallet) {
|
if (m_currentWallet) {
|
||||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||||
@@ -151,14 +151,14 @@ void WalletManager::openWalletAsync(const QString &path, const QString &password
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Wallet *WalletManager::recoveryWallet(const QString &path, const QString &memo, NetworkType::Type nettype, quint64 restoreHeight, quint64 kdfRounds)
|
Wallet *WalletManager::recoveryWallet(const QString &path, const QString &seed, const QString &seed_offset, NetworkType::Type nettype, quint64 restoreHeight, quint64 kdfRounds)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
if (m_currentWallet) {
|
if (m_currentWallet) {
|
||||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||||
delete m_currentWallet;
|
delete m_currentWallet;
|
||||||
}
|
}
|
||||||
Monero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), "", memo.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, kdfRounds);
|
Monero::Wallet * w = m_pimpl->recoveryWallet(path.toStdString(), "", seed.toStdString(), static_cast<Monero::NetworkType>(nettype), restoreHeight, kdfRounds, seed_offset.toStdString());
|
||||||
m_currentWallet = new Wallet(w);
|
m_currentWallet = new Wallet(w);
|
||||||
return m_currentWallet;
|
return m_currentWallet;
|
||||||
}
|
}
|
||||||
@@ -184,6 +184,13 @@ Wallet *WalletManager::createWalletFromDevice(const QString &path, const QString
|
|||||||
{
|
{
|
||||||
QMutexLocker locker(&m_mutex);
|
QMutexLocker locker(&m_mutex);
|
||||||
WalletPassphraseListenerImpl tmpListener(this);
|
WalletPassphraseListenerImpl tmpListener(this);
|
||||||
|
m_mutex_passphraseReceiver.lock();
|
||||||
|
m_passphraseReceiver = &tmpListener;
|
||||||
|
m_mutex_passphraseReceiver.unlock();
|
||||||
|
const auto cleanup = sg::make_scope_guard([this]() noexcept {
|
||||||
|
QMutexLocker passphrase_locker(&m_mutex_passphraseReceiver);
|
||||||
|
this->m_passphraseReceiver = nullptr;
|
||||||
|
});
|
||||||
|
|
||||||
if (m_currentWallet) {
|
if (m_currentWallet) {
|
||||||
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
qDebug() << "Closing open m_currentWallet" << m_currentWallet;
|
||||||
@@ -461,15 +468,43 @@ bool WalletManager::saveQrCode(const QString &code, const QString &path) const
|
|||||||
return QRCodeImageProvider::genQrImage(code, &size).scaled(size.expandedTo(QSize(240, 240)), Qt::KeepAspectRatio).save(path, "PNG", 100);
|
return QRCodeImageProvider::genQrImage(code, &size).scaled(size.expandedTo(QSize(240, 240)), Qt::KeepAspectRatio).save(path, "PNG", 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletManager::checkUpdatesAsync(const QString &software, const QString &subdir)
|
void WalletManager::checkUpdatesAsync(
|
||||||
|
const QString &software,
|
||||||
|
const QString &subdir,
|
||||||
|
const QString &buildTag,
|
||||||
|
const QString &version)
|
||||||
{
|
{
|
||||||
m_scheduler.run([this, software, subdir] {
|
m_scheduler.run([this, software, subdir, buildTag, version] {
|
||||||
emit checkUpdatesComplete(checkUpdates(software, subdir));
|
const auto updateInfo = Monero::WalletManager::checkUpdates(
|
||||||
|
software.toStdString(),
|
||||||
|
subdir.toStdString(),
|
||||||
|
buildTag.toStdString().c_str(),
|
||||||
|
version.toStdString().c_str());
|
||||||
|
if (!std::get<0>(updateInfo))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString version = QString::fromStdString(std::get<1>(updateInfo));
|
||||||
|
const QByteArray hashFromDns = QByteArray::fromHex(QString::fromStdString(std::get<2>(updateInfo)).toUtf8());
|
||||||
|
const QString downloadUrl = QString::fromStdString(std::get<4>(updateInfo));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const QString binaryFilename = QUrl(downloadUrl).fileName();
|
||||||
|
QPair<QString, QString> signers;
|
||||||
|
const QString signedHash = Updater().fetchSignedHash(binaryFilename, hashFromDns, signers).toHex();
|
||||||
|
|
||||||
|
qInfo() << "Update found" << version << downloadUrl << "hash" << signedHash << "signed by" << signers;
|
||||||
|
emit checkUpdatesComplete(version, downloadUrl, signedHash, signers.first, signers.second);
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
qCritical() << "Failed to fetch and verify signed hash:" << e.what();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
QString WalletManager::checkUpdates(const QString &software, const QString &subdir) const
|
QString WalletManager::checkUpdates(const QString &software, const QString &subdir) const
|
||||||
{
|
{
|
||||||
qDebug() << "Checking for updates";
|
qDebug() << "Checking for updates";
|
||||||
@@ -499,6 +534,7 @@ bool WalletManager::clearWalletCache(const QString &wallet_path) const
|
|||||||
|
|
||||||
WalletManager::WalletManager(QObject *parent)
|
WalletManager::WalletManager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
, m_passphraseReceiver(nullptr)
|
||||||
, m_scheduler(this)
|
, m_scheduler(this)
|
||||||
{
|
{
|
||||||
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
|
m_pimpl = Monero::WalletManagerFactory::getWalletManager();
|
||||||
@@ -509,22 +545,16 @@ WalletManager::~WalletManager()
|
|||||||
m_scheduler.shutdownWaitForFinished();
|
m_scheduler.shutdownWaitForFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletManager::onWalletPassphraseNeeded(Monero::Wallet *)
|
void WalletManager::onWalletPassphraseNeeded(bool on_device)
|
||||||
{
|
{
|
||||||
m_mutex_pass.lock();
|
emit this->walletPassphraseNeeded(on_device);
|
||||||
m_passphrase_abort = false;
|
|
||||||
emit this->walletPassphraseNeeded();
|
|
||||||
|
|
||||||
m_cond_pass.wait(&m_mutex_pass);
|
|
||||||
m_mutex_pass.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletManager::onPassphraseEntered(const QString &passphrase, bool entry_abort)
|
void WalletManager::onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort)
|
||||||
{
|
{
|
||||||
m_mutex_pass.lock();
|
QMutexLocker locker(&m_mutex_passphraseReceiver);
|
||||||
m_passphrase = passphrase;
|
if (m_passphraseReceiver != nullptr)
|
||||||
m_passphrase_abort = entry_abort;
|
{
|
||||||
|
m_passphraseReceiver->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
|
||||||
m_cond_pass.wakeAll();
|
}
|
||||||
m_mutex_pass.unlock();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,16 +36,16 @@
|
|||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
#include <QMutex>
|
|
||||||
#include "qt/FutureScheduler.h"
|
#include "qt/FutureScheduler.h"
|
||||||
#include "NetworkType.h"
|
#include "NetworkType.h"
|
||||||
|
#include "PassphraseHelper.h"
|
||||||
|
|
||||||
class Wallet;
|
class Wallet;
|
||||||
namespace Monero {
|
namespace Monero {
|
||||||
struct WalletManager;
|
struct WalletManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
class WalletManager : public QObject
|
class WalletManager : public QObject, public PassprasePrompter
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(bool connected READ connected)
|
Q_PROPERTY(bool connected READ connected)
|
||||||
@@ -83,7 +83,7 @@ public:
|
|||||||
Q_INVOKABLE void openWalletAsync(const QString &path, const QString &password, NetworkType::Type nettype = NetworkType::MAINNET, quint64 kdfRounds = 1);
|
Q_INVOKABLE void openWalletAsync(const QString &path, const QString &password, NetworkType::Type nettype = NetworkType::MAINNET, quint64 kdfRounds = 1);
|
||||||
|
|
||||||
// wizard: recoveryWallet path; hint: internally it recorvers wallet and set password = ""
|
// wizard: recoveryWallet path; hint: internally it recorvers wallet and set password = ""
|
||||||
Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &memo,
|
Q_INVOKABLE Wallet * recoveryWallet(const QString &path, const QString &seed, const QString &seed_offset,
|
||||||
NetworkType::Type nettype = NetworkType::MAINNET, quint64 restoreHeight = 0, quint64 kdfRounds = 1);
|
NetworkType::Type nettype = NetworkType::MAINNET, quint64 restoreHeight = 0, quint64 kdfRounds = 1);
|
||||||
|
|
||||||
Q_INVOKABLE Wallet * createWalletFromKeys(const QString &path,
|
Q_INVOKABLE Wallet * createWalletFromKeys(const QString &path,
|
||||||
@@ -176,23 +176,32 @@ public:
|
|||||||
Q_INVOKABLE bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector<QString> &unknown_parameters, QString &error) const;
|
Q_INVOKABLE bool parse_uri(const QString &uri, QString &address, QString &payment_id, uint64_t &amount, QString &tx_description, QString &recipient_name, QVector<QString> &unknown_parameters, QString &error) const;
|
||||||
Q_INVOKABLE QVariantMap parse_uri_to_object(const QString &uri) const;
|
Q_INVOKABLE QVariantMap parse_uri_to_object(const QString &uri) const;
|
||||||
Q_INVOKABLE bool saveQrCode(const QString &, const QString &) const;
|
Q_INVOKABLE bool saveQrCode(const QString &, const QString &) const;
|
||||||
Q_INVOKABLE void checkUpdatesAsync(const QString &software, const QString &subdir);
|
Q_INVOKABLE void checkUpdatesAsync(
|
||||||
|
const QString &software,
|
||||||
|
const QString &subdir,
|
||||||
|
const QString &buildTag,
|
||||||
|
const QString &version);
|
||||||
Q_INVOKABLE QString checkUpdates(const QString &software, const QString &subdir) const;
|
Q_INVOKABLE QString checkUpdates(const QString &software, const QString &subdir) const;
|
||||||
|
|
||||||
// clear/rename wallet cache
|
// clear/rename wallet cache
|
||||||
Q_INVOKABLE bool clearWalletCache(const QString &fileName) const;
|
Q_INVOKABLE bool clearWalletCache(const QString &fileName) const;
|
||||||
|
|
||||||
Q_INVOKABLE void onWalletPassphraseNeeded(Monero::Wallet * wallet);
|
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
|
||||||
Q_INVOKABLE void onPassphraseEntered(const QString &passphrase, bool entry_abort=false);
|
virtual void onWalletPassphraseNeeded(bool on_device) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
void walletOpened(Wallet * wallet);
|
void walletOpened(Wallet * wallet);
|
||||||
void walletCreated(Wallet * wallet);
|
void walletCreated(Wallet * wallet);
|
||||||
void walletPassphraseNeeded();
|
void walletPassphraseNeeded(bool onDevice);
|
||||||
void deviceButtonRequest(quint64 buttonCode);
|
void deviceButtonRequest(quint64 buttonCode);
|
||||||
void deviceButtonPressed();
|
void deviceButtonPressed();
|
||||||
void checkUpdatesComplete(const QString &result) const;
|
void checkUpdatesComplete(
|
||||||
|
const QString &version,
|
||||||
|
const QString &downloadUrl,
|
||||||
|
const QString &hash,
|
||||||
|
const QString &firstSigner,
|
||||||
|
const QString &secondSigner) const;
|
||||||
void miningStatus(bool isMining) const;
|
void miningStatus(bool isMining) const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
@@ -208,12 +217,8 @@ private:
|
|||||||
Monero::WalletManager * m_pimpl;
|
Monero::WalletManager * m_pimpl;
|
||||||
mutable QMutex m_mutex;
|
mutable QMutex m_mutex;
|
||||||
QPointer<Wallet> m_currentWallet;
|
QPointer<Wallet> m_currentWallet;
|
||||||
|
PassphraseReceiver * m_passphraseReceiver;
|
||||||
QWaitCondition m_cond_pass;
|
QMutex m_mutex_passphraseReceiver;
|
||||||
QMutex m_mutex_pass;
|
|
||||||
QString m_passphrase;
|
|
||||||
bool m_passphrase_abort;
|
|
||||||
|
|
||||||
FutureScheduler m_scheduler;
|
FutureScheduler m_scheduler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -61,18 +61,25 @@
|
|||||||
#include "wallet/api/wallet2_api.h"
|
#include "wallet/api/wallet2_api.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include "MainApp.h"
|
#include "MainApp.h"
|
||||||
|
#include "qt/downloader.h"
|
||||||
#include "qt/ipc.h"
|
#include "qt/ipc.h"
|
||||||
|
#include "qt/network.h"
|
||||||
|
#include "qt/updater.h"
|
||||||
#include "qt/utils.h"
|
#include "qt/utils.h"
|
||||||
#include "qt/TailsOS.h"
|
#include "qt/TailsOS.h"
|
||||||
#include "qt/KeysFiles.h"
|
#include "qt/KeysFiles.h"
|
||||||
#include "qt/MoneroSettings.h"
|
#include "qt/MoneroSettings.h"
|
||||||
#include "qt/prices.h"
|
#include "qt/NetworkAccessBlockingFactory.h"
|
||||||
|
|
||||||
// IOS exclusions
|
// IOS exclusions
|
||||||
#ifndef Q_OS_IOS
|
#ifndef Q_OS_IOS
|
||||||
#include "daemon/DaemonManager.h"
|
#include "daemon/DaemonManager.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
#include <QOpenGLContext>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef WITH_SCANNER
|
#ifdef WITH_SCANNER
|
||||||
#include "QR-Code-scanner/QrCodeScanner.h"
|
#include "QR-Code-scanner/QrCodeScanner.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -88,11 +95,8 @@
|
|||||||
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
|
Q_IMPORT_PLUGIN(QXcbIntegrationPlugin);
|
||||||
#endif
|
#endif
|
||||||
Q_IMPORT_PLUGIN(QSvgIconPlugin)
|
Q_IMPORT_PLUGIN(QSvgIconPlugin)
|
||||||
Q_IMPORT_PLUGIN(QGifPlugin)
|
|
||||||
Q_IMPORT_PLUGIN(QICNSPlugin)
|
Q_IMPORT_PLUGIN(QICNSPlugin)
|
||||||
Q_IMPORT_PLUGIN(QICOPlugin)
|
|
||||||
Q_IMPORT_PLUGIN(QJpegPlugin)
|
Q_IMPORT_PLUGIN(QJpegPlugin)
|
||||||
Q_IMPORT_PLUGIN(QMngPlugin)
|
|
||||||
Q_IMPORT_PLUGIN(QSvgPlugin)
|
Q_IMPORT_PLUGIN(QSvgPlugin)
|
||||||
Q_IMPORT_PLUGIN(QTgaPlugin)
|
Q_IMPORT_PLUGIN(QTgaPlugin)
|
||||||
Q_IMPORT_PLUGIN(QTiffPlugin)
|
Q_IMPORT_PLUGIN(QTiffPlugin)
|
||||||
@@ -124,7 +128,9 @@ Q_IMPORT_PLUGIN(QtQuick2PrivateWidgetsPlugin)
|
|||||||
Q_IMPORT_PLUGIN(QtQuickControls2Plugin)
|
Q_IMPORT_PLUGIN(QtQuickControls2Plugin)
|
||||||
Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin)
|
Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin)
|
||||||
Q_IMPORT_PLUGIN(QmlXmlListModelPlugin)
|
Q_IMPORT_PLUGIN(QmlXmlListModelPlugin)
|
||||||
|
#ifdef WITH_SCANNER
|
||||||
Q_IMPORT_PLUGIN(QMultimediaDeclarativeModule)
|
Q_IMPORT_PLUGIN(QMultimediaDeclarativeModule)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -177,6 +183,17 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
MainApp app(argc, argv);
|
MainApp app(argc, argv);
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
if (isOpenGL)
|
||||||
|
{
|
||||||
|
QOpenGLContext ctx;
|
||||||
|
isOpenGL = ctx.create() && ctx.format().version() >= qMakePair(2, 1);
|
||||||
|
if (!isOpenGL) {
|
||||||
|
qputenv("QMLSCENE_DEVICE", "softwarecontext");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
app.setApplicationName("monero-core");
|
app.setApplicationName("monero-core");
|
||||||
app.setOrganizationDomain("getmonero.org");
|
app.setOrganizationDomain("getmonero.org");
|
||||||
app.setOrganizationName("monero-project");
|
app.setOrganizationName("monero-project");
|
||||||
@@ -208,6 +225,7 @@ int main(int argc, char *argv[])
|
|||||||
qCritical() << "Error: accounts root directory could not be set";
|
qCritical() << "Error: accounts root directory could not be set";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
moneroAccountsDir = QDir::toNativeSeparators(moneroAccountsDir);
|
||||||
|
|
||||||
#if defined(Q_OS_LINUX)
|
#if defined(Q_OS_LINUX)
|
||||||
if (isDesktop) app.setWindowIcon(QIcon(":/images/appicon.ico"));
|
if (isDesktop) app.setWindowIcon(QIcon(":/images/appicon.ico"));
|
||||||
@@ -221,6 +239,16 @@ int main(int argc, char *argv[])
|
|||||||
QCoreApplication::translate("main", "Log to specified file"),
|
QCoreApplication::translate("main", "Log to specified file"),
|
||||||
QCoreApplication::translate("main", "file"));
|
QCoreApplication::translate("main", "file"));
|
||||||
|
|
||||||
|
QCommandLineOption verifyUpdateOption("verify-update", "\
|
||||||
|
Verify update binary using 'shasum'-compatible (SHA256 algo) output signed by two maintainers.\n\
|
||||||
|
* Requires 'hashes.txt' - signed 'shasum' output \
|
||||||
|
(i.e. 'gpg -o hashes.txt --clear-sign <shasum_output>') generated by a maintainer.\n\
|
||||||
|
* Requires 'hashes.txt.sig' - detached signature of 'hashes.txt' \
|
||||||
|
(i.e. 'gpg -b hashes.txt') generated by another maintainer.", "update-binary");
|
||||||
|
parser.addOption(verifyUpdateOption);
|
||||||
|
|
||||||
|
QCommandLineOption disableCheckUpdatesOption("disable-check-updates", "Disable automatic check for updates.");
|
||||||
|
parser.addOption(disableCheckUpdatesOption);
|
||||||
QCommandLineOption testQmlOption("test-qml");
|
QCommandLineOption testQmlOption("test-qml");
|
||||||
testQmlOption.setFlags(QCommandLineOption::HiddenFromHelp);
|
testQmlOption.setFlags(QCommandLineOption::HiddenFromHelp);
|
||||||
parser.addOption(logPathOption);
|
parser.addOption(logPathOption);
|
||||||
@@ -231,7 +259,7 @@ int main(int argc, char *argv[])
|
|||||||
Monero::Utils::onStartup();
|
Monero::Utils::onStartup();
|
||||||
|
|
||||||
// Log settings
|
// Log settings
|
||||||
const QString logPath = getLogPath(parser.value(logPathOption));
|
const QString logPath = QDir::toNativeSeparators(getLogPath(parser.value(logPathOption)));
|
||||||
Monero::Wallet::init(argv[0], "monero-wallet-gui", logPath.toStdString().c_str(), true);
|
Monero::Wallet::init(argv[0], "monero-wallet-gui", logPath.toStdString().c_str(), true);
|
||||||
qInstallMessageHandler(messageHandler);
|
qInstallMessageHandler(messageHandler);
|
||||||
|
|
||||||
@@ -244,6 +272,32 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
qWarning().noquote() << "app startd" << "(log: " + logPath + ")";
|
qWarning().noquote() << "app startd" << "(log: " + logPath + ")";
|
||||||
|
|
||||||
|
if (parser.isSet(verifyUpdateOption))
|
||||||
|
{
|
||||||
|
const QString updateBinaryFullPath = parser.value(verifyUpdateOption);
|
||||||
|
const QFileInfo updateBinaryInfo(updateBinaryFullPath);
|
||||||
|
const QString updateBinaryDir = QDir::toNativeSeparators(updateBinaryInfo.absolutePath()) + QDir::separator();
|
||||||
|
const QString hashesTxt = updateBinaryDir + "hashes.txt";
|
||||||
|
const QString hashesTxtSig = hashesTxt + ".sig";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const QByteArray updateBinaryContents = fileGetContents(updateBinaryFullPath);
|
||||||
|
const QPair<QString, QString> signers = Updater().verifySignaturesAndHashSum(
|
||||||
|
fileGetContents(hashesTxt),
|
||||||
|
fileGetContents(hashesTxtSig),
|
||||||
|
updateBinaryInfo.fileName(),
|
||||||
|
updateBinaryContents.data(),
|
||||||
|
updateBinaryContents.size());
|
||||||
|
qCritical() << "successfully verified, signed by" << signers.first << "and" << signers.second;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
qCritical() << e.what();
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Desktop entry
|
// Desktop entry
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
registerXdgMime(app);
|
registerXdgMime(app);
|
||||||
@@ -295,6 +349,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// registering types for QML
|
// registering types for QML
|
||||||
qmlRegisterType<clipboardAdapter>("moneroComponents.Clipboard", 1, 0, "Clipboard");
|
qmlRegisterType<clipboardAdapter>("moneroComponents.Clipboard", 1, 0, "Clipboard");
|
||||||
|
qmlRegisterType<Downloader>("moneroComponents.Downloader", 1, 0, "Downloader");
|
||||||
|
|
||||||
// Temporary Qt.labs.settings replacement
|
// Temporary Qt.labs.settings replacement
|
||||||
qmlRegisterType<MoneroSettings>("moneroComponents.Settings", 1, 0, "MoneroSettings");
|
qmlRegisterType<MoneroSettings>("moneroComponents.Settings", 1, 0, "MoneroSettings");
|
||||||
@@ -363,6 +418,9 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
|
||||||
|
engine.setNetworkAccessManagerFactory(new NetworkAccessBlockingFactory);
|
||||||
|
#endif
|
||||||
OSCursor cursor;
|
OSCursor cursor;
|
||||||
engine.rootContext()->setContextProperty("globalCursor", &cursor);
|
engine.rootContext()->setContextProperty("globalCursor", &cursor);
|
||||||
OSHelper osHelper;
|
OSHelper osHelper;
|
||||||
@@ -429,6 +487,7 @@ int main(int argc, char *argv[])
|
|||||||
engine.rootContext()->setContextProperty("homePath", QDir::homePath());
|
engine.rootContext()->setContextProperty("homePath", QDir::homePath());
|
||||||
engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath());
|
engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath());
|
||||||
engine.rootContext()->setContextProperty("idealThreadCount", QThread::idealThreadCount());
|
engine.rootContext()->setContextProperty("idealThreadCount", QThread::idealThreadCount());
|
||||||
|
engine.rootContext()->setContextProperty("disableCheckUpdatesFlag", parser.isSet(disableCheckUpdatesOption));
|
||||||
|
|
||||||
bool builtWithScanner = false;
|
bool builtWithScanner = false;
|
||||||
#ifdef WITH_SCANNER
|
#ifdef WITH_SCANNER
|
||||||
@@ -436,8 +495,8 @@ int main(int argc, char *argv[])
|
|||||||
#endif
|
#endif
|
||||||
engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner);
|
engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner);
|
||||||
|
|
||||||
Prices prices;
|
Network network;
|
||||||
engine.rootContext()->setContextProperty("Prices", &prices);
|
engine.rootContext()->setContextProperty("Network", &network);
|
||||||
|
|
||||||
// Load main window (context properties needs to be defined obove this line)
|
// Load main window (context properties needs to be defined obove this line)
|
||||||
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
|
||||||
|
|||||||
@@ -27,6 +27,9 @@
|
|||||||
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "oshelper.h"
|
#include "oshelper.h"
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QStandardPaths>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
@@ -82,6 +85,11 @@ OSHelper::OSHelper(QObject *parent) : QObject(parent)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString OSHelper::downloadLocation() const
|
||||||
|
{
|
||||||
|
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
|
||||||
|
}
|
||||||
|
|
||||||
bool OSHelper::openContainingFolder(const QString &filePath) const
|
bool OSHelper::openContainingFolder(const QString &filePath) const
|
||||||
{
|
{
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
@@ -105,6 +113,12 @@ bool OSHelper::openContainingFolder(const QString &filePath) const
|
|||||||
return QDesktopServices::openUrl(url);
|
return QDesktopServices::openUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString OSHelper::openSaveFileDialog(const QString &title, const QString &folder, const QString &filename) const
|
||||||
|
{
|
||||||
|
const QString hint = (folder.isEmpty() ? "" : folder + QDir::separator()) + filename;
|
||||||
|
return QFileDialog::getSaveFileName(nullptr, title, hint);
|
||||||
|
}
|
||||||
|
|
||||||
QString OSHelper::temporaryFilename() const
|
QString OSHelper::temporaryFilename() const
|
||||||
{
|
{
|
||||||
QString tempFileName;
|
QString tempFileName;
|
||||||
@@ -151,3 +165,47 @@ QString OSHelper::temporaryPath() const
|
|||||||
{
|
{
|
||||||
return QDir::tempPath();
|
return QDir::tempPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OSHelper::installed() const
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
static constexpr const wchar_t installKey[] =
|
||||||
|
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Monero GUI Wallet_is1";
|
||||||
|
static constexpr const wchar_t installValue[] = L"InstallLocation";
|
||||||
|
|
||||||
|
DWORD size;
|
||||||
|
LSTATUS status =
|
||||||
|
::RegGetValueW(HKEY_LOCAL_MACHINE, installKey, installValue, RRF_RT_REG_SZ, nullptr, nullptr, &size);
|
||||||
|
if (status == ERROR_FILE_NOT_FOUND)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (status != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
qCritical() << "RegGetValueW failed (get size)" << status;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring installLocation;
|
||||||
|
installLocation.resize(size / sizeof(std::wstring::value_type));
|
||||||
|
size = installLocation.size() * sizeof(std::wstring::value_type);
|
||||||
|
status = ::RegGetValueW(
|
||||||
|
HKEY_LOCAL_MACHINE,
|
||||||
|
installKey,
|
||||||
|
installValue,
|
||||||
|
RRF_RT_REG_SZ,
|
||||||
|
nullptr,
|
||||||
|
&installLocation[0],
|
||||||
|
&size);
|
||||||
|
if (status != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
qCritical() << "RegGetValueW Failed (read)" << status;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QDir installDir(QString(reinterpret_cast<const QChar *>(&installLocation[0])));
|
||||||
|
return installDir == QDir(QCoreApplication::applicationDirPath());
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,15 +36,22 @@
|
|||||||
class OSHelper : public QObject
|
class OSHelper : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool installed READ installed CONSTANT);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit OSHelper(QObject *parent = 0);
|
explicit OSHelper(QObject *parent = 0);
|
||||||
|
|
||||||
|
Q_INVOKABLE QString downloadLocation() const;
|
||||||
Q_INVOKABLE bool openContainingFolder(const QString &filePath) const;
|
Q_INVOKABLE bool openContainingFolder(const QString &filePath) const;
|
||||||
|
Q_INVOKABLE QString openSaveFileDialog(const QString &title, const QString &folder, const QString &filename) const;
|
||||||
Q_INVOKABLE QString temporaryFilename() const;
|
Q_INVOKABLE QString temporaryFilename() const;
|
||||||
Q_INVOKABLE QString temporaryPath() const;
|
Q_INVOKABLE QString temporaryPath() const;
|
||||||
Q_INVOKABLE bool removeTemporaryWallet(const QString &walletName) const;
|
Q_INVOKABLE bool removeTemporaryWallet(const QString &walletName) const;
|
||||||
Q_INVOKABLE bool isCapsLock() const;
|
Q_INVOKABLE bool isCapsLock() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool installed() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "TransactionHistoryModel.h"
|
#include "TransactionHistoryModel.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
/**
|
/**
|
||||||
@@ -204,9 +205,14 @@ bool TransactionHistorySortFilterModel::filterAcceptsRow(int source_row, const Q
|
|||||||
break;
|
break;
|
||||||
case TransactionHistoryModel::TransactionTimeStampRole:
|
case TransactionHistoryModel::TransactionTimeStampRole:
|
||||||
{
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
|
QDateTime from = dateFromFilter().startOfDay();
|
||||||
|
QDateTime to = dateToFilter().endOfDay();
|
||||||
|
#else
|
||||||
QDateTime from = QDateTime(dateFromFilter());
|
QDateTime from = QDateTime(dateFromFilter());
|
||||||
QDateTime to = QDateTime(dateToFilter());
|
QDateTime to = QDateTime(dateToFilter());
|
||||||
to = to.addDays(1); // including upperbound
|
to = to.addDays(1); // including upperbound
|
||||||
|
#endif
|
||||||
QDateTime timestamp = data.toDateTime();
|
QDateTime timestamp = data.toDateTime();
|
||||||
bool matchFrom = from.isNull() || timestamp.isNull() || timestamp >= from;
|
bool matchFrom = from.isNull() || timestamp.isNull() || timestamp >= from;
|
||||||
bool matchTo = to.isNull() || timestamp.isNull() || timestamp <= to;
|
bool matchTo = to.isNull() || timestamp.isNull() || timestamp <= to;
|
||||||
|
|||||||
18
src/openpgp/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
file(GLOB_RECURSE SOURCES *.cpp)
|
||||||
|
file(GLOB_RECURSE HEADERS *.h)
|
||||||
|
|
||||||
|
find_library(GCRYPT_LIBRARY gcrypt)
|
||||||
|
find_library(GPG_ERROR_LIBRARY gpg-error)
|
||||||
|
|
||||||
|
add_library(openpgp
|
||||||
|
${SOURCES}
|
||||||
|
${HEADERS})
|
||||||
|
|
||||||
|
target_include_directories(openpgp
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_SOURCE_DIR}/monero/contrib/epee/include)
|
||||||
|
|
||||||
|
target_link_libraries(openpgp
|
||||||
|
PUBLIC
|
||||||
|
${GCRYPT_LIBRARY}
|
||||||
|
${GPG_ERROR_LIBRARY})
|
||||||
107
src/openpgp/hash.h
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (c) 2020, 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gcrypt.h>
|
||||||
|
#include <span.h>
|
||||||
|
|
||||||
|
namespace openpgp
|
||||||
|
{
|
||||||
|
|
||||||
|
class hash
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum algorithm : uint8_t
|
||||||
|
{
|
||||||
|
sha256 = 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
hash(const hash &) = delete;
|
||||||
|
hash &operator=(const hash &) = delete;
|
||||||
|
|
||||||
|
hash(uint8_t algorithm)
|
||||||
|
: algo(algorithm)
|
||||||
|
, consumed(0)
|
||||||
|
{
|
||||||
|
if (gcry_md_open(&md, algo, 0) != GPG_ERR_NO_ERROR)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to create message digest object");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~hash()
|
||||||
|
{
|
||||||
|
gcry_md_close(md);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash &operator<<(uint8_t byte)
|
||||||
|
{
|
||||||
|
gcry_md_putc(md, byte);
|
||||||
|
++consumed;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash &operator<<(const epee::span<const uint8_t> &bytes)
|
||||||
|
{
|
||||||
|
gcry_md_write(md, &bytes[0], bytes.size());
|
||||||
|
consumed += bytes.size();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash &operator<<(const std::vector<uint8_t> &bytes)
|
||||||
|
{
|
||||||
|
return *this << epee::to_span(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> finish() const
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> result(gcry_md_get_algo_dlen(algo));
|
||||||
|
const void *digest = gcry_md_read(md, algo);
|
||||||
|
if (digest == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to read the digest");
|
||||||
|
}
|
||||||
|
memcpy(&result[0], digest, result.size());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t consumed_bytes() const
|
||||||
|
{
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const uint8_t algo;
|
||||||
|
gcry_md_hd_t md;
|
||||||
|
size_t consumed;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
78
src/openpgp/mpi.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// Copyright (c) 2020, 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gcrypt.h>
|
||||||
|
|
||||||
|
namespace openpgp
|
||||||
|
{
|
||||||
|
|
||||||
|
class mpi
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
mpi(const mpi &) = delete;
|
||||||
|
mpi &operator=(const mpi &) = delete;
|
||||||
|
|
||||||
|
mpi(mpi &&other)
|
||||||
|
: data(other.data)
|
||||||
|
{
|
||||||
|
other.data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename byte_container,
|
||||||
|
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
|
||||||
|
mpi(const byte_container &buffer, gcry_mpi_format format = GCRYMPI_FMT_USG)
|
||||||
|
: mpi(&buffer[0], buffer.size(), format)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
mpi(const void *buffer, size_t size, gcry_mpi_format format = GCRYMPI_FMT_USG)
|
||||||
|
{
|
||||||
|
if (gcry_mpi_scan(&data, format, buffer, size, nullptr) != GPG_ERR_NO_ERROR)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to read mpi from buffer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~mpi()
|
||||||
|
{
|
||||||
|
gcry_mpi_release(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gcry_mpi_t &get() const
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
gcry_mpi_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace openpgp
|
||||||
382
src/openpgp/openpgp.cpp
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
// Copyright (c) 2020, 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 "openpgp.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <locale>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <string_coding.h>
|
||||||
|
|
||||||
|
#include "hash.h"
|
||||||
|
#include "mpi.h"
|
||||||
|
#include "packet_stream.h"
|
||||||
|
#include "s_expression.h"
|
||||||
|
#include "serialization.h"
|
||||||
|
|
||||||
|
namespace openpgp
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string::const_iterator find_next_line(std::string::const_iterator begin, const std::string::const_iterator &end)
|
||||||
|
{
|
||||||
|
begin = std::find(begin, end, '\n');
|
||||||
|
return begin != end ? ++begin : end;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string::const_iterator find_line_starting_with(
|
||||||
|
std::string::const_iterator it,
|
||||||
|
const std::string::const_iterator &end,
|
||||||
|
const std::string &starts_with)
|
||||||
|
{
|
||||||
|
for (std::string::const_iterator next_line; it != end; it = next_line)
|
||||||
|
{
|
||||||
|
next_line = find_next_line(it, end);
|
||||||
|
const size_t line_length = static_cast<size_t>(std::distance(it, next_line));
|
||||||
|
if (line_length >= starts_with.size() && std::equal(starts_with.begin(), starts_with.end(), it))
|
||||||
|
{
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string::const_iterator find_empty_line(std::string::const_iterator it, const std::string::const_iterator &end)
|
||||||
|
{
|
||||||
|
for (; it != end && *it != '\r' && *it != '\n'; it = find_next_line(it, end))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_armored_block_contents(const std::string &text, const std::string &block_name)
|
||||||
|
{
|
||||||
|
static constexpr const char dashes[] = "-----";
|
||||||
|
const std::string armor_header = dashes + block_name + dashes;
|
||||||
|
auto block_start = find_line_starting_with(text.begin(), text.end(), armor_header);
|
||||||
|
auto block_headers = find_next_line(block_start, text.end());
|
||||||
|
auto block_end = find_line_starting_with(block_headers, text.end(), dashes);
|
||||||
|
auto contents_begin = find_next_line(find_empty_line(block_headers, block_end), block_end);
|
||||||
|
if (contents_begin == block_end)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("armored block not found");
|
||||||
|
}
|
||||||
|
return std::string(contents_begin, block_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
public_key_rsa::public_key_rsa(s_expression expression, size_t bits)
|
||||||
|
: m_expression(std::move(expression))
|
||||||
|
, m_bits(bits)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const gcry_sexp_t &public_key_rsa::get() const
|
||||||
|
{
|
||||||
|
return m_expression.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t public_key_rsa::bits() const
|
||||||
|
{
|
||||||
|
return m_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public_key_block::public_key_block(const std::string &armored)
|
||||||
|
: public_key_block(epee::to_byte_span(epee::to_span(epee::string_encoding::base64_decode(
|
||||||
|
strip_line_breaks(get_armored_block_contents(armored, "BEGIN PGP PUBLIC KEY BLOCK"))))))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Public-Key expiration, User ID and Public-Key certification, Subkey binding checks
|
||||||
|
public_key_block::public_key_block(const epee::span<const uint8_t> buffer)
|
||||||
|
{
|
||||||
|
packet_stream packets(buffer);
|
||||||
|
|
||||||
|
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::user_id);
|
||||||
|
if (data == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("user id is missing");
|
||||||
|
}
|
||||||
|
m_user_id.assign(data->begin(), data->end());
|
||||||
|
|
||||||
|
const auto append_public_key = [this](const std::vector<uint8_t> &data) {
|
||||||
|
deserializer<std::vector<uint8_t>> serialized(data);
|
||||||
|
|
||||||
|
const auto version = serialized.read_big_endian<uint8_t>();
|
||||||
|
if (version != 4)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unsupported public key version");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* const auto timestamp = */ serialized.read_big_endian<uint32_t>();
|
||||||
|
|
||||||
|
const auto algorithm = serialized.read_big_endian<uint8_t>();
|
||||||
|
if (algorithm != openpgp::algorithm::rsa)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unsupported public key algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const mpi public_key_n = serialized.read_mpi();
|
||||||
|
const mpi public_key_e = serialized.read_mpi();
|
||||||
|
|
||||||
|
emplace_back(
|
||||||
|
s_expression("(public-key (rsa (n %m) (e %m)))", public_key_n.get(), public_key_e.get()),
|
||||||
|
gcry_mpi_get_nbits(public_key_n.get()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
data = packets.find_first(packet_tag::type::public_key);
|
||||||
|
if (data == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("public key is missing");
|
||||||
|
}
|
||||||
|
append_public_key(*data);
|
||||||
|
|
||||||
|
packets.for_each(packet_tag::type::public_subkey, append_public_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string public_key_block::user_id() const
|
||||||
|
{
|
||||||
|
return m_user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Signature expiration check
|
||||||
|
signature_rsa::signature_rsa(
|
||||||
|
uint8_t algorithm,
|
||||||
|
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
|
||||||
|
uint8_t hash_algorithm,
|
||||||
|
const std::vector<uint8_t> &hashed_data,
|
||||||
|
type type,
|
||||||
|
s_expression signature,
|
||||||
|
uint8_t version)
|
||||||
|
: m_hash_algorithm(hash_algorithm)
|
||||||
|
, m_hash_leftmost_bytes(hash_leftmost_bytes)
|
||||||
|
, m_hashed_appendix(format_hashed_appendix(algorithm, hash_algorithm, hashed_data, type, version))
|
||||||
|
, m_signature(std::move(signature))
|
||||||
|
, m_type(type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
signature_rsa signature_rsa::from_armored(const std::string &armored_signed_message)
|
||||||
|
{
|
||||||
|
return from_base64(get_armored_block_contents(armored_signed_message, "BEGIN PGP SIGNATURE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
signature_rsa signature_rsa::from_base64(const std::string &base64)
|
||||||
|
{
|
||||||
|
std::string decoded = epee::string_encoding::base64_decode(strip_line_breaks(base64));
|
||||||
|
epee::span<const uint8_t> buffer(reinterpret_cast<const uint8_t *>(&decoded[0]), decoded.size());
|
||||||
|
return from_buffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
signature_rsa signature_rsa::from_buffer(const epee::span<const uint8_t> input)
|
||||||
|
{
|
||||||
|
packet_stream packets(input);
|
||||||
|
|
||||||
|
const std::vector<uint8_t> *data = packets.find_first(packet_tag::type::signature);
|
||||||
|
if (data == nullptr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("signature is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer<std::vector<uint8_t>> buffer(*data);
|
||||||
|
|
||||||
|
const auto version = buffer.read_big_endian<uint8_t>();
|
||||||
|
if (version != 4)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unsupported signature version");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto signature_type = static_cast<type>(buffer.read_big_endian<uint8_t>());
|
||||||
|
|
||||||
|
const auto algorithm = buffer.read_big_endian<uint8_t>();
|
||||||
|
if (algorithm != openpgp::algorithm::rsa)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("unsupported signature algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto hash_algorithm = buffer.read_big_endian<uint8_t>();
|
||||||
|
|
||||||
|
const auto hashed_data_length = buffer.read_big_endian<uint16_t>();
|
||||||
|
std::vector<uint8_t> hashed_data = buffer.read(hashed_data_length);
|
||||||
|
|
||||||
|
const auto unhashed_data_length = buffer.read_big_endian<uint16_t>();
|
||||||
|
buffer.read_span(unhashed_data_length);
|
||||||
|
|
||||||
|
std::pair<uint8_t, uint8_t> hash_leftmost_bytes{buffer.read_big_endian<uint8_t>(), buffer.read_big_endian<uint8_t>()};
|
||||||
|
|
||||||
|
const mpi signature = buffer.read_mpi();
|
||||||
|
|
||||||
|
return signature_rsa(
|
||||||
|
algorithm,
|
||||||
|
std::move(hash_leftmost_bytes),
|
||||||
|
hash_algorithm,
|
||||||
|
hashed_data,
|
||||||
|
signature_type,
|
||||||
|
s_expression("(sig-val (rsa (s %m)))", signature.get()),
|
||||||
|
version);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool signature_rsa::verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const
|
||||||
|
{
|
||||||
|
const s_expression signed_data = hash_message(message, public_key.bits());
|
||||||
|
return gcry_pk_verify(m_signature.get(), signed_data.get(), public_key.get()) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_expression signature_rsa::hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const
|
||||||
|
{
|
||||||
|
switch (m_type)
|
||||||
|
{
|
||||||
|
case type::binary_document:
|
||||||
|
return hash_bytes(message, public_key_bits);
|
||||||
|
case type::canonical_text_document:
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> crlf_formatted;
|
||||||
|
crlf_formatted.reserve(message.size());
|
||||||
|
const size_t message_size = message.size();
|
||||||
|
for (size_t offset = 0; offset < message_size; ++offset)
|
||||||
|
{
|
||||||
|
const auto &character = message[offset];
|
||||||
|
if (character == '\r')
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (character == '\n')
|
||||||
|
{
|
||||||
|
const bool skip_last_crlf = offset + 1 == message_size;
|
||||||
|
if (skip_last_crlf)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crlf_formatted.push_back('\r');
|
||||||
|
}
|
||||||
|
crlf_formatted.push_back(character);
|
||||||
|
}
|
||||||
|
return hash_bytes(epee::to_span(crlf_formatted), public_key_bits);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("unsupported signature type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> signature_rsa::hash_asn_object_id() const
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, nullptr, &size) != GPG_ERR_NO_ERROR)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID) size");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> asn_object_id(size);
|
||||||
|
if (gcry_md_algo_info(m_hash_algorithm, GCRYCTL_GET_ASNOID, &asn_object_id[0], &size) != GPG_ERR_NO_ERROR)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to get ASN.1 Object Identifier (OID)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return asn_object_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_expression signature_rsa::hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const
|
||||||
|
{
|
||||||
|
const std::vector<uint8_t> plain_hash = (hash(m_hash_algorithm) << message << m_hashed_appendix).finish();
|
||||||
|
if (plain_hash.size() < 2)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("insufficient message hash size");
|
||||||
|
}
|
||||||
|
if (plain_hash[0] != m_hash_leftmost_bytes.first || plain_hash[1] != m_hash_leftmost_bytes.second)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("signature checksum doesn't match the expected value");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> asn_object_id = hash_asn_object_id();
|
||||||
|
|
||||||
|
const size_t public_key_bytes = bits_to_bytes(public_key_bits);
|
||||||
|
if (public_key_bytes < plain_hash.size() + asn_object_id.size() + 11)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("insufficient public key bit length");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> emsa_pkcs1_v1_5_encoded;
|
||||||
|
emsa_pkcs1_v1_5_encoded.reserve(public_key_bytes);
|
||||||
|
emsa_pkcs1_v1_5_encoded.push_back(0);
|
||||||
|
emsa_pkcs1_v1_5_encoded.push_back(1);
|
||||||
|
const size_t ps_size = public_key_bytes - plain_hash.size() - asn_object_id.size() - 3;
|
||||||
|
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), ps_size, 0xff);
|
||||||
|
emsa_pkcs1_v1_5_encoded.push_back(0);
|
||||||
|
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), asn_object_id.begin(), asn_object_id.end());
|
||||||
|
emsa_pkcs1_v1_5_encoded.insert(emsa_pkcs1_v1_5_encoded.end(), plain_hash.begin(), plain_hash.end());
|
||||||
|
|
||||||
|
mpi value(emsa_pkcs1_v1_5_encoded);
|
||||||
|
return s_expression("(data (flags raw) (value %m))", value.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> signature_rsa::format_hashed_appendix(
|
||||||
|
uint8_t algorithm,
|
||||||
|
uint8_t hash_algorithm,
|
||||||
|
const std::vector<uint8_t> &hashed_data,
|
||||||
|
uint8_t type,
|
||||||
|
uint8_t version)
|
||||||
|
{
|
||||||
|
const uint16_t hashed_data_size = static_cast<uint16_t>(hashed_data.size());
|
||||||
|
const uint32_t hashed_pefix_size = sizeof(version) + sizeof(type) + sizeof(algorithm) + sizeof(hash_algorithm) +
|
||||||
|
sizeof(hashed_data_size) + hashed_data.size();
|
||||||
|
|
||||||
|
std::vector<uint8_t> appendix;
|
||||||
|
appendix.reserve(hashed_pefix_size + sizeof(version) + sizeof(uint8_t) + sizeof(hashed_pefix_size));
|
||||||
|
appendix.push_back(version);
|
||||||
|
appendix.push_back(type);
|
||||||
|
appendix.push_back(algorithm);
|
||||||
|
appendix.push_back(hash_algorithm);
|
||||||
|
appendix.push_back(static_cast<uint8_t>(hashed_data_size >> 8));
|
||||||
|
appendix.push_back(static_cast<uint8_t>(hashed_data_size));
|
||||||
|
appendix.insert(appendix.end(), hashed_data.begin(), hashed_data.end());
|
||||||
|
appendix.push_back(version);
|
||||||
|
appendix.push_back(0xff);
|
||||||
|
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 24));
|
||||||
|
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 16));
|
||||||
|
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size >> 8));
|
||||||
|
appendix.push_back(static_cast<uint8_t>(hashed_pefix_size));
|
||||||
|
|
||||||
|
return appendix;
|
||||||
|
}
|
||||||
|
|
||||||
|
message_armored::message_armored(const std::string &message_armored)
|
||||||
|
: m_message(get_armored_block_contents(message_armored, "BEGIN PGP SIGNED MESSAGE"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
message_armored::operator epee::span<const uint8_t>() const
|
||||||
|
{
|
||||||
|
return epee::to_byte_span(epee::to_span(m_message));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace openpgp
|
||||||
127
src/openpgp/openpgp.h
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
// Copyright (c) 2020, 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gcrypt.h>
|
||||||
|
|
||||||
|
#include <span.h>
|
||||||
|
|
||||||
|
#include "s_expression.h"
|
||||||
|
|
||||||
|
namespace openpgp
|
||||||
|
{
|
||||||
|
|
||||||
|
enum algorithm : uint8_t
|
||||||
|
{
|
||||||
|
rsa = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
class public_key_rsa
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
public_key_rsa(s_expression expression, size_t bits);
|
||||||
|
|
||||||
|
size_t bits() const;
|
||||||
|
const gcry_sexp_t &get() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
s_expression m_expression;
|
||||||
|
size_t m_bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
class public_key_block : public std::vector<public_key_rsa>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
public_key_block(const std::string &armored);
|
||||||
|
public_key_block(const epee::span<const uint8_t> buffer);
|
||||||
|
|
||||||
|
std::string user_id() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_user_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
class signature_rsa
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum type : uint8_t
|
||||||
|
{
|
||||||
|
binary_document = 0,
|
||||||
|
canonical_text_document = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
signature_rsa(
|
||||||
|
uint8_t algorithm,
|
||||||
|
std::pair<uint8_t, uint8_t> hash_leftmost_bytes,
|
||||||
|
uint8_t hash_algorithm,
|
||||||
|
const std::vector<uint8_t> &hashed_data,
|
||||||
|
type type,
|
||||||
|
s_expression signature,
|
||||||
|
uint8_t version);
|
||||||
|
|
||||||
|
static signature_rsa from_armored(const std::string &armored_signed_message);
|
||||||
|
static signature_rsa from_base64(const std::string &base64);
|
||||||
|
static signature_rsa from_buffer(const epee::span<const uint8_t> input);
|
||||||
|
|
||||||
|
bool verify(const epee::span<const uint8_t> message, const public_key_rsa &public_key) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
s_expression hash_message(const epee::span<const uint8_t> message, size_t public_key_bits) const;
|
||||||
|
std::vector<uint8_t> hash_asn_object_id() const;
|
||||||
|
s_expression hash_bytes(const epee::span<const uint8_t> message, size_t public_key_bits) const;
|
||||||
|
|
||||||
|
static std::vector<uint8_t> format_hashed_appendix(
|
||||||
|
uint8_t algorithm,
|
||||||
|
uint8_t hash_algorithm,
|
||||||
|
const std::vector<uint8_t> &hashed_data,
|
||||||
|
uint8_t type,
|
||||||
|
uint8_t version);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t m_hash_algorithm;
|
||||||
|
std::pair<uint8_t, uint8_t> m_hash_leftmost_bytes;
|
||||||
|
std::vector<uint8_t> m_hashed_appendix;
|
||||||
|
s_expression m_signature;
|
||||||
|
type m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
class message_armored
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
message_armored(const std::string &message_armored);
|
||||||
|
|
||||||
|
operator epee::span<const uint8_t>() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_message;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace openpgp
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
// Copyright (c) 2017-2018, The Monero Project
|
// Copyright (c) 2020, The Monero Project
|
||||||
//
|
//
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
// permitted provided that the following conditions are met:
|
// permitted provided that the following conditions are met:
|
||||||
//
|
//
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
// conditions and the following disclaimer.
|
// conditions and the following disclaimer.
|
||||||
//
|
//
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
// 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
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
// materials provided with the distribution.
|
// materials provided with the distribution.
|
||||||
//
|
//
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
// 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
|
// used to endorse or promote products derived from this software without specific
|
||||||
// prior written permission.
|
// prior written permission.
|
||||||
//
|
//
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
// 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
|
// 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
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
@@ -26,60 +26,63 @@
|
|||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
// 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.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
import QtQuick 2.9
|
#pragma once
|
||||||
import QtQuick.Controls 1.4
|
|
||||||
import moneroComponents.Wallet 1.0
|
|
||||||
import "." as MoneroComponents
|
|
||||||
|
|
||||||
Item {
|
#include <vector>
|
||||||
id: item
|
|
||||||
property string message: ""
|
|
||||||
property bool active: false
|
|
||||||
height: 180
|
|
||||||
width: 320
|
|
||||||
property int margin: 15
|
|
||||||
x: parent.width - width - margin
|
|
||||||
y: parent.height - height * scale.yScale - margin * scale.yScale
|
|
||||||
|
|
||||||
Rectangle {
|
#include <span.h>
|
||||||
color: "#FF6C3C"
|
|
||||||
border.color: "black"
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
TextArea {
|
#include "serialization.h"
|
||||||
id:versionText
|
|
||||||
readOnly: true
|
namespace openpgp
|
||||||
backgroundVisible: false
|
{
|
||||||
textFormat: TextEdit.AutoText
|
|
||||||
anchors.fill: parent
|
class packet_stream
|
||||||
font.family: MoneroComponents.Style.fontRegular.name
|
{
|
||||||
font.pixelSize: 12
|
public:
|
||||||
textMargin: 20
|
packet_stream(const epee::span<const uint8_t> buffer)
|
||||||
textColor: "white"
|
: packet_stream(deserializer<epee::span<const uint8_t>>(buffer))
|
||||||
text: item.message
|
{
|
||||||
wrapMode: Text.WrapAnywhere
|
}
|
||||||
}
|
|
||||||
|
template <
|
||||||
|
typename byte_container,
|
||||||
|
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
|
||||||
|
packet_stream(deserializer<byte_container> buffer)
|
||||||
|
{
|
||||||
|
while (!buffer.empty())
|
||||||
|
{
|
||||||
|
packet_tag tag = buffer.read_packet_tag();
|
||||||
|
packets.push_back({std::move(tag), buffer.read(tag.length)});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
transform: Scale {
|
const std::vector<uint8_t> *find_first(packet_tag::type type) const
|
||||||
id: scale
|
{
|
||||||
yScale: item.active ? 1 : 0
|
for (const auto &packet : packets)
|
||||||
|
{
|
||||||
Behavior on yScale {
|
if (packet.first.packet_type == type)
|
||||||
NumberAnimation { duration: 500; easing.type: Easing.InOutCubic }
|
{
|
||||||
}
|
return &packet.second;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
template <typename Callback>
|
||||||
id: hider
|
void for_each(packet_tag::type type, Callback &callback) const
|
||||||
interval: 30000; running: false; repeat: false
|
{
|
||||||
onTriggered: { item.active = false }
|
for (const auto &packet : packets)
|
||||||
|
{
|
||||||
|
if (packet.first.packet_type == type)
|
||||||
|
{
|
||||||
|
callback(packet.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function show(message) {
|
private:
|
||||||
item.visible = true
|
std::vector<std::pair<packet_tag, std::vector<uint8_t>>> packets;
|
||||||
item.message = message
|
};
|
||||||
item.active = true
|
|
||||||
hider.running = true
|
} // namespace openpgp
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
// Copyright (c) 2014-2019, The Monero Project
|
// Copyright (c) 2020, The Monero Project
|
||||||
//
|
//
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without modification, are
|
// Redistribution and use in source and binary forms, with or without modification, are
|
||||||
// permitted provided that the following conditions are met:
|
// permitted provided that the following conditions are met:
|
||||||
//
|
//
|
||||||
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
// conditions and the following disclaimer.
|
// conditions and the following disclaimer.
|
||||||
//
|
//
|
||||||
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
// 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
|
// of conditions and the following disclaimer in the documentation and/or other
|
||||||
// materials provided with the distribution.
|
// materials provided with the distribution.
|
||||||
//
|
//
|
||||||
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
// 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
|
// used to endorse or promote products derived from this software without specific
|
||||||
// prior written permission.
|
// prior written permission.
|
||||||
//
|
//
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
// 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
|
// 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
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
@@ -26,26 +26,53 @@
|
|||||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
// 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.
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
import QtQuick 2.9
|
#pragma once
|
||||||
import QtQuick.Layouts 1.2
|
|
||||||
import QtQuick.Controls 2.0
|
|
||||||
|
|
||||||
import "../components" as MoneroComponents
|
#include <algorithm>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
Rectangle {
|
#include <gcrypt.h>
|
||||||
property bool active: false
|
|
||||||
Layout.preferredWidth: 30
|
|
||||||
Layout.fillHeight: true
|
|
||||||
property string activeColor: MoneroComponents.Style.defaultFontColor
|
|
||||||
property string inactiveColor: MoneroComponents.Style.progressBarBackgroundColor
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
Rectangle {
|
namespace openpgp
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
{
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
width: 10
|
class s_expression
|
||||||
height: 10
|
{
|
||||||
radius: 10
|
public:
|
||||||
color: parent.active ? parent.activeColor : parent.inactiveColor
|
s_expression(const s_expression &) = delete;
|
||||||
|
s_expression &operator=(const s_expression &) = delete;
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
s_expression(Args... args)
|
||||||
|
{
|
||||||
|
if (gcry_sexp_build(&data, nullptr, args...) != GPG_ERR_NO_ERROR)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("failed to build S-expression");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s_expression(s_expression &&other)
|
||||||
|
{
|
||||||
|
std::swap(data, other.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_expression(gcry_sexp_t data)
|
||||||
|
: data(data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~s_expression()
|
||||||
|
{
|
||||||
|
gcry_sexp_release(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const gcry_sexp_t &get() const
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
gcry_sexp_t data = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace openpgp
|
||||||
172
src/openpgp/serialization.h
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
// Copyright (c) 2020, 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mpi.h"
|
||||||
|
|
||||||
|
namespace openpgp
|
||||||
|
{
|
||||||
|
|
||||||
|
size_t bits_to_bytes(size_t bits)
|
||||||
|
{
|
||||||
|
constexpr const uint16_t bits_in_byte = 8;
|
||||||
|
return (bits + bits_in_byte - 1) / bits_in_byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string strip_line_breaks(const std::string &string)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
result.reserve(string.size());
|
||||||
|
for (const auto &character : string)
|
||||||
|
{
|
||||||
|
if (character != '\r' && character != '\n')
|
||||||
|
{
|
||||||
|
result.push_back(character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct packet_tag
|
||||||
|
{
|
||||||
|
enum type : uint8_t
|
||||||
|
{
|
||||||
|
signature = 2,
|
||||||
|
public_key = 6,
|
||||||
|
user_id = 13,
|
||||||
|
public_subkey = 14,
|
||||||
|
};
|
||||||
|
|
||||||
|
const type packet_type;
|
||||||
|
const size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename byte_container,
|
||||||
|
typename = typename std::enable_if<(sizeof(typename byte_container::value_type) == 1)>::type>
|
||||||
|
class deserializer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
deserializer(byte_container buffer)
|
||||||
|
: buffer(std::move(buffer))
|
||||||
|
, cursor(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return buffer.size() - cursor == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_tag read_packet_tag()
|
||||||
|
{
|
||||||
|
const auto tag = read_big_endian<uint8_t>();
|
||||||
|
|
||||||
|
constexpr const uint8_t format_mask = 0b11000000;
|
||||||
|
constexpr const uint8_t format_old_tag = 0b10000000;
|
||||||
|
if ((tag & format_mask) != format_old_tag)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("invalid packet tag");
|
||||||
|
}
|
||||||
|
|
||||||
|
const packet_tag::type packet_type = static_cast<packet_tag::type>((tag & 0b00111100) >> 2);
|
||||||
|
const uint8_t length_type = tag & 0b00000011;
|
||||||
|
|
||||||
|
size_t length;
|
||||||
|
switch (length_type)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
length = read_big_endian<uint8_t>();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
length = read_big_endian<uint16_t>();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
length = read_big_endian<uint32_t>();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("unsupported packet length type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {packet_type, length};
|
||||||
|
}
|
||||||
|
|
||||||
|
mpi read_mpi()
|
||||||
|
{
|
||||||
|
const size_t bit_length = read_big_endian<uint16_t>();
|
||||||
|
return mpi(read_span(bits_to_bytes(bit_length)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> read(size_t size)
|
||||||
|
{
|
||||||
|
if (buffer.size() - cursor < size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("insufficient buffer size");
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t offset = cursor;
|
||||||
|
cursor += size;
|
||||||
|
|
||||||
|
return {&buffer[offset], &buffer[cursor]};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
|
||||||
|
T read_big_endian()
|
||||||
|
{
|
||||||
|
if (buffer.size() - cursor < sizeof(T))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("insufficient buffer size");
|
||||||
|
}
|
||||||
|
T result = 0;
|
||||||
|
for (size_t read = 0; read < sizeof(T); ++read)
|
||||||
|
{
|
||||||
|
result = (result << 8) | static_cast<uint8_t>(buffer[cursor++]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
epee::span<const uint8_t> read_span(size_t size)
|
||||||
|
{
|
||||||
|
if (buffer.size() - cursor < size)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("insufficient buffer size");
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t offset = cursor;
|
||||||
|
cursor += size;
|
||||||
|
|
||||||
|
return {reinterpret_cast<const uint8_t *>(&buffer[offset]), size};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
byte_container buffer;
|
||||||
|
size_t cursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace openpgp
|
||||||
@@ -43,11 +43,20 @@
|
|||||||
#include "KeysFiles.h"
|
#include "KeysFiles.h"
|
||||||
|
|
||||||
|
|
||||||
WalletKeysFiles::WalletKeysFiles(const qint64 &modified, const QString &path, const quint8 &networkType, const QString &address)
|
WalletKeysFiles::WalletKeysFiles(const QFileInfo &info, quint8 networkType, QString address)
|
||||||
: m_modified(modified), m_path(path), m_networkType(networkType), m_address(address)
|
: m_fileName(info.fileName())
|
||||||
|
, m_modified(info.lastModified().toSecsSinceEpoch())
|
||||||
|
, m_path(QDir::toNativeSeparators(info.absoluteFilePath()))
|
||||||
|
, m_networkType(networkType)
|
||||||
|
, m_address(std::move(address))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString WalletKeysFiles::fileName() const
|
||||||
|
{
|
||||||
|
return m_fileName;
|
||||||
|
}
|
||||||
|
|
||||||
qint64 WalletKeysFiles::modified() const
|
qint64 WalletKeysFiles::modified() const
|
||||||
{
|
{
|
||||||
return m_modified;
|
return m_modified;
|
||||||
@@ -127,11 +136,7 @@ void WalletKeysFilesModel::findWallets(const QString &moneroAccountsDir)
|
|||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QFileInfo info(wallet);
|
this->addWalletKeysFile(WalletKeysFiles(wallet, networkType, std::move(address)));
|
||||||
const QDateTime modifiedAt = info.lastModified();
|
|
||||||
|
|
||||||
this->addWalletKeysFile(WalletKeysFiles(modifiedAt.toSecsSinceEpoch(),
|
|
||||||
info.absoluteFilePath(), networkType, address));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,6 +157,8 @@ QVariant WalletKeysFilesModel::data(const QModelIndex & index, int role) const {
|
|||||||
return QVariant();
|
return QVariant();
|
||||||
|
|
||||||
const WalletKeysFiles &walletKeyFile = m_walletKeyFiles[index.row()];
|
const WalletKeysFiles &walletKeyFile = m_walletKeyFiles[index.row()];
|
||||||
|
if (role == FileNameRole)
|
||||||
|
return walletKeyFile.fileName();
|
||||||
if (role == ModifiedRole)
|
if (role == ModifiedRole)
|
||||||
return walletKeyFile.modified();
|
return walletKeyFile.modified();
|
||||||
else if (role == PathRole)
|
else if (role == PathRole)
|
||||||
@@ -165,6 +172,7 @@ QVariant WalletKeysFilesModel::data(const QModelIndex & index, int role) const {
|
|||||||
|
|
||||||
QHash<int, QByteArray> WalletKeysFilesModel::roleNames() const {
|
QHash<int, QByteArray> WalletKeysFilesModel::roleNames() const {
|
||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
roles[FileNameRole] = "fileName";
|
||||||
roles[ModifiedRole] = "modified";
|
roles[ModifiedRole] = "modified";
|
||||||
roles[PathRole] = "path";
|
roles[PathRole] = "path";
|
||||||
roles[NetworkTypeRole] = "networktype";
|
roles[NetworkTypeRole] = "networktype";
|
||||||
|
|||||||
@@ -37,14 +37,16 @@
|
|||||||
class WalletKeysFiles
|
class WalletKeysFiles
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
WalletKeysFiles(const qint64 &modified, const QString &path, const quint8 &networkType, const QString &address);
|
WalletKeysFiles(const QFileInfo &info, quint8 networkType, QString address);
|
||||||
|
|
||||||
|
QString fileName() const;
|
||||||
qint64 modified() const;
|
qint64 modified() const;
|
||||||
QString path() const;
|
QString path() const;
|
||||||
quint8 networkType() const;
|
quint8 networkType() const;
|
||||||
QString address() const;
|
QString address() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString m_fileName;
|
||||||
qint64 m_modified;
|
qint64 m_modified;
|
||||||
QString m_path;
|
QString m_path;
|
||||||
quint8 m_networkType;
|
quint8 m_networkType;
|
||||||
@@ -56,7 +58,8 @@ class WalletKeysFilesModel : public QAbstractListModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum KeysFilesRoles {
|
enum KeysFilesRoles {
|
||||||
ModifiedRole = Qt::UserRole + 1,
|
FileNameRole = Qt::UserRole + 1,
|
||||||
|
ModifiedRole,
|
||||||
PathRole,
|
PathRole,
|
||||||
NetworkTypeRole,
|
NetworkTypeRole,
|
||||||
AddressRole
|
AddressRole
|
||||||
|
|||||||
67
src/qt/NetworkAccessBlockingFactory.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/* Ricochet - https://ricochet.im/
|
||||||
|
* Copyright (C) 2014, John Brooks <john.brooks@dereferenced.net>
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are
|
||||||
|
* met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * 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.
|
||||||
|
*
|
||||||
|
* * Neither the names of the copyright owners 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
|
||||||
|
* OWNER 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Through the QQmlNetworkAccessManagerFactory below, all network requests
|
||||||
|
* created via QML will be passed to this object; including, for example,
|
||||||
|
* <img> tags parsed in rich Text items.
|
||||||
|
*
|
||||||
|
* Ricochet's UI does not directly cause network requests for any reason. These
|
||||||
|
* are always a potentially deanonymizing bug. This object will block them,
|
||||||
|
* and assert if appropriate.
|
||||||
|
*/
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
class BlockedNetworkAccessManager : public QNetworkAccessManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlockedNetworkAccessManager(QObject *parent)
|
||||||
|
: QNetworkAccessManager(parent)
|
||||||
|
{
|
||||||
|
setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QLatin1String("0.0.0.0"), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0)
|
||||||
|
{
|
||||||
|
qCritical() << "QML attempted to load a network resource from" << req.url() << " - this is potentially an input sanitization flaw.";
|
||||||
|
return QNetworkAccessManager::createRequest(op, QNetworkRequest(), outgoingData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkAccessBlockingFactory : public QQmlNetworkAccessManagerFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual QNetworkAccessManager *create(QObject *parent)
|
||||||
|
{
|
||||||
|
return new BlockedNetworkAccessManager(parent);
|
||||||
|
}
|
||||||
|
};
|
||||||
205
src/qt/ScopeGuard.h
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
// Author: ricab
|
||||||
|
// Source: https://github.com/ricab/scope_guard
|
||||||
|
//
|
||||||
|
// This is free and unencumbered software released into the public domain.
|
||||||
|
//
|
||||||
|
// Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
// distribute this software, either in source code form or as a compiled
|
||||||
|
// binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
// means.
|
||||||
|
//
|
||||||
|
// In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
// of this software dedicate any and all copyright interest in the
|
||||||
|
// software to the public domain. We make this dedication for the benefit
|
||||||
|
// of the public at large and to the detriment of our heirs and
|
||||||
|
// successors. We intend this dedication to be an overt act of
|
||||||
|
// relinquishment in perpetuity of all present and future rights to this
|
||||||
|
// software under copyright law.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
// For more information, please refer to <http://unlicense.org>
|
||||||
|
|
||||||
|
#ifndef SCOPE_GUARD_HPP_
|
||||||
|
#define SCOPE_GUARD_HPP_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#if __cplusplus >= 201703L && defined(SG_REQUIRE_NOEXCEPT_IN_CPP17)
|
||||||
|
#define SG_REQUIRE_NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace sg
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
/* --- Some custom type traits --- */
|
||||||
|
|
||||||
|
// Type trait determining whether a type is callable with no arguments
|
||||||
|
template<typename T, typename = void>
|
||||||
|
struct is_noarg_callable_t
|
||||||
|
: public std::false_type
|
||||||
|
{}; // in general, false
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_noarg_callable_t<T, decltype(std::declval<T&&>()())>
|
||||||
|
: public std::true_type
|
||||||
|
{}; // only true when call expression valid
|
||||||
|
|
||||||
|
// Type trait determining whether a no-argument callable returns void
|
||||||
|
template<typename T>
|
||||||
|
struct returns_void_t
|
||||||
|
: public std::is_same<void, decltype(std::declval<T&&>()())>
|
||||||
|
{};
|
||||||
|
|
||||||
|
/* Type trait determining whether a no-arg callable is nothrow invocable if
|
||||||
|
required. This is where SG_REQUIRE_NOEXCEPT logic is encapsulated. */
|
||||||
|
template<typename T>
|
||||||
|
struct is_nothrow_invocable_if_required_t
|
||||||
|
: public
|
||||||
|
#ifdef SG_REQUIRE_NOEXCEPT
|
||||||
|
std::is_nothrow_invocable<T> /* Note: _r variants not enough to
|
||||||
|
confirm void return: any return can be
|
||||||
|
discarded so all returns are
|
||||||
|
compatible with void */
|
||||||
|
#else
|
||||||
|
std::true_type
|
||||||
|
#endif
|
||||||
|
{};
|
||||||
|
|
||||||
|
// logic AND of two or more type traits
|
||||||
|
template<typename A, typename B, typename... C>
|
||||||
|
struct and_t : public and_t<A, and_t<B, C...>>
|
||||||
|
{}; // for more than two arguments
|
||||||
|
|
||||||
|
template<typename A, typename B>
|
||||||
|
struct and_t<A, B> : public std::conditional<A::value, B, A>::type
|
||||||
|
{}; // for two arguments
|
||||||
|
|
||||||
|
// Type trait determining whether a type is a proper scope_guard callback.
|
||||||
|
template<typename T>
|
||||||
|
struct is_proper_sg_callback_t
|
||||||
|
: public and_t<is_noarg_callable_t<T>,
|
||||||
|
returns_void_t<T>,
|
||||||
|
is_nothrow_invocable_if_required_t<T>,
|
||||||
|
std::is_nothrow_destructible<T>>
|
||||||
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
/* --- The actual scope_guard template --- */
|
||||||
|
|
||||||
|
template<typename Callback,
|
||||||
|
typename = typename std::enable_if<
|
||||||
|
is_proper_sg_callback_t<Callback>::value>::type>
|
||||||
|
class scope_guard;
|
||||||
|
|
||||||
|
|
||||||
|
/* --- Now the friend maker --- */
|
||||||
|
|
||||||
|
template<typename Callback>
|
||||||
|
detail::scope_guard<Callback> make_scope_guard(Callback&& callback)
|
||||||
|
noexcept(std::is_nothrow_constructible<Callback, Callback&&>::value); /*
|
||||||
|
we need this in the inner namespace due to MSVC bugs preventing
|
||||||
|
sg::detail::scope_guard from befriending a sg::make_scope_guard
|
||||||
|
template instance in the parent namespace (see https://is.gd/xFfFhE). */
|
||||||
|
|
||||||
|
|
||||||
|
/* --- The template specialization that actually defines the class --- */
|
||||||
|
|
||||||
|
template<typename Callback>
|
||||||
|
class scope_guard<Callback> final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Callback callback_type;
|
||||||
|
|
||||||
|
scope_guard(scope_guard&& other)
|
||||||
|
noexcept(std::is_nothrow_constructible<Callback, Callback&&>::value);
|
||||||
|
|
||||||
|
~scope_guard() noexcept; // highlight noexcept dtor
|
||||||
|
|
||||||
|
void dismiss() noexcept;
|
||||||
|
|
||||||
|
public:
|
||||||
|
scope_guard() = delete;
|
||||||
|
scope_guard(const scope_guard&) = delete;
|
||||||
|
scope_guard& operator=(const scope_guard&) = delete;
|
||||||
|
scope_guard& operator=(scope_guard&&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit scope_guard(Callback&& callback)
|
||||||
|
noexcept(std::is_nothrow_constructible<Callback, Callback&&>::value); /*
|
||||||
|
meant for friends only */
|
||||||
|
|
||||||
|
friend scope_guard<Callback> make_scope_guard<Callback>(Callback&&)
|
||||||
|
noexcept(std::is_nothrow_constructible<Callback, Callback&&>::value); /*
|
||||||
|
only make_scope_guard can create scope_guards from scratch (i.e. non-move)
|
||||||
|
*/
|
||||||
|
|
||||||
|
private:
|
||||||
|
Callback m_callback;
|
||||||
|
bool m_active;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
/* --- Now the single public maker function --- */
|
||||||
|
|
||||||
|
using detail::make_scope_guard; // see comment on declaration above
|
||||||
|
|
||||||
|
} // namespace sg
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<typename Callback>
|
||||||
|
sg::detail::scope_guard<Callback>::scope_guard(Callback&& callback)
|
||||||
|
noexcept(std::is_nothrow_constructible<Callback, Callback&&>::value)
|
||||||
|
: m_callback(std::forward<Callback>(callback)) /* use () instead of {} because
|
||||||
|
of DR 1467 (https://is.gd/WHmWuo), which still impacts older compilers
|
||||||
|
(e.g. GCC 4.x and clang <=3.6, see https://godbolt.org/g/TE9tPJ and
|
||||||
|
https://is.gd/Tsmh8G) */
|
||||||
|
, m_active{true}
|
||||||
|
{}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<typename Callback>
|
||||||
|
sg::detail::scope_guard<Callback>::~scope_guard() noexcept
|
||||||
|
{
|
||||||
|
if(m_active)
|
||||||
|
m_callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<typename Callback>
|
||||||
|
sg::detail::scope_guard<Callback>::scope_guard(scope_guard&& other)
|
||||||
|
noexcept(std::is_nothrow_constructible<Callback, Callback&&>::value)
|
||||||
|
: m_callback(std::forward<Callback>(other.m_callback)) // idem
|
||||||
|
, m_active{std::move(other.m_active)}
|
||||||
|
{
|
||||||
|
other.m_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<typename Callback>
|
||||||
|
inline void sg::detail::scope_guard<Callback>::dismiss() noexcept
|
||||||
|
{
|
||||||
|
m_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
template<typename Callback>
|
||||||
|
inline auto sg::detail::make_scope_guard(Callback&& callback)
|
||||||
|
noexcept(std::is_nothrow_constructible<Callback, Callback&&>::value)
|
||||||
|
-> detail::scope_guard<Callback>
|
||||||
|
{
|
||||||
|
return detail::scope_guard<Callback>{std::forward<Callback>(callback)};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* SCOPE_GUARD_HPP_ */
|
||||||
222
src/qt/downloader.cpp
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
// Copyright (c) 2020, 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 "downloader.h"
|
||||||
|
|
||||||
|
#include <QReadLocker>
|
||||||
|
#include <QWriteLocker>
|
||||||
|
|
||||||
|
#include "updater.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
class DownloaderStateGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DownloaderStateGuard(bool &active, QReadWriteLock &mutex, std::function<void()> onActiveChanged)
|
||||||
|
: m_active(active)
|
||||||
|
, m_acquired(false)
|
||||||
|
, m_mutex(mutex)
|
||||||
|
, m_onActiveChanged(std::move(onActiveChanged))
|
||||||
|
{
|
||||||
|
{
|
||||||
|
QWriteLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
if (m_active)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_active = true;
|
||||||
|
}
|
||||||
|
m_onActiveChanged();
|
||||||
|
|
||||||
|
m_acquired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
~DownloaderStateGuard()
|
||||||
|
{
|
||||||
|
if (!m_acquired)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QWriteLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
m_active = false;
|
||||||
|
}
|
||||||
|
m_onActiveChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool acquired() const
|
||||||
|
{
|
||||||
|
return m_acquired;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool &m_active;
|
||||||
|
bool m_acquired;
|
||||||
|
QReadWriteLock &m_mutex;
|
||||||
|
std::function<void()> m_onActiveChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Downloader::Downloader(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_active(false)
|
||||||
|
, m_httpClient(new HttpClient())
|
||||||
|
, m_network(this)
|
||||||
|
, m_scheduler(this)
|
||||||
|
{
|
||||||
|
QObject::connect(m_httpClient.get(), SIGNAL(contentLengthChanged()), this, SIGNAL(totalChanged()));
|
||||||
|
QObject::connect(m_httpClient.get(), SIGNAL(receivedChanged()), this, SIGNAL(loadedChanged()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Downloader::~Downloader()
|
||||||
|
{
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Downloader::cancel()
|
||||||
|
{
|
||||||
|
m_httpClient->cancel();
|
||||||
|
|
||||||
|
QWriteLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
m_contents.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Downloader::get(const QString &url, const QString &hash, const QJSValue &callback)
|
||||||
|
{
|
||||||
|
auto future = m_scheduler.run(
|
||||||
|
[this, url, hash]() {
|
||||||
|
DownloaderStateGuard stateGuard(m_active, m_mutex, [this]() {
|
||||||
|
emit activeChanged();
|
||||||
|
});
|
||||||
|
if (!stateGuard.acquired())
|
||||||
|
{
|
||||||
|
return QJSValueList({"downloading is already running"});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QWriteLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
m_contents.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string response;
|
||||||
|
{
|
||||||
|
QString error;
|
||||||
|
auto task = m_scheduler.run([this, &error, &response, &url] {
|
||||||
|
error = m_network.get(m_httpClient, url, response);
|
||||||
|
});
|
||||||
|
if (!task.first)
|
||||||
|
{
|
||||||
|
return QJSValueList({"failed to start downloading task"});
|
||||||
|
}
|
||||||
|
task.second.waitForFinished();
|
||||||
|
|
||||||
|
if (!error.isEmpty())
|
||||||
|
{
|
||||||
|
return QJSValueList({error});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.empty())
|
||||||
|
{
|
||||||
|
return QJSValueList({"empty response"});
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const QByteArray calculatedHash = Updater().getHash(&response[0], response.size());
|
||||||
|
if (QByteArray::fromHex(hash.toUtf8()) != calculatedHash)
|
||||||
|
{
|
||||||
|
return QJSValueList({"hash sum mismatch"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
return QJSValueList({e.what()});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
QWriteLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
m_contents = std::move(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QJSValueList({});
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
|
||||||
|
return future.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Downloader::saveToFile(const QString &path) const
|
||||||
|
{
|
||||||
|
QWriteLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
if (m_active || m_contents.empty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(path);
|
||||||
|
if (!file.open(QIODevice::WriteOnly))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_cast<size_t>(file.write(m_contents.data(), m_contents.size())) != m_contents.size())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Downloader::active() const
|
||||||
|
{
|
||||||
|
QReadLocker locker(&m_mutex);
|
||||||
|
|
||||||
|
return m_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 Downloader::loaded() const
|
||||||
|
{
|
||||||
|
return m_httpClient->received();
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 Downloader::total() const
|
||||||
|
{
|
||||||
|
return m_httpClient->contentLength();
|
||||||
|
}
|
||||||
67
src/qt/downloader.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) 2020, 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
|
#include "network.h"
|
||||||
|
|
||||||
|
class Downloader : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool active READ active NOTIFY activeChanged);
|
||||||
|
Q_PROPERTY(quint64 loaded READ loaded NOTIFY loadedChanged);
|
||||||
|
Q_PROPERTY(quint64 total READ total NOTIFY totalChanged);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Downloader(QObject *parent = nullptr);
|
||||||
|
~Downloader();
|
||||||
|
|
||||||
|
Q_INVOKABLE void cancel();
|
||||||
|
Q_INVOKABLE bool get(const QString &url, const QString &hash, const QJSValue &callback);
|
||||||
|
Q_INVOKABLE bool saveToFile(const QString &path) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void activeChanged() const;
|
||||||
|
void loadedChanged() const;
|
||||||
|
void totalChanged() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool active() const;
|
||||||
|
quint64 loaded() const;
|
||||||
|
quint64 total() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_active;
|
||||||
|
std::string m_contents;
|
||||||
|
std::shared_ptr<HttpClient> m_httpClient;
|
||||||
|
mutable QReadWriteLock m_mutex;
|
||||||
|
Network m_network;
|
||||||
|
mutable FutureScheduler m_scheduler;
|
||||||
|
};
|
||||||
165
src/qt/network.cpp
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
// Copyright (c) 2020, 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 "network.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
using epee::net_utils::http::fields_list;
|
||||||
|
using epee::net_utils::http::http_response_info;
|
||||||
|
using epee::net_utils::http::http_simple_client;
|
||||||
|
|
||||||
|
HttpClient::HttpClient(QObject *parent /* = nullptr */)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_cancel(false)
|
||||||
|
, m_contentLength(0)
|
||||||
|
, m_received(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::cancel()
|
||||||
|
{
|
||||||
|
m_cancel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 HttpClient::contentLength() const
|
||||||
|
{
|
||||||
|
return m_contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint64 HttpClient::received() const
|
||||||
|
{
|
||||||
|
return m_received;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpClient::on_header(const http_response_info &headers)
|
||||||
|
{
|
||||||
|
if (m_cancel.exchange(false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t contentLength = 0;
|
||||||
|
if (!epee::string_tools::get_xtype_from_string(contentLength, headers.m_header_info.m_content_length))
|
||||||
|
{
|
||||||
|
qWarning() << "Failed to get Content-Length";
|
||||||
|
}
|
||||||
|
m_contentLength = contentLength;
|
||||||
|
emit contentLengthChanged();
|
||||||
|
|
||||||
|
m_received = 0;
|
||||||
|
emit receivedChanged();
|
||||||
|
|
||||||
|
return http_simple_client::on_header(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpClient::handle_target_data(std::string &piece_of_transfer)
|
||||||
|
{
|
||||||
|
if (m_cancel.exchange(false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_received += piece_of_transfer.size();
|
||||||
|
emit receivedChanged();
|
||||||
|
|
||||||
|
return http_simple_client::handle_target_data(piece_of_transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::Network(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_scheduler(this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Network::get(const QString &url, const QJSValue &callback, const QString &contentType /* = {} */) const
|
||||||
|
{
|
||||||
|
m_scheduler.run(
|
||||||
|
[this, url, contentType] {
|
||||||
|
std::string response;
|
||||||
|
std::shared_ptr<http_simple_client> httpClient(new http_simple_client());
|
||||||
|
QString error = get(httpClient, url, response, contentType);
|
||||||
|
return QJSValueList({url, QString::fromStdString(response), error});
|
||||||
|
},
|
||||||
|
callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Network::getJSON(const QString &url, const QJSValue &callback) const
|
||||||
|
{
|
||||||
|
get(url, callback, "application/json; charset=utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Network::get(const QString &url, const QString &contentType /* = {} */) const
|
||||||
|
{
|
||||||
|
std::string response;
|
||||||
|
QString error = get(std::shared_ptr<http_simple_client>(new http_simple_client()), url, response, contentType);
|
||||||
|
if (!error.isEmpty())
|
||||||
|
{
|
||||||
|
throw std::runtime_error(QString("failed to fetch %1: %2").arg(url).arg(error).toStdString());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Network::get(
|
||||||
|
std::shared_ptr<http_simple_client> httpClient,
|
||||||
|
const QString &url,
|
||||||
|
std::string &response,
|
||||||
|
const QString &contentType /* = {} */) const
|
||||||
|
{
|
||||||
|
const QUrl urlParsed(url);
|
||||||
|
httpClient->set_server(urlParsed.host().toStdString(), urlParsed.scheme() == "https" ? "443" : "80", {});
|
||||||
|
|
||||||
|
const QString uri = (urlParsed.hasQuery() ? urlParsed.path() + "?" + urlParsed.query() : urlParsed.path());
|
||||||
|
const http_response_info *pri = NULL;
|
||||||
|
constexpr std::chrono::milliseconds timeout = std::chrono::seconds(15);
|
||||||
|
|
||||||
|
fields_list headers({{"User-Agent", randomUserAgent().toStdString()}});
|
||||||
|
if (!contentType.isEmpty())
|
||||||
|
{
|
||||||
|
headers.push_back({"Content-Type", contentType.toStdString()});
|
||||||
|
}
|
||||||
|
const bool result = httpClient->invoke(uri.toStdString(), "GET", {}, timeout, std::addressof(pri), headers);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return "unknown error";
|
||||||
|
}
|
||||||
|
if (!pri)
|
||||||
|
{
|
||||||
|
return "internal error";
|
||||||
|
}
|
||||||
|
if (pri->m_response_code != 200)
|
||||||
|
{
|
||||||
|
return QString("response code %1").arg(pri->m_response_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
response = std::move(pri->m_body);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
89
src/qt/network.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright (c) 2020, 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QtNetwork>
|
||||||
|
|
||||||
|
// TODO: wallet_merged - epee library triggers the warnings
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
|
#pragma GCC diagnostic ignored "-Wreorder"
|
||||||
|
#include <net/http_client.h>
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
#include "FutureScheduler.h"
|
||||||
|
|
||||||
|
class HttpClient : public QObject, public epee::net_utils::http::http_simple_client
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(quint64 contentLength READ contentLength NOTIFY contentLengthChanged);
|
||||||
|
Q_PROPERTY(quint64 received READ received NOTIFY receivedChanged);
|
||||||
|
|
||||||
|
public:
|
||||||
|
HttpClient(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void cancel();
|
||||||
|
quint64 contentLength() const;
|
||||||
|
quint64 received() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void contentLengthChanged() const;
|
||||||
|
void receivedChanged() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool on_header(const epee::net_utils::http::http_response_info &headers) final;
|
||||||
|
bool handle_target_data(std::string &piece_of_transfer) final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<bool> m_cancel;
|
||||||
|
std::atomic<size_t> m_contentLength;
|
||||||
|
std::atomic<size_t> m_received;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Network : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Network(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE void get(const QString &url, const QJSValue &callback, const QString &contentType = {}) const;
|
||||||
|
Q_INVOKABLE void getJSON(const QString &url, const QJSValue &callback) const;
|
||||||
|
|
||||||
|
std::string get(const QString &url, const QString &contentType = {}) const;
|
||||||
|
QString get(
|
||||||
|
std::shared_ptr<epee::net_utils::http::http_simple_client> httpClient,
|
||||||
|
const QString &url,
|
||||||
|
std::string &response,
|
||||||
|
const QString &contentType = {}) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable FutureScheduler m_scheduler;
|
||||||
|
};
|
||||||
@@ -1,110 +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 <QDebug>
|
|
||||||
#include <QtCore>
|
|
||||||
|
|
||||||
// TODO: wallet_merged - epee library triggers the warnings
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
|
||||||
#pragma GCC diagnostic ignored "-Wreorder"
|
|
||||||
#include <net/http_client.h>
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
#include "prices.h"
|
|
||||||
|
|
||||||
Prices::Prices(QObject *parent)
|
|
||||||
: QObject(parent)
|
|
||||||
, m_scheduler(this)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Prices::getJSON(const QString url) const
|
|
||||||
{
|
|
||||||
qDebug() << QString("Fetching: %1").arg(url);
|
|
||||||
|
|
||||||
m_scheduler.run([this, url] {
|
|
||||||
epee::net_utils::http::http_simple_client http_client;
|
|
||||||
|
|
||||||
const QUrl urlParsed(url);
|
|
||||||
http_client.set_server(urlParsed.host().toStdString(), urlParsed.scheme() == "https" ? "443" : "80", {});
|
|
||||||
|
|
||||||
const QString uri = (urlParsed.hasQuery() ? urlParsed.path() + "?" + urlParsed.query() : urlParsed.path());
|
|
||||||
const epee::net_utils::http::http_response_info* pri = NULL;
|
|
||||||
constexpr std::chrono::milliseconds timeout = std::chrono::seconds(15);
|
|
||||||
|
|
||||||
const bool result = http_client.invoke(
|
|
||||||
uri.toStdString(),
|
|
||||||
"GET",
|
|
||||||
{},
|
|
||||||
timeout,
|
|
||||||
std::addressof(pri),
|
|
||||||
{
|
|
||||||
{"Content-Type", "application/json; charset=utf-8"},
|
|
||||||
{"User-Agent", randomUserAgent().toStdString()}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result)
|
|
||||||
{
|
|
||||||
this->gotError("unknown error");
|
|
||||||
}
|
|
||||||
else if (!pri)
|
|
||||||
{
|
|
||||||
this->gotError("internal error (null response ptr)");
|
|
||||||
}
|
|
||||||
else if (pri->m_response_code != 200)
|
|
||||||
{
|
|
||||||
this->gotError(QString("response code: %1").arg(pri->m_response_code));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson({&pri->m_body[0], static_cast<int>(pri->m_body.size())});
|
|
||||||
if (doc.isEmpty())
|
|
||||||
{
|
|
||||||
this->gotError("bad JSON");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Insert source url for later reference
|
|
||||||
QJsonObject docobj = doc.object();
|
|
||||||
docobj["_url"] = url;
|
|
||||||
doc.setObject(docobj);
|
|
||||||
|
|
||||||
QVariantMap vMap = doc.object().toVariantMap();
|
|
||||||
emit priceJsonReceived(vMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Prices::gotError(const QString &message) const
|
|
||||||
{
|
|
||||||
qCritical() << "[Fiat API] Error:" << message;
|
|
||||||
emit priceJsonError(message);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
#ifndef PRICES_H
|
|
||||||
#define PRICES_H
|
|
||||||
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QtNetwork>
|
|
||||||
|
|
||||||
#include "FutureScheduler.h"
|
|
||||||
|
|
||||||
class Prices : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
Prices(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
public:
|
|
||||||
Q_INVOKABLE void getJSON(const QString url) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void gotError(const QString &message) const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void priceJsonReceived(QVariantMap document) const;
|
|
||||||
void priceJsonError(QString message) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
mutable FutureScheduler m_scheduler;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // PRICES_H
|
|
||||||
171
src/qt/updater.cpp
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
// Copyright (c) 2020, 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 "updater.h"
|
||||||
|
|
||||||
|
#include <common/util.h>
|
||||||
|
|
||||||
|
#include <openpgp/hash.h>
|
||||||
|
|
||||||
|
#include "network.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
Updater::Updater()
|
||||||
|
{
|
||||||
|
m_maintainers.emplace_back(fileGetContents(":/monero/utils/gpg_keys/binaryfate.asc").toStdString());
|
||||||
|
m_maintainers.emplace_back(fileGetContents(":/monero/utils/gpg_keys/fluffypony.asc").toStdString());
|
||||||
|
m_maintainers.emplace_back(fileGetContents(":/monero/utils/gpg_keys/luigi1111.asc").toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Updater::fetchSignedHash(
|
||||||
|
const QString &binaryFilename,
|
||||||
|
const QByteArray &hashFromDns,
|
||||||
|
QPair<QString, QString> &signers) const
|
||||||
|
{
|
||||||
|
static constexpr const char hashesTxtUrl[] = "https://web.getmonero.org/downloads/hashes.txt";
|
||||||
|
static constexpr const char hashesTxtSigUrl[] = "https://web.getmonero.org/downloads/hashes.txt.sig";
|
||||||
|
|
||||||
|
const Network network;
|
||||||
|
std::string hashesTxt = network.get(hashesTxtUrl);
|
||||||
|
std::string hashesTxtSig = network.get(hashesTxtSigUrl);
|
||||||
|
|
||||||
|
const QByteArray signedHash = verifyParseSignedHahes(
|
||||||
|
QByteArray(&hashesTxt[0], hashesTxt.size()),
|
||||||
|
QByteArray(&hashesTxtSig[0], hashesTxtSig.size()),
|
||||||
|
binaryFilename,
|
||||||
|
signers);
|
||||||
|
|
||||||
|
if (signedHash != hashFromDns)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("DNS hash mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
return signedHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Updater::verifyParseSignedHahes(
|
||||||
|
const QByteArray &armoredSignedHashes,
|
||||||
|
const QByteArray &secondDetachedSignature,
|
||||||
|
const QString &binaryFilename,
|
||||||
|
QPair<QString, QString> &signers) const
|
||||||
|
{
|
||||||
|
const QString signedMessage = verifySignature(armoredSignedHashes, signers.first);
|
||||||
|
|
||||||
|
signers.second = verifySignature(
|
||||||
|
epee::span<const uint8_t>(
|
||||||
|
reinterpret_cast<const uint8_t *>(armoredSignedHashes.data()),
|
||||||
|
armoredSignedHashes.size()),
|
||||||
|
openpgp::signature_rsa::from_buffer(epee::span<const uint8_t>(
|
||||||
|
reinterpret_cast<const uint8_t *>(secondDetachedSignature.data()),
|
||||||
|
secondDetachedSignature.size())));
|
||||||
|
|
||||||
|
if (signers.first == signers.second)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("both signatures were generated by the same person");
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseShasumOutput(signedMessage, binaryFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
QPair<QString, QString> Updater::verifySignaturesAndHashSum(
|
||||||
|
const QByteArray &armoredSignedHashes,
|
||||||
|
const QByteArray &secondDetachedSignature,
|
||||||
|
const QString &binaryFilename,
|
||||||
|
const void *binaryData,
|
||||||
|
size_t binarySize) const
|
||||||
|
{
|
||||||
|
QPair<QString, QString> signers;
|
||||||
|
const QByteArray signedHash =
|
||||||
|
verifyParseSignedHahes(armoredSignedHashes, secondDetachedSignature, binaryFilename, signers);
|
||||||
|
const QByteArray calculatedHash = getHash(binaryData, binarySize);
|
||||||
|
if (signedHash != calculatedHash)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("hash sum mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
return signers;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Updater::getHash(const void *data, size_t size) const
|
||||||
|
{
|
||||||
|
QByteArray hash(sizeof(crypto::hash), 0);
|
||||||
|
tools::sha256sum(static_cast<const uint8_t *>(data), size, *reinterpret_cast<crypto::hash *>(hash.data()));
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Updater::parseShasumOutput(const QString &message, const QString &filename) const
|
||||||
|
{
|
||||||
|
for (const auto &line : message.splitRef("\n"))
|
||||||
|
{
|
||||||
|
const auto trimmed = line.trimmed();
|
||||||
|
if (trimmed.endsWith(filename))
|
||||||
|
{
|
||||||
|
const int pos = trimmed.indexOf(' ');
|
||||||
|
if (pos != -1)
|
||||||
|
{
|
||||||
|
return QByteArray::fromHex(trimmed.left(pos).toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (trimmed.startsWith(filename))
|
||||||
|
{
|
||||||
|
const int pos = trimmed.lastIndexOf(' ');
|
||||||
|
if (pos != -1)
|
||||||
|
{
|
||||||
|
return QByteArray::fromHex(trimmed.right(trimmed.size() - pos).toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("hash not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Updater::verifySignature(const QByteArray &armoredSignedMessage, QString &signer) const
|
||||||
|
{
|
||||||
|
const std::string messageString = armoredSignedMessage.toStdString();
|
||||||
|
const openpgp::message_armored signedMessage(messageString);
|
||||||
|
signer = verifySignature(signedMessage, openpgp::signature_rsa::from_armored(messageString));
|
||||||
|
|
||||||
|
const epee::span<const uint8_t> message = signedMessage;
|
||||||
|
return QString(QByteArray(reinterpret_cast<const char *>(&message[0]), message.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Updater::verifySignature(const epee::span<const uint8_t> data, const openpgp::signature_rsa &signature) const
|
||||||
|
{
|
||||||
|
for (const auto &maintainer : m_maintainers)
|
||||||
|
{
|
||||||
|
for (const auto &public_key : maintainer)
|
||||||
|
{
|
||||||
|
if (signature.verify(data, public_key))
|
||||||
|
{
|
||||||
|
return QString::fromStdString(maintainer.user_id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("not signed by a maintainer");
|
||||||
|
}
|
||||||
64
src/qt/updater.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) 2020, 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.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QPair>
|
||||||
|
|
||||||
|
#include <openpgp/openpgp.h>
|
||||||
|
|
||||||
|
class Updater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Updater();
|
||||||
|
|
||||||
|
QByteArray fetchSignedHash(
|
||||||
|
const QString &binaryFilename,
|
||||||
|
const QByteArray &hashFromDns,
|
||||||
|
QPair<QString, QString> &signers) const;
|
||||||
|
QByteArray getHash(const void *data, size_t size) const;
|
||||||
|
QPair<QString, QString> verifySignaturesAndHashSum(
|
||||||
|
const QByteArray &armoredSignedHashes,
|
||||||
|
const QByteArray &secondDetachedSignature,
|
||||||
|
const QString &binaryFilename,
|
||||||
|
const void *binaryData,
|
||||||
|
size_t binarySize) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QByteArray verifyParseSignedHahes(
|
||||||
|
const QByteArray &armoredSignedHashes,
|
||||||
|
const QByteArray &secondDetachedSignature,
|
||||||
|
const QString &binaryFilename,
|
||||||
|
QPair<QString, QString> &signers) const;
|
||||||
|
QString verifySignature(const QByteArray &armoredSignedMessage, QString &signer) const;
|
||||||
|
QString verifySignature(const epee::span<const uint8_t> data, const openpgp::signature_rsa &signature) const;
|
||||||
|
QByteArray parseShasumOutput(const QString &message, const QString &filename) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<openpgp::public_key_block> m_maintainers;
|
||||||
|
};
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
#include "TailsOS.h"
|
#include "TailsOS.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
@@ -37,6 +38,24 @@ bool fileExists(QString path) {
|
|||||||
return check_file.exists() && check_file.isFile();
|
return check_file.exists() && check_file.isFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray fileGetContents(QString path)
|
||||||
|
{
|
||||||
|
QFile file(path);
|
||||||
|
if (!file.open(QFile::ReadOnly))
|
||||||
|
{
|
||||||
|
throw std::runtime_error(QString("failed to open %1").arg(path).toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
data.resize(file.size());
|
||||||
|
if (file.read(data.data(), data.size()) != data.size())
|
||||||
|
{
|
||||||
|
throw std::runtime_error(QString("failed to read %1").arg(path).toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray fileOpen(QString path) {
|
QByteArray fileOpen(QString path) {
|
||||||
QFile file(path);
|
QFile file(path);
|
||||||
if(!file.open(QFile::ReadOnly | QFile::Text))
|
if(!file.open(QFile::ReadOnly | QFile::Text))
|
||||||
@@ -50,7 +69,8 @@ QByteArray fileOpen(QString path) {
|
|||||||
bool fileWrite(QString path, QString data) {
|
bool fileWrite(QString path, QString data) {
|
||||||
QFile file(path);
|
QFile file(path);
|
||||||
if(file.open(QIODevice::WriteOnly)){
|
if(file.open(QIODevice::WriteOnly)){
|
||||||
QTextStream out(&file); out << data << endl;
|
QTextStream out(&file);
|
||||||
|
out << data << '\n';
|
||||||
file.close();
|
file.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
||||||
bool fileExists(QString path);
|
bool fileExists(QString path);
|
||||||
|
QByteArray fileGetContents(QString path);
|
||||||
QByteArray fileOpen(QString path);
|
QByteArray fileOpen(QString path);
|
||||||
bool fileWrite(QString path, QString data);
|
bool fileWrite(QString path, QString data);
|
||||||
QString getAccountName();
|
QString getAccountName();
|
||||||
|
|||||||