diff --git a/.gdb_history b/.gdb_history new file mode 100644 index 0000000..4846c14 --- /dev/null +++ b/.gdb_history @@ -0,0 +1,61 @@ +b ContactDataStore::dump() +r +n +q +b ContactDataStore::dump() +r +n +c +./build.sh +$(./build.sh) +$(./build.sh) +q +r +q +r +b ContactDataStore::dump() +r +n +b ContactItem::toJson() +r +c +n +q +b ContactItem::toJson() +r +n +c +c +c +c +c +c +c +c +q +b FileSystem::writeContacts(QString file, json j) +b FileSystem::writeContacts +r +q +b FileSystem::writeContacts +r +b ContactDataStore::dump() +r +c +n +q +b FileSystem::writeContacts +r +n +q +r +b FileEncryption::encrypt +r +s +n +q +b FileSystem::readContactsOldFormat +r +n +c +q diff --git a/README.md b/README.md index a36f828..31d4041 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ SilentDragonLite does automatic note and utxo management, which means it doesn't ## Compiling from source * SilentDragonLite is written in C++ 14, and can be compiled with g++/clang++/visual c++. * It also depends on Qt5, which you can get from [here](https://www.qt.io/download). -* You'll need Rust v1.37 + +* You'll need Rust v1.41 + ## Building on Linux diff --git a/application.qrc b/application.qrc index 63d9c5d..e6c1b2d 100644 --- a/application.qrc +++ b/application.qrc @@ -8,26 +8,71 @@ res/paymentreq.gif res/icon.ico res/mail.png + res/darkwing.png + res/SDLogo.png + res/sdlogo2.png + res/Berg.png + res/Denio.png + res/Duke.png + res/Sharpee.png + res/Yoda.png + res/Mickey.png + res/Snoopy.png + res/Popey.png + res/Garfield.png + res/Pinguin.png + res/Stag.png + res/Elsa.png + res/send.png + res/send.svg + res/addcontact.svg + res/send-new.svg + res/add_contact.svg + res/notification.svg + res/send-new-white.png + res/add_contact.png + res/notification.png + res/rahmen-message.png + res/message-icon.svg + res/lock_green.png + res/lock_orange.png + res/unlocked.png + res/getAddrWhite.png + res/send-white.png + res/requestWhite.png + res/addContactWhite.png + res/getAddrBlack.png + res/sendBlack.png + res/requestBlack.png + res/addContactBlack.png + res/unknownBlack.png + res/unknownWhite.png res/hushdlogo.gif - res/logobig.gif + res/silentdragonlite-animated.gif + res/silentdragonlite-animated-dark.gif + res/silentdragonlite-animated-startup.gif + res/silentdragonlite-animated-startup-dark.gif + res/loaderblack.gif + res/loaderwhite.gif - res/silentdragonlite_de.qm - res/silentdragonlite_es.qm - res/silentdragonlite_fr.qm - res/silentdragonlite_pt.qm - res/silentdragonlite_it.qm - res/silentdragonlite_hr.qm - res/silentdragonlite_fa.qm - res/silentdragonlite_id.qm + res/silentdragonlite_de.qm + res/silentdragonlite_es.qm + res/silentdragonlite_fr.qm + res/silentdragonlite_pt.qm + res/silentdragonlite_it.qm + res/silentdragonlite_hr.qm + res/silentdragonlite_fa.qm + res/silentdragonlite_id.qm - res/css/blue.css - res/css/dark.css - res/css/default.css - res/css/light.css + res/css/Blue.css + res/css/Dark.css + res/css/Default.css + res/css/Light.css + res/css/Midnight.css res/images/blue/unchecked.png diff --git a/compile.log b/compile.log new file mode 100644 index 0000000..6c6584f --- /dev/null +++ b/compile.log @@ -0,0 +1,5 @@ +Compiling SilentDragonLite 1.2.2 with 4 threads... +g++ -c -include bin/SilentDragonLite -pipe -g -std=gnu++1y -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQAPPLICATION_CLASS=QApplication -D_FORTIFY_SOURCE=2 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -Isrc/3rdparty -Isrc -Isingleapplication -Ires -isystem /usr/include/x86_64-linux-gnu/qt5 -isystem /usr/include/x86_64-linux-gnu/qt5/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt5/QtGui -isystem /usr/include/x86_64-linux-gnu/qt5/QtWebSockets -isystem /usr/include/x86_64-linux-gnu/qt5/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt5/QtCore -Ibin -Isrc -I/usr/lib/x86_64-linux-gnu/qt5/mkspecs/linux-g++ -o bin/mainwindow.o src/mainwindow.cpp +g++ -c -include bin/SilentDragonLite -pipe -g -std=gnu++1y -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQAPPLICATION_CLASS=QApplication -D_FORTIFY_SOURCE=2 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -Isrc/3rdparty -Isrc -Isingleapplication -Ires -isystem /usr/include/x86_64-linux-gnu/qt5 -isystem /usr/include/x86_64-linux-gnu/qt5/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt5/QtGui -isystem /usr/include/x86_64-linux-gnu/qt5/QtWebSockets -isystem /usr/include/x86_64-linux-gnu/qt5/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt5/QtCore -Ibin -Isrc -I/usr/lib/x86_64-linux-gnu/qt5/mkspecs/linux-g++ -o bin/sendtab.o src/sendtab.cpp +g++ -c -include bin/SilentDragonLite -pipe -g -std=gnu++1y -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQAPPLICATION_CLASS=QApplication -D_FORTIFY_SOURCE=2 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -Isrc/3rdparty -Isrc -Isingleapplication -Ires -isystem /usr/include/x86_64-linux-gnu/qt5 -isystem /usr/include/x86_64-linux-gnu/qt5/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt5/QtGui -isystem /usr/include/x86_64-linux-gnu/qt5/QtWebSockets -isystem /usr/include/x86_64-linux-gnu/qt5/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt5/QtCore -Ibin -Isrc -I/usr/lib/x86_64-linux-gnu/qt5/mkspecs/linux-g++ -o bin/addressbook.o src/addressbook.cpp +g++ -c -include bin/SilentDragonLite -pipe -g -std=gnu++1y -Wall -W -D_REENTRANT -fPIC -DQT_DEPRECATED_WARNINGS -DQAPPLICATION_CLASS=QApplication -D_FORTIFY_SOURCE=2 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_WEBSOCKETS_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB -I. -Isrc/3rdparty -Isrc -Isingleapplication -Ires -isystem /usr/include/x86_64-linux-gnu/qt5 -isystem /usr/include/x86_64-linux-gnu/qt5/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt5/QtGui -isystem /usr/include/x86_64-linux-gnu/qt5/QtWebSockets -isystem /usr/include/x86_64-linux-gnu/qt5/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt5/QtCore -Ibin -Isrc -I/usr/lib/x86_64-linux-gnu/qt5/mkspecs/linux-g++ -o bin/addresscombo.o src/addresscombo.cpp diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 089d4ac..d1c7722 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -1177,7 +1177,7 @@ version = "0.1.0" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", - "silentdragonlitelib 0.1.0 (git+https://github.com/MyHush/silentdragonlite-cli?rev=7efa024660cbe08e7eadf2524134e153c89c51ad)", + "silentdragonlitelib 0.1.0 (git+https://github.com/DenioD/silentdragonlite-cli?rev=d2887d07879a93bdd9b2c8bd12504bb977e82fe0)", ] [[package]] @@ -1640,7 +1640,7 @@ dependencies = [ [[package]] name = "silentdragonlitelib" version = "0.1.0" -source = "git+https://github.com/MyHush/silentdragonlite-cli?rev=7efa024660cbe08e7eadf2524134e153c89c51ad#7efa024660cbe08e7eadf2524134e153c89c51ad" +source = "git+https://github.com/DenioD/silentdragonlite-cli?rev=d2887d07879a93bdd9b2c8bd12504bb977e82fe0#d2887d07879a93bdd9b2c8bd12504bb977e82fe0" dependencies = [ "base58 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bellman 0.1.0 (git+https://github.com/MyHush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37)", @@ -1652,6 +1652,7 @@ dependencies = [ "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "json 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.27 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", "pairing 0.14.2 (git+https://github.com/MyHush/librustzcash.git?rev=1a0204113d487cdaaf183c2967010e5214ff9e37)", @@ -2630,7 +2631,7 @@ dependencies = [ "checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35" "checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" "checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" -"checksum silentdragonlitelib 0.1.0 (git+https://github.com/MyHush/silentdragonlite-cli?rev=7efa024660cbe08e7eadf2524134e153c89c51ad)" = "" +"checksum silentdragonlitelib 0.1.0 (git+https://github.com/DenioD/silentdragonlite-cli?rev=d2887d07879a93bdd9b2c8bd12504bb977e82fe0)" = "" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 65c97ad..d46b55f 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -11,4 +11,4 @@ crate-type = ["staticlib"] [dependencies] libc = "0.2.58" lazy_static = "1.4.0" -silentdragonlitelib = { git = "https://github.com/MyHush/silentdragonlite-cli", rev = "7efa024660cbe08e7eadf2524134e153c89c51ad" } +silentdragonlitelib = { git = "https://github.com/DenioD/silentdragonlite-cli", rev = "d2887d07879a93bdd9b2c8bd12504bb977e82fe0" } diff --git a/peda-session-SilentDragonLite.txt b/peda-session-SilentDragonLite.txt new file mode 100644 index 0000000..12c5dfb --- /dev/null +++ b/peda-session-SilentDragonLite.txt @@ -0,0 +1,2 @@ +break FileSystem::readContactsOldFormat + diff --git a/res/Berg.png b/res/Berg.png new file mode 100644 index 0000000..0aab208 Binary files /dev/null and b/res/Berg.png differ diff --git a/res/Denio.png b/res/Denio.png new file mode 100644 index 0000000..00c043a Binary files /dev/null and b/res/Denio.png differ diff --git a/res/Duke.png b/res/Duke.png new file mode 100644 index 0000000..4310cef Binary files /dev/null and b/res/Duke.png differ diff --git a/res/Elsa.png b/res/Elsa.png new file mode 100644 index 0000000..b9d1341 Binary files /dev/null and b/res/Elsa.png differ diff --git a/res/Garfield.png b/res/Garfield.png new file mode 100644 index 0000000..c9bf3fc Binary files /dev/null and b/res/Garfield.png differ diff --git a/res/Mickey.png b/res/Mickey.png new file mode 100644 index 0000000..e278799 Binary files /dev/null and b/res/Mickey.png differ diff --git a/res/Pinguin.png b/res/Pinguin.png new file mode 100644 index 0000000..f8083b6 Binary files /dev/null and b/res/Pinguin.png differ diff --git a/res/Popey.png b/res/Popey.png new file mode 100644 index 0000000..9b986af Binary files /dev/null and b/res/Popey.png differ diff --git a/res/SDLogo.png b/res/SDLogo.png new file mode 100644 index 0000000..5dde4f0 Binary files /dev/null and b/res/SDLogo.png differ diff --git a/res/Sharpee.png b/res/Sharpee.png new file mode 100644 index 0000000..00b392e Binary files /dev/null and b/res/Sharpee.png differ diff --git a/res/Snoopy.png b/res/Snoopy.png new file mode 100644 index 0000000..38f8fac Binary files /dev/null and b/res/Snoopy.png differ diff --git a/res/Stag.png b/res/Stag.png new file mode 100644 index 0000000..e2b8dbd Binary files /dev/null and b/res/Stag.png differ diff --git a/res/Yoda.png b/res/Yoda.png new file mode 100644 index 0000000..74a4d8f Binary files /dev/null and b/res/Yoda.png differ diff --git a/res/addContactBlack.png b/res/addContactBlack.png new file mode 100644 index 0000000..984c6e3 Binary files /dev/null and b/res/addContactBlack.png differ diff --git a/res/addContactWhite.png b/res/addContactWhite.png new file mode 100644 index 0000000..fea4efc Binary files /dev/null and b/res/addContactWhite.png differ diff --git a/res/add_contact.png b/res/add_contact.png new file mode 100644 index 0000000..c00a055 Binary files /dev/null and b/res/add_contact.png differ diff --git a/res/add_contact.svg b/res/add_contact.svg new file mode 100644 index 0000000..30899b8 --- /dev/null +++ b/res/add_contact.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/res/addcontact.svg b/res/addcontact.svg new file mode 100644 index 0000000..1a71008 --- /dev/null +++ b/res/addcontact.svg @@ -0,0 +1,7 @@ + + + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon + + \ No newline at end of file diff --git a/res/css/blue.css b/res/css/Blue.css similarity index 100% rename from res/css/blue.css rename to res/css/Blue.css diff --git a/res/css/dark.css b/res/css/Dark.css similarity index 96% rename from res/css/dark.css rename to res/css/Dark.css index 7846b25..29dc683 100644 --- a/res/css/dark.css +++ b/res/css/Dark.css @@ -1,5 +1,5 @@ -QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QTableView::item, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow +QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QTableView::item, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow, ChatModel, requestDialog { background-color: #303335; color: #ffffff; @@ -25,7 +25,7 @@ QTabWidget QTabBar::tab:hover { background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #747577, stop: 1 #3E4244); color:#fff; border: 1px ridge #fff; -min-height: 20px +min-height: 20px; } QHeaderView { /* Table Header */ diff --git a/res/css/default.css b/res/css/Default.css similarity index 100% rename from res/css/default.css rename to res/css/Default.css diff --git a/res/css/light.css b/res/css/Light.css similarity index 100% rename from res/css/light.css rename to res/css/Light.css diff --git a/res/css/Midnight.css b/res/css/Midnight.css new file mode 100644 index 0000000..39347c1 --- /dev/null +++ b/res/css/Midnight.css @@ -0,0 +1,139 @@ +/* +Theme: Midnight Qt +Version: 1.0.2 +Reference: https://doc.qt.io/qt-5/stylesheet-reference.html + +Author: Charles Sharpe +Date: Apr. 23, 2020 +Website: https://www.csharpe.me +License: https://opensource.org/licenses/MIT +*/ + +QWidget, QMainWindow, QMenuBar, QMenu, QDialog, QTabWidget, QTableView, QTableView::item, QScrollArea, QGroupBox, QPlainTextEdit, QLineEdit, QLabel, MainWindow +{ +background-color: #111; +color: #fff; +} + +QPushButton { +padding: 10px 15px; +} + +QPushButton:hover { +background: #222; +} + +QLineEdit, QRadioButton::indicator::unchecked, QCheckBox::indicator::unchecked { +background: #222; +border: 1px solid #333; +border-radius: 3px; +} + +QLineEdit { +font-size: 12px; +} + +QLineEdit:focus { +border: 1px solid #9d8400; +} + +QWidget QLabel { +font-size: 11pt; +} + +QWidget QCheckBox { +font-weight: bold; +} + +QTabWidget QTabBar::tab { +min-height: 15px; +padding: 15px 25px; +border: 1px ridge #222; +left: 1px; /* Fix 1px alignment */ +background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #333, stop: 1 #111); +} + +QTabWidget QTabBar::tab:selected { +background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #555, stop: 1 #111); +color:#fff; +border: 1px ridge #222; +border-bottom: 0px; /* Overwrites border-bottom */ +} + +QTabWidget QTabBar::tab:hover { +background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #555, stop: 1 #111); +} + +QHeaderView { /* Table Header */ +background-color:#111; +} + +QHeaderView::section { /* Table Header Sections */ +qproperty-alignment:center; +background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #333, stop: 1 #111); +color:#fff; +min-height:25px; +font-weight:bold; +font-size:12px; +outline:0; +border:1px ridge #222; +padding: 2px 5px; +} + +QHeaderView::section:last { +border-right: 0px ridge #222; +} + +QScrollArea { +background:transparent; +border:0px; +} + +QTableView { /* Table - has to be selected as a class otherwise it throws off QCalendarWidget */ +background:#111; +} + +QTableView::item { /* Table Item */ +background-color:#111; +border:1px solid #222; +font-size:12px; +} + +QTableView::item:selected { /* Table Item Selected */ +background-color:#fff; +color:#000; +} + +QMenuBar { +background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #222, stop: 1 #111); +color: #fff; +} + +QMenuBar::item { +background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #222, stop: 1 #111); +color: #fff; +padding: 5px 7px; +margin: 0px; +} + +QMenuBar::item:selected { +background-color:qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.25, stop: 0 #333, stop: 1 #111); +} + +QMenu { +border:1px solid #222; +} + +QMenu::item { +padding: 7px 15px; +} + +QMenu::item:selected { +background: #222; +} + +QMenu::separator { + height: 1px; + margin: 3px 7px 3px 7px; /* space at ends of separator */ + background: #222; +} diff --git a/res/darkwing.png b/res/darkwing.png new file mode 100644 index 0000000..6fbe046 Binary files /dev/null and b/res/darkwing.png differ diff --git a/res/getAddrBlack.png b/res/getAddrBlack.png new file mode 100644 index 0000000..d90c9a5 Binary files /dev/null and b/res/getAddrBlack.png differ diff --git a/res/getAddrWhite.png b/res/getAddrWhite.png new file mode 100644 index 0000000..28b30fb Binary files /dev/null and b/res/getAddrWhite.png differ diff --git a/res/hushdark.png b/res/hushdark.png new file mode 100644 index 0000000..9aef6a2 Binary files /dev/null and b/res/hushdark.png differ diff --git a/res/loaderblack.gif b/res/loaderblack.gif new file mode 100644 index 0000000..764a3f8 Binary files /dev/null and b/res/loaderblack.gif differ diff --git a/res/loaderwhite.gif b/res/loaderwhite.gif new file mode 100644 index 0000000..6eede46 Binary files /dev/null and b/res/loaderwhite.gif differ diff --git a/res/lock.png b/res/lock.png new file mode 100644 index 0000000..b05dc49 Binary files /dev/null and b/res/lock.png differ diff --git a/res/lock.svg b/res/lock.svg new file mode 100644 index 0000000..8d584dd --- /dev/null +++ b/res/lock.svg @@ -0,0 +1,7 @@ + + + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon + + \ No newline at end of file diff --git a/res/lock_blue.svg b/res/lock_blue.svg new file mode 100644 index 0000000..752bc53 --- /dev/null +++ b/res/lock_blue.svg @@ -0,0 +1,7 @@ + + + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon + + \ No newline at end of file diff --git a/res/lock_green.png b/res/lock_green.png new file mode 100644 index 0000000..cd7c2ab Binary files /dev/null and b/res/lock_green.png differ diff --git a/res/lock_green.svg b/res/lock_green.svg new file mode 100644 index 0000000..1c5e4ef --- /dev/null +++ b/res/lock_green.svg @@ -0,0 +1,7 @@ + + + + + Svg Vector Icons : http://www.onlinewebfonts.com/icon + + \ No newline at end of file diff --git a/res/lock_orange.png b/res/lock_orange.png new file mode 100644 index 0000000..11a5c55 Binary files /dev/null and b/res/lock_orange.png differ diff --git a/res/message-icon.svg b/res/message-icon.svg new file mode 100644 index 0000000..fe61e44 --- /dev/null +++ b/res/message-icon.svg @@ -0,0 +1,9 @@ + + + + + + + diff --git a/res/notification.png b/res/notification.png new file mode 100644 index 0000000..f746609 Binary files /dev/null and b/res/notification.png differ diff --git a/res/notification.svg b/res/notification.svg new file mode 100644 index 0000000..3db8333 --- /dev/null +++ b/res/notification.svg @@ -0,0 +1,10 @@ + + + + + + + diff --git a/res/rahmen-message.png b/res/rahmen-message.png new file mode 100644 index 0000000..3b486a4 Binary files /dev/null and b/res/rahmen-message.png differ diff --git a/res/requestBlack.png b/res/requestBlack.png new file mode 100644 index 0000000..f46f9c2 Binary files /dev/null and b/res/requestBlack.png differ diff --git a/res/requestWhite.png b/res/requestWhite.png new file mode 100644 index 0000000..851a750 Binary files /dev/null and b/res/requestWhite.png differ diff --git a/res/sdlogo2.png b/res/sdlogo2.png new file mode 100644 index 0000000..fee4fab Binary files /dev/null and b/res/sdlogo2.png differ diff --git a/res/send-new-white.png b/res/send-new-white.png new file mode 100644 index 0000000..4c7461c Binary files /dev/null and b/res/send-new-white.png differ diff --git a/res/send-new.svg b/res/send-new.svg new file mode 100644 index 0000000..ce2df0a --- /dev/null +++ b/res/send-new.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/res/send-white.png b/res/send-white.png new file mode 100644 index 0000000..96c7621 Binary files /dev/null and b/res/send-white.png differ diff --git a/res/send.png b/res/send.png new file mode 100644 index 0000000..f0fce10 Binary files /dev/null and b/res/send.png differ diff --git a/res/send.svg b/res/send.svg new file mode 100644 index 0000000..874d686 --- /dev/null +++ b/res/send.svg @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/res/sendBlack.png b/res/sendBlack.png new file mode 100644 index 0000000..62d9d1e Binary files /dev/null and b/res/sendBlack.png differ diff --git a/res/silentdragonlite-animated-dark.gif b/res/silentdragonlite-animated-dark.gif new file mode 100644 index 0000000..10999e1 Binary files /dev/null and b/res/silentdragonlite-animated-dark.gif differ diff --git a/res/silentdragonlite-animated-startup-dark.gif b/res/silentdragonlite-animated-startup-dark.gif new file mode 100644 index 0000000..1e72373 Binary files /dev/null and b/res/silentdragonlite-animated-startup-dark.gif differ diff --git a/res/silentdragonlite-animated-startup.gif b/res/silentdragonlite-animated-startup.gif new file mode 100644 index 0000000..fa1edc6 Binary files /dev/null and b/res/silentdragonlite-animated-startup.gif differ diff --git a/res/silentdragonlite-animated.gif b/res/silentdragonlite-animated.gif new file mode 100644 index 0000000..c7c521c Binary files /dev/null and b/res/silentdragonlite-animated.gif differ diff --git a/res/unknownBlack.png b/res/unknownBlack.png new file mode 100644 index 0000000..faf65e0 Binary files /dev/null and b/res/unknownBlack.png differ diff --git a/res/unknownWhite.png b/res/unknownWhite.png new file mode 100644 index 0000000..7853e1e Binary files /dev/null and b/res/unknownWhite.png differ diff --git a/res/unkownBlack.png b/res/unkownBlack.png new file mode 100644 index 0000000..faf65e0 Binary files /dev/null and b/res/unkownBlack.png differ diff --git a/res/unlocked.png b/res/unlocked.png new file mode 100644 index 0000000..d6e6c90 Binary files /dev/null and b/res/unlocked.png differ diff --git a/silentdragon-lite.pro b/silentdragon-lite.pro index 6e9d880..57144f4 100644 --- a/silentdragon-lite.pro +++ b/silentdragon-lite.pro @@ -13,6 +13,7 @@ PRECOMPILED_HEADER = src/precompiled.h QT += widgets QT += websockets + TARGET = SilentDragonLite TEMPLATE = app @@ -33,6 +34,7 @@ mac: LIBS+= -Wl,-bind_at_load RESOURCES = application.qrc + MOC_DIR = bin OBJECTS_DIR = bin UI_DIR = src @@ -65,7 +67,24 @@ SOURCES += \ src/datamodel.cpp \ src/controller.cpp \ src/liteinterface.cpp \ - src/camount.cpp + src/camount.cpp \ + src/chatbubbleme.cpp \ + src/chatbubblepartner.cpp \ + src/chatmodel.cpp \ + src/contactmodel.cpp \ + src/DataStore/DataStore.cpp \ + src/DataStore/ChatDataStore.cpp \ + src/DataStore/SietchDataStore.cpp \ + src/DataStore/ContactDataStore.cpp \ + src/Model/ChatItem.cpp \ + src/Model/ContactRequestChatItem.cpp \ + src/Model/ContactItem.cpp \ + src/Model/ContactRequest.cpp \ + src/Chat/Helper/ChatIDGenerator.cpp \ + src/Chat/Chat.cpp \ + src/FileSystem/FileSystem.cpp \ + src/Crypto/FileEncryption.cpp \ + src/Crypto/passwd.cpp HEADERS += \ src/firsttimewizard.h \ @@ -94,10 +113,17 @@ HEADERS += \ src/controller.h \ src/liteinterface.h \ src/camount.h \ - lib/silentdragonlitelib.h + lib/silentdragonlitelib.h \ + src/chatbubbleme.h \ + src/chatbubblepartner.h \ + src/chatmodel.h \ + src/contactmodel.h FORMS += \ + src/contactrequest.ui \ + src/deposithush.ui \ src/encryption.ui \ + src/hushrequest.ui \ src/mainwindow.ui \ src/migration.ui \ src/newseed.ui \ @@ -109,15 +135,21 @@ FORMS += \ src/confirm.ui \ src/privkey.ui \ src/memodialog.ui \ + src/startupencryption.ui \ src/viewalladdresses.ui \ src/connection.ui \ src/addressbook.ui \ src/mobileappconnector.ui \ src/createhushconfdialog.ui \ src/recurringdialog.ui \ + src/requestContactDialog.ui \ src/newrecurring.ui \ src/requestdialog.ui \ - src/recurringmultiple.ui + src/removeencryption.ui \ + src/recurringmultiple.ui \ + src/chatbubbleme.ui \ + src/deposithush.ui \ + src/chatbubblepartner.ui TRANSLATIONS = res/silentdragonlite_es.ts \ diff --git a/src/Chat/Chat.cpp b/src/Chat/Chat.cpp new file mode 100644 index 0000000..672808c --- /dev/null +++ b/src/Chat/Chat.cpp @@ -0,0 +1,101 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#include "Chat.h" +#include "../addressbook.h" +#include "../DataStore/DataStore.h" +Chat::Chat() {} + +ChatMemoEdit::ChatMemoEdit(QWidget* parent) : QTextEdit(parent) { + QObject::connect(this, &QTextEdit::textChanged, this, &ChatMemoEdit::updateDisplayChat); +} + +void ChatMemoEdit::updateDisplayChat() { + QString txt = this->toPlainText(); + if (lenDisplayLabelchat) + lenDisplayLabelchat->setText(QString::number(txt.toUtf8().size()) + "/" + QString::number(maxlenchat)); + + if (txt.toUtf8().size() <= maxlenchat) { + // Everything is fine + if (sendChatButton) + sendChatButton->setEnabled(true); + + if (lenDisplayLabelchat) + lenDisplayLabelchat->setStyleSheet(""); + } + else { + // Overweight + if (sendChatButton) + sendChatButton->setEnabled(false); + + if (lenDisplayLabelchat) + lenDisplayLabelchat->setStyleSheet("color: red;"); + } +} + +void ChatMemoEdit::setMaxLenChat(int len) { + this->maxlenchat = len; + updateDisplayChat(); +} + +void ChatMemoEdit::SetSendChatButton(QPushButton* button) { + this->sendChatButton = button; +} + +void ChatMemoEdit::setLenDisplayLabelChat(QLabel* label) { + this->lenDisplayLabelchat = label; +} + +void Chat::renderChatBox(Ui::MainWindow *ui, QListView *view, QLabel *label) +{ + + QStandardItemModel *chat = new QStandardItemModel(); + DataStore::getChatDataStore()->dump(); // test to see if the chat items in datastore are correctly dumped to json + for (auto &p : AddressBook::getInstance()->getAllAddressLabels()) + { + for (auto &c : DataStore::getChatDataStore()->getAllMemos()) + { + + if ( + (p.getName() == ui->contactNameMemo->text().trimmed()) && + (p.getPartnerAddress() == c.second.getAddress()) && + (c.second.isOutgoing() == true)) + { + + QStandardItem *Items = new QStandardItem(c.second.toChatLine()); + + Items->setData(OUTGOING, Qt::UserRole + 1); + chat->appendRow(Items); + ui->listChat->setModel(chat); + + + } + else + { + ui->listChat->setModel(chat); + } + + if ( + (p.getName() == ui->contactNameMemo->text().trimmed()) && + (p.getMyAddress() == c.second.getAddress()) && + (c.second.isOutgoing() == false) && + (c.second.getCid() == p.getCid()) + ) + { + QStandardItem *Items1 = new QStandardItem(c.second.toChatLine()); + Items1->setData(INCOMING, Qt::UserRole + 1); + chat->appendRow(Items1); + ui->listChat->setModel(chat); + + + } + else + { + + ui->listChat->setModel(chat); + } + } + } +} + + \ No newline at end of file diff --git a/src/Chat/Chat.h b/src/Chat/Chat.h new file mode 100644 index 0000000..6d8b01a --- /dev/null +++ b/src/Chat/Chat.h @@ -0,0 +1,34 @@ +#ifndef CHAT_H +#define CHAT_H + +#include +#include +#include +#include +#include +#include +#include +#include "precompiled.h" +#include "mainwindow.h" +#include "controller.h" +#include "settings.h" +#include "camount.h" + +#include "../Model/ChatItem.h" + + +class Chat // Chat Controller +{ + private: + QTableView* parent; + Ui::MainWindow* ui; + MainWindow* main; + std::map cidMap; + std::map requestZaddrMap; + public: + Chat(); + void renderChatBox(Ui::MainWindow* ui, QListView *view, QLabel *label); // action + +}; + +#endif \ No newline at end of file diff --git a/src/Chat/Helper/ChatDelegator.h b/src/Chat/Helper/ChatDelegator.h new file mode 100644 index 0000000..e4474a8 --- /dev/null +++ b/src/Chat/Helper/ChatDelegator.h @@ -0,0 +1,193 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#ifndef CHATDELEGATOR_H +#define CHATDELEGATOR_H + +#include +#include +#include +#include + +enum RenderType +{ + OUTGOING=0, + INCOMING=1, + INDATE=2, + OUTDATE=3 +}; + +class ListViewDelegate : public QAbstractItemDelegate +{ + int d_radius; + int d_toppadding; + int d_bottompadding; + int d_leftpadding; + int d_rightpadding; + int d_verticalmargin; + int d_horizontalmargin; + int d_pointerwidth; + int d_pointerheight; + float d_widthfraction; + public: + inline ListViewDelegate(QObject *parent = nullptr); + + protected: + inline void paint(QPainter *painter, QStyleOptionViewItem const &option, QModelIndex const &index) const; + inline QSize sizeHint(QStyleOptionViewItem const &option, QModelIndex const &index) const; +}; + +inline ListViewDelegate::ListViewDelegate(QObject *parent): QAbstractItemDelegate(parent), d_radius(5), d_toppadding(5), d_bottompadding(3), d_leftpadding(5), d_rightpadding(5), d_verticalmargin(5), d_horizontalmargin(10), d_pointerwidth(4), d_pointerheight(17), d_widthfraction(.6) +{ + +} + +inline void ListViewDelegate::paint(QPainter *painter, QStyleOptionViewItem const &option, QModelIndex const &index) const +{ + QTextDocument bodydoc; + QTextOption textOption(bodydoc.defaultTextOption()); + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + bodydoc.setDefaultTextOption(textOption); + bodydoc.setDefaultFont(QFont("Roboto", 12)); + QString bodytext(index.data(Qt::DisplayRole).toString()); + bodydoc.setHtml(bodytext); + qreal contentswidth = option.rect.width() * d_widthfraction - d_horizontalmargin - d_pointerwidth - d_leftpadding - d_rightpadding; + bodydoc.setTextWidth(contentswidth); + qreal bodyheight = bodydoc.size().height(); + int outgoing = index.data(Qt::UserRole + 1).toInt(); + int outdate = index.data(Qt::UserRole + 1).toInt(); + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + // uncomment to see the area provided to paint this item + + //painter->drawRect(option.rect); + + painter->translate(option.rect.left() + d_horizontalmargin, option.rect.top() + ((index.row() == 0) ? d_verticalmargin : 0)); + + QColor bgcolor("#ffffff"); + switch(outgoing) + { + case INDATE: + bgcolor = "transparent"; + break; + + case OUTDATE: + bgcolor = "transparent"; + break; + + case OUTGOING: + bgcolor = "#f8f9fa"; + break; + + default: + case INCOMING: + bgcolor = "#535353"; + break; + } + + // create chat bubble + QPainterPath pointie; + + // left bottom + pointie.moveTo(0, bodyheight + d_toppadding + d_bottompadding); + + // right bottom + pointie.lineTo(0 + contentswidth + d_pointerwidth + d_leftpadding + d_rightpadding - d_radius, + bodyheight + d_toppadding + d_bottompadding); + pointie.arcTo(0 + contentswidth + d_pointerwidth + d_leftpadding + d_rightpadding - 2 * d_radius, + bodyheight + d_toppadding + d_bottompadding - 2 * d_radius, + 2 * d_radius, 2 * d_radius, 270, 90); + + // right top + pointie.lineTo(0 + contentswidth + d_pointerwidth + d_leftpadding + d_rightpadding, 0 + d_radius); + pointie.arcTo(0 + contentswidth + d_pointerwidth + d_leftpadding + d_rightpadding - 2 * d_radius, 0, + 2 * d_radius, 2 * d_radius, 0, 90); + + // left top + pointie.lineTo(0 + d_pointerwidth + d_radius, 0); + pointie.arcTo(0 + d_pointerwidth, 0, 2 * d_radius, 2 * d_radius, 90, 90); + + // left bottom almost (here is the pointie) + pointie.lineTo(0 + d_pointerwidth, bodyheight + d_toppadding + d_bottompadding - d_pointerheight); + pointie.closeSubpath(); + + // rotate bubble for outgoing messages + if ((outgoing == OUTGOING) || (outdate == OUTDATE)) + { + painter->translate(option.rect.width() - pointie.boundingRect().width() - d_horizontalmargin - d_pointerwidth, 0); + painter->translate(pointie.boundingRect().center()); + painter->rotate(180); + painter->translate(-pointie.boundingRect().center()); + } + + // now paint it! + painter->setPen(QPen(bgcolor)); + painter->drawPath(pointie); + painter->fillPath(pointie, QBrush(bgcolor)); + + // rotate back or painter is going to paint the text rotated... + if ((outgoing == OUTGOING) || (outdate == OUTDATE)) + { + painter->translate(pointie.boundingRect().center()); + painter->rotate(-180); + painter->translate(-pointie.boundingRect().center()); + } + + // set text color used to draw message body + QAbstractTextDocumentLayout::PaintContext ctx; + switch(outgoing) + { + case INDATE: + ctx.palette.setColor(QPalette::Text, QColor("Black")); + break; + + case OUTDATE: + ctx.palette.setColor(QPalette::Text, QColor("Black")); + break; + + case OUTGOING: + ctx.palette.setColor(QPalette::Text, QColor("Black")); + break; + + default: + case INCOMING: + ctx.palette.setColor(QPalette::Text, QColor("whitesmoke")); + break; + } + + + // draw body text + painter->translate((outgoing == OUTGOING ? 0 : d_pointerwidth) + d_leftpadding, 0); + painter->translate((outdate == OUTDATE ? 0 : d_pointerwidth) + d_leftpadding, 0); + bodydoc.documentLayout()->draw(painter, ctx); + + painter->restore(); +} + +inline QSize ListViewDelegate::sizeHint(QStyleOptionViewItem const &option, QModelIndex const &index) const +{ + QTextDocument bodydoc; + QTextOption textOption(bodydoc.defaultTextOption()); + textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + bodydoc.setDefaultTextOption(textOption); + bodydoc.setDefaultFont(QFont("Roboto", 12)); + QString bodytext(index.data(Qt::DisplayRole).toString()); + bodydoc.setHtml(bodytext); + + // the width of the contents are the (a fraction of the window width) minus (margins + padding + width of the bubble's tail) + qreal contentswidth = option.rect.width() * d_widthfraction - d_horizontalmargin - d_pointerwidth - d_leftpadding - d_rightpadding; + + // set this available width on the text document + bodydoc.setTextWidth(contentswidth); + + QSize size(bodydoc.idealWidth() + d_horizontalmargin + d_pointerwidth + d_leftpadding + d_rightpadding, + bodydoc.size().height() + d_bottompadding + d_toppadding + d_verticalmargin + 1); // I dont remember why +1, haha, might not be necessary + + if (index.row() == 0) // have extra margin at top of first item + size += QSize(0, d_verticalmargin); + + return size; +} + +#endif \ No newline at end of file diff --git a/src/Chat/Helper/ChatIDGenerator.cpp b/src/Chat/Helper/ChatIDGenerator.cpp new file mode 100644 index 0000000..2acea1b --- /dev/null +++ b/src/Chat/Helper/ChatIDGenerator.cpp @@ -0,0 +1,28 @@ +#include "ChatIDGenerator.h" + +ChatIDGenerator* ChatIDGenerator::getInstance() +{ + if(ChatIDGenerator::instance == nullptr) + ChatIDGenerator::instance = new ChatIDGenerator(); + + return ChatIDGenerator::instance; +} + +QString ChatIDGenerator::generateID(ChatItem item) +{ + QString key = QString::number(item.getTimestamp()) + QString("-"); + key += QString( + QCryptographicHash::hash( + QString( + QString::number(item.getTimestamp()) + + item.getAddress() + + item.getContact() + + item.getMemo() + ).toUtf8(), + QCryptographicHash::Md5 + ).toHex() + ); + return key; +} + +ChatIDGenerator* ChatIDGenerator::instance = nullptr; \ No newline at end of file diff --git a/src/Chat/Helper/ChatIDGenerator.h b/src/Chat/Helper/ChatIDGenerator.h new file mode 100644 index 0000000..d9f6e4a --- /dev/null +++ b/src/Chat/Helper/ChatIDGenerator.h @@ -0,0 +1,18 @@ +#ifndef CHATIDGENERATOR_H +#define CHATIDGENERATOR_H + +#include +#include +#include "../../Model/ChatItem.h" + +class ChatIDGenerator +{ + private: + static ChatIDGenerator* instance; + + public: + static ChatIDGenerator* getInstance(); + QString generateID(ChatItem item); +}; + +#endif \ No newline at end of file diff --git a/src/Crypto/FileEncryption.cpp b/src/Crypto/FileEncryption.cpp new file mode 100644 index 0000000..fae9bbc --- /dev/null +++ b/src/Crypto/FileEncryption.cpp @@ -0,0 +1,114 @@ +#include "FileEncryption.h" + +void FileEncryption::showConfig() +{ + qInfo() << FILEENCRYPTION_CHUNK_SIZE; +} + +int FileEncryption::encrypt(QString target_file, QString source_file, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) +{ + unsigned char plain_data[FILEENCRYPTION_CHUNK_SIZE]; + unsigned char cipher_data[FILEENCRYPTION_CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; + unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; + crypto_secretstream_xchacha20poly1305_state state; + FILE *target, *source; + unsigned long long cipher_len; + size_t rlen; + int eof; + unsigned char tag; + + if(!FileEncryption::exists(source_file.toStdString())) + { + qDebug() << "File not exits" << source_file; + return -1; + } + + source = fopen(source_file.toStdString().c_str(), "rb"); + target = fopen(target_file.toStdString().c_str(), "wb"); + crypto_secretstream_xchacha20poly1305_init_push(&state, header, key); + fwrite(header, 1, sizeof header, target); + do + { + rlen = fread(plain_data, 1, sizeof plain_data, source); + eof = feof(source); + tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; + crypto_secretstream_xchacha20poly1305_push( + &state, + cipher_data, + &cipher_len, + plain_data, + rlen, + NULL, + 0, + tag + ); + + fwrite(cipher_data, 1, (size_t) cipher_len, target); + } + while (! eof); + + fclose(target); + fclose(source); + return 0; +} + +int FileEncryption::decrypt(QString target_file, QString source_file, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) +{ + unsigned char buf_in[FILEENCRYPTION_CHUNK_SIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; + unsigned char buf_out[FILEENCRYPTION_CHUNK_SIZE]; + unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; + crypto_secretstream_xchacha20poly1305_state st; + FILE *fp_t, *fp_s; + unsigned long long out_len; + size_t rlen; + int eof; + int ret = -1; + unsigned char tag; + + if(!FileEncryption::exists(source_file.toStdString())) + { + qDebug() << "File not exits" << source_file; + return -1; + } + + fp_s = fopen(source_file.toStdString().c_str(), "rb"); + fp_t = fopen(target_file.toStdString().c_str(), "wb"); + fread(header, 1, sizeof header, fp_s); + if (crypto_secretstream_xchacha20poly1305_init_pull(&st, header, key) != 0) + { + goto ret; /* incomplete header */ + } + + do + { + rlen = fread(buf_in, 1, sizeof buf_in, fp_s); + eof = feof(fp_s); + if (crypto_secretstream_xchacha20poly1305_pull( + &st, + buf_out, + &out_len, + &tag, + buf_in, + rlen, + NULL, + 0 + ) != 0) + { + goto ret; /* corrupted chunk */ + } + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && ! eof) + { + goto ret; /* premature end (end of file reached before the end of the stream) */ + } + + fwrite(buf_out, 1, (size_t) out_len, fp_t); + } + while (! eof); + ret = 0; + +ret: + fclose(fp_t); + fclose(fp_s); + return ret; +} \ No newline at end of file diff --git a/src/Crypto/FileEncryption.h b/src/Crypto/FileEncryption.h new file mode 100644 index 0000000..c7a09d5 --- /dev/null +++ b/src/Crypto/FileEncryption.h @@ -0,0 +1,24 @@ +#ifndef FILEENCRYPTION_H +#define FILEENCRYPTION_H +#include +#include +#include +#include + +#define FILEENCRYPTION_CHUNK_SIZE 4096 + +class FileEncryption +{ + private: + inline static bool exists (const std::string& name) { + std::ifstream f(name.c_str()); + return f.good(); + } + public: + static void showConfig(); + static int encrypt(QString target_file, QString source_file, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); + static int decrypt(QString target_file, QString source_file, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); +}; + + +#endif \ No newline at end of file diff --git a/src/Crypto/passwd.cpp b/src/Crypto/passwd.cpp new file mode 100644 index 0000000..98996b0 --- /dev/null +++ b/src/Crypto/passwd.cpp @@ -0,0 +1,60 @@ +#include "passwd.h" + +void PASSWD::show_hex_buff(unsigned char buf[]) +{ + int i; + for (uint8_t i=0; i < crypto_secretstream_xchacha20poly1305_KEYBYTES; i++) + printf("%02X ", buf[i]); + printf("\n"); +} + +const unsigned char* PASSWD::key(QString password) +{ + + int length = password.length(); + + char *sequence = NULL; + sequence = new char[length+1]; + strncpy(sequence, password.toLocal8Bit(), length +1); + + #define MESSAGE ((const unsigned char *) sequence) + #define MESSAGE_LEN length + + unsigned char hash[crypto_secretstream_xchacha20poly1305_KEYBYTES]; + + crypto_hash_sha256(hash, MESSAGE, MESSAGE_LEN); + + qDebug()<<"Generating SaltHash from password: " < +#include +#include + +class PASSWD +{ + public: + static void show_hex_buff(unsigned char buf[]); + static const unsigned char* key(QString); +}; + +#endif \ No newline at end of file diff --git a/src/DataStore/ChatDataStore.cpp b/src/DataStore/ChatDataStore.cpp new file mode 100644 index 0000000..82f9db3 --- /dev/null +++ b/src/DataStore/ChatDataStore.cpp @@ -0,0 +1,127 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#include "ChatDataStore.h" +#include "addressbook.h" +#include "chatmodel.h" + + +ChatDataStore* ChatDataStore::getInstance() +{ + if(!ChatDataStore::instanced) + { + ChatDataStore::instanced = true; + ChatDataStore::instance = new ChatDataStore(); + } + + return ChatDataStore::instance; +} + +void ChatDataStore::clear() +{ + this->data.clear(); +} + + +void ChatDataStore::setData(QString key, ChatItem value) +{ + this->data[key] = value; +} + +ChatItem ChatDataStore::getData(QString key) +{ + return this->data[key]; +} + +QString ChatDataStore::getPassword() +{ + + return _password; +} + +void ChatDataStore::setPassword(QString password) +{ + + _password = password; +} + +QString ChatDataStore::dump() +{ + json chats; + chats["count"] = this->data.size(); + json j = {}; + for (auto &c: this->data) + { + j.push_back(c.second.toJson()); + } + chats["chatitems"] = j; + return QString::fromStdString(chats.dump()); +} + +std::map ChatDataStore::getAllRawChatItems() +{ + return this->data; +} + +std::map ChatDataStore::getAllNewContactRequests() +{ + std::map filteredItems; + + for(auto &c: this->data) + { + if ( + (c.second.isOutgoing() == false) && + (c.second.getType() == "Cont") && + (c.second.isContact() == false) && + (c.second.getMemo().startsWith("{")) + + ) + + { + filteredItems[c.first] = c.second; + } + } + return filteredItems; +} + +std::map ChatDataStore::getAllOldContactRequests() +{ + std::map filteredItems; + + for(auto &c: this->data) + { + if ( + (c.second.isOutgoing() == false) && + (c.second.getType() == "Cont") && + (c.second.isContact() == true) && + (c.second.getMemo().startsWith("{")) + ) + { + filteredItems[c.first] = c.second; + } + } + return filteredItems; +} + +std::map ChatDataStore::getAllMemos() +{ + std::map filteredItems; + for(auto &c: this->data) + { + if ( + + (c.second.getMemo().startsWith("{") == false) && + (c.second.getMemo().isEmpty() == false) + + + ) + { + filteredItems[c.first] = c.second; + } + } + return filteredItems; +} + + +ChatDataStore* ChatDataStore::instance = nullptr; +bool ChatDataStore::instanced = false; \ No newline at end of file diff --git a/src/DataStore/ChatDataStore.h b/src/DataStore/ChatDataStore.h new file mode 100644 index 0000000..c1233b6 --- /dev/null +++ b/src/DataStore/ChatDataStore.h @@ -0,0 +1,44 @@ +#ifndef CHATDATASTORE_H +#define CHATDATASTORE_H +#include "../chatmodel.h" +using json = nlohmann::json; + +class ChatDataStore +{ + private: + static bool instanced; + static ChatDataStore* instance; + Ui::MainWindow* ui; + MainWindow* main; + std::map data; + ChatDataStore() + { + + } + + public: + static ChatDataStore* getInstance(); + void clear(); + void setData(QString key, ChatItem value); + ChatItem getData(QString key); + std::map getAllRawChatItems(); + std::map getAllNewContactRequests(); + std::map getAllOldContactRequests(); + std::map getAllMemos(); + QString getPassword(); + + void setPassword(QString Password); + QString _password; + + QString dump(); + + ~ChatDataStore() + { + ChatDataStore::instanced = false; + ChatDataStore::instance = nullptr; + } +}; + + + +#endif \ No newline at end of file diff --git a/src/DataStore/ContactDataStore.cpp b/src/DataStore/ContactDataStore.cpp new file mode 100644 index 0000000..e853770 --- /dev/null +++ b/src/DataStore/ContactDataStore.cpp @@ -0,0 +1,50 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#include "ContactDataStore.h" +#include + +ContactDataStore* ContactDataStore::getInstance() +{ + if(!ContactDataStore::instanced) + { + ContactDataStore::instanced = true; + ContactDataStore::instance = new ContactDataStore(); + } + + return ContactDataStore::instance; +} + +void ContactDataStore::clear() +{ + this->data.clear(); +} + + +void ContactDataStore::setData(QString key, ContactItem value) +{ + this->data[key] = value; +} + +ContactItem ContactDataStore::getData(QString key) +{ + return this->data[key]; +} + +QString ContactDataStore::dump() +{ + json contacts; + contacts["count"] = this->data.size(); + json j = {}; + for (auto &c: this->data) + { + qDebug() << c.second.toQTString(); + c.second.toJson(); + j.push_back(c.second.toJson()); + } + contacts["contacts"] = j; + return QString::fromStdString(contacts.dump(4)); +} + +ContactDataStore* ContactDataStore::instance = nullptr; +bool ContactDataStore::instanced = false; \ No newline at end of file diff --git a/src/DataStore/ContactDataStore.h b/src/DataStore/ContactDataStore.h new file mode 100644 index 0000000..009657e --- /dev/null +++ b/src/DataStore/ContactDataStore.h @@ -0,0 +1,34 @@ +#ifndef CONTACTDATASTORE_H +#define CONTACTDATASTORE_H +#include "../Model/ContactItem.h" +#include +using json = nlohmann::json; + +class ContactDataStore +{ + private: + static bool instanced; + static ContactDataStore* instance; + std::map data; + ContactDataStore() + { + + } + + public: + static ContactDataStore* getInstance(); + void clear(); + void setData(QString key, ContactItem value); + ContactItem getData(QString key); + QString dump(); + + ~ContactDataStore() + { + ContactDataStore::instanced = false; + ContactDataStore::instance = nullptr; + } +}; + + + +#endif \ No newline at end of file diff --git a/src/DataStore.h b/src/DataStore/DataStore-deprecated.h similarity index 100% rename from src/DataStore.h rename to src/DataStore/DataStore-deprecated.h diff --git a/src/DataStore/DataStore.cpp b/src/DataStore/DataStore.cpp new file mode 100644 index 0000000..ae85ed6 --- /dev/null +++ b/src/DataStore/DataStore.cpp @@ -0,0 +1,16 @@ +#include "DataStore.h" + +SietchDataStore* DataStore::getSietchDataStore() +{ + return SietchDataStore::getInstance(); +} + +ChatDataStore* DataStore::getChatDataStore() +{ + return ChatDataStore::getInstance(); +} + +ContactDataStore* DataStore::getContactDataStore() +{ + return ContactDataStore::getInstance(); +} \ No newline at end of file diff --git a/src/DataStore/DataStore.h b/src/DataStore/DataStore.h new file mode 100644 index 0000000..9fa42d3 --- /dev/null +++ b/src/DataStore/DataStore.h @@ -0,0 +1,16 @@ +#ifndef DATASTORE_H +#define DATASTORE_H + +#include "SietchDataStore.h" +#include "ChatDataStore.h" +#include "ContactDataStore.h" + +class DataStore +{ + public: + static SietchDataStore* getSietchDataStore(); + static ChatDataStore* getChatDataStore(); + static ContactDataStore* getContactDataStore(); +}; + +#endif \ No newline at end of file diff --git a/src/DataStore/SietchDataStore.cpp b/src/DataStore/SietchDataStore.cpp new file mode 100644 index 0000000..140ab3f --- /dev/null +++ b/src/DataStore/SietchDataStore.cpp @@ -0,0 +1,35 @@ +#include "SietchDataStore.h" + +SietchDataStore* SietchDataStore::getInstance() +{ + if(!SietchDataStore::instanced) + { + SietchDataStore::instanced = true; + SietchDataStore::instance = new SietchDataStore(); + } + + return SietchDataStore::instance; +} + +void SietchDataStore::clear() +{ + this->data.clear(); +} + +void SietchDataStore::setData(QString key, QString value) +{ + this->data[key] = value; +} + +QString SietchDataStore::getData(QString key) +{ + return this->data[key]; +} + +QString SietchDataStore::dump() +{ + return ""; +} + +SietchDataStore* SietchDataStore::instance = nullptr; +bool SietchDataStore::instanced = false; \ No newline at end of file diff --git a/src/DataStore/SietchDataStore.h b/src/DataStore/SietchDataStore.h new file mode 100644 index 0000000..5b5b498 --- /dev/null +++ b/src/DataStore/SietchDataStore.h @@ -0,0 +1,31 @@ +#ifndef SIETCHDATASTORE_H +#define SIETCHDATASTORE_H + +using json = nlohmann::json; + +class SietchDataStore +{ + private: + static bool instanced; + static SietchDataStore* instance; + std::map data; + SietchDataStore() + { + + } + + public: + static SietchDataStore* getInstance(); + void clear(); + void setData(QString key, QString value); + QString getData(QString key); + QString dump(); + + ~SietchDataStore() + { + SietchDataStore::instanced = false; + SietchDataStore::instance = nullptr; + } +}; + +#endif \ No newline at end of file diff --git a/src/FileSystem/FileSystem.cpp b/src/FileSystem/FileSystem.cpp new file mode 100644 index 0000000..055ca88 --- /dev/null +++ b/src/FileSystem/FileSystem.cpp @@ -0,0 +1,127 @@ +#include "FileSystem.h" + +#include +#include + +FileSystem::FileSystem() +{ +} + +FileSystem* FileSystem::getInstance() +{ + if(!FileSystem::instanced) + { + FileSystem::instanced = true; + FileSystem::instance = new FileSystem(); + FileEncryption::showConfig(); + } + + return FileSystem::instance; +} + +/*QList FileSystem::readContacts(QString file) +{ + //return this->readContactsOldFormat(file); //will be called if addresses are in the old dat-format + + QFile _file(file); + if (_file.exists()) + { + std::ifstream f(file.toStdString().c_str(), std::ios::binary); + if(f.is_open()) + { + std::vector buffer(std::istreambuf_iterator(f), {}); + //todo covert to string to use is as json to feed the data store in addressbook + } + + f.close(); + } + else + { + qInfo() << file << "not exist"; + } +} + +void FileSystem::writeContacts(QString file, QString data) +{ + qDebug() << data; + QFile _file(file); + if (_file.exists()) + { + std::ofstream f(file.toStdString().c_str()); + if(f.is_open()) + { + //ENCRYPT HERE + + f << data.toStdString(); + } + + f.close(); + } + else + { + qInfo() << file << "not exist"; + } +} + +void FileSystem::writeContactsOldFormat(QString file, QList contacts) +{ + QFile _file(file); + _file.open(QIODevice::ReadWrite | QIODevice::Truncate); + QDataStream out(&_file); // we will serialize the data into the file + QList> _contacts; + for(auto &item: contacts) + { + QList c; + c.push_back(item.getName()); + c.push_back(item.getPartnerAddress()); + c.push_back(item.getMyAddress()); + c.push_back(item.getCid()); + c.push_back(item.getAvatar()); + _contacts.push_back(c); + } + out << QString("v0") << _contacts; + _file.close(); +} + +QList FileSystem::readContactsOldFormat(QString file) +{ + QList contacts; + QFile _file(file); + if (_file.exists()) + { + contacts.clear(); + _file.open(QIODevice::ReadOnly); + QDataStream in(&_file); // read the data serialized from the file + QString version; + in >> version; + qDebug() << "Read " << version << " Hush contacts from disk..."; + qDebug() << "Detected old addressbook format"; + QList> stuff; + in >> stuff; + //qDebug() << "Stuff: " << stuff; + for (int i=0; i < stuff.size(); i++) + { + ContactItem contact = ContactItem(stuff[i][0],stuff[i][1], stuff[i][2], stuff[i][3],stuff[i][4]); + contacts.push_back(contact); + + } + + _file.close(); + } + else + { + qDebug() << "No Hush contacts found on disk!"; + } + + return contacts; +} + +FileSystem::~FileSystem() +{ + this->instance = nullptr; + this->instanced = false; + delete this->instance; +}*/ + +FileSystem *FileSystem::instance = nullptr; +bool FileSystem::instanced = false; \ No newline at end of file diff --git a/src/FileSystem/FileSystem.h b/src/FileSystem/FileSystem.h new file mode 100644 index 0000000..34fc5fc --- /dev/null +++ b/src/FileSystem/FileSystem.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 +#ifndef FILESYSTEM_H +#define FILESYSTEM_H + +#include +#include +#include "../Model/ContactItem.h" +#include "../Crypto/FileEncryption.h" +#include +using json = nlohmann::json; +class FileSystem +{ + private: + static bool instanced; + static FileSystem* instance; + FileSystem(); + + public: + static FileSystem* getInstance(); + QList readContacts(QString file); + void writeContacts(QString file, QString data); + + //converter + QList readContactsOldFormat(QString file); + void writeContactsOldFormat(QString file, QList contacts); + ~FileSystem(); + +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogContext.h b/src/Logger/LogContext.h new file mode 100644 index 0000000..bd8e988 --- /dev/null +++ b/src/Logger/LogContext.h @@ -0,0 +1,11 @@ +#ifndef LOGCONTEXT_H +#define LOGCONTEXT_H + +#include +class LogContext +{ + public: + virtual void log(std::string message) {}; +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogCrtitical.h b/src/Logger/LogCrtitical.h new file mode 100644 index 0000000..cf08849 --- /dev/null +++ b/src/Logger/LogCrtitical.h @@ -0,0 +1,18 @@ +#ifndef LOGCRITICAL_H +#define LOGCRITICAL_H + +#include "LogType.h" +#include "LogStrategy.h" +#include "LogWriter.h" + +class LogCritical : public LogStrategy +{ + public: + void log(std::string message) + { + LogWriter* lw = LogWriter::getInstance(); + lw->write(LogType::CRITICAL, message); + } +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogDebug.h b/src/Logger/LogDebug.h new file mode 100644 index 0000000..e1dd644 --- /dev/null +++ b/src/Logger/LogDebug.h @@ -0,0 +1,18 @@ +#ifndef LOGDEBUG_H +#define LOGDEBUG_H + +#include "LogType.h" +#include "LogStrategy.h" +#include "LogWriter.h" + +class LogDebug : public LogStrategy +{ + public: + void log(std::string message) + { + LogWriter* lw = LogWriter::getInstance(); + lw->write(LogType::DEBUG, message); + } +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogError.h b/src/Logger/LogError.h new file mode 100644 index 0000000..34399c8 --- /dev/null +++ b/src/Logger/LogError.h @@ -0,0 +1,18 @@ +#ifndef LOGERROR_H +#define LOGERROR_H + +#include "LogType.h" +#include "LogStrategy.h" +#include "LogWriter.h" + +class LogError : public LogStrategy +{ + public: + void log(std::string message) + { + LogWriter* lw = LogWriter::getInstance(); + lw->write(LogType::ERROR, message); + } +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogFatal.h b/src/Logger/LogFatal.h new file mode 100644 index 0000000..4b376d3 --- /dev/null +++ b/src/Logger/LogFatal.h @@ -0,0 +1,18 @@ +#ifndef LOGFATAL_H +#define LOGFATAL_H + +#include "LogType.h" +#include "LogStrategy.h" +#include "LogWriter.h" + +class LogFatal : public LogStrategy +{ + public: + void log(std::string message) + { + LogWriter* lw = LogWriter::getInstance(); + lw->write(LogType::FATAL, message); + } +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogInfo.h b/src/Logger/LogInfo.h new file mode 100644 index 0000000..2a876a1 --- /dev/null +++ b/src/Logger/LogInfo.h @@ -0,0 +1,18 @@ +#ifndef LOGINFO_H +#define LOGINFO_H + +#include "LogType.h" +#include "LogStrategy.h" +#include "LogWriter.h" + +class LogInfo : public LogStrategy +{ + public: + void log(std::string message) + { + LogWriter* lw = LogWriter::getInstance(); + lw->write(LogType::INFO, message); + } +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogStrategy.h b/src/Logger/LogStrategy.h new file mode 100644 index 0000000..d619ae0 --- /dev/null +++ b/src/Logger/LogStrategy.h @@ -0,0 +1,11 @@ +#ifndef LOGSTRATEGY_H +#define LOGSTRATEGY_H + +#include +class LogStrategy +{ + public: + virtual void log(std::string message) {}; +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogSuccess.h b/src/Logger/LogSuccess.h new file mode 100644 index 0000000..8dae948 --- /dev/null +++ b/src/Logger/LogSuccess.h @@ -0,0 +1,18 @@ +#ifndef LOGSUCCESS_H +#define LOGSUCCESS_H + +#include "LogType.h" +#include "LogStrategy.h" +#include "LogWriter.h" + +class LogSuccess : public LogStrategy +{ + public: + void log(std::string message) + { + LogWriter* lw = LogWriter::getInstance(); + lw->write(LogType::SUCCESS, message); + } +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogType.h b/src/Logger/LogType.h new file mode 100644 index 0000000..3cb006a --- /dev/null +++ b/src/Logger/LogType.h @@ -0,0 +1,47 @@ +#ifndef LOGTYPE_H +#define LOGTYPE_H + +#include + +class LogType +{ + public: + enum TYPE { + INFO = 0, + DEBUG = 1, + SUCCESS = 2, + WARNING = 3, + ERROR = 4, + FATAL = 5, + CRITICAL = 6 + }; + + static std::string enum2String(int type) + { + switch (type) + { + default: + case 0: + return "INFO"; + + case 1: + return "DEBUG"; + + case 2: + return "SUCCESS"; + + case 3: + return "WARNING"; + + case 4: + return "ERROR"; + + case 5: + return "FATAL"; + + case 6: + return "CRITICAL"; + } + } +}; +#endif \ No newline at end of file diff --git a/src/Logger/LogWarning.h b/src/Logger/LogWarning.h new file mode 100644 index 0000000..58d6222 --- /dev/null +++ b/src/Logger/LogWarning.h @@ -0,0 +1,18 @@ +#ifndef LOGWARNING_H +#define LOGWARNING_H + +#include "LogType.h" +#include "LogStrategy.h" +#include "LogWriter.h" + +class LogWarning : public LogStrategy +{ + public: + void log(std::string message) + { + LogWriter* lw = LogWriter::getInstance(); + lw->write(LogType::WARNING, message); + } +}; + +#endif \ No newline at end of file diff --git a/src/Logger/LogWriter.cpp b/src/Logger/LogWriter.cpp new file mode 100644 index 0000000..622de6a --- /dev/null +++ b/src/Logger/LogWriter.cpp @@ -0,0 +1,35 @@ +#include "LogWriter.h" + +LogWriter* LogWriter::getInstance() +{ + if(instance == nullptr) + instance = new LogWriter(); + + return instance; +} + +void LogWriter::setLogFile(std::string file) +{ + this->logfile = file; +} + +void LogWriter::write(LogType::TYPE type, std::string message) +{ + std::ofstream writer(this->logfile, std::ios::out | std::ios::app); + if(writer.good()) + { + time_t now = time(0); + tm *ltm = localtime(&now); + std::stringstream ss; + ss << "[" << LogType::enum2String(type) << "] " << + ltm->tm_mon << "-" << + ltm->tm_mday << "-" << + (1900 + ltm->tm_year) << " " << + ltm->tm_hour << ":" << + ltm->tm_min << ":" << + ltm->tm_sec << " > " << message; + writer << ss.str() << "\n"; + } + + writer.close(); +} \ No newline at end of file diff --git a/src/Logger/LogWriter.h b/src/Logger/LogWriter.h new file mode 100644 index 0000000..f4e9776 --- /dev/null +++ b/src/Logger/LogWriter.h @@ -0,0 +1,22 @@ +#ifndef LOGWRITER_H +#define LOGWRITER_H + +#include +#include +#include +#include +#include "LogType.h" + +class LogWriter +{ + public: + static LogWriter* getInstance(); + std::string logfile = ""; + void setLogFile(std::string file); + void write(LogType::TYPE t, std::string message); + + private: + static LogWriter* instance; +}; + +#endif \ No newline at end of file diff --git a/src/Logger/Logger.h b/src/Logger/Logger.h new file mode 100644 index 0000000..4a307c8 --- /dev/null +++ b/src/Logger/Logger.h @@ -0,0 +1,25 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include "LogContext.h" +#include "LogStrategy.h" +#include "LogWriter.h" + +class Logger : LogContext +{ + private: + LogStrategy * strategy = nullptr; + + public: + Logger(LogStrategy * strategy) + { + this->strategy = strategy; + } + + void log(std::string message) + { + this->strategy->log(message); + } +}; +LogWriter* LogWriter::instance = nullptr; +#endif \ No newline at end of file diff --git a/src/Logger/SimpleLogger.h b/src/Logger/SimpleLogger.h new file mode 100644 index 0000000..7d18dc3 --- /dev/null +++ b/src/Logger/SimpleLogger.h @@ -0,0 +1,84 @@ +#ifndef SIMPLELOGGER_H +#define SIMPLELOGGER_H + +#include "Logger.h" +#include "LogInfo.h" +#include "LogDebug.h" +#include "LogSuccess.h" +#include "LogWarning.h" +#include "LogError.h" +#include "LogFatal.h" +#include "LogCrtitical.h" +#include "LogWriter.h" + +class SimpleLogger +{ + public: + SimpleLogger() + { + LogWriter::getInstance()->setLogFile("log.txt"); + } + + SimpleLogger(std::string logFile) + { + LogWriter::getInstance()->setLogFile(logFile); + } + + void logInfo(std::string message) + { + Logger* logger = nullptr; + LogStrategy* li = new LogInfo(); + logger = new Logger(li); + logger->log(message); + } + + void logDebug(std::string message) + { + Logger* logger = nullptr; + LogStrategy* li = new LogDebug(); + logger = new Logger(li); + logger->log(message); + } + + void logSuccess(std::string message) + { + Logger* logger = nullptr; + LogStrategy* li = new LogSuccess(); + logger = new Logger(li); + logger->log(message); + } + + void logWarning(std::string message) + { + Logger* logger = nullptr; + LogStrategy* li = new LogWarning(); + logger = new Logger(li); + logger->log(message); + } + + void logError(std::string message) + { + Logger* logger = nullptr; + LogStrategy* li = new LogError(); + logger = new Logger(li); + logger->log(message); + } + + void logFatal(std::string message) + { + Logger* logger = nullptr; + LogStrategy* li = new LogFatal(); + logger = new Logger(li); + logger->log(message); + } + + void logCritical(std::string message) + { + Logger* logger = nullptr; + LogStrategy* li = new LogCritical(); + logger = new Logger(li); + logger->log(message); + } +}; + +#endif \ No newline at end of file diff --git a/src/Logger/test.cpp b/src/Logger/test.cpp new file mode 100644 index 0000000..a717d18 --- /dev/null +++ b/src/Logger/test.cpp @@ -0,0 +1,14 @@ +#include "SimpleLogger.h" + +int main(int argc, char** argv) +{ + SimpleLogger sl = SimpleLogger("/tmp/simplelog.log"); + sl.logInfo("test info"); + sl.logDebug("test debug"); + sl.logSuccess("test success"); + sl.logWarning("test warning"); + sl.logError("test error"); + sl.logFatal("test fatal"); + sl.logCritical("test critical"); + return 0; +} \ No newline at end of file diff --git a/src/Model/ChatItem.cpp b/src/Model/ChatItem.cpp new file mode 100644 index 0000000..66b5176 --- /dev/null +++ b/src/Model/ChatItem.cpp @@ -0,0 +1,206 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#include "ChatItem.h" + +ChatItem::ChatItem() {} + +ChatItem::ChatItem(long timestamp, QString address, QString contact, QString memo, QString requestZaddr, QString type, QString cid, QString txid, int confirmations, bool notarize, bool iscontact) +{ + _timestamp = timestamp; + _address = address; + _contact = contact; + _memo = memo; + _requestZaddr = requestZaddr; + _type = type; + _cid = cid; + _txid = txid; + _confirmations = confirmations; + _outgoing = false; + _notarize = notarize; + _iscontact = iscontact; +} + +ChatItem::ChatItem(long timestamp, QString address, QString contact, QString memo, QString requestZaddr, QString type, QString cid, QString txid, int confirmations, bool outgoing, bool notarize, bool iscontact) +{ + _timestamp = timestamp; + _address = address; + _contact = contact; + _memo = memo; + _requestZaddr = requestZaddr; + _type = type; + _cid = cid; + _txid = txid; + _confirmations = confirmations; + _outgoing = outgoing; + _notarize = notarize; + _iscontact = iscontact; + +} + +long ChatItem::getTimestamp() +{ + return _timestamp; +} + +QString ChatItem::getAddress() +{ + return _address; +} + +QString ChatItem::getContact() +{ + return _contact; +} + +QString ChatItem::getMemo() +{ + return _memo; +} + +QString ChatItem::getRequestZaddr() +{ + return _requestZaddr; +} +QString ChatItem::getType() +{ + return _type; +} + +QString ChatItem::getCid() +{ + return _cid; +} + +QString ChatItem::getTxid() +{ + return _txid; +} +int ChatItem::getConfirmations() +{ + return _confirmations; +} + +bool ChatItem::isOutgoing() +{ + return _outgoing; +} + +bool ChatItem::isNotarized() +{ + return _notarize; +} + +bool ChatItem::isContact() +{ + return _iscontact; +} + +void ChatItem::setTimestamp(long timestamp) +{ + _timestamp = timestamp; +} + +void ChatItem::setAddress(QString address) +{ + _address = address; +} + +void ChatItem::setContact(QString contact) +{ + _contact = contact; +} + +void ChatItem::setMemo(QString memo) +{ + _memo = memo; +} + +void ChatItem::setRequestZaddr(QString requestZaddr) +{ + _requestZaddr = requestZaddr; +} + +void ChatItem::setType(QString type) +{ + _type = type; +} + +void ChatItem::setCid(QString cid) +{ + _cid = cid; +} +void ChatItem::setTxid(QString txid) +{ + _txid = txid; +} +void ChatItem::setConfirmations(int confirmations) +{ + _confirmations = confirmations; +} + +void ChatItem::toggleOutgo() +{ + _outgoing = true; +} +void ChatItem::notarized() +{ + _notarize = false; +} + +void ChatItem::contact(bool iscontact) +{ + _iscontact = iscontact; +} + + +QString ChatItem::toChatLine() +{ + QDateTime myDateTime; + QString lock; + myDateTime.setTime_t(_timestamp); + + if (_notarize == true) + + { + + lock = " "; + + }else{ + + lock = " "; + } + if ((_confirmations > 0) && (_notarize == false)) + + { + + lock = " "; + } + + + QString line = QString("") + myDateTime.toString("yyyy-MM-dd hh:mm"); + line += QString(lock) + QString(""); + line +=QString("

") + _memo.toHtmlEscaped() + QString("

"); + return line; +} + +json ChatItem::toJson() +{ + json j; + j["_timestamp"] = _timestamp; + j["_address"] = _address.toStdString(); + j["_contact"] = _contact.toStdString(); + j["_memo"] = _memo.toStdString(); + j["_requestZaddr"] = _requestZaddr.toStdString(); + j["_type"] = _type.toStdString(); + j["_cid"] = _cid.toStdString(); + j["_txid"] = _txid.toStdString(); + j["_confirmations"] = _confirmations; + j["_outgoing"] = _outgoing; + return j; +} + +ChatItem::~ChatItem() +{ + +} \ No newline at end of file diff --git a/src/Model/ChatItem.h b/src/Model/ChatItem.h new file mode 100644 index 0000000..900c994 --- /dev/null +++ b/src/Model/ChatItem.h @@ -0,0 +1,60 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#ifndef CHATITEM_H +#define CHATITEM_H + +#include +using json = nlohmann::json; + +class ChatItem +{ + private: + long _timestamp; + QString _address; + QString _contact; + QString _memo; + QString _requestZaddr; + QString _type; + QString _cid; + QString _txid; + int _confirmations; + bool _outgoing = false; + bool _notarize = false; + bool _iscontact = false; + + public: + ChatItem(); + ChatItem(long timestamp, QString address, QString contact, QString memo,QString requestZaddr, QString type, QString cid, QString txid, int confirmations, bool notarize, bool iscontact); + ChatItem(long timestamp, QString address, QString contact, QString memo, QString requestZaddr, QString type, QString cid, QString txid, int confirmations, bool outgoing, bool notarize, bool iscontact); + long getTimestamp(); + QString getAddress(); + QString getContact(); + QString getMemo(); + QString getRequestZaddr(); + QString getType(); + QString getCid(); + QString getTxid(); + int getConfirmations(); + bool isOutgoing(); + bool isdouble(); + bool isNotarized(); + bool isContact(); + void setTimestamp(long timestamp); + void setAddress(QString address); + void setContact(QString contact); + void setMemo(QString memo); + void setRequestZaddr(QString requestZaddr); + void setType(QString type); + void setCid(QString cid); + void setTxid(QString txid); + void setConfirmations(int confirmations); + void toggleOutgo(); + void notarized(); + void contact(bool iscontact); + QString toChatLine(); + json toJson(); + ~ChatItem(); +}; + +#endif \ No newline at end of file diff --git a/src/Model/ContactItem.cpp b/src/Model/ContactItem.cpp new file mode 100644 index 0000000..b542270 --- /dev/null +++ b/src/Model/ContactItem.cpp @@ -0,0 +1,98 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 +#include "ContactItem.h" +#include "chatmodel.h" +#include "Model/ChatItem.h" +#include "controller.h" + +ContactItem::ContactItem() {} + +ContactItem::ContactItem(QString name, QString partnerAddress) +{ + _name = name; + _partnerAddress = partnerAddress; +} + +ContactItem::ContactItem(QString name, QString partnerAddress, QString myAddress, QString cid) +{ + _name = name; + _myAddress = myAddress; + _partnerAddress = partnerAddress; + _cid = cid; + +} + +ContactItem::ContactItem(QString name, QString partnerAddress, QString myAddress, QString cid, QString avatar) +{ + _name = name; + _myAddress = myAddress; + _partnerAddress = partnerAddress; + _cid = cid; + _avatar = avatar; + +} + +QString ContactItem::getName() const +{ + return _name; +} + +QString ContactItem::getMyAddress() const +{ + return _myAddress; +} + +QString ContactItem::getPartnerAddress() const +{ + return _partnerAddress; +} + +QString ContactItem::getCid() const +{ + return _cid; +} + +QString ContactItem::getAvatar() const +{ + return _avatar; +} + +void ContactItem::setName(QString name) +{ + _name = name; +} + +void ContactItem::setMyAddress(QString myAddress) +{ + _myAddress = myAddress; +} + +void ContactItem::setPartnerAddress(QString partnerAddress) +{ + _partnerAddress = partnerAddress; +} + +void ContactItem::setcid(QString cid) +{ + _cid = cid; +} +void ContactItem::setAvatar(QString avatar) +{ + _avatar = avatar; +} + +QString ContactItem::toQTString() +{ + return _name + "|" + _partnerAddress + "|" + _myAddress + "|" + _cid + "|" + _avatar; +} + +json ContactItem::toJson() +{ + json j; + j["_myAddress"] = _myAddress.toStdString(); + j["_partnerAddress"] = _partnerAddress.toStdString(); + j["_name"] = _name.toStdString(); + j["_cid"] = _cid.toStdString(); + j["_avatar"] = _avatar.toStdString(); + return j; +} \ No newline at end of file diff --git a/src/Model/ContactItem.h b/src/Model/ContactItem.h new file mode 100644 index 0000000..e5e313d --- /dev/null +++ b/src/Model/ContactItem.h @@ -0,0 +1,40 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 +#ifndef CONTACTITEM_H +#define CONTACTITEM_H + +#include +#include +#include "mainwindow.h" +using json = nlohmann::json; + +class ContactItem +{ +private: + QString _myAddress; + QString _partnerAddress; + QString _name; + QString _cid; + QString _avatar; + QString _pubkey; + +public: + ContactItem(); + ContactItem(QString name, QString partnerAddress); + ContactItem(QString name, QString partnerAddress, QString myAddress, QString cid); + ContactItem(QString name, QString partnerAddress, QString myAddress, QString cid, QString avatar); + QString getName() const; + QString getMyAddress() const; + QString getPartnerAddress() const; + QString getCid() const; + QString getAvatar() const; + void setName(QString name); + void setMyAddress(QString myAddress); + void setPartnerAddress(QString partnerAddress); + void setcid(QString cid); + void setAvatar(QString avatar); + QString toQTString(); + json toJson(); +}; + +#endif \ No newline at end of file diff --git a/src/Model/ContactRequest.cpp b/src/Model/ContactRequest.cpp new file mode 100644 index 0000000..3ad804f --- /dev/null +++ b/src/Model/ContactRequest.cpp @@ -0,0 +1,96 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#include "ContactRequest.h" + +ContactRequest::ContactRequest() {} + +ContactRequest::ContactRequest(QString sender, QString receiver, QString memo, QString cid, QString label, QString avatar) +{ + _senderAddress = sender; + _receiverAddress = receiver; + _memo = memo; + _cid = cid; + _label = label; + _avatar = avatar; +} + +QString ContactRequest::getSenderAddress() +{ + return _senderAddress; +} + +QString ContactRequest::getReceiverAddress() +{ + return _receiverAddress; +} + +QString ContactRequest::getMemo() +{ + return _memo; +} + +QString ContactRequest::getCid() +{ + return _cid; +} + +QString ContactRequest::getLabel() +{ + return _label; +} + +QString ContactRequest::getAvatar() +{ + return _avatar; +} + +void ContactRequest::setSenderAddress(QString address) +{ + _senderAddress = address; +} + +void ContactRequest::setReceiverAddress(QString address) +{ + _receiverAddress = address; +} + +void ContactRequest::setMemo(QString memo) +{ + _memo = memo; +} + +void ContactRequest::setCid(QString cid) +{ + _cid = cid; +} + +void ContactRequest::setLabel(QString label) +{ + _label = label; +} + +void ContactRequest::setAvatar(QString avatar) +{ + _avatar = avatar; +} + +QString ContactRequest::toString() +{ + return "sender: " + _senderAddress + " receiver: " + _receiverAddress + " memo: " + _memo + " cid: " + _cid + " label: " + _label + " avatar: " + _avatar; +} + +void ContactRequest::clear() +{ + _senderAddress = ""; + _receiverAddress = ""; + _memo = ""; + _cid = ""; + _label = ""; + _avatar = ""; +} + +ContactRequest::~ContactRequest() +{ + clear(); +} \ No newline at end of file diff --git a/src/Model/ContactRequest.h b/src/Model/ContactRequest.h new file mode 100644 index 0000000..930ad56 --- /dev/null +++ b/src/Model/ContactRequest.h @@ -0,0 +1,40 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#ifndef CONTACTREQUEST_H +#define CONTACTREQUEST_H + +#include +using json = nlohmann::json; + +class ContactRequest +{ + private: + QString _senderAddress; + QString _receiverAddress; + QString _memo; + QString _cid; + QString _label; + QString _avatar; + + public: + ContactRequest(); + ContactRequest(QString sender, QString receiver, QString memo, QString cid, QString label, QString avatar); + QString getSenderAddress(); + QString getReceiverAddress(); + QString getMemo(); + QString getCid(); + QString getLabel(); + QString getAvatar(); + void setSenderAddress(QString address); + void setReceiverAddress(QString contact); + void setMemo(QString memo); + void setCid(QString cid); + void setLabel(QString label); + void setAvatar(QString avatar); + QString toString(); + void clear(); + ~ContactRequest(); +}; + +#endif \ No newline at end of file diff --git a/src/Model/ContactRequestChatItem.cpp b/src/Model/ContactRequestChatItem.cpp new file mode 100644 index 0000000..d6cd443 --- /dev/null +++ b/src/Model/ContactRequestChatItem.cpp @@ -0,0 +1 @@ +#include "ContactRequestChatItem.h" \ No newline at end of file diff --git a/src/Model/ContactRequestChatItem.h b/src/Model/ContactRequestChatItem.h new file mode 100644 index 0000000..d1720de --- /dev/null +++ b/src/Model/ContactRequestChatItem.h @@ -0,0 +1,11 @@ +#ifdef CONTACTREQUESTCHATITEM_H +#define CONTACTREQUESTCHATITEM_H + +#include "ChatItem.h" + +class ContactRequestChatItem : ChatItem +{ + +}; + +#endif \ No newline at end of file diff --git a/src/addressbook.cpp b/src/addressbook.cpp index a00a355..e2f481d 100644 --- a/src/addressbook.cpp +++ b/src/addressbook.cpp @@ -1,14 +1,19 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + #include "addressbook.h" #include "ui_addressbook.h" #include "ui_mainwindow.h" #include "settings.h" #include "mainwindow.h" #include "controller.h" +#include "DataStore/DataStore.h" +#include "FileSystem/FileSystem.h" AddressBookModel::AddressBookModel(QTableView *parent) : QAbstractTableModel(parent) { - headers << tr("Label") << tr("Address"); + headers << tr("Avatar")<< tr("Label") << tr("Address") << tr("HushChatAddress") << tr("CID"); this->parent = parent; loadData(); } @@ -34,14 +39,21 @@ void AddressBookModel::loadData() parent->horizontalHeader()->restoreState( QSettings().value( "addresstablegeometry" - ).toByteArray() + ).toByteArray() ); + } -void AddressBookModel::addNewLabel(QString label, QString addr) +void AddressBookModel::addNewLabel(QString label, QString addr, QString myAddr, QString cid, QString avatar) { //labels.push_back(QPair(label, addr)); - AddressBook::getInstance()->addAddressLabel(label, addr); + AddressBook::getInstance()->addAddressLabel(label, addr, myAddr, cid, avatar); + updateUi(); + +} + +void AddressBookModel::updateUi() +{ labels.clear(); labels = AddressBook::getInstance()->getAllAddressLabels(); dataChanged(index(0, 0), index(labels.size()-1, columnCount(index(0,0))-1)); @@ -53,22 +65,27 @@ void AddressBookModel::removeItemAt(int row) if (row >= labels.size()) return; - AddressBook::getInstance()->removeAddressLabel(labels[row].first, labels[row].second); + AddressBook::getInstance()->removeAddressLabel(labels[row].getName(), labels[row].getPartnerAddress(), labels[row].getMyAddress(),labels[row].getCid(),labels[row].getAvatar()); labels.clear(); labels = AddressBook::getInstance()->getAllAddressLabels(); dataChanged(index(0, 0), index(labels.size()-1, columnCount(index(0,0))-1)); layoutChanged(); } -QPair AddressBookModel::itemAt(int row) +ContactItem AddressBookModel::itemAt(int row) { - if (row >= labels.size()) - return QPair(); + if (row >= labels.size()) + { + ContactItem item = ContactItem("", "", "", "",""); + return item; + } + return labels.at(row); } + int AddressBookModel::rowCount(const QModelIndex&) const { return labels.size(); @@ -86,9 +103,14 @@ QVariant AddressBookModel::data(const QModelIndex &index, int role) const { switch(index.column()) { - case 0: return labels.at(index.row()).first; - case 1: return labels.at(index.row()).second; + case 0: return labels.at(index.row()).getAvatar(); + case 1: return labels.at(index.row()).getName(); + case 2: return labels.at(index.row()).getPartnerAddress(); + case 3: return labels.at(index.row()).getMyAddress(); + case 4: return labels.at(index.row()).getCid(); + } + } return QVariant(); @@ -125,6 +147,38 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) // Connect the dialog's closing to updating the label address completor QObject::connect(&d, &QDialog::finished, [=] (auto) { parent->updateLabels(); }); + Controller* rpc = parent->getRPC(); + QObject::connect(ab.newZaddr, &QPushButton::clicked, [&] () { + + bool sapling = true; + try + { + rpc->createNewZaddr(sapling, [=] (json reply) { + QString myAddr = QString::fromStdString(reply.get()[0]); + QString message = QString("New Chat Address for your partner: ") + myAddr; + QString cid = QUuid::createUuid().toString(QUuid::WithoutBraces); + + rpc->refreshAddresses(); + + parent->ui->listReceiveAddresses->insertItem(0, myAddr); + parent->ui->listReceiveAddresses->setCurrentIndex(0); + + qDebug() << " new Addr in Addressbook" << myAddr; + ab.cid->setText(cid); + ab.addr_chat->setText(myAddr); + }); + } + + catch(...) + { + + + qDebug() << QString("Caught something nasty with myZaddr Addressbook"); + } + }); + + // model.updateUi(); //todo fix updating gui after adding + // If there is a target then make it the addr for the "Add to" button if (target != nullptr && Settings::isValidAddress(target->text())) { @@ -135,7 +189,12 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) // Add new address button QObject::connect(ab.addNew, &QPushButton::clicked, [&] () { auto addr = ab.addr->text().trimmed(); + auto myAddr = ab.addr_chat->text().trimmed(); QString newLabel = ab.label->text(); + QString cid = ab.cid->text(); + + + QString avatar = QString(":/icons/res/") + ab.comboBoxAvatar->currentText() + QString(".png"); if (addr.isEmpty() || newLabel.isEmpty()) { @@ -170,10 +229,31 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) ); return; } - - model.addNewLabel(newLabel, ab.addr->text()); + + ////// We need a better popup here. + AddressBook::getInstance()->addAddressLabel(newLabel, addr, myAddr, cid,avatar); + + QMessageBox::information( + parent, + QObject::tr("Added Contact"), + QObject::tr("successfully added your new contact").arg(newLabel), + QMessageBox::Ok + ); + return; + + + // rpc->refresh(true); + model.updateUi(); + + rpc->refreshContacts( + parent->ui->listContactWidget + + ); + }); + // AddressBook::getInstance()->addAddressLabel(newLabel, ab.addr->text(), cid); + // Import Button QObject::connect(ab.btnImport, &QPushButton::clicked, [&] () { // Get the import file name. @@ -209,7 +289,7 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) continue; // Add label, address. - model.addNewLabel(items.at(1), items.at(0)); + model.addNewLabel(items.at(1), items.at(0), "", "", ""); numImported++; } @@ -220,8 +300,8 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) ); }); - auto fnSetTargetLabelAddr = [=] (QLineEdit* target, QString label, QString addr) { - target->setText(label % "/" % addr); + auto fnSetTargetLabelAddr = [=] (QLineEdit* target, QString label, QString addr, QString myAddr, QString cid, QString avatar) { + target->setText(label % "/" % addr % myAddr); }; // Double-Click picks the item @@ -233,10 +313,13 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) if (index.row() < 0) return; - QString lbl = model.itemAt(index.row()).first; - QString addr = model.itemAt(index.row()).second; + QString lbl = model.itemAt(index.row()).getName(); + QString addr = model.itemAt(index.row()).getPartnerAddress(); + QString myAddr = model.itemAt(index.row()).getMyAddress(); + QString cid = model.itemAt(index.row()).getCid(); + QString avatar = model.itemAt(index.row()).getCid(); d.accept(); - fnSetTargetLabelAddr(target, lbl, addr); + fnSetTargetLabelAddr(target, lbl, addr, myAddr, cid, avatar); }); // Right-Click @@ -246,15 +329,18 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) if (index.row() < 0) return; - QString lbl = model.itemAt(index.row()).first; - QString addr = model.itemAt(index.row()).second; + QString lbl = model.itemAt(index.row()).getName(); + QString addr = model.itemAt(index.row()).getPartnerAddress(); + QString myAddr = model.itemAt(index.row()).getMyAddress(); + QString cid = model.itemAt(index.row()).getCid(); + QString avatar = model.itemAt(index.row()).getAvatar(); QMenu menu(parent); if (target != nullptr) menu.addAction("Pick", [&] () { d.accept(); - fnSetTargetLabelAddr(target, lbl, addr); + fnSetTargetLabelAddr(target, lbl, addr, myAddr, cid, avatar); }); menu.addAction(QObject::tr("Copy address"), [&] () { @@ -273,12 +359,13 @@ void AddressBook::open(MainWindow* parent, QLineEdit* target) auto selection = ab.addresses->selectionModel(); if (selection && selection->hasSelection() && selection->selectedRows().size() > 0) { auto item = model.itemAt(selection->selectedRows().at(0).row()); - fnSetTargetLabelAddr(target, item.first, item.second); + fnSetTargetLabelAddr(target, item.getName(), item.getMyAddress(), item.getPartnerAddress(), item.getCid(), item.getAvatar()); } }; // Refresh after the dialog is closed to update the labels everywhere. parent->getRPC()->refresh(true); + model.updateUi(); //todo fix updating gui after adding } //============= @@ -307,26 +394,71 @@ void AddressBook::readFromStorage() file.open(QIODevice::ReadOnly); QDataStream in(&file); // read the data serialized from the file QString version; - in >> version >> allLabels; + in >> version; + QList> stuff; + in >> stuff; + + //////////////found old addrbook, and rename it to .bak + if (version != "v2") + { + auto filename = QStringLiteral("addresslabels.dat"); + auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + QFile address(dir.filePath(filename)); + + address.rename(dir.filePath("addresslabels.bak")); + + }else{ + for (int i=0; i < stuff.size(); i++) + { + + ContactItem contact = ContactItem(stuff[i][0],stuff[i][1], stuff[i][2], stuff[i][3],stuff[i][4]); + + allLabels.push_back(contact); + } + } + + + // qDebug() << "Read " << version << " Hush contacts from disk..."; file.close(); } + else + { + qDebug() << "No Hush contacts found on disk!"; + } // Special. // Add the default silentdragon donation address if it isn't already present // QList allAddresses; // std::transform(allLabels.begin(), allLabels.end(), - // std::back_inserter(allAddresses), [=] (auto i) { return i.second; }); + // std::back_inserter(allAddresses), [=] (auto i) { return i.getPartnerAddress(); }); // if (!allAddresses.contains(Settings::getDonationAddr(true))) { // allLabels.append(QPair("silentdragon donation", Settings::getDonationAddr(true))); // } } + void AddressBook::writeToStorage() { + //FileSystem::getInstance()->writeContacts(AddressBook::writeableFile(), DataStore::getContactDataStore()->dump()); + + // FileSystem::getInstance()->writeContactsOldFormat(AddressBook::writeableFile(), allLabels); + + QFile file(AddressBook::writeableFile()); file.open(QIODevice::ReadWrite | QIODevice::Truncate); QDataStream out(&file); // we will serialize the data into the file - out << QString("v1") << allLabels; + QList> contacts; + for(auto &item: allLabels) + { + QList c; + c.push_back(item.getName()); + c.push_back(item.getPartnerAddress()); + c.push_back(item.getMyAddress()); + c.push_back(item.getCid()); + c.push_back(item.getAvatar()); + contacts.push_back(c); + } + out << QString("v2") << contacts; file.close(); } @@ -346,27 +478,27 @@ QString AddressBook::writeableFile() // Add a new address/label to the database -void AddressBook::addAddressLabel(QString label, QString address) +void AddressBook::addAddressLabel(QString label, QString address, QString myAddr, QString cid, QString avatar) { Q_ASSERT(Settings::isValidAddress(address)); - - // First, remove any existing label + // getName(), remove any existing label // Iterate over the list and remove the label/address for (int i=0; i < allLabels.size(); i++) - if (allLabels[i].first == label) - removeAddressLabel(allLabels[i].first, allLabels[i].second); + if (allLabels[i].getName() == label) + removeAddressLabel(allLabels[i].getName(), allLabels[i].getPartnerAddress(),allLabels[i].getMyAddress(), allLabels[i].getCid(), allLabels[i].getAvatar()); - allLabels.push_back(QPair(label, address)); + ContactItem item = ContactItem(label, address, myAddr, cid, avatar); + allLabels.push_back(item); writeToStorage(); } // Remove a new address/label from the database -void AddressBook::removeAddressLabel(QString label, QString address) +void AddressBook::removeAddressLabel(QString label, QString address, QString myAddr, QString cid, QString avatar) { // Iterate over the list and remove the label/address for (int i=0; i < allLabels.size(); i++) { - if (allLabels[i].first == label && allLabels[i].second == address) + if (allLabels[i].getName() == label && allLabels[i].getPartnerAddress() == address) { allLabels.removeAt(i); writeToStorage(); @@ -380,9 +512,9 @@ void AddressBook::updateLabel(QString oldlabel, QString address, QString newlabe // Iterate over the list and update the label/address for (int i = 0; i < allLabels.size(); i++) { - if (allLabels[i].first == oldlabel && allLabels[i].second == address) + if (allLabels[i].getName() == oldlabel && allLabels[i].getPartnerAddress() == address) { - allLabels[i].first = newlabel; + allLabels[i].setName(newlabel); writeToStorage(); return; } @@ -390,7 +522,7 @@ void AddressBook::updateLabel(QString oldlabel, QString address, QString newlabe } // Read all addresses -const QList>& AddressBook::getAllAddressLabels() +const QList& AddressBook::getAllAddressLabels() { if (allLabels.isEmpty()) readFromStorage(); @@ -402,8 +534,8 @@ const QList>& AddressBook::getAllAddressLabels() QString AddressBook::getLabelForAddress(QString addr) { for (auto i : allLabels) - if (i.second == addr) - return i.first; + if (i.getPartnerAddress() == addr) + return i.getName(); return ""; } @@ -412,8 +544,8 @@ QString AddressBook::getLabelForAddress(QString addr) QString AddressBook::getAddressForLabel(QString label) { for (auto i: allLabels) - if (i.first == label) - return i.second; + if (i.getName() == label) + return i.getPartnerAddress(); return ""; } @@ -428,6 +560,8 @@ QString AddressBook::addLabelToAddress(QString addr) return addr; } + + QString AddressBook::addressFromAddressLabel(const QString& lblAddr) { return lblAddr.trimmed().split("/").last(); diff --git a/src/addressbook.h b/src/addressbook.h index af64c07..b4fe7a8 100644 --- a/src/addressbook.h +++ b/src/addressbook.h @@ -2,6 +2,8 @@ #define ADDRESSBOOK_H #include "precompiled.h" +#include "contactmodel.h" +#include "FileSystem/FileSystem.h" class MainWindow; @@ -10,10 +12,12 @@ class AddressBookModel : public QAbstractTableModel { public: AddressBookModel(QTableView* parent); ~AddressBookModel(); - - void addNewLabel(QString label, QString addr); + + void addNewLabel(QString label, QString address, QString myAddr, QString cid, QString avatar); + void updateUi(); void removeItemAt(int row); - QPair itemAt(int row); + //QPair itemAt(int row); + ContactItem itemAt(int row); int rowCount(const QModelIndex &parent) const; int columnCount(const QModelIndex &parent) const; @@ -25,8 +29,10 @@ private: void saveData(); QTableView* parent; - QList> labels; - QStringList headers; + //QList> labels; + QList labels; + QStringList headers; + }; class AddressBook { @@ -39,21 +45,33 @@ public: static QString addressFromAddressLabel(const QString& lblAddr); // Add a new address/label to the database - void addAddressLabel(QString label, QString address); + void addAddressLabel(QString label, QString address, QString myAddr, QString cid, QString avatar); // Remove a new address/label from the database - void removeAddressLabel(QString label, QString address); + void removeAddressLabel(QString label, QString address, QString myAddr, QString cid, QString avatar); // Update a label/address void updateLabel(QString oldlabel, QString address, QString newlabel); // Read all addresses - const QList>& getAllAddressLabels(); + const QList& getAllAddressLabels(); // Get an address's first label QString getLabelForAddress(QString address); // Get a Label's address QString getAddressForLabel(QString label); + + QString get_avatar_name(); + void set_avatar_name(QString avatar_name); + + + + + + + + + private: AddressBook(); @@ -61,7 +79,8 @@ private: void writeToStorage(); QString writeableFile(); - QList> allLabels; + QList allLabels; + static AddressBook* instance; }; diff --git a/src/addressbook.ui b/src/addressbook.ui index a20ee8f..80b3a80 100644 --- a/src/addressbook.ui +++ b/src/addressbook.ui @@ -13,81 +13,8 @@ Address Book - - - - - Add New Address - - - - - - Address (z-Addr or t-Addr) - - - - - - - - - - Label - - - - - - - 40 - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Add to Address Book - - - - - - - - - - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - - + + @@ -108,6 +35,269 @@ + + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + + + + + Add New Address + + + + + + Address (z-Addr or t-Addr) + + + + + + + <html><head/><body><p>Nickname :</p></body></html> + + + + + + + + 0 + 25 + + + + + + + + + 0 + 25 + + + + 25 + + + + + + + HushChat Address - give this Address only to your contact + + + + + + + + 0 + 25 + + + + + + + + <html><head/><body><p>Conversation ID:</p></body></html> + + + + + + + + 0 + 25 + + + + + + + + + 0 + 25 + + + + Add to Address Book + + + + + + + + SDLogo + + + + :/icons/res/SDLogo.png + + + + + + Duke + + + + :/icons/res/Duke.png + + + + + + Denio + + + + :/icons/res/Denio.png + + + + + + Berg + + + + :/icons/res/Berg.png + + + + + + Sharpee + + + + :/icons/res/Sharpee.png + + + + + + Elsa + + + + :/icons/res/Elsa.png + + + + + + Yoda + + + + :/icons/res/Yoda.png + + + + + + Garflied + + + + :/icons/res/Garfield.png + + + + + + Snoopy + + + + :/icons/res/Snoopy.png + + + + + + Popey + + + + :/icons/res/Popey.png + + + + + + Pinguin + + + + :/icons/res/Pinguin.png + + + + + + Mickey + + + + :/icons/res/Mickey.png + + + + + + Stag + + + + :/icons/res/Stag.png + + + + + + + + + <html><head/><body><p>Avatar :</p></body></html> + + + + + + + + 0 + 25 + + + + Create a new HushChat zaddr + + + + + + diff --git a/src/chatbubbleme.cpp b/src/chatbubbleme.cpp new file mode 100644 index 0000000..1f17250 --- /dev/null +++ b/src/chatbubbleme.cpp @@ -0,0 +1,14 @@ +#include "chatbubbleme.h" +#include "ui_chatbubbleme.h" + +ChatBubbleMe::ChatBubbleMe(QWidget *parent) : + QWidget(parent), + ui(new Ui::ChatBubbleMe) +{ + ui->setupUi(this); +} + +ChatBubbleMe::~ChatBubbleMe() +{ + delete ui; +} diff --git a/src/chatbubbleme.h b/src/chatbubbleme.h new file mode 100644 index 0000000..86e9c6f --- /dev/null +++ b/src/chatbubbleme.h @@ -0,0 +1,22 @@ +#ifndef CHATBUBBLEME_H +#define CHATBUBBLEME_H + +#include + +namespace Ui { +class ChatBubbleMe; +} + +class ChatBubbleMe : public QWidget +{ + Q_OBJECT + +public: + explicit ChatBubbleMe(QWidget *parent = nullptr); + ~ChatBubbleMe(); + +private: + Ui::ChatBubbleMe *ui; +}; + +#endif // CHATBUBBLEME_H diff --git a/src/chatbubbleme.ui b/src/chatbubbleme.ui new file mode 100644 index 0000000..f35baaa --- /dev/null +++ b/src/chatbubbleme.ui @@ -0,0 +1,57 @@ + + + ChatBubbleMe + + + + 0 + 0 + 646 + 76 + + + + Form + + + QWidget{ + background: whitesmoke; + border: 1px solid #afafaf; + border-radius: 3px; +} +QLabel +{ + background: none; + border: none; +} + + + + + 10 + 30 + 611 + 41 + + + + Lorem ipsum dolor sit amet + + + + + + 10 + 10 + 611 + 17 + + + + 12/03/2020 12:34 + + + + + + diff --git a/src/chatbubblepartner.cpp b/src/chatbubblepartner.cpp new file mode 100644 index 0000000..690f608 --- /dev/null +++ b/src/chatbubblepartner.cpp @@ -0,0 +1,14 @@ +#include "chatbubblepartner.h" +#include "ui_chatbubblepartner.h" + +ChatBubblePartner::ChatBubblePartner(QWidget *parent) : + QWidget(parent), + ui(new Ui::ChatBubblePartner) +{ + ui->setupUi(this); +} + +ChatBubblePartner::~ChatBubblePartner() +{ + delete ui; +} diff --git a/src/chatbubblepartner.h b/src/chatbubblepartner.h new file mode 100644 index 0000000..feb950b --- /dev/null +++ b/src/chatbubblepartner.h @@ -0,0 +1,22 @@ +#ifndef CHATBUBBLEPARTNER_H +#define CHATBUBBLEPARTNER_H + +#include + +namespace Ui { +class ChatBubblePartner; +} + +class ChatBubblePartner : public QWidget +{ + Q_OBJECT + +public: + explicit ChatBubblePartner(QWidget *parent = nullptr); + ~ChatBubblePartner(); + +private: + Ui::ChatBubblePartner *ui; +}; + +#endif // CHATBUBBLEPARTNER_H diff --git a/src/chatbubblepartner.ui b/src/chatbubblepartner.ui new file mode 100644 index 0000000..4decec9 --- /dev/null +++ b/src/chatbubblepartner.ui @@ -0,0 +1,57 @@ + + + ChatBubblePartner + + + + 0 + 0 + 646 + 76 + + + + Form + + + QWidget{ + background: #c8e1ff; + border: 1px solid #fefefe; + border-radius: 3px; +} +QLabel +{ + background: none; + border: none; +} + + + + + 10 + 30 + 611 + 41 + + + + Lorem ipsum dolor sit amet + + + + + + 10 + 10 + 611 + 17 + + + + 12/03/2020 12:34 + + + + + + diff --git a/src/chatmodel.cpp b/src/chatmodel.cpp new file mode 100644 index 0000000..fe4e92b --- /dev/null +++ b/src/chatmodel.cpp @@ -0,0 +1,1018 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 +#include "chatmodel.h" +#include "settings.h" +#include "ui_confirm.h" +#include "controller.h" +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "ui_requestContactDialog.h" +#include "addressbook.h" +#include "ui_contactrequest.h" +#include +#include +#include "DataStore/DataStore.h" + +using namespace std; + +ContactRequest contactRequest = ContactRequest(); + +ChatModel::ChatModel(std::map chatItems) +{ + this->chatItems = chatItems; +} + +ChatModel::ChatModel(std::vector chatItems) +{ + this->setItems(chatItems); +} + +std::map ChatModel::getItems() +{ + return this->chatItems; +} + +void ChatModel::setItems(std::map items) +{ + this->chatItems = chatItems; +} + +void ChatModel::setItems(std::vector items) +{ + for(ChatItem c : items) + { + this->chatItems[ChatIDGenerator::getInstance()->generateID(c)] = c; //this->generateChatItemID(c)] = c; + } +} + +void ChatModel::clear() +{ + this->chatItems.clear(); +} + +void ChatModel::addMessage(ChatItem item) +{ + QString key = ChatIDGenerator::getInstance()->generateID(item); //this->generateChatItemID(item); + this->chatItems[key] = item; +} + +void ChatModel::addMessage(QString timestamp, ChatItem item) +{ + QString key = ChatIDGenerator::getInstance()->generateID(item);//this->generateChatItemID(item); + timestamp = "0"; + this->chatItems[key] = item; +} + +void ChatModel::showMessages() +{ + for(auto &c : this->chatItems) + { + // qDebug() << c.second.toChatLine(); + } + +} + +void ChatModel::addAddressbylabel(QString address, QString label) +{ + this->AddressbyLabelMap[address] = label; +} + +QString ChatModel::Addressbylabel(QString address) +{ + for(auto& pair : this->AddressbyLabelMap) + { + + } + + if(this->AddressbyLabelMap.count(address) > 0) + { + return this->AddressbyLabelMap[address]; + } + + return QString("0xdeadbeef"); +} + +void MainWindow::renderContactRequest(){ + + Ui_requestDialog requestContact; + QDialog dialog(this); + requestContact.setupUi(&dialog); + Settings::saveRestore(&dialog); + + QString icon; + auto theme = Settings::getInstance()->get_theme_name(); + if (theme == "Dark" || theme == "Midnight") { + icon = ":/icons/res/unknownWhite.png"; + }else{ + icon = ":/icons/res/unknownBlack.png"; + } + + QPixmap unknownWhite(icon); + QIcon addnewAddrIcon(unknownWhite); + + + + + QStandardItemModel* contactRequest = new QStandardItemModel(); + + + + for (auto &c : DataStore::getChatDataStore()->getAllNewContactRequests()) + + { + + QStandardItem* Items = new QStandardItem(QString(c.second.getRequestZaddr())); + contactRequest->appendRow(Items); + requestContact.requestContact->setModel(contactRequest); + Items->setData(QIcon(addnewAddrIcon),Qt::DecorationRole); + requestContact.requestContact->setIconSize(QSize(40,50)); + requestContact.requestContact->setUniformItemSizes(true); + requestContact.requestContact->show(); + requestContact.zaddrnew->setVisible(false); + requestContact.zaddrnew->setText(c.second.getRequestZaddr()); + + + + } + + + QStandardItemModel* contactRequestOld = new QStandardItemModel(); + + for (auto &c : DataStore::getChatDataStore()->getAllOldContactRequests()) + { + QStandardItem* Items = new QStandardItem(c.second.getContact()); + contactRequestOld->appendRow(Items); + requestContact.requestContactOld->setModel(contactRequestOld); + requestContact.zaddrold->setVisible(false); + requestContact.zaddrold->setText(c.second.getRequestZaddr()); + + + } + + + + QObject::connect(requestContact.requestContact, &QTableView::clicked, [&] () { + + for (auto &c : DataStore::getChatDataStore()->getAllRawChatItems()){ + QModelIndex index = requestContact.requestContact->currentIndex(); + QString label_contact = index.data(Qt::DisplayRole).toString(); + QStandardItemModel* contactMemo = new QStandardItemModel(); + + + + + if ((c.second.isOutgoing() == false) && (label_contact == c.second.getContact())) + + { + + QStandardItem* Items = new QStandardItem(c.second.getMemo()); + contactMemo->appendRow(Items); + requestContact.requestMemo->setModel(contactMemo); + requestContact.requestMemo->show(); + + requestContact.requestCID->setText(c.second.getCid()); + requestContact.requestCID->setVisible(false); + requestContact.requestZaddr->setText(c.second.getRequestZaddr()); + requestContact.requestMyAddr->setText(c.second.getAddress()); + + }else{ + + } + + } + + }); + + + + QObject::connect(requestContact.requestContactOld, &QTableView::clicked, [&] () { + + for (auto &c : DataStore::getChatDataStore()->getAllRawChatItems()){ + QModelIndex index = requestContact.requestContactOld->currentIndex(); + QString label_contactold = index.data(Qt::DisplayRole).toString(); + QStandardItemModel* contactMemo = new QStandardItemModel(); + + if ((c.second.isOutgoing() == false) && (label_contactold == c.second.getContact())) + + { + + QStandardItem* Items = new QStandardItem(c.second.getMemo()); + contactMemo->appendRow(Items); + requestContact.requestMemo->setModel(contactMemo); + requestContact.requestMemo->show(); + + requestContact.requestCID->setText(c.second.getCid()); + requestContact.requestCID->setVisible(false); + requestContact.requestZaddr->setText(c.second.getRequestZaddr()); + requestContact.requestMyAddr->setText(c.second.getAddress()); + + }else{ + + } + } + + + + }); + + QObject::connect(requestContact.pushButton, &QPushButton::clicked, [&] () { + + QString cid = requestContact.requestCID->text(); + auto addr = requestContact.requestZaddr->text().trimmed(); + QString newLabel = requestContact.requestLabel->text().trimmed(); + auto myAddr = requestContact.requestMyAddr->text().trimmed(); + + QString avatar = QString(":/icons/res/") + requestContact.comboBoxAvatar->currentText() + QString(".png"); + + if (addr.isEmpty() || newLabel.isEmpty()) + { + QMessageBox::critical( + this, + QObject::tr("Address or Label Error"), + QObject::tr("Address or Label cannot be empty"), + QMessageBox::Ok + ); + return; + } + + // Test if address is valid. + if (!Settings::isValidAddress(addr)) + { + QMessageBox::critical( + this, + QObject::tr("Address Format Error"), + QObject::tr("%1 doesn't seem to be a valid hush address.").arg(addr), + QMessageBox::Ok + ); + return; + } + + + AddressBook::getInstance()->addAddressLabel(newLabel, addr, myAddr, cid, avatar); + rpc->refreshContacts( + ui->listContactWidget); + + QMessageBox::information(this, "Added Contact","successfully added your new contact. You can now Chat with this contact"); + + }); + + dialog.exec(); + +} + +void ChatModel::addCid(QString tx, QString cid) +{ + this->cidMap[tx] = cid; +} + +void ChatModel::addHeader(QString tx, QString headerbytes) +{ + this->headerMap[tx] = headerbytes; +} + +void ChatModel::addMemo(QString tx, QString memo) +{ + this->OldMemoByTx[tx] = memo; +} + + + +void ChatModel::addrequestZaddr(QString tx, QString requestZaddr) +{ + this->requestZaddrMap[tx] = requestZaddr; +} + +void ChatModel::addconfirmations(QString tx, int confirmation) +{ + this->confirmationsMap[tx] = confirmation; +} + +QString ChatModel::getCidByTx(QString tx) +{ + for(auto& pair : this->cidMap) + { + + } + + if(this->cidMap.count(tx) > 0) + { + return this->cidMap[tx]; + } + + return QString("0xdeadbeef"); +} + +QString ChatModel::getMemoByTx(QString tx) +{ + for(auto& pair : this->OldMemoByTx) + { + + } + + if(this->OldMemoByTx.count(tx) > 0) + { + return this->OldMemoByTx[tx]; + } + + return QString("0xdeadbeef"); +} + + + + +QString ChatModel::getHeaderByTx(QString tx) +{ + for(auto& pair : this->headerMap) + { + + } + + if(this->headerMap.count(tx) > 0) + { + return this->headerMap[tx]; + } + + return QString("0xdeadbeef"); +} + +QString ChatModel::getConfirmationByTx(QString tx) +{ + for(auto& pair : this->confirmationsMap) + { + + } + + if(this->confirmationsMap.count(tx) > 0) + { + return this->confirmationsMap[tx]; + } + + return QString("0xdeadbeef"); +} + +QString ChatModel::getrequestZaddrByTx(QString tx) +{ + for(auto& pair : this->requestZaddrMap) + { + + } + + if(this->requestZaddrMap.count(tx) > 0) + { + return this->requestZaddrMap[tx]; + } + + return QString("0xdeadbeef"); +} + +void ChatModel::killCidCache() +{ + this->cidMap.clear(); +} + +void ChatModel::killrequestZaddrCache() +{ + this->requestZaddrMap.clear(); +} + +void ChatModel::killConfirmationCache() +{ + this->confirmationsMap.clear(); +} +void ChatModel::killMemoCache() +{ + this->OldMemoByTx.clear(); +} + +QString MainWindow::createHeaderMemo(QString type, QString cid, QString zaddr, QString headerbytes, QString publickey, int version=0, int headerNumber=1) +{ + + QString header=""; + QJsonDocument j; + QJsonObject h; + // We use short keynames to use less space for metadata and so allow + // the user to send more actual data in memos + h["h"] = headerNumber; // header number + h["v"] = version; // HushChat version + h["z"] = zaddr; // zaddr to respond to + h["cid"] = cid; // conversation id + h["t"] = type; // Memo or incoming contact request + h["e"] = headerbytes; // Memo or incoming contact request + h["p"] = publickey; // Memo or incoming contact request + + + j.setObject(h); + header = j.toJson(); + return header; + +} + + +// Create a Tx from the current state of the Chat page. +Tx MainWindow::createTxFromChatPage() { + Tx tx; + CAmount totalAmt; + // For each addr/amt in the Chat tab + { + + QString amtStr = "0"; + CAmount amt; + + + amt = CAmount::fromDecimalString("0"); + totalAmt = totalAmt + amt; + + + for(auto &c : AddressBook::getInstance()->getAllAddressLabels()) + + if (ui->contactNameMemo->text().trimmed() == c.getName()) { + + QString cid = c.getCid(); + QString myAddr = c.getMyAddress(); + QString type = "Memo"; + QString addr = c.getPartnerAddress(); + + + + /////////User input for chatmemos + QString memoplain = ui->memoTxtChat->toPlainText().trimmed(); + + /////////We convert the user input from QString to unsigned char*, so we can encrypt it later + int lengthmemo = memoplain.length(); + + char *memoplainchar = NULL; + memoplainchar = new char[lengthmemo+1]; + strncpy(memoplainchar, memoplain.toLocal8Bit(), lengthmemo +1); + + /////////We convert the CID from QString to unsigned char*, so we can encrypt it later + int lengthcid = cid.length(); + + char *cidchar = NULL; + cidchar = new char[lengthcid+1]; + strncpy(cidchar, cid.toLocal8Bit(), lengthcid +1); + + + + QString pubkey = this->getPubkeyByAddress(addr); + QString passphrase = DataStore::getChatDataStore()->getPassword(); + QString hashEncryptionKey = passphrase; + int length = hashEncryptionKey.length(); + + + ////////////////Generate the secretkey for our message encryption + + char *hashEncryptionKeyraw = NULL; + hashEncryptionKeyraw = new char[length+1]; + strncpy(hashEncryptionKeyraw, hashEncryptionKey.toLocal8Bit(), length +1); + + #define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw) + #define MESSAGEAS1_LEN length + unsigned char hash[crypto_kx_SEEDBYTES]; + + crypto_hash_sha256(hash,MESSAGEAS1, MESSAGEAS1_LEN); + + + unsigned char sk[crypto_kx_SECRETKEYBYTES]; + unsigned char pk[crypto_kx_PUBLICKEYBYTES]; + unsigned char server_rx[crypto_kx_SESSIONKEYBYTES], server_tx[crypto_kx_SESSIONKEYBYTES]; + + if (crypto_kx_seed_keypair(pk,sk, + hash) !=0) { + } + ////////////////Get the pubkey from Bob, so we can create the share key + + const QByteArray pubkeyBobArray = QByteArray::fromHex(pubkey.toLatin1()); + const unsigned char *pubkeyBob = reinterpret_cast(pubkeyBobArray.constData()); + /////Create the shared key for sending the message + + if (crypto_kx_server_session_keys(server_rx, server_tx, + pk, sk, pubkeyBob) != 0) { + /* Suspicious client public key, bail out */ + } + + + + ////////////Now lets encrypt the message Alice send to Bob////////////////////////////// + #define MESSAGE (const unsigned char *) memoplainchar + #define MESSAGE_LEN lengthmemo + #define CIPHERTEXT_LEN (crypto_secretstream_xchacha20poly1305_ABYTES + MESSAGE_LEN) + unsigned char ciphertext[CIPHERTEXT_LEN]; + unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; + + crypto_secretstream_xchacha20poly1305_state state; + + /* Set up a new stream: initialize the state and create the header */ + crypto_secretstream_xchacha20poly1305_init_push(&state, header, server_tx); + + + /* Now, encrypt the first chunk. `c1` will contain an encrypted, + * authenticated representation of `MESSAGE_PART1`. */ + crypto_secretstream_xchacha20poly1305_push + (&state, ciphertext, NULL, MESSAGE, MESSAGE_LEN, NULL, 0, crypto_secretstream_xchacha20poly1305_TAG_FINAL); + + ////Create the HM for this message + QString headerbytes = QByteArray(reinterpret_cast(header), crypto_secretstream_xchacha20poly1305_HEADERBYTES).toHex(); + QString publickeyAlice = QByteArray(reinterpret_cast(pk), crypto_kx_PUBLICKEYBYTES).toHex(); + + + QString hmemo= createHeaderMemo(type,cid,myAddr,headerbytes,publickeyAlice); + + /////Ciphertext Memo + QString memo = QByteArray(reinterpret_cast(ciphertext), CIPHERTEXT_LEN).toHex(); + + + tx.toAddrs.push_back(ToFields{addr, amt, hmemo}); + tx.toAddrs.push_back(ToFields{addr, amt, memo}); + + } + } + + tx.fee = Settings::getMinerFee(); + + return tx; + +} + +void MainWindow::sendChat() { + +////////////////////////////Todo: Check if a Contact is selected////////// + + // Create a Tx from the values on the send tab. Note that this Tx object + // might not be valid yet. + + QString Name = ui->contactNameMemo->text(); + + if ((ui->contactNameMemo->text().isEmpty()) || (ui->memoTxtChat->toPlainText().trimmed().isEmpty())) { + + QMessageBox msg(QMessageBox::Critical, tr("You have to select a contact and insert a Memo"), + tr("You have selected no Contact from Contactlist,\n") + tr("\nor your Memo is empty"), + QMessageBox::Ok, this); + + msg.exec(); + return; + } + + int max = 235; + QString chattext = ui->memoTxtChat->toPlainText(); + int size = chattext.size(); + + if (size > max){ + + QMessageBox msg(QMessageBox::Critical, tr("Your Message is too long"), + tr("You can only write messages with 235 character maximum \n") + tr("\n Please reduce your message to 235 character."), + QMessageBox::Ok, this); + + msg.exec(); + return; + } + + Tx tx = createTxFromChatPage(); + + QString error = doSendChatTxValidations(tx); + + if (!error.isEmpty()) { + // Something went wrong, so show an error and exit + QMessageBox msg(QMessageBox::Critical, tr("Message Error"), error, + QMessageBox::Ok, this); + + msg.exec(); + + // abort the Tx + return; + } + + auto movie = new QMovie(this); + auto movie1 = new QMovie(this); + movie->setFileName(":/img/res/loaderblack.gif"); + movie1->setFileName(":/img/res/loaderwhite.gif"); + + auto theme = Settings::getInstance()->get_theme_name(); + if (theme == "Dark" || theme == "Midnight") { + + connect(movie, &QMovie::frameChanged, [=]{ + ui->sendChatButton->setIcon(movie->currentPixmap()); + }); + movie->start(); + ui->sendChatButton->show(); + ui->sendChatButton->setEnabled(false); + + } else { + + connect(movie1, &QMovie::frameChanged, [=]{ + ui->sendChatButton->setIcon(movie1->currentPixmap()); + }); + movie1->start(); + ui->sendChatButton->show(); + ui->sendChatButton->setEnabled(false); + } + + ui->memoTxtChat->clear(); + + // And send the Tx + rpc->executeTransaction(tx, + [=] (QString txid) { + ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); + + + QTimer::singleShot(1000, [=]() { + + if (theme == "Dark" || theme == "Midnight") { + QPixmap send(":/icons/res/send-white.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + movie->stop(); + ui->sendChatButton->setEnabled(true); + }else{ + + QPixmap send(":/icons/res/sendBlack.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + movie1->stop(); + ui->sendChatButton->setEnabled(true); + } + + }); + + // Force a UI update so we get the unconfirmed Tx + rpc->refresh(true); + ui->memoTxtChat->clear(); + + }, + // Errored out + [=] (QString opid, QString errStr) { + ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000); + + if (!opid.isEmpty()) + errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr; + + QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok); + movie->stop(); + + + if (theme == "Dark" || theme == "Midnight") { + QPixmap send(":/icons/res/send-white.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + movie->stop(); + ui->sendChatButton->setEnabled(true); + }else{ + + QPixmap send(":/icons/res/sendBlack.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + movie1->stop(); + ui->sendChatButton->setEnabled(true); + } + + + + } + ); + + } + +QString MainWindow::doSendChatTxValidations(Tx tx) { + // Check to see if we have enough verified funds to send the Tx. + + CAmount total; + for (auto toAddr : tx.toAddrs) { + if (!Settings::isValidAddress(toAddr.addr)) { + QString addr = (toAddr.addr.length() > 100 ? toAddr.addr.left(100) + "..." : toAddr.addr); + return QString(tr("Recipient Address ")) % addr % tr(" is Invalid"); + } + + // This technically shouldn't be possible, but issue #62 seems to have discovered a bug + // somewhere, so just add a check to make sure. + if (toAddr.amount.toqint64() < 0) { + return QString(tr("Amount for address '%1' is invalid!").arg(toAddr.addr)); + } + + total = total + toAddr.amount; + } + total = total + tx.fee; + + auto available = rpc->getModel()->getAvailableBalance(); + + if (available < total) { + return tr("Not enough available funds to send this transaction\n\nHave: %1\nNeed: %2\n\nNote: Funds need 1 confirmations before they can be spent") + .arg(available.toDecimalhushString(), total.toDecimalhushString()); + } + + return ""; +} + +void::MainWindow::addContact() +{ + Ui_Dialog request; + QDialog dialog(this); + request.setupUi(&dialog); + Settings::saveRestore(&dialog); + +QObject::connect(request.newZaddr, &QPushButton::clicked, [&] () { + try + { + bool sapling = true; + rpc->createNewZaddr(sapling, [=] (json reply) { + QString myAddr = QString::fromStdString(reply.get()[0]); + rpc->refreshAddresses(); + request.myzaddr->setText(myAddr); + ui->listReceiveAddresses->insertItem(0, myAddr); + ui->listReceiveAddresses->setCurrentIndex(0); + }); + + }catch(...) + { + + + qDebug() << QString("Caught something nasty with myZaddr Contact"); + } +}); + + QString cid = QUuid::createUuid().toString(QUuid::WithoutBraces); + + QObject::connect(request.sendRequestButton, &QPushButton::clicked, [&] () { + + QString addr = request.zaddr->text(); + QString myAddr = request.myzaddr->text().trimmed(); + QString memo = request.memorequest->toPlainText().trimmed(); + QString avatar = QString(":/icons/res/") + request.comboBoxAvatar->currentText() + QString(".png"); + QString label = request.labelRequest->text().trimmed(); + + + contactRequest.setSenderAddress(myAddr); + contactRequest.setReceiverAddress(addr); + contactRequest.setMemo(memo); + contactRequest.setCid(cid); + contactRequest.setAvatar(avatar); + contactRequest.setLabel(label); + + }); + + QObject::connect(request.sendRequestButton, &QPushButton::clicked, this, &MainWindow::saveandsendContact); + // QObject::connect(request.onlyAdd, &QPushButton::clicked, this, &MainWindow::saveContact); + + dialog.exec(); + + rpc->refreshContacts(ui->listContactWidget); + +} + +void MainWindow::saveandsendContact() +{ + this->ContactRequest(); + +} + +// Create a Tx for a contact Request +Tx MainWindow::createTxForSafeContactRequest() +{ + Tx tx; +{ + CAmount totalAmt; + QString amtStr = "0"; + CAmount amt; + QString headerbytes = ""; + amt = CAmount::fromDecimalString("0"); + totalAmt = totalAmt + amt; + + QString cid = contactRequest.getCid(); + QString myAddr = contactRequest.getSenderAddress(); + QString type = "Cont"; + QString addr = contactRequest.getReceiverAddress(); + + + QString memo = contactRequest.getMemo(); + // QString privkey = rpc->fetchPrivKey(myAddr); + QString passphrase = DataStore::getChatDataStore()->getPassword(); + QString hashEncryptionKey = passphrase; + int length = hashEncryptionKey.length(); + + ////////////////Generate the secretkey for our message encryption + char *hashEncryptionKeyraw = NULL; + hashEncryptionKeyraw = new char[length+1]; + strncpy(hashEncryptionKeyraw, hashEncryptionKey.toLocal8Bit(), length +1); + #define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw) + #define MESSAGEAS1_LEN length + + + unsigned char hash[crypto_kx_SEEDBYTES]; + + crypto_hash_sha256(hash,MESSAGEAS1, MESSAGEAS1_LEN); + + + unsigned char sk[crypto_kx_SECRETKEYBYTES]; + unsigned char pk[crypto_kx_PUBLICKEYBYTES]; + + if (crypto_kx_seed_keypair(pk,sk, + hash) !=0) { + } + + QString publicKey = QByteArray(reinterpret_cast(pk), crypto_kx_PUBLICKEYBYTES).toHex(); + + QString hmemo= createHeaderMemo(type,cid,myAddr,"", publicKey); + + + tx.toAddrs.push_back(ToFields{addr, amt, hmemo}); + tx.toAddrs.push_back(ToFields{addr, amt, memo}); + tx.fee = Settings::getMinerFee(); + +} + + return tx; +} + +void MainWindow::ContactRequest() { + + if (contactRequest.getReceiverAddress().isEmpty() || contactRequest.getMemo().isEmpty()) { + + QMessageBox msg(QMessageBox::Critical, tr("You have to select a contact and insert a Memo"), + tr("You have selected no Contact from Contactlist,\n") + tr("\nor your Memo is empty"), + QMessageBox::Ok, this); + + msg.exec(); + return; + } + + if (contactRequest.getSenderAddress().size() > 80) { + + QMessageBox msg(QMessageBox::Critical, tr("Missing HushChat Address"), + tr("You have to create your HushChat address to send a contact request,\n"), + QMessageBox::Ok, this); + + msg.exec(); + return; + } + + int max = 235; + QString chattext = contactRequest.getMemo();; + int size = chattext.size(); + + if (size > max){ + + // auto addr = ""; + // if (! Settings::isZAddress(AddressBook::addressFromAddressLabel(addr->text()))) { + QMessageBox msg(QMessageBox::Critical, tr("Your Message is too long"), + tr("You can only write messages with 235 character maximum \n") + tr("\n Please reduce your message to 235 character."), + QMessageBox::Ok, this); + + msg.exec(); + return; + } + + Tx tx = createTxForSafeContactRequest(); + + QString error = doSendRequestTxValidations(tx); + + if (!error.isEmpty()) { + // Something went wrong, so show an error and exit + QMessageBox msg(QMessageBox::Critical, tr("Message Error"), error, + QMessageBox::Ok, this); + + msg.exec(); + + // abort the Tx + return; + } + + // Create a new Dialog to show that we are computing/sending the Tx + auto d = new QDialog(this); + auto connD = new Ui_ConnectionDialog(); + connD->setupUi(d); + QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated.gif"); + QMovie *movie2 = new QMovie(":/img/res/silentdragonlite-animated-dark.gif"); + auto theme = Settings::getInstance()->get_theme_name(); + if (theme == "Dark" || theme == "Midnight") { + movie2->setScaledSize(QSize(512,512)); + connD->topIcon->setMovie(movie2); + movie2->start(); + } else { + movie1->setScaledSize(QSize(512,512)); + connD->topIcon->setMovie(movie1); + movie1->start(); + } + + connD->status->setText(tr("Please wait...")); + connD->statusDetail->setText(tr("Your contact request will be sent")); + + d->show(); + ui->memoTxtChat->clear(); + + // And send the Tx + rpc->executeTransaction(tx, + [=] (QString txid) { + ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); + + connD->status->setText(tr("Done!")); + connD->statusDetail->setText(txid); + + QTimer::singleShot(1000, [=]() { + d->accept(); + d->close(); + delete connD; + delete d; + + }); + + /////Add this contact after we sent the request + + QString addr = contactRequest.getReceiverAddress(); + QString newLabel = contactRequest.getLabel(); + QString myAddr = contactRequest.getSenderAddress(); + QString cid = contactRequest.getCid(); + QString avatar = contactRequest.getAvatar(); + + if (addr.isEmpty() || newLabel.isEmpty()) + { + QMessageBox::critical( + this, + QObject::tr("Address or Label Error"), + QObject::tr("Address or Label cannot be empty"), + QMessageBox::Ok + ); + return; + } + + // Test if address is valid. + if (!Settings::isValidAddress(addr)) + { + QMessageBox::critical( + this, + QObject::tr("Address Format Error"), + QObject::tr("%1 doesn't seem to be a valid hush address.").arg(addr), + QMessageBox::Ok + ); + return; + } + + ///////Todo: Test if label allready exist! + + ////// Success, so show it + AddressBook::getInstance()->addAddressLabel(newLabel, addr, myAddr, cid, avatar); + rpc->refreshContacts( + ui->listContactWidget); + QMessageBox::information( + this, + QObject::tr("Added Contact"), + QObject::tr("successfully added your new contact").arg(newLabel), + QMessageBox::Ok + + ); + return; + // Force a UI update so we get the unconfirmed Tx + // rpc->refresh(true); + ui->memoTxtChat->clear(); + rpc->refresh(true); + rpc->refreshContacts( + ui->listContactWidget); + + }, + // Errored out + [=] (QString opid, QString errStr) { + ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000); + + d->accept(); + d->close(); + delete connD; + delete d; + + if (!opid.isEmpty()) + errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr; + + QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok); + } + ); + + } + + +QString MainWindow::doSendRequestTxValidations(Tx tx) { + // Check to see if we have enough verified funds to send the Tx. + + CAmount total; + for (auto toAddr : tx.toAddrs) { + if (!Settings::isValidAddress(toAddr.addr)) { + QString addr = (toAddr.addr.length() > 100 ? toAddr.addr.left(100) + "..." : toAddr.addr); + return QString(tr("Recipient Address ")) % addr % tr(" is Invalid"); + } + + // This technically shouldn't be possible, but issue #62 seems to have discovered a bug + // somewhere, so just add a check to make sure. + if (toAddr.amount.toqint64() < 0) { + return QString(tr("Amount for address '%1' is invalid!").arg(toAddr.addr)); + } + + total = total + toAddr.amount; + } + total = total + tx.fee; + + auto available = rpc->getModel()->getAvailableBalance(); + + if (available < total) { + return tr("Not enough available funds to send this transaction\n\nHave: %1\nNeed: %2\n\nNote: Funds need 1 confirmations before they can be spent") + .arg(available.toDecimalhushString(), total.toDecimalhushString()); + } + + return ""; +} + + diff --git a/src/chatmodel.h b/src/chatmodel.h new file mode 100644 index 0000000..1676993 --- /dev/null +++ b/src/chatmodel.h @@ -0,0 +1,76 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#ifndef CHATMODEL_H +#define CHATMODEL_H +#include +#include +#include +#include +#include +#include +#include +#include "precompiled.h" +#include "mainwindow.h" +#include "controller.h" +#include "settings.h" +#include "camount.h" +#include "Model/ChatItem.h" +#include "Model/ContactRequest.h" +#include "Chat/Helper/ChatDelegator.h" +#include "Chat/Helper/ChatIDGenerator.h" + + +namespace Ui { + class MainWindow; +} +class ChatModel +{ + private: + std::map chatItems; + QTableView* parent; + Ui::MainWindow* ui; + MainWindow* main; + std::map cidMap; + std::map requestZaddrMap; + std::map confirmationsMap; + std::map> sendrequestMap; + std::map headerMap; + std::map AddressbyLabelMap; + std::map OldMemoByTx; + + + public: + ChatModel() {}; + ChatModel(std::map chatItems); + ChatModel(std::vector chatItems); + std::map getItems(); + void setItems(std::map items); + QString zaddr(); + void setItems(std::vector items); + void triggerRequest(); + void showMessages(); + void clear(); + void addAddressbylabel(QString addr, QString label); + void addMessage(ChatItem item); + void addMessage(QString timestamp, ChatItem item); + void addCid(QString tx, QString cid); + void addHeader(QString tx, QString headerbytes); + void addMemo(QString tx, QString memo); + void addrequestZaddr(QString tx, QString requestZaddr); + void addconfirmations(QString tx, int confirmation); + void addSendRequest(int i, QString myAddr, QString cid, QString addr ); + QString getCidByTx(QString tx); + QString getHeaderByTx(QString tx); + QString getrequestZaddrByTx(QString tx); + QString getConfirmationByTx(QString tx); + QString getMemoByTx(QString tx); + QString Addressbylabel(QString addr); + void killCidCache(); + void killConfirmationCache(); + void killrequestZaddrCache(); + void killMemoCache(); + +}; + +#endif \ No newline at end of file diff --git a/src/connection.cpp b/src/connection.cpp index 99666ac..569ebed 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -10,73 +10,85 @@ using json = nlohmann::json; -ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc) +ConnectionLoader::ConnectionLoader(MainWindow* main, Controller* rpc) { this->main = main; this->rpc = rpc; + d = new QDialog(main); connD = new Ui_ConnectionDialog(); connD->setupUi(d); - QPixmap logo(":/img/res/logobig.gif"); - connD->topIcon->setBasePixmap( - logo.scaled( - 256, - 256, - Qt::KeepAspectRatio, - Qt::SmoothTransformation - ) - ); + + auto theme = Settings::getInstance()->get_theme_name(); + qDebug() << theme << "theme has loaded"; + auto size = QSize(512,512); + + if (theme == "Dark" || theme == "Midnight") { + QMovie *movie2 = new QMovie(":/img/res/silentdragonlite-animated-startup-dark.gif");; + movie2->setScaledSize(size); + qDebug() << "Animation dark loaded"; + connD->topIcon->setMovie(movie2); + movie2->start(); + } else { + QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated-startup.gif");; + movie1->setScaledSize(size); + qDebug() << "Animation light loaded"; + connD->topIcon->setMovie(movie1); + movie1->start(); + } + main->logger->write("Set animation"); + qDebug() << "Set animation"; isSyncing = new QAtomicInteger(); } -ConnectionLoader::~ConnectionLoader() +ConnectionLoader::~ConnectionLoader() { delete isSyncing; delete connD; delete d; } -void ConnectionLoader::loadConnection() +void ConnectionLoader::loadConnection() { QTimer::singleShot(1, [=]() { this->doAutoConnect(); }); if (!Settings::getInstance()->isHeadless()) d->exec(); } -void ConnectionLoader::doAutoConnect() +void ConnectionLoader::doAutoConnect() { qDebug() << "Doing autoconnect"; auto config = std::shared_ptr(new ConnectionConfig()); - config->dangerous = true; + config->dangerous = false; config->server = Settings::getInstance()->getSettings().server; // Initialize the library main->logger->write(QObject::tr("Attempting to initialize library with ") + config->server); // Check to see if there's an existing wallet - if (litelib_wallet_exists(Settings::getDefaultChainName().toStdString().c_str())) + if (litelib_wallet_exists(Settings::getDefaultChainName().toStdString().c_str())) { main->logger->write(QObject::tr("Using existing wallet.")); char* resp = litelib_initialize_existing( - config->dangerous, + config->dangerous, config->server.toStdString().c_str() ); QString response = litelib_process_response(resp); - if (response.toUpper().trimmed() != "OK") + if (response.toUpper().trimmed() != "OK") { showError(response); return; } - } + } else { main->logger->write(QObject::tr("Create/restore wallet.")); createOrRestore(config->dangerous, config->server); d->show(); - } - + } + auto connection = makeConnection(config); auto me = this; @@ -97,14 +109,14 @@ void ConnectionLoader::doAutoConnect() // When sync is done, set the connection this->doRPCSetConnection(connection); }); - + // While it is syncing, we'll show the status updates while it is alive. QObject::connect(syncTimer, &QTimer::timeout, [=]() { // Check the sync status if (isSyncing != nullptr && isSyncing->load()) { // Get the sync status connection->doRPC("syncstatus", "", [=](json reply) { - if (isSyncing != nullptr && reply.find("synced_blocks") != reply.end()) + if (isSyncing != nullptr && reply.find("synced_blocks") != reply.end()) { qint64 synced = reply["synced_blocks"].get(); qint64 total = reply["total_blocks"].get(); @@ -117,8 +129,8 @@ void ConnectionLoader::doAutoConnect() qDebug() << "Sync error" << err; }); } - }); - + }); + syncTimer->setInterval(1* 1000); syncTimer->start(); @@ -127,16 +139,16 @@ void ConnectionLoader::doAutoConnect() }); } -void ConnectionLoader::createOrRestore(bool dangerous, QString server) +void ConnectionLoader::createOrRestore(bool dangerous, QString server) { // Close the startup dialog, since we'll be showing the wizard d->hide(); // Create a wizard - FirstTimeWizard wizard(dangerous, server); + FirstTimeWizard wizard(dangerous, server); wizard.exec(); } -void ConnectionLoader::doRPCSetConnection(Connection* conn) +void ConnectionLoader::doRPCSetConnection(Connection* conn) { qDebug() << "Connectionloader finished, setting connection"; rpc->setConnection(conn); @@ -144,26 +156,26 @@ void ConnectionLoader::doRPCSetConnection(Connection* conn) QTimer::singleShot(1, [=]() { delete this; }); } -Connection* ConnectionLoader::makeConnection(std::shared_ptr config) +Connection* ConnectionLoader::makeConnection(std::shared_ptr config) { return new Connection(main, config); } // Update the UI with the status -void ConnectionLoader::showInformation(QString info, QString detail) +void ConnectionLoader::showInformation(QString info, QString detail) { connD->status->setText(info); connD->statusDetail->setText(detail); } /** - * Show error will close the loading dialog and show an error. + * Show error will close the loading dialog and show an error. */ -void ConnectionLoader::showError(QString explanation) -{ +void ConnectionLoader::showError(QString explanation) +{ rpc->noConnection(); QMessageBox::critical( - main, + main, QObject::tr("Connection Error"), explanation, QMessageBox::Ok @@ -171,7 +183,7 @@ void ConnectionLoader::showError(QString explanation) d->close(); } -QString litelib_process_response(char* resp) +QString litelib_process_response(char* resp) { char* resp_copy = new char[strlen(resp) + 1]; //a safer version of strcpy @@ -185,15 +197,15 @@ QString litelib_process_response(char* resp) /*********************************************************************************** * Connection, Executor and Callback Class - ************************************************************************************/ -void Executor::run() + ************************************************************************************/ +void Executor::run() { char* resp = litelib_execute(this->cmd.toStdString().c_str(), this->args.toStdString().c_str()); QString reply = litelib_process_response(resp); //qDebug() << "RPC Reply=" << reply; auto parsed = json::parse( - reply.toStdString().c_str(), - nullptr, + reply.toStdString().c_str(), + nullptr, false ); if (parsed.is_discarded() || parsed.is_null()) @@ -204,14 +216,14 @@ void Executor::run() } -void Callback::processRPCCallback(json resp) +void Callback::processRPCCallback(json resp) { this->cb(resp); // Destroy self delete this; } -void Callback::processError(QString resp) +void Callback::processError(QString resp) { this->errCb(resp); // Destroy self @@ -226,7 +238,7 @@ Connection::Connection(MainWindow* m, std::shared_ptr conf) qRegisterMetaType("json"); } -void Connection::doRPC(const QString cmd, const QString args, const std::function& cb, const std::function& errCb) +void Connection::doRPC(const QString cmd, const QString args, const std::function& cb, const std::function& errCb) { if (shutdownInProgress) // Ignoring RPC because shutdown in progress @@ -242,26 +254,26 @@ void Connection::doRPC(const QString cmd, const QString args, const std::functio QObject::connect(runner, &Executor::responseReady, c, &Callback::processRPCCallback); QObject::connect(runner, &Executor::handleError, c, &Callback::processError); - QThreadPool::globalInstance()->start(runner); + QThreadPool::globalInstance()->start(runner); } -void Connection::doRPCWithDefaultErrorHandling(const QString cmd, const QString args, const std::function& cb) +void Connection::doRPCWithDefaultErrorHandling(const QString cmd, const QString args, const std::function& cb) { doRPC(cmd, args, cb, [=] (QString err) { this->showTxError(err); }); -} +} -void Connection::doRPCIgnoreError(const QString cmd, const QString args, const std::function& cb) +void Connection::doRPCIgnoreError(const QString cmd, const QString args, const std::function& cb) { doRPC(cmd, args, cb, [=] (auto) { // Ignored error handling }); } -void Connection::showTxError(const QString& error) +void Connection::showTxError(const QString& error) { - if (error.isNull()) + if (error.isNull()) return; // Prevent multiple dialog boxes from showing, because they're all called async @@ -271,8 +283,8 @@ void Connection::showTxError(const QString& error) shown = true; QMessageBox::critical( - main, - QObject::tr("Transaction Error"), + main, + QObject::tr("Transaction Error"), QObject::tr("There was an error sending the transaction. The error was:") + "\n\n" + error, QMessageBox::StandardButton::Ok ); @@ -281,8 +293,8 @@ void Connection::showTxError(const QString& error) /** * Prevent all future calls from going through - */ -void Connection::shutdown() + */ +void Connection::shutdown() { shutdownInProgress = true; } diff --git a/src/connection.h b/src/connection.h index 5ed3a09..7130710 100644 --- a/src/connection.h +++ b/src/connection.h @@ -5,6 +5,7 @@ #include "ui_connection.h" #include "precompiled.h" + using json = nlohmann::json; class Controller; diff --git a/src/connection.ui b/src/connection.ui index c421f68..8c950bd 100644 --- a/src/connection.ui +++ b/src/connection.ui @@ -9,10 +9,22 @@ 0 0 - 513 - 201 + 512 + 512 + + + 512 + 512 + + + + + 512 + 512 + + SilentDragonLite @@ -33,7 +45,7 @@ 0 - + 0 @@ -55,7 +67,7 @@ - Starting Up + The Dragon Awakens... Qt::AlignCenter @@ -84,13 +96,6 @@ - - - FilledIconLabel - QLabel -
fillediconlabel.h
-
-
diff --git a/src/contactmodel.cpp b/src/contactmodel.cpp new file mode 100644 index 0000000..f3d529f --- /dev/null +++ b/src/contactmodel.cpp @@ -0,0 +1,159 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + +#include "contactmodel.h" +#include "addressbook.h" +#include "mainwindow.h" +#include "chatmodel.h" +#include "requestdialog.h" +#include "ui_requestdialog.h" +#include "ui_hushrequest.h" +#include "settings.h" +#include "controller.h" + +void ContactModel::addLabel(QString addr, QString label) +{ + this->AddressMap[addr] = label; +} + +QString ContactModel::getContactbyAddress(QString addr) +{ + for(auto& pair : this->AddressMap) + { + + } + + if(this->AddressMap.count(addr) > 0) + { + return this->AddressMap[addr]; + } + + return QString("0xdeadbeef"); +} + +void ContactModel::renderContactList(QListView* view) +{ + QStandardItemModel* contact = new QStandardItemModel(); + + for(auto &c : AddressBook::getInstance()->getAllAddressLabels()) + { + QString avatar = c.getAvatar(); + + QStandardItem* Items1 = new QStandardItem(c.getName()); + Items1->setData(QIcon(avatar),Qt::DecorationRole); + + contact->appendRow(Items1); + view->setModel(contact); + view->setIconSize(QSize(60,70)); + view->setUniformItemSizes(true); + view->setDragDropMode(QAbstractItemView::DropOnly); + view->show(); + QString addr = c.getPartnerAddress(); + QString label = c.getName(); + this->addLabel(addr, label); + } +} + +void MainWindow::showRequesthush() { + + Ui_hushrequest req; + QDialog d(this); + req.setupUi(&d); + Settings::saveRestore(&d); + + QString label = ui->contactNameMemo->text(); + for(auto &p : AddressBook::getInstance()->getAllAddressLabels()) + { + + if (p.getName() == label) + + { + + QString addr = p.getPartnerAddress(); + QString myzaddr = p.getMyAddress(); + + req.txtFrom->setText(addr); + req.lblAddressInfo->setText(myzaddr); + // Amount textbox + req.txtAmount->setValidator(this->getAmountValidator()); + QObject::connect(req.txtAmount, &QLineEdit::textChanged, [=] (auto text) { + CAmount amount = CAmount::fromDecimalString(text); + if (Settings::getInstance()->get_currency_name() == "USD") { + req.txtAmountUSD->setText(amount.toDecimalUSDString()); + } else if (Settings::getInstance()->get_currency_name() == "EUR") { + req.txtAmountUSD->setText(amount.toDecimalEURString()); + } else if (Settings::getInstance()->get_currency_name() == "BTC") { + req.txtAmountUSD->setText(amount.toDecimalBTCString()); + } else if (Settings::getInstance()->get_currency_name() == "CNY") { + req.txtAmountUSD->setText(amount.toDecimalCNYString()); + } else if (Settings::getInstance()->get_currency_name() == "RUB") { + req.txtAmountUSD->setText(amount.toDecimalRUBString()); + } else if (Settings::getInstance()->get_currency_name() == "CAD") { + req.txtAmountUSD->setText(amount.toDecimalCADString()); + } else if (Settings::getInstance()->get_currency_name() == "SGD") { + req.txtAmountUSD->setText(amount.toDecimalSGDString()); + } else if (Settings::getInstance()->get_currency_name() == "CHF") { + req.txtAmountUSD->setText(amount.toDecimalCHFString()); + } else if (Settings::getInstance()->get_currency_name() == "INR") { + req.txtAmountUSD->setText(amount.toDecimalINRString()); + } else if (Settings::getInstance()->get_currency_name() == "GBP") { + req.txtAmountUSD->setText(amount.toDecimalGBPString()); + } else if (Settings::getInstance()->get_currency_name() == "AUD") { + req.txtAmountUSD->setText(amount.toDecimalBTCString()); + } + }); + CAmount amount = CAmount::fromDecimalString(req.txtAmount->text()); + if (Settings::getInstance()->get_currency_name() == "USD") { + req.txtAmountUSD->setText(amount.toDecimalUSDString()); + } else if (Settings::getInstance()->get_currency_name() == "EUR") { + req.txtAmountUSD->setText(amount.toDecimalEURString()); + } else if (Settings::getInstance()->get_currency_name() == "BTC") { + req.txtAmountUSD->setText(amount.toDecimalBTCString()); + } else if (Settings::getInstance()->get_currency_name() == "CNY") { + req.txtAmountUSD->setText(amount.toDecimalCNYString()); + } else if (Settings::getInstance()->get_currency_name() == "RUB") { + req.txtAmountUSD->setText(amount.toDecimalRUBString()); + } else if (Settings::getInstance()->get_currency_name() == "CAD") { + req.txtAmountUSD->setText(amount.toDecimalCADString()); + } else if (Settings::getInstance()->get_currency_name() == "SGD") { + req.txtAmountUSD->setText(amount.toDecimalSGDString()); + } else if (Settings::getInstance()->get_currency_name() == "CHF") { + req.txtAmountUSD->setText(amount.toDecimalCHFString()); + } else if (Settings::getInstance()->get_currency_name() == "INR") { + req.txtAmountUSD->setText(amount.toDecimalINRString()); + } else if (Settings::getInstance()->get_currency_name() == "GBP") { + req.txtAmountUSD->setText(amount.toDecimalGBPString()); + } else if (Settings::getInstance()->get_currency_name() == "AUD") { + req.txtAmountUSD->setText(amount.toDecimalBTCString()); + } + req.txtMemo->setAcceptButton(req.buttonBox->button(QDialogButtonBox::Ok)); + req.txtMemo->setLenDisplayLabel(req.lblMemoLen); + req.txtMemo->setMaxLen(400); + + req.txtFrom->setFocus(); + + + } + } + if (d.exec() == QDialog::Accepted) { + // Construct a hush Payment URI with the data and pay it immediately. + CAmount amount = CAmount::fromDecimalString(req.txtAmount->text()); + QString memoURI = "hush:" + req.lblAddressInfo->text() + + "?amt=" + amount.toDecimalString() + + "&memo=" + QUrl::toPercentEncoding(req.txtMemo->toPlainText()); + + QString sendURI = "hush:" + AddressBook::addressFromAddressLabel(req.txtFrom->text()) + + "?amt=0.0001" + + "&memo=" + QUrl::toPercentEncoding(memoURI); + + // If the disclosed address in the memo doesn't have a balance, it will automatically fallback to the default + // sapling address + this->payhushURI(sendURI, req.lblAddressInfo->text()); + + + + } + +} + + diff --git a/src/contactmodel.h b/src/contactmodel.h new file mode 100644 index 0000000..584b5f9 --- /dev/null +++ b/src/contactmodel.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 +#ifndef CONTACTMODEL_H +#define CONTACTMODEL_H + +#include "Model/ContactItem.h" +#include +#include "mainwindow.h" + + +class ContactModel + +{ + public: + MainWindow* main; + std::map AddressMap; + + QString getContactbyAddress(QString addr); + void addLabel(QString addr, QString label); + + ContactModel() {} + void renderContactList(QListView* view); + + +}; + +#endif \ No newline at end of file diff --git a/src/contactrequest.ui b/src/contactrequest.ui new file mode 100644 index 0000000..d187eb1 --- /dev/null +++ b/src/contactrequest.ui @@ -0,0 +1,491 @@ + + + Dialog + + + + 0 + 0 + 780 + 351 + + + + + 0 + 0 + + + + + 780 + 351 + + + + + 780 + 351 + + + + Send Contact Request + + + + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Insert a nickname for your contact:</span></p></body></html> + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Choose an avatar for your contact:</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Insert a memo for your request:</span></p></body></html> + + + + + + + 6 + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Your HushChat Address:</span></p></body></html> + + + + + + + + 0 + 0 + + + + Create New Address + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + + 650 + 25 + + + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Insert the address of your contact:</span></p></body></html> + + + + + + + + 650 + 25 + + + + + 650 + 25 + + + + <html><head/><body><p><span style=" color:#d3d7cf;">Generate your HushChat Address - please wait a second - </span></p></body></html> + + + + + + + QLayout::SetMaximumSize + + + + + + 0 + 0 + + + + + 250 + 25 + + + + 25 + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + 0 + 25 + + + + + SDLogo + + + + :/icons/res/SDLogo.png + + + + + + Duke + + + + :/icons/res/Duke.png + + + + + + Denio + + + + :/icons/res/Denio.png + + + + + + Berg + + + + :/icons/res/Berg.png + + + + + + Stag + + + + :/icons/res/Stag.png + + + + + + Sharpee + + + + :/icons/res/Sharpee.png + + + + + + Elsa + + + + :/icons/res/Elsa.png + + + + + + Yoda + + + + :/icons/res/Yoda.png + + + + + + Garfield + + + + :/icons/res/Garfield.png + + + + + + Snoopy + + + + :/icons/res/Snoopy.png + + + + + + Popey + + + + :/icons/res/Popey.png + + + + + + Pinguin + + + + :/icons/res/Pinguin.png + + + + + + Mickey + + + + :/icons/res/Mickey.png + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + 0/512 + + + + + + + Add a memo to your request + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + Cancel + + + false + + + false + + + + + + + Add Contact and Send Request + + + false + + + false + + + + + + + + + cancel + clicked() + Dialog + reject() + + + 396 + 262 + + + 389 + 207 + + + + + sendRequestButton + clicked() + Dialog + accept() + + + 536 + 262 + + + 389 + 207 + + + + + diff --git a/src/controller.cpp b/src/controller.cpp index 18a158e..7ce4c2d 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -1,3 +1,6 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + #include "controller.h" #include "mainwindow.h" #include "addressbook.h" @@ -5,11 +8,12 @@ #include "version.h" #include "camount.h" #include "websockets.h" -#include "DataStore.h" -template<> -DataStore* DataStore::instance = nullptr; -template<> -bool DataStore::instanced = false; +#include "Model/ChatItem.h" +#include "DataStore/DataStore.h" + +ChatModel *chatModel = new ChatModel(); +Chat *chat = new Chat(); +ContactModel *contactModel = new ContactModel(); using json = nlohmann::json; @@ -87,13 +91,33 @@ void Controller::setConnection(Connection* c) // Create Sietch zdust addr at startup. // Using DataStore singelton, to store the data outside of lambda, bing bada boom :D - for(uint8_t i = 0; i < 10; i++) + for(uint8_t i = 0; i < 6; i++) { zrpc->createNewSietchZaddr( [=] (json reply) { QString zdust = QString::fromStdString(reply.get()[0]); - DataStore::getInstance()->setData("Sietch" + QString(i), zdust.toUtf8()); + DataStore::getSietchDataStore()->setData("Sietch" + QString(i), zdust.toUtf8()); }); } + refreshContacts( + ui->listContactWidget + + ); +} + +std::string Controller::encryptDecrypt(std::string toEncrypt) +{ + + int radomInteger = rand() % 1000000000 +100000; + + + char key = radomInteger; + std::string output = toEncrypt; + + for (int i = 0; i < toEncrypt.size(); i++) + output[i] = toEncrypt[i] ^ key; + + return output; + } // Build the RPC JSON Parameters for this tx @@ -105,35 +129,72 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx) json rec = json::object(); //creating the JSON dust parameters in a std::vector to iterate over there during tx - std::vector dust(10); - dust.resize(10, json::object()); + std::vector dust(6); + dust.resize(6, json::object()); // Create Sietch zdust addr again to not use it twice. // Using DataStore singelton, to store the data outside of lambda, bing bada boom :D - for(uint8_t i = 0; i < 10; i++) + for(uint8_t i = 0; i < 6; i++) { zrpc->createNewSietchZaddr( [=] (json reply) { QString zdust = QString::fromStdString(reply.get()[0]); - DataStore::getInstance()->setData(QString("Sietch") + QString(i), zdust.toUtf8()); + DataStore::getSietchDataStore()->setData(QString("Sietch") + QString(i), zdust.toUtf8()); } ); } // Set sietch zdust addr to json. // Using DataStore singelton, to store the data into the dusts, bing bada boom :D - for(uint8_t i = 0; i < 10; i++) + for(uint8_t i = 0; i < 6; i++) { - dust.at(i)["address"] = DataStore::getInstance()->getData(QString("Sietch" + QString(i))).toStdString(); + dust.at(i)["address"] = DataStore::getSietchDataStore()->getData(QString("Sietch" + QString(i))).toStdString(); } - DataStore::getInstance()->clear(); // clears the datastore + DataStore::getSietchDataStore()->clear(); // clears the datastore - // Dust amt/memo, construct the JSON - for(uint8_t i = 0; i < 10; i++) + const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + + int sizerandomString = rand() % 120 +10; + const int randomStringLength = sizerandomString; + + QString randomString; + for(int i=0; i(hash),MESSAGE_LEN1); +std::string encrypt = this->encryptDecrypt(decryptedMemo); +QString randomHashafter1 = QByteArray(reinterpret_cast(encrypt.c_str()),encrypt.size()).toHex(); +dust.at(i)["memo"] = randomHashafter1.toStdString(); + + } + + for(uint8_t i = 0; i < 6; i++) + { + dust.at(i)["amount"] = 0; + } + // For each addr/amt/memo, construct the JSON and also build the confirm dialog box for (int i=0; i < tx.toAddrs.size(); i++) @@ -147,36 +208,15 @@ void Controller::fillTxJsonParams(json& allRecepients, Tx tx) allRecepients.push_back(rec) ; } - int decider = qrand() % ((100 + 1)-1)+ 1;// random int between 1 and 100 - //50% chance of adding another zdust, shuffle. - - if(decider % 4 == 3) allRecepients.insert(std::begin(allRecepients), { dust.at(0), dust.at(1), dust.at(2), dust.at(3), dust.at(4), - dust.at(5), - dust.at(6), - dust.at(7), - dust.at(8) + dust.at(5) + }) ; - // std::shuffle(allRecepients.begin(),allRecepients.end(),std::random_device()); - else - allRecepients.insert(std::begin(allRecepients), { - dust.at(0), - dust.at(1), - dust.at(2), - dust.at(3), - dust.at(4), - dust.at(5), - dust.at(6), - dust.at(7), - dust.at(8), - dust.at(9) - }); - // std::shuffle(allRecepients.begin(),allRecepients.end(),std::random_device()); } void Controller::noConnection() @@ -245,12 +285,15 @@ void Controller::getInfoThenRefresh(bool force) zrpc->fetchInfo([=] (const json& reply) { prevCallSucceeded = true; int curBlock = reply["latest_block_height"].get(); - int longestchain = reply["longestchain"].get(); - int notarized = reply["notarized"].get(); + bool doUpdate = force || (model->getLatestBlock() != curBlock); int difficulty = reply["difficulty"].get(); int blocks_until_halving= 340000 - curBlock; int halving_days = (blocks_until_halving * 150) / (60*60*24) ; - bool doUpdate = force || (model->getLatestBlock() != curBlock); + int longestchain = reply["longestchain"].get(); + int notarized = reply["notarized"].get(); + + + model->setLatestBlock(curBlock); if ( Settings::getInstance()->get_currency_name() == "EUR" || @@ -266,7 +309,11 @@ void Controller::getInfoThenRefresh(bool force) ); ui->longestchain->setText( "Block: " + QLocale(QLocale::German).toString(longestchain) + ); + + + ui->difficulty->setText( QLocale(QLocale::German).toString(difficulty) ); @@ -312,6 +359,7 @@ void Controller::getInfoThenRefresh(bool force) QString chainName = Settings::getInstance()->isTestnet() ? "test" : "main"; main->statusLabel->setText(chainName + "(" + QString::number(curBlock) + ")"); + // use currency ComboBox as input if (Settings::getInstance()->get_currency_name() == "USD") @@ -329,6 +377,8 @@ void Controller::getInfoThenRefresh(bool force) " $ " + (QLocale(QLocale::English).toString(cap,'f', 2)) ); + + } else if (Settings::getInstance()->get_currency_name() == "EUR") { @@ -545,8 +595,12 @@ void Controller::getInfoThenRefresh(bool force) refreshAddresses(); // This calls refreshZSentTransactions() and refreshReceivedZTrans() refreshTransactions(); } + + int lag = longestchain - notarized ; + this->setLag(lag); }, [=](QString err) { // hushd has probably disappeared. + this->noConnection(); // Prevent multiple dialog boxes, because these are called async @@ -557,7 +611,7 @@ void Controller::getInfoThenRefresh(bool force) QMessageBox::critical( main, QObject::tr("Connection Error"), - QObject::tr("There was an error connecting to hushd. The error was") + ": \n\n"+ err, + QObject::tr("There was an error connecting to the server. Please check your internet connection. The error was") + ": \n\n"+ err, QMessageBox::StandardButton::Ok ); shown = false; @@ -567,6 +621,20 @@ void Controller::getInfoThenRefresh(bool force) }); } +int Controller::getLag() +{ + + return _lag; + +} + +void Controller::setLag(int lag) +{ + + _lag = lag; + +} + void Controller::refreshAddresses() { if (!zrpc->haveConnection()) @@ -621,7 +689,7 @@ void Controller::processUnspent(const json& reply, QMap* balan QString txid = QString::fromStdString(it["created_in_txid"]); CAmount amount = CAmount::fromqint64(it["value"].get()); - bool spendable = it["unconfirmed_spent"].is_null() && it["spent"].is_null(); // TODO: Wait for 4 confirmations + bool spendable = it["unconfirmed_spent"].is_null() && it["spent"].is_null(); // TODO: Wait for 1 confirmations bool pending = !it["unconfirmed_spent"].is_null(); unspentOutputs->push_back( @@ -655,7 +723,9 @@ void Controller::updateUIBalances() CAmount balAvailable = balT + balVerified; if (balZ < 0) balZ = CAmount::fromqint64(0); - + double price = (Settings::getInstance()->getBTCPrice() / 1000); + // ui->PriceMemo->setText(" The price of \n one HushChat \n Message is :\n BTC " + (QLocale(QLocale::English).toString(price, 'f',8)) + //+ " Messages left :" + ((balTotal.toDecimalhushString()) /0.0001) ); // Balances table ui->balSheilded->setText(balZ.toDecimalhushString()); ui->balVerified->setText(balVerified.toDecimalhushString()); @@ -808,6 +878,7 @@ void Controller::refreshBalances() CAmount balAvailable = balT + balVerified; model->setAvailableBalance(balAvailable); updateUIBalances(); + }); // 2. Get the UTXOs @@ -838,115 +909,531 @@ void Controller::refreshBalances() }); } -void Controller::refreshTransactions() -{ +void Controller::refreshTransactions() { if (!zrpc->haveConnection()) return noConnection(); zrpc->fetchTransactions([=] (json reply) { QList txdata; - for (auto& it : reply.get()) - { + for (auto& it : reply.get()) { QString address; CAmount total_amount; QList items; long confirmations; - if (it.find("unconfirmed") != it.end() && it["unconfirmed"].get()) + if (it.find("unconfirmed") != it.end() && it["unconfirmed"].get()) { confirmations = 0; - else + } else { confirmations = model->getLatestBlock() - it["block_height"].get() + 1; + } auto txid = QString::fromStdString(it["txid"]); auto datetime = it["datetime"].get(); - + // First, check if there's outgoing metadata - if (!it["outgoing_metadata"].is_null()) - { - for (auto o: it["outgoing_metadata"].get()) - { - QString address; + if (!it["outgoing_metadata"].is_null()) { + + for (auto o: it["outgoing_metadata"].get()) + { + // if (chatModel->getCidByTx(txid) == QString("0xdeadbeef")){ + QString address; + address = QString::fromStdString(o["address"]); - + // Sent items are -ve CAmount amount = CAmount::fromqint64(-1* o["value"].get()); // Check for Memos + + if (confirmations == 0) { + chatModel->addconfirmations(txid, confirmations); + } + + if ((confirmations == 1) && (chatModel->getConfirmationByTx(txid) != QString("0xdeadbeef"))){ + DataStore::getChatDataStore()->clear(); + chatModel->killConfirmationCache(); + chatModel->killMemoCache(); + this->refresh(true); + } QString memo; - if (!o["memo"].is_null()) - memo = QString::fromStdString(o["memo"]); + QString cid; + QString headerbytes; + QString publickey; + if (!o["memo"].is_null()) { + memo = QString::fromStdString(o["memo"].get()); - items.push_back(TransactionItemDetail{address, amount, memo}); - total_amount = total_amount + amount; - } + if (memo.startsWith("{")) { + try + { + QJsonDocument headermemo = QJsonDocument::fromJson(memo.toUtf8()); + + cid = headermemo["cid"].toString(); + headerbytes = headermemo["e"].toString(); + + chatModel->addCid(txid, cid); + chatModel->addHeader(txid, headerbytes); + + } catch(...) { - // Concat all the addresses - - QList addresses; - for (auto item : items) - { - if (item.amount == 0 ) + + } + } + + bool isNotarized; + + if (confirmations > getLag()) { - } - else - { - addresses.push_back(item.address); - address = addresses.join(","); + isNotarized = true; + }else{ + + isNotarized = false; } + + if (chatModel->getCidByTx(txid) != QString("0xdeadbeef")){ + + cid = chatModel->getCidByTx(txid); + + }else{ + cid = ""; + } + + + if (chatModel->getHeaderByTx(txid) != QString("0xdeadbeef")){ + + headerbytes = chatModel->getHeaderByTx(txid); + + }else{ + headerbytes = ""; + } + + if (main->getPubkeyByAddress(address) != QString("0xdeadbeef")){ + + publickey = main->getPubkeyByAddress(address); + + + + }else{ + publickey = ""; + } + + /////We need to filter out Memos smaller then the ciphertext size, or it will dump + + if ((memo.startsWith("{") == false) && (headerbytes.length() > 20)) + { + + QString passphrase = DataStore::getChatDataStore()->getPassword(); + QString hashEncryptionKey = passphrase; + int length = hashEncryptionKey.length(); + + ////////////////Generate the secretkey for our message encryption + + char *hashEncryptionKeyraw = NULL; + hashEncryptionKeyraw = new char[length+1]; + strncpy(hashEncryptionKeyraw, hashEncryptionKey.toLocal8Bit(), length +1); + const QByteArray pubkeyBobArray = QByteArray::fromHex(publickey.toLatin1()); + const unsigned char *pubkeyBob = reinterpret_cast(pubkeyBobArray.constData()); + + + + + #define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw)/////////// + #define MESSAGEAS1_LEN length + + + unsigned char hash1[crypto_kx_SEEDBYTES]; + + crypto_hash_sha256(hash1,MESSAGEAS1, MESSAGEAS1_LEN); + unsigned char sk[crypto_kx_SECRETKEYBYTES]; + unsigned char pk[crypto_kx_PUBLICKEYBYTES]; + + if (crypto_kx_seed_keypair(pk,sk, + hash1) !=0) { + } + + unsigned char server_rx[crypto_kx_SESSIONKEYBYTES], server_tx[crypto_kx_SESSIONKEYBYTES]; + + + ////////////////Get the pubkey from Bob, so we can create the share key + + + /////Create the shared key for sending the message + + if (crypto_kx_server_session_keys(server_rx, server_tx, + pk, sk, pubkeyBob) != 0) { + /* Suspicious client public key, bail out */ + } + + + const QByteArray ba = QByteArray::fromHex(memo.toLatin1()); + const unsigned char *encryptedMemo = reinterpret_cast(ba.constData()); + + const QByteArray ba1 = QByteArray::fromHex(headerbytes.toLatin1()); + const unsigned char *header = reinterpret_cast(ba1.constData()); + + int encryptedMemoSize1 = ba.length(); + + QString memodecrypt; + + if (encryptedMemoSize1 > 15) + { + //////unsigned char* as message from QString + #define MESSAGE2 (const unsigned char *) encryptedMemo + + ///////// length of the encrypted message + #define CIPHERTEXT1_LEN encryptedMemoSize1 + + ///////Message length is smaller then the encrypted message + #define MESSAGE1_LEN encryptedMemoSize1 - crypto_secretstream_xchacha20poly1305_ABYTES + + //////Set the length of the decrypted message + + unsigned char decrypted[MESSAGE1_LEN]; + unsigned char tag[crypto_secretstream_xchacha20poly1305_TAG_FINAL]; + crypto_secretstream_xchacha20poly1305_state state; + + /////Our decrypted message is now in decrypted. We need it as QString to render it + /////Only the QString gives weird data, so convert first to std::string + // crypto_secretstream_xchacha20poly1305_keygen(client_rx); + if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, server_tx) != 0) { + /* Invalid header, no need to go any further */ + } + + if (crypto_secretstream_xchacha20poly1305_pull + (&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) { + /* Invalid/incomplete/corrupted ciphertext - abort */ + } + + std::string decryptedMemo(reinterpret_cast(decrypted),MESSAGE1_LEN); + + memodecrypt = QString::fromUtf8( decryptedMemo.data(), decryptedMemo.size()); + + }else{ + memodecrypt = ""; + + } + /////Now we can convert it to QString + + + + //////////////Give us the output of the decrypted message as debug to see if it was successfully + + ChatItem item = ChatItem( + datetime, + address, + QString(""), + memodecrypt, + QString(""), + QString(""), + cid, + txid, + confirmations, + true, + isNotarized, + false + ); + DataStore::getChatDataStore()->setData(ChatIDGenerator::getInstance()->generateID(item), item); + + updateUIBalances(); + + } + + } + + items.push_back(TransactionItemDetail{address, amount, memo}); + total_amount = total_amount + amount; + + } + + { + QList addresses; + for (auto item : items) { + // Concat all the addresses + + + addresses.push_back(item.address); + address = addresses.join(","); } - } - - txdata.push_back( - TransactionItem{"send", datetime, address, txid,confirmations, items} - ); - - } - else - { + } + + txdata.push_back(TransactionItem{ + "send", datetime, address, txid,confirmations, items + }); + + } else { // Incoming Transaction address = (it["address"].is_null() ? "" : QString::fromStdString(it["address"])); model->markAddressUsed(address); + QString memo; - if (!it["memo"].is_null()) + if (!it["memo"].is_null()) { memo = QString::fromStdString(it["memo"]); + } - items.push_back( - TransactionItemDetail{ - address, - CAmount::fromqint64(it["amount"].get()), - memo - } - ); - - - TransactionItem tx{ - "Receive", - datetime, + items.push_back(TransactionItemDetail{ address, - txid, - confirmations, - items + CAmount::fromqint64(it["amount"].get()), + memo + }); + + TransactionItem tx{ + "Receive", datetime, address, txid,confirmations, items }; txdata.push_back(tx); + + QString type; + QString publickey; + QString headerbytes; + QString cid; + QString requestZaddr; + QString contactname; + bool isContact; + + if (!it["memo"].is_null()) { + + if (memo.startsWith("{")) { + try + { + QJsonDocument headermemo = QJsonDocument::fromJson(memo.toUtf8()); + + cid = headermemo["cid"].toString(); + type = headermemo["t"].toString(); + requestZaddr = headermemo["z"].toString(); + headerbytes = headermemo["e"].toString(); + publickey = headermemo["p"].toString(); + + chatModel->addCid(txid, cid); + chatModel->addrequestZaddr(txid, requestZaddr); + chatModel->addHeader(txid, headerbytes); + + if (publickey.length() > 10){ + main->addPubkey(requestZaddr, publickey); + } + + } catch(...) + { + + } + } + + if (chatModel->getCidByTx(txid) != QString("0xdeadbeef")){ + + cid = chatModel->getCidByTx(txid); + + }else{ + cid = ""; + } + + if (chatModel->getrequestZaddrByTx(txid) != QString("0xdeadbeef")){ + + requestZaddr = chatModel->getrequestZaddrByTx(txid); + }else{ + requestZaddr = ""; + } + + + if (chatModel->getHeaderByTx(txid) != QString("0xdeadbeef")){ + + headerbytes = chatModel->getHeaderByTx(txid); + + + }else{ + headerbytes = ""; + } + + if (main->getPubkeyByAddress(requestZaddr) != QString("0xdeadbeef")){ + + publickey = main->getPubkeyByAddress(requestZaddr); + + + }else{ + publickey = ""; + } + + if (contactModel->getContactbyAddress(requestZaddr) != QString("0xdeadbeef")){ + + isContact = true; + contactname = contactModel->getContactbyAddress(requestZaddr); + + }else{ + + isContact = false; + contactname = ""; + + } + + bool isNotarized; + + if (confirmations > getLag()) + { + isNotarized = true; + }else{ + + isNotarized = false; + } + + int position = it["position"].get(); + + if ((memo.startsWith("{") == false) && (headerbytes > 0)) + { + + if (chatModel->getMemoByTx(txid) == QString("0xdeadbeef")){ + + + if (position == 1) + { + + chatModel->addMemo(txid, headerbytes); + }else{} + + QString passphrase = DataStore::getChatDataStore()->getPassword(); + QString hashEncryptionKey = passphrase; + int length = hashEncryptionKey.length(); + + char *hashEncryptionKeyraw = NULL; + hashEncryptionKeyraw = new char[length+1]; + strncpy(hashEncryptionKeyraw, hashEncryptionKey.toLocal8Bit(), length +1); + + //const QByteArray ba2 = QByteArray::fromHex(hashEncryptionKey.toLatin1()); + // const unsigned char *hashEncryptionKeyraw = reinterpret_cast(ba2.constData()); + + const QByteArray pubkeyBobArray = QByteArray::fromHex(publickey.toLatin1()); + const unsigned char *pubkeyBob = reinterpret_cast(pubkeyBobArray.constData()); + + + #define MESSAGEAS1 ((const unsigned char *) hashEncryptionKeyraw)/////////// + #define MESSAGEAS1_LEN length + + + unsigned char hash1[crypto_kx_SEEDBYTES]; + + crypto_hash_sha256(hash1,MESSAGEAS1, MESSAGEAS1_LEN); + unsigned char sk[crypto_kx_SECRETKEYBYTES]; + unsigned char pk[crypto_kx_PUBLICKEYBYTES]; + + if (crypto_kx_seed_keypair(pk,sk, + hash1) !=0) { + + + } + unsigned char client_rx[crypto_kx_SESSIONKEYBYTES], client_tx[crypto_kx_SESSIONKEYBYTES]; + + + ////////////////Get the pubkey from Bob, so we can create the share key + + + /////Create the shared key for sending the message + + if (crypto_kx_client_session_keys(client_rx, client_tx, + pk, sk, pubkeyBob) != 0) { + /* Suspicious client public key, bail out */ + } + + + + const QByteArray ba = QByteArray::fromHex(memo.toLatin1()); + const unsigned char *encryptedMemo = reinterpret_cast(ba.constData()); + + const QByteArray ba1 = QByteArray::fromHex(headerbytes.toLatin1()); + const unsigned char *header = reinterpret_cast(ba1.constData()); + + int encryptedMemoSize1 = ba.length(); + int headersize = ba1.length(); + + //////unsigned char* as message from QString + #define MESSAGE2 (const unsigned char *) encryptedMemo + + ///////// length of the encrypted message + #define CIPHERTEXT1_LEN encryptedMemoSize1 + + ///////Message length is smaller then the encrypted message + #define MESSAGE1_LEN encryptedMemoSize1 - crypto_secretstream_xchacha20poly1305_ABYTES + + //////Set the length of the decrypted message + + unsigned char decrypted[MESSAGE1_LEN]; + unsigned char tag[crypto_secretstream_xchacha20poly1305_TAG_FINAL]; + crypto_secretstream_xchacha20poly1305_state state; + + /////Our decrypted message is now in decrypted. We need it as QString to render it + /////Only the QString gives weird data, so convert first to std::string + // crypto_secretstream_xchacha20poly1305_keygen(client_rx); + if (crypto_secretstream_xchacha20poly1305_init_pull(&state, header, client_rx) != 0) { + /* Invalid header, no need to go any further */ + } + + if (crypto_secretstream_xchacha20poly1305_pull + (&state, decrypted, NULL, tag, MESSAGE2, CIPHERTEXT1_LEN, NULL, 0) != 0) { + /* Invalid/incomplete/corrupted ciphertext - abort */ + } + + std::string decryptedMemo(reinterpret_cast(decrypted),MESSAGE1_LEN); + + /////Now we can convert it to QString + QString memodecrypt; + + memodecrypt = QString::fromUtf8( decryptedMemo.data(), decryptedMemo.size()); + + + + // } + //////////////Give us the output of the decrypted message as debug to see if it was successfully + + + + ChatItem item = ChatItem( + datetime, + address, + contactname, + memodecrypt, + requestZaddr, + type, + cid, + txid, + confirmations, + false, + isNotarized, + isContact + ); + DataStore::getChatDataStore()->setData(ChatIDGenerator::getInstance()->generateID(item), item); + + }else{ + + } + + }else{ + + ChatItem item = ChatItem( + datetime, + address, + contactname, + memo, + requestZaddr, + type, + cid, + txid, + confirmations, + false, + isNotarized, + isContact + ); + DataStore::getChatDataStore()->setData(ChatIDGenerator::getInstance()->generateID(item), item); + } + } } - - } + } // Calculate the total unspent amount that's pending. This will need to be // shown in the UI so the user can keep track of pending funds CAmount totalPending; - for (auto txitem : txdata) - { - if (txitem.confirmations == 0) - { - for (auto item: txitem.items) - { + for (auto txitem : txdata) { + if (txitem.confirmations == 0) { + for (auto item: txitem.items) { totalPending = totalPending + item.amount; } } @@ -957,9 +1444,26 @@ void Controller::refreshTransactions() // Update UI Balance updateUIBalances(); - // Update model data, which updates the table view - transactionsTableModel->replaceData(txdata); - }); + // Update model data, which updates the table view + transactionsTableModel->replaceData(txdata); + chat->renderChatBox(ui, ui->listChat,ui->memoSizeChat); + ui->listChat->verticalScrollBar()->setValue( + ui->listChat->verticalScrollBar()->maximum()); + + }); +} + +void Controller::refreshChat(QListView *listWidget, QLabel *label) +{ + chat->renderChatBox(ui, listWidget, label); + ui->listChat->verticalScrollBar()->setValue( + ui->listChat->verticalScrollBar()->maximum()); + +} + +void Controller::refreshContacts(QListView *listWidget) +{ + contactModel->renderContactList(listWidget); } // If the wallet is encrpyted and locked, we need to unlock it @@ -1523,9 +2027,27 @@ void Controller::shutdownhushd() QDialog d(main); Ui_ConnectionDialog connD; connD.setupUi(&d); - connD.topIcon->setBasePixmap(QIcon(":/icons/res/icon.ico").pixmap(256, 256)); + auto theme = Settings::getInstance()->get_theme_name(); + auto size = QSize(512,512); + + if (theme == "Dark" || theme == "Midnight") { + QMovie *movie2 = new QMovie(":/img/res/silentdragonlite-animated-startup-dark.gif");; + movie2->setScaledSize(size); + qDebug() << "Animation dark loaded"; + connD.topIcon->setMovie(movie2); + movie2->start(); connD.status->setText(QObject::tr("Please wait for SilentDragonLite to exit")); connD.statusDetail->setText(QObject::tr("Waiting for hushd to exit")); + } else { + QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated-startup.gif");; + movie1->setScaledSize(size); + qDebug() << "Animation light loaded"; + connD.topIcon->setMovie(movie1); + movie1->start(); + connD.status->setText(QObject::tr("Please wait for SilentDragonLite to exit")); + connD.statusDetail->setText(QObject::tr("Waiting for hushd to exit")); + } + bool finished = false; zrpc->saveWallet([&] (json) { if (!finished) diff --git a/src/controller.h b/src/controller.h index 29d5c0d..195827c 100644 --- a/src/controller.h +++ b/src/controller.h @@ -1,3 +1,6 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + #ifndef RPCCLIENT_H #define RPCCLIENT_H @@ -11,7 +14,11 @@ #include "mainwindow.h" #include "liteinterface.h" #include "connection.h" - +#include "chatmodel.h" +#include "Chat/Chat.h" +#include "Model/ContactRequestChatItem.h" +#include "Model/ContactItem.h" +#include "contactmodel.h" using json = nlohmann::json; struct WatchedTx { @@ -33,7 +40,11 @@ public: Connection* getConnection() { return zrpc->getConnection(); } void setConnection(Connection* c); void refresh(bool force = false); - void refreshAddresses(); + void refreshAddresses(); + int getLag(); + void setLag(int lag); + int _lag; + std::string encryptDecrypt(std::string); void checkForUpdate(bool silent = true); void refreshZECPrice(); @@ -71,6 +82,9 @@ public: void refreshAUDCAP(); + void refreshChat(QListView *listWidget, QLabel *label); + void refreshContacts(QListView *listWidget); + void executeStandardUITransaction(Tx tx); void executeTransaction(Tx tx, diff --git a/src/deposithush.ui b/src/deposithush.ui new file mode 100644 index 0000000..6ac7e71 --- /dev/null +++ b/src/deposithush.ui @@ -0,0 +1,132 @@ + + + deposithush + + + + 0 + 0 + 792 + 650 + + + + Deposit Hush + + + + + + + 16777215 + 71 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:16pt;">Please use the following hush address to transfer funds to SilentDragonLite. You can either copy the address or use the QR Code. </span></p></body></html> + + + + + + + <html><head/><body><p align="center">QR Code of your Hush Address</p></body></html> + + + + + + + <html><head/><body><p align="center"><span style=" text-decoration: underline;">Your Hush Address </span></p></body></html> + + + + + + + Hush zaddr + + + + + + + Copy Address + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + 0 + 0 + + + + background-color: #fff + + + + + + + + + + + QRCodeLabel + QLabel +
qrcodelabel.h
+
+
+ + + + buttonBox + accepted() + deposithush + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + deposithush + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/encryption.ui b/src/encryption.ui index b4ab606..7f2388c 100644 --- a/src/encryption.ui +++ b/src/encryption.ui @@ -10,69 +10,58 @@ 300 + + + 0 + 0 + + + + + 400 + 300 + + + + + 400 + 300 + + Encrypt Your Wallet - - - - Qt::Horizontal - - - - + - Encryption Password: + Encryption Passphrase: - - - - Confirm Password: - - - - - - - QLineEdit::Password - - - - - - - color: red; - - - Passwords don't match - - - Qt::AlignCenter - - - - - - - QLineEdit::Password - - - - - + + - Qt::Horizontal + Qt::Vertical - + + + 20 + 40 + + + - + + + + 0 + 0 + + - WARNING: If you forget your password, the only way to recover the wallet is from the seed phrase. + <html><head/><body><p><span style=" font-size:14pt; color:#ef2929;">WARNING:</span> If you forget your passphrase, the only way to recover the wallet is from the seed phrase. If you don't have a backup of your seed phrase, please do it now!</p></body></html> Qt::AlignCenter @@ -82,8 +71,35 @@ - + + + + Qt::Horizontal + + + + + + + QLineEdit::Password + + + + + + + Confirm Passphrase: + + + + + + + 0 + 0 + + Qt::Horizontal @@ -95,7 +111,7 @@ - + Qt::Vertical @@ -108,18 +124,51 @@ - - + + - Qt::Vertical + Qt::Horizontal - - - 20 - 40 - + + + + + + QLineEdit::Password - + + + + + + + 0 + 0 + + + + color: red; + + + <html><head/><body><p><span style=" font-style:italic;">Passphrase don't match</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-style:italic;">16 letters minimum</span></p></body></html> + + diff --git a/src/firsttimewizard.cpp b/src/firsttimewizard.cpp index 438f737..5ef96bb 100644 --- a/src/firsttimewizard.cpp +++ b/src/firsttimewizard.cpp @@ -3,6 +3,8 @@ #include "ui_newseed.h" #include "ui_restoreseed.h" #include "ui_newwallet.h" +#include "mainwindow.h" +#include "DataStore/DataStore.h" #include "../lib/silentdragonlitelib.h" @@ -38,31 +40,74 @@ int FirstTimeWizard::nextId() const { NewOrRestorePage::NewOrRestorePage(FirstTimeWizard *parent) : QWizardPage(parent) { setTitle("Create or Restore wallet."); + + QWidget* pageWidget = new QWidget(); Ui_CreateWalletForm form; form.setupUi(pageWidget); + - // Exclusive buttons + auto fnPasswordEdited = [=](const QString&) { + // Enable the Finish button if the passwords match. + QString Password = form.txtPassword->text(); + + if (!form.txtPassword->text().isEmpty() && + form.txtPassword->text() == form.txtConfirmPassword->text() && Password.size() >= 16) { + + form.lblPasswordMatch->setText(""); + parent->button(QWizard::CommitButton)->setEnabled(true); + setButtonText(QWizard::CommitButton, "Next"); + form.radioRestoreWallet->setEnabled(true); + form.radioNewWallet->setEnabled(true); + form.radioNewWallet->setChecked(true); + + +DataStore::getChatDataStore()->setPassword(Password); + //main->setPassword(Password); + + //qDebug()<<"Objekt gesetzt"; + + + // Exclusive buttons QObject::connect(form.radioNewWallet, &QRadioButton::clicked, [=](bool checked) { if (checked) { form.radioRestoreWallet->setChecked(false); + } }); QObject::connect(form.radioRestoreWallet, &QRadioButton::clicked, [=](bool checked) { if (checked) { form.radioNewWallet->setChecked(false); + } }); - form.radioNewWallet->setChecked(true); + - registerField("intro.new", form.radioNewWallet); + + } else { + form.lblPasswordMatch->setText(tr("Passphrase don't match or You have entered too few letters (16 minimum)")); + + parent->button(QWizard::CommitButton)->setEnabled(false); + form.radioRestoreWallet->setEnabled(false); + form.radioNewWallet->setEnabled(false); + } + + }; QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(pageWidget); setLayout(layout); + + + QObject::connect(form.txtConfirmPassword, &QLineEdit::textChanged, fnPasswordEdited); + QObject::connect(form.txtPassword, &QLineEdit::textChanged, fnPasswordEdited); + registerField("intro.new", form.radioNewWallet); + form.radioRestoreWallet->setEnabled(false); + form.radioNewWallet->setEnabled(false); setCommitPage(true); - setButtonText(QWizard::CommitButton, "Next"); + + } NewSeedPage::NewSeedPage(FirstTimeWizard *parent) : QWizardPage(parent) { @@ -81,6 +126,7 @@ NewSeedPage::NewSeedPage(FirstTimeWizard *parent) : QWizardPage(parent) { void NewSeedPage::initializePage() { // Call the library to create a new wallet. + char* resp = litelib_initialize_new(parent->dangerous, parent->server.toStdString().c_str()); QString reply = litelib_process_response(resp); @@ -90,7 +136,11 @@ void NewSeedPage::initializePage() { } else { QString seed = QString::fromStdString(parsed["seed"].get()); form.txtSeed->setPlainText(seed); + + } + + } // Will be called just before closing. Make sure we can save the seed in the wallet @@ -175,4 +225,4 @@ bool RestoreSeedPage::validatePage() { return true; } } -} \ No newline at end of file +} diff --git a/src/firsttimewizard.h b/src/firsttimewizard.h index 276c0cd..8d84b61 100644 --- a/src/firsttimewizard.h +++ b/src/firsttimewizard.h @@ -5,11 +5,18 @@ #include "ui_newseed.h" #include "ui_restoreseed.h" +#include "mainwindow.h" + + class FirstTimeWizard: public QWizard { + + + public: FirstTimeWizard(bool dangerous, QString server); + protected: int nextId() const; @@ -27,11 +34,16 @@ private: friend class NewOrRestorePage; friend class NewSeedPage; friend class RestoreSeedPage; + + + }; class NewOrRestorePage: public QWizardPage { public: + NewOrRestorePage(FirstTimeWizard* parent); + }; diff --git a/src/fundhushchat.ui b/src/fundhushchat.ui new file mode 100644 index 0000000..fb923db --- /dev/null +++ b/src/fundhushchat.ui @@ -0,0 +1,72 @@ + + + + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 30 + 240 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + diff --git a/src/hushrequest.ui b/src/hushrequest.ui new file mode 100644 index 0000000..f51aebd --- /dev/null +++ b/src/hushrequest.ui @@ -0,0 +1,156 @@ + + + hushrequest + + + + 0 + 0 + 663 + 529 + + + + Request Payment + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + Request payment from a Sapling address. You'll send a hush 0.0001 transaction to the address with a hush payment URI. The memo will be included in the transaction when the address pays you. + + + true + + + + + + + Request From + + + + + + + Qt::Horizontal + + + + 541 + 20 + + + + + + + + zaddr + + + + + + + Amount in + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + Amount + + + + + + + Amount USD + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Memo + + + + + + + 0 / 512 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + + My Address + + + + + + + The recipient will see this address in the "to" field when they pay your request. + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + MemoEdit + QPlainTextEdit +
memoedit.h
+
+
+ + +
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5352a95..4f278e7 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,3 +1,6 @@ +// Copyright 2019-2020 The Hush developers +// GPLv3 + #include "mainwindow.h" #include "addressbook.h" #include "viewalladdresses.h" @@ -14,16 +17,48 @@ #include "settings.h" #include "version.h" #include "connection.h" +#include "ui_contactrequest.h" +#include "ui_deposithush.h" +#include "ui_requestContactDialog.h" +#include "chatmodel.h" #include "requestdialog.h" +#include "ui_startupencryption.h" +#include "ui_removeencryption.h" #include "websockets.h" +#include "sodium.h" +#include "sodium/crypto_generichash_blake2b.h" #include +#include "FileSystem/FileSystem.h" +#include "Crypto/passwd.h" +#include "Crypto/FileEncryption.h" +#include "DataStore/DataStore.h" +#include "firsttimewizard.h" using json = nlohmann::json; + + +#ifdef Q_OS_WIN +auto dirwallet = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("silentdragonlite/silentdragonlite-wallet.dat"); +auto dirwalletenc = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("silentdragonlite/silentdragonlite-wallet-enc.dat"); +auto dirwalletbackup = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("silentdragonlite/silentdragonlite-wallet.datBackup"); +#endif +#ifdef Q_OS_MACOS +auto dirwallet = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("silentdragonlite/silentdragonlite-wallet.dat"); +auto dirwalletenc = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("silentdragonlite/silentdragonlite-wallet-enc.dat"); +auto dirwalletbackup = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("silentdragonlite/silentdragonlite-wallet.datBackup"); +#endif +#ifdef Q_OS_LINUX +auto dirwallet = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(".silentdragonlite/silentdragonlite-wallet.dat"); +auto dirwalletenc = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(".silentdragonlite/silentdragonlite-wallet-enc.dat"); +auto dirwalletbackup = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)).filePath(".silentdragonlite/silentdragonlite-wallet.datBackup"); +#endif + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { + // Include css QString theme_name; try @@ -32,15 +67,28 @@ MainWindow::MainWindow(QWidget *parent) : } catch (...) { - theme_name = "default"; + theme_name = "Dark"; } this->slot_change_theme(theme_name); ui->setupUi(this); - logger = new Logger(this, QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("silentdragonlite-wallet.log")); + logger = new Logger(this, QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("silentdragonlite-wallet.log")); + // Check for encryption + + + + if(fileExists(dirwalletenc)) + { + this->removeWalletEncryptionStartUp(); + } + + ui->memoTxtChat->setAutoFillBackground(false); + ui->memoTxtChat->setPlaceholderText("Send Message"); + ui->memoTxtChat->setTextColor(Qt::white); + // Status Bar setupStatusBar(); @@ -152,6 +200,7 @@ MainWindow::MainWindow(QWidget *parent) : setupReceiveTab(); setupBalancesTab(); setuphushdTab(); + setupchatTab(); rpc = new Controller(this); @@ -167,6 +216,12 @@ MainWindow::MainWindow(QWidget *parent) : createWebsocket(wormholecode); } } + +bool MainWindow::fileExists(QString path) +{ + QFileInfo check_file(path); + return (check_file.exists() && check_file.isFile()); +} void MainWindow::createWebsocket(QString wormholecode) { qDebug() << "Listening for app connections on port 8777"; @@ -221,6 +276,10 @@ void MainWindow::doClose() { closeEvent(nullptr); } +void MainWindow::doClosePw() { + closeEventpw(nullptr); +} + void MainWindow::closeEvent(QCloseEvent* event) { QSettings s; @@ -230,134 +289,370 @@ void MainWindow::closeEvent(QCloseEvent* event) { s.sync(); + // Let the RPC know to shut down any running service. rpc->shutdownhushd(); + int passphraselenght = DataStore::getChatDataStore()->getPassword().length(); + +// Check is encryption is ON for SDl + if(passphraselenght > 0) + + { + // delete old file before + + //auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + QFile fileoldencryption(dirwalletenc); + fileoldencryption.remove(); + + // Encrypt our wallet.dat + QString str = DataStore::getChatDataStore()->getPassword(); + // QString str = ed.txtPassword->text(); // data comes from user inputs + int length = str.length(); + + char *sequence = NULL; + sequence = new char[length+1]; + strncpy(sequence, str.toLocal8Bit(), length +1); + + #define MESSAGE ((const unsigned char *) sequence) + #define MESSAGE_LEN length + + unsigned char hash[crypto_secretstream_xchacha20poly1305_KEYBYTES]; + + crypto_hash_sha256(hash,MESSAGE, MESSAGE_LEN); + + #define PASSWORD sequence + #define KEY_LEN crypto_box_SEEDBYTES + + + + /////////we use the Hash of the Password as Salt, not perfect but still a good solution. + + unsigned char key[KEY_LEN]; + + if (crypto_pwhash + (key, sizeof key, PASSWORD, strlen(PASSWORD), hash, + crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + /* out of memory */ +} + + auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + // auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + QString source_file = dir.filePath("addresslabels.dat"); + QString target_enc_file = dir.filePath("addresslabels.dat.enc"); + QString sourceWallet_file = dirwallet; + QString target_encWallet_file = dirwalletenc; + + FileEncryption::encrypt(target_enc_file, source_file, key); + FileEncryption::encrypt(target_encWallet_file, sourceWallet_file, key); + + ///////////////// we rename the plaintext wallet.dat to Backup, for testing. + + QFile wallet(dirwallet); + QFile address(dir.filePath("addresslabels.dat")); + wallet.remove(); + address.remove(); + } + // Bubble up if (event) QMainWindow::closeEvent(event); } +void MainWindow::closeEventpw(QCloseEvent* event) { + + // Let the RPC know to shut down any running service. + rpc->shutdownhushd(); + + +} + void MainWindow::encryptWallet() { - // Check if wallet is already encrypted - auto encStatus = rpc->getModel()->getEncryptionStatus(); - if (encStatus.first) { - QMessageBox::information(this, tr("Wallet is already encrypted"), - tr("Your wallet is already encrypted with a password.\nPlease use 'Remove Wallet Encryption' if you want to remove the wallet encryption."), - QMessageBox::Ok - ); - return; - } QDialog d(this); Ui_encryptionDialog ed; ed.setupUi(&d); // Handle edits on the password box + + auto fnPasswordEdited = [=](const QString&) { // Enable the OK button if the passwords match. + QString password = ed.txtPassword->text(); + if (!ed.txtPassword->text().isEmpty() && - ed.txtPassword->text() == ed.txtConfirmPassword->text()) { + ed.txtPassword->text() == ed.txtConfirmPassword->text() && password.size() >= 16) { + ed.lblPasswordMatch->setText(""); + ed.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); + } else { + //ed.lblPasswordMatch->setText(tr("Passphrase don't match or You have entered too few letters (16 minimum)")); + ed.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + } + + }; + + QObject::connect(ed.txtConfirmPassword, &QLineEdit::textChanged, fnPasswordEdited); + QObject::connect(ed.txtPassword, &QLineEdit::textChanged, fnPasswordEdited); + + if (d.exec() == QDialog::Accepted) + { + + QString passphrase = ed.txtPassword->text(); // data comes from user inputs + int length = passphrase.length(); + DataStore::getChatDataStore()->setPassword(passphrase); + + char *sequence = NULL; + sequence = new char[length+1]; + strncpy(sequence, passphrase.toLocal8Bit(), length +1); + + #define MESSAGE ((const unsigned char *) sequence) + #define MESSAGE_LEN length + + unsigned char hash[crypto_secretstream_xchacha20poly1305_KEYBYTES]; + + crypto_hash_sha256(hash,MESSAGE, MESSAGE_LEN); + + #define PASSWORD sequence + #define KEY_LEN crypto_box_SEEDBYTES + + + + /////////we use the Hash of the Password as Salt, not perfect but still a good solution. + + unsigned char key[KEY_LEN]; + + if (crypto_pwhash + (key, sizeof key, PASSWORD, strlen(PASSWORD), hash, + crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + /* out of memory */ +} + + auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + QString source_file = dir.filePath("addresslabels.dat"); + QString target_enc_file = dir.filePath("addresslabels.dat.enc"); + QString sourceWallet_file = dirwallet; + QString target_encWallet_file = dirwalletenc; + + FileEncryption::encrypt(target_enc_file, source_file, key); + FileEncryption::encrypt(target_encWallet_file, sourceWallet_file, key); + + QFile wallet(dirwallet); + QFile address(dir.filePath("addresslabels.dat")); + wallet.rename(dirwalletbackup); + address.rename(dir.filePath("addresslabels.datBackup")); + + QMessageBox::information(this, tr("Wallet Encryption Success"), + QString("Successfully encrypted your wallet"), + QMessageBox::Ok + ); + } +} + +void MainWindow::removeWalletEncryption() { + QDialog d(this); + Ui_removeencryption ed; + ed.setupUi(&d); + + if (fileExists(dirwalletenc) == false) { + QMessageBox::information(this, tr("Wallet is not encrypted"), + tr("Your wallet is not encrypted with a passphrase."), + QMessageBox::Ok + ); + return; + } + + auto fnPasswordEdited = [=](const QString&) { + QString password = ed.txtPassword->text(); + // Enable the OK button if the passwords match. + if (!ed.txtPassword->text().isEmpty() && + ed.txtPassword->text() == ed.txtConfirmPassword->text() && password.size() >= 16) { ed.lblPasswordMatch->setText(""); ed.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } else { ed.lblPasswordMatch->setText(tr("Passwords don't match")); ed.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } + }; QObject::connect(ed.txtConfirmPassword, &QLineEdit::textChanged, fnPasswordEdited); QObject::connect(ed.txtPassword, &QLineEdit::textChanged, fnPasswordEdited); - ed.txtPassword->setText(""); - ed.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + if (d.exec() == QDialog::Accepted) + { + QString str = ed.txtPassword->text(); // data comes from user inputs + int length = str.length(); - auto fnShowError = [=](QString title, const json& res) { - QMessageBox::critical(this, title, - tr("Error was:\n") + QString::fromStdString(res.dump()), - QMessageBox::Ok - ); - }; + char *sequence = NULL; + sequence = new char[length+1]; + strncpy(sequence, str.toLocal8Bit(), length +1); - if (d.exec() == QDialog::Accepted) { - rpc->encryptWallet(ed.txtPassword->text(), [=](json res) { - if (isJsonResultSuccess(res)) { - // Save the wallet - rpc->saveWallet([=] (json reply) { - if (isJsonResultSuccess(reply)) { - QMessageBox::information(this, tr("Wallet Encrypted"), - tr("Your wallet was successfully encrypted! The password will be needed to send funds or export private keys."), - QMessageBox::Ok - ); - } else { - fnShowError(tr("Wallet Encryption Failed"), reply); - } - }); + #define MESSAGE ((const unsigned char *) sequence) + #define MESSAGE_LEN length + + unsigned char hash[crypto_secretstream_xchacha20poly1305_KEYBYTES]; + + crypto_hash_sha256(hash,MESSAGE, MESSAGE_LEN); + + #define PASSWORD sequence + #define KEY_LEN crypto_box_SEEDBYTES + + + + /////////we use the Hash of the Password as Salt, not perfect but still a good solution. + + unsigned char key[KEY_LEN]; + + if (crypto_pwhash + (key, sizeof key, PASSWORD, strlen(PASSWORD), hash, + crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + /* out of memory */ +} + + + + auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + QString target_encwallet_file = dirwalletenc; + QString target_decwallet_file = dirwallet; + QString target_encaddr_file = dir.filePath("addresslabels.dat.enc"); + QString target_decaddr_file = dir.filePath("addresslabels.dat"); + + FileEncryption::decrypt(target_decwallet_file, target_encwallet_file, key); + FileEncryption::decrypt(target_decaddr_file, target_encaddr_file, key); + + QFile filencrypted(dirwalletenc); + QFile wallet(dirwallet); + + if (wallet.size() > 0) + { + + QMessageBox::information(this, tr("Wallet decryption Success"), + QString("Successfully delete the encryption"), + QMessageBox::Ok + ); + + filencrypted.remove(); + + }else{ + + QMessageBox::critical(this, tr("Wallet Encryption Failed"), + QString("False password, please try again"), + QMessageBox::Ok + ); + this->removeWalletEncryption(); + } - // And then refresh the UI - rpc->refresh(true); - } else { - fnShowError(tr("Wallet Encryption Failed"), res); - } - }); } + } -void MainWindow::removeWalletEncryption() { - // Check if wallet is already encrypted - auto encStatus = rpc->getModel()->getEncryptionStatus(); - if (!encStatus.first) { - QMessageBox::information(this, tr("Wallet is not encrypted"), - tr("Your wallet is not encrypted with a password."), +void MainWindow::removeWalletEncryptionStartUp() { + QDialog d(this); + Ui_startup ed; + ed.setupUi(&d); + + if (d.exec() == QDialog::Accepted) + { + QString password = ed.txtPassword->text(); // data comes from user inputs + int length = password.length(); + DataStore::getChatDataStore()->setPassword(password); + char *sequence = NULL; + sequence = new char[length+1]; + strncpy(sequence, password.toLocal8Bit(), length +1); + + #define MESSAGE ((const unsigned char *) sequence) + #define MESSAGE_LEN length + + unsigned char hash[crypto_secretstream_xchacha20poly1305_KEYBYTES]; + + crypto_hash_sha256(hash,MESSAGE, MESSAGE_LEN); + + #define PASSWORD sequence + #define KEY_LEN crypto_box_SEEDBYTES + + + + /////////we use the Hash of the Password as Salt, not perfect but still a good solution. + + unsigned char key[KEY_LEN]; + + if (crypto_pwhash + (key, sizeof key, PASSWORD, strlen(PASSWORD), hash, + crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_SENSITIVE, + crypto_pwhash_ALG_DEFAULT) != 0) { + /* out of memory */ + } + + + { + auto dir = QDir(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); + auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + QString target_encwallet_file = dirwalletenc; + QString target_decwallet_file = dirwallet; + QString target_encaddr_file = dir.filePath("addresslabels.dat.enc"); + QString target_decaddr_file = dir.filePath("addresslabels.dat"); + + FileEncryption::decrypt(target_decwallet_file, target_encwallet_file, key); + FileEncryption::decrypt(target_decaddr_file, target_encaddr_file, key); + + } + + auto dirHome = QDir(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); + QFile wallet(dirwallet); + //QFile backup(dirHome.filePath(".silentdragonlite/silentdragonlite-wallet.datBACKUP"));*/ + + if (wallet.size() > 0) + { + if (fileExists(dirwalletbackup)) + + { + + QMessageBox::information(this, tr("You still have plaintext data on your disk!"), + QString("WARNING: Delete it only if you have a backup of your Wallet Seed."), + QMessageBox::Ok + ); + // backup.remove(); + + } + + QMessageBox::information(this, tr("Wallet Encryption Success"), + QString("SDL is ready to Rock"), + QMessageBox::Ok + ); + + + }else{ + + QMessageBox::critical(this, tr("Wallet Encryption Failed"), + QString("false password please try again"), QMessageBox::Ok ); - return; + this->removeWalletEncryptionStartUp(); + } + + }else{ + + this->doClosePw(); } + +} - bool ok; - QString password = QInputDialog::getText(this, tr("Wallet Password"), - tr("Please enter your wallet password"), QLineEdit::Password, "", &ok); +QString MainWindow::getPassword() +{ - // If cancel was pressed, just return - if (!ok) { - return; - } + return _password; +} - if (password.isEmpty()) { - QMessageBox::critical(this, tr("Wallet Decryption Failed"), - tr("Please enter a password to decrypt your wallet!"), - QMessageBox::Ok - ); - return; - } +void MainWindow::setPassword(QString password) +{ - rpc->removeWalletEncryption(password, [=] (json res) { - if (isJsonResultSuccess(res)) { - // Save the wallet - rpc->saveWallet([=] (json reply) { - if(isJsonResultSuccess(reply)) { - QMessageBox::information(this, tr("Wallet Encryption Removed"), - tr("Your wallet was successfully decrypted! You will no longer need a password to send funds or export private keys."), - QMessageBox::Ok - ); - } else { - QMessageBox::critical(this, tr("Wallet Decryption Failed"), - QString::fromStdString(reply["error"].get()), - QMessageBox::Ok - ); - } - }); - - // And then refresh the UI - rpc->refresh(true); - } else { - QMessageBox::critical(this, tr("Wallet Decryption Failed"), - QString::fromStdString(res["error"].get()), - QMessageBox::Ok - ); - } - }); + _password = password; } void MainWindow::setupStatusBar() { @@ -368,6 +663,7 @@ void MainWindow::setupStatusBar() { loadingMovie->start(); loadingLabel->setAttribute(Qt::WA_NoSystemBackground); loadingLabel->setMovie(loadingMovie); + ui->statusBar->addPermanentWidget(loadingLabel); loadingLabel->setVisible(false); @@ -429,9 +725,6 @@ void MainWindow::setupSettingsModal() { this->slot_change_currency(currency_name); - ; - - // Setup theme combo int theme_index = settings.comboBoxTheme->findText(Settings::getInstance()->get_theme_name(), Qt::MatchExactly); settings.comboBoxTheme->setCurrentIndex(theme_index); @@ -454,7 +747,7 @@ void MainWindow::setupSettingsModal() { // Tell the user to restart QMessageBox::information(this, tr("Currency Change"), tr("This change can take a few seconds."), QMessageBox::Ok); }); - + // Check for updates settings.chkCheckUpdates->setChecked(Settings::getInstance()->getCheckForUpdates()); @@ -767,6 +1060,27 @@ void MainWindow::exportSeed() { }); } +void MainWindow::addPubkey(QString requestZaddr, QString pubkey) +{ + this->pubkeyMap[requestZaddr] = pubkey; +} + +QString MainWindow::getPubkeyByAddress(QString requestZaddr) +{ + for(auto& pair : this->pubkeyMap) + { + + } + + if(this->pubkeyMap.count(requestZaddr) > 0) + { + return this->pubkeyMap[requestZaddr]; + } + + return QString("0xdeadbeef"); +} + + void MainWindow::exportAllKeys() { exportKeys(""); } @@ -845,6 +1159,35 @@ void MainWindow::setupBalancesTab() { ui->unconfirmedWarning->setVisible(false); ui->lblSyncWarning->setVisible(false); ui->lblSyncWarningReceive->setVisible(false); + QObject::connect(ui->depositHushButton, &QPushButton::clicked, [=](){ + + Ui_deposithush deposithush; + QDialog dialog(this); + deposithush.setupUi(&dialog); + Settings::saveRestore(&dialog); + + QList allAddresses; + + allAddresses = getRPC()->getModel()->getAllZAddresses(); + QString depositzaddr = allAddresses[1]; + deposithush.qrcodeDisplayDeposit->setQrcodeString(depositzaddr); + deposithush.zaddr->setText(depositzaddr); + + QObject::connect(deposithush.CopyAddress, &QPushButton::clicked, [=](){ + + QGuiApplication::clipboard()->setText(depositzaddr); + ui->statusBar->showMessage(tr("Copied to clipboard"), 3 * 1000); + + }); + + + + dialog.exec(); + + + + + }); // Setup context menu on balances tab @@ -878,6 +1221,7 @@ void MainWindow::setupBalancesTab() { menu.exec(ui->balancesTable->viewport()->mapToGlobal(pos)); }); + } void MainWindow::setuphushdTab() { @@ -899,8 +1243,22 @@ void MainWindow::setupTransactionsTab() { }); // Set up context menu on transactions tab + auto theme = Settings::getInstance()->get_theme_name(); + if (theme == "Dark" || theme == "Midnight") { + ui->listChat->setStyleSheet("background-image: url(:/icons/res/sdlogo.png) ;background-attachment: fixed ;background-position: center center ;background-repeat: no-repeat;background-size: cover"); + } + if (theme == "Default") {ui->listChat->setStyleSheet("background-image: url(:/icons/res/sdlogo2.png) ;background-attachment: fixed ;background-position: center center ;background-repeat: no-repeat;background-size: cover");} + + ui->listChat->setResizeMode(QListView::Adjust); + ui->listChat->setWordWrap(true); + ui->listChat->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + ui->listChat->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + ui->listChat->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + ui->listChat->setMinimumSize(200,350); + ui->listChat->setItemDelegate(new ListViewDelegate()); + ui->listChat->show(); + ui->transactionsTable->setContextMenuPolicy(Qt::CustomContextMenu); - // Table right click QObject::connect(ui->transactionsTable, &QTableView::customContextMenuRequested, [=] (QPoint pos) { QModelIndex index = ui->transactionsTable->indexAt(pos); @@ -974,13 +1332,263 @@ void MainWindow::setupTransactionsTab() { qApp->processEvents(); // Click the memo button - this->memoButtonClicked(1, true); + this->memoButtonClicked(1, true); }); } } menu.exec(ui->transactionsTable->viewport()->mapToGlobal(pos)); }); + +} + +void MainWindow::setupchatTab() { + + /////////////Setting Icons for Chattab and different themes + + auto theme = Settings::getInstance()->get_theme_name(); + if (theme == "Dark" || theme == "Midnight") { + QPixmap send(":/icons/res/send-white.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + + QPixmap notification(":/icons/res/requestWhite.png"); + QIcon notificationIcon(notification); + ui->pushContact->setIcon(notificationIcon); + + QPixmap addContact(":/icons/res/addContactWhite.png"); + QIcon addContactIcon(addContact); + ui->safeContactRequest->setIcon(addContactIcon); + + QPixmap newAddr(":/icons/res/getAddrWhite.png"); + QIcon addnewAddrIcon(newAddr); + ui->givemeZaddr->setIcon(addnewAddrIcon); + + ui->memoTxtChat->setTextColor("White"); + + }else{ + + QPixmap send(":/icons/res/sendBlack.png"); + QIcon sendIcon(send); + ui->sendChatButton->setIcon(sendIcon); + + QPixmap notification(":/icons/res/requestBlack.png"); + QIcon notificationIcon(notification); + ui->pushContact->setIcon(notificationIcon); + + QPixmap addContact(":/icons/res/addContactBlack.png"); + QIcon addContactIcon(addContact); + ui->safeContactRequest->setIcon(addContactIcon); + + QPixmap newAddr(":/icons/res/getAddrBlack.png"); + QIcon addnewAddrIcon(newAddr); + ui->givemeZaddr->setIcon(addnewAddrIcon); + + ui->memoTxtChat->setTextColor("Black"); + } + + QObject::connect(ui->sendChatButton, &QPushButton::clicked, this, &MainWindow::sendChat); + QObject::connect(ui->safeContactRequest, &QPushButton::clicked, this, &MainWindow::addContact); + QObject::connect(ui->pushContact, &QPushButton::clicked, this , &MainWindow::renderContactRequest); + + ui->contactNameMemo->setText(""); + + /////Copy Chatmessages + + QMenu* contextMenuChat; + QAction* copymessage; + QAction* viewexplorer; + QAction* copytxid; + contextMenuChat = new QMenu(ui->listChat); + copymessage = new QAction("Copy message to clipboard",contextMenuChat); + viewexplorer = new QAction("View on block explorer",contextMenuChat); + copytxid = new QAction("Copy txid to clipboard ",contextMenuChat); + + QObject::connect(ui->listContactWidget, &QTableView::clicked, [=] () { + + ui->listChat->setContextMenuPolicy(Qt::ActionsContextMenu); + ui->listChat->addAction(copymessage); + ui->listChat->addAction(viewexplorer); + ui->listChat->addAction(copytxid); + + QObject::connect(copymessage, &QAction::triggered, [=] { + + + QModelIndex index = ui->listChat->currentIndex(); + QString memo_chat = index.data(Qt::DisplayRole).toString(); + QClipboard *clipboard = QGuiApplication::clipboard(); + int startPos = memo_chat.indexOf("

") + 3; + int endPos = memo_chat.indexOf("

"); + int length = endPos - startPos; + QString copymemo = memo_chat.mid(startPos, length); + + clipboard->setText(copymemo); + ui->statusBar->showMessage(tr("Copied message to clipboard"), 3 * 1000); + +}); + QObject::connect(copytxid, &QAction::triggered, [=] { + + QModelIndex index = ui->listChat->currentIndex(); + QString memo_chat = index.data(Qt::DisplayRole).toString(); + QClipboard *clipboard = QGuiApplication::clipboard(); + + int startPos = memo_chat.indexOf("

") + 3; + int endPos = memo_chat.indexOf("

"); + int length = endPos - startPos; + QString copymemo = memo_chat.mid(startPos, length); + int startPosT = memo_chat.indexOf("") + 7; + int endPosT = memo_chat.indexOf(""); + int lengthT = endPosT - startPosT; + + QString time = memo_chat.mid(startPosT, lengthT); + + for (auto &c : DataStore::getChatDataStore()->getAllRawChatItems()){ + + if (c.second.getMemo() == copymemo) + { + int timestamp = c.second.getTimestamp(); + QDateTime myDateTime; + QString lock; + myDateTime.setTime_t(timestamp); + QString timestamphtml = myDateTime.toString("yyyy-MM-dd hh:mm"); + + if(timestamphtml == time) + + { + clipboard->setText(c.second.getTxid()); + ui->statusBar->showMessage(tr("Copied Txid to clipboard"), 3 * 1000); + }else{} + + } + +} + +}); + + QObject::connect(viewexplorer, &QAction::triggered, [=] { + + QModelIndex index = ui->listChat->currentIndex(); + QString memo_chat = index.data(Qt::DisplayRole).toString(); + + int startPos = memo_chat.indexOf("

") + 3; + int endPos = memo_chat.indexOf("

"); + int length = endPos - startPos; + QString copymemo = memo_chat.mid(startPos, length); + int startPosT = memo_chat.indexOf("") + 7; + int endPosT = memo_chat.indexOf(""); + int lengthT = endPosT - startPosT; + + QString time = memo_chat.mid(startPosT, lengthT); + + for (auto &c : DataStore::getChatDataStore()->getAllRawChatItems()){ + + if (c.second.getMemo() == copymemo) + { + int timestamp = c.second.getTimestamp(); + QDateTime myDateTime; + QString lock; + myDateTime.setTime_t(timestamp); + QString timestamphtml = myDateTime.toString("yyyy-MM-dd hh:mm"); + + if(timestamphtml == time) + + { + + Settings::openTxInExplorer(c.second.getTxid()); + + }else{} + + } + +} +}); + +}); + +///////// Add contextmenu + QMenu* contextMenu; + QAction* requestAction; + QAction* editAction; + QAction* HushAction; + QAction* requestHushAction; + QAction* subatomicAction; + contextMenu = new QMenu(ui->listContactWidget); + requestAction = new QAction("Send a contact request - coming soon",contextMenu); + editAction = new QAction("Delete this contact",contextMenu); + HushAction = new QAction("Send a friend some Hush - coming soon",contextMenu); + requestHushAction = new QAction("Request some Hush - coming soon",contextMenu); + subatomicAction = new QAction("Make a subatomic swap with a friend- coming soon",contextMenu); + + +///////// Set selected Zaddr for Chat with click + + QObject::connect(ui->listContactWidget, &QTableView::clicked, [=] () { + + ui->listContactWidget->setContextMenuPolicy(Qt::ActionsContextMenu); + ui->listContactWidget->addAction(requestAction); + ui->listContactWidget->addAction(editAction); + ui->listContactWidget->addAction(HushAction); + ui->listContactWidget->addAction(requestHushAction); + ui->listContactWidget->addAction(subatomicAction); + + QObject::connect(requestHushAction, &QAction::triggered, [=]() { + QModelIndex index = ui->listContactWidget->currentIndex(); + QString label_contact = index.data(Qt::DisplayRole).toString(); + + for(auto &p : AddressBook::getInstance()->getAllAddressLabels()) + if (label_contact == p.getName()) { + ui->contactNameMemo->setText(p.getName()); + rpc->refresh(true); + + } + MainWindow::showRequesthush(); + + }); + + QObject::connect(editAction, &QAction::triggered, [=]() { + QModelIndex index = ui->listContactWidget->currentIndex(); + QString label_contact = index.data(Qt::DisplayRole).toString(); + + for(auto &p : AddressBook::getInstance()->getAllAddressLabels()) + if (label_contact == p.getName()) { + + QString label1 = p.getName(); + QString addr = p.getPartnerAddress(); + QString myzaddr = p.getMyAddress(); + QString cid = p.getCid(); + QString avatar = p.getAvatar(); + + + AddressBook::getInstance()->removeAddressLabel(label1, addr, myzaddr, cid,avatar); + rpc->refreshContacts( + ui->listContactWidget); + rpc->refresh(true); + } + }); + + QModelIndex index = ui->listContactWidget->currentIndex(); + QString label_contact = index.data(Qt::DisplayRole).toString(); + + for(auto &p : AddressBook::getInstance()->getAllAddressLabels()) + if (label_contact == p.getName()) { + ui->contactNameMemo->setText(p.getName()); + rpc->refresh(true); + + } + }); + +ui->memoTxtChat->setLenDisplayLabelChat(ui->memoSizeChat); +} + +void MainWindow::updateChat() +{ + rpc->refreshChat(ui->listChat,ui->memoSizeChat); + rpc->refresh(true); +} + +void MainWindow::updateContacts() +{ + } void MainWindow::addNewZaddr(bool sapling) { @@ -1214,6 +1822,7 @@ void MainWindow::setupReceiveTab() { // Receive tab add/update label QObject::connect(ui->rcvUpdateLabel, &QPushButton::clicked, [=]() { QString addr = ui->listReceiveAddresses->currentText(); + if (addr.isEmpty()) return; @@ -1227,7 +1836,7 @@ void MainWindow::setupReceiveTab() { if (!curLabel.isEmpty() && label.isEmpty()) { info = "Removed Label '" % curLabel % "'"; - AddressBook::getInstance()->removeAddressLabel(curLabel, addr); + AddressBook::getInstance()->removeAddressLabel(curLabel, addr, "", "","" ); } else if (!curLabel.isEmpty() && !label.isEmpty()) { info = "Updated Label '" % curLabel % "' to '" % label % "'"; @@ -1235,7 +1844,7 @@ void MainWindow::setupReceiveTab() { } else if (curLabel.isEmpty() && !label.isEmpty()) { info = "Added Label '" % label % "'"; - AddressBook::getInstance()->addAddressLabel(label, addr); + AddressBook::getInstance()->addAddressLabel(label, addr, "", "", ""); } // Update labels everywhere on the UI @@ -1285,7 +1894,7 @@ void MainWindow::updateTAddrCombo(bool checked) { auto allTaddrs = this->rpc->getModel()->getAllTAddresses(); QSet labels; for (auto p : AddressBook::getInstance()->getAllAddressLabels()) { - labels.insert(p.second); + labels.insert(p.getPartnerAddress()); } std::for_each(allTaddrs.begin(), allTaddrs.end(), [=, &addrs] (auto& taddr) { // If the address is in the address book, add it. @@ -1377,7 +1986,7 @@ void MainWindow::slot_change_theme(const QString& theme_name) } catch (...) { - saved_theme_name = "default"; + saved_theme_name = "Dark"; } QFile qFile(":/css/res/css/" + saved_theme_name +".css"); @@ -1406,3 +2015,17 @@ MainWindow::~MainWindow() delete wsserver; delete wormhole; } +void MainWindow::on_givemeZaddr_clicked() +{ + + bool sapling = true; + rpc->createNewZaddr(sapling, [=] (json reply) { + QString hushchataddr = QString::fromStdString(reply.get()[0]); + QClipboard *zaddr_Clipboard = QApplication::clipboard(); + zaddr_Clipboard ->setText(hushchataddr); + QMessageBox::information(this, "Your new HushChat address was copied to your clipboard!",hushchataddr); + ui->listReceiveAddresses->insertItem(0, hushchataddr); + ui->listReceiveAddresses->setCurrentIndex(0); + + }); +} diff --git a/src/mainwindow.h b/src/mainwindow.h index 50f833a..62ff3f3 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -5,12 +5,14 @@ #include "logger.h" #include "recurring.h" +#include "firsttimewizard.h" // Forward declare to break circular dependency. class Controller; class Settings; class WSServer; class WormholeClient; +class ChatModel; using json = nlohmann::json; @@ -47,18 +49,32 @@ public: QRegExpValidator* getAmountValidator() { return amtValidator; } QString doSendTxValidations(Tx tx); + QString doSendChatTxValidations(Tx tx); + QString doSendRequestTxValidations(Tx tx); + QString getCid(); + QString getPassword(); + std::map pubkeyMap; + QString getPubkeyByAddress(QString requestZaddr); + void setPassword(QString Password); + void addPubkey(QString requestZaddr, QString pubkey); + + + void replaceWormholeClient(WormholeClient* newClient); bool isWebsocketListening(); void createWebsocket(QString wormholecode); void stopWebsocket(); - + void saveContact(); + void saveandsendContact(); + void showRequesthush(); + void balancesReady(); void payhushURI(QString uri = "", QString myAddr = ""); void updateLabels(); void updateTAddrCombo(bool checked); - + // Disable recurring on mainnet void disableRecurring(); @@ -71,37 +87,68 @@ public: QLabel* statusIcon; QLabel* loadingLabel; QWidget* hushdtab; + //ChatItem* currentChatItem; + Logger* logger; void doClose(); + void doClosePw(); + QString createHeaderMemo(QString type, QString cid, QString zaddr,QString headerbytes,QString publickey, int version, int headerNumber); public slots: void slot_change_theme(const QString& themeName); void slot_change_currency(const QString& currencyName); + -private: +private slots: + + + void on_givemeZaddr_clicked(); + +private: + + bool fileExists(QString path); void closeEvent(QCloseEvent* event); + void closeEventpw(QCloseEvent* event); + QString _password; + void setupSendTab(); void setupTransactionsTab(); void setupReceiveTab(); void setupBalancesTab(); void setuphushdTab(); + void setupchatTab(); + void renderContactRequest(); + + void updateContacts(); + void updateChat(); void setupSettingsModal(); void setupStatusBar(); void clearSendForm(); + Tx createTxFromSendPage(); bool confirmTx(Tx tx, RecurringPaymentInfo* rpi); + Tx createTxFromChatPage(); + Tx createTxForSafeContactRequest(); + + void encryptWallet(); void removeWalletEncryption(); + void removeWalletEncryptionStartUp(); void cancelButton(); void sendButton(); + void sendChat(); + void addContact(); + void ContactRequest(); + + void addAddressSection(); void maxAmountChecked(int checked); @@ -131,13 +178,18 @@ private: void restoreSavedStates(); bool eventFilter(QObject *object, QEvent *event); + + + bool uiPaymentsReady = false; QString pendingURIPayment; WSServer* wsserver = nullptr; WormholeClient* wormhole = nullptr; + Controller* rpc = nullptr; + QCompleter* labelCompleter = nullptr; QRegExpValidator* amtValidator = nullptr; QRegExpValidator* feesValidator = nullptr; @@ -147,4 +199,21 @@ private: QMovie* loadingMovie; }; +class ChatMemoEdit : public QTextEdit +{ +public: + ChatMemoEdit(QWidget* parent); + + void setMaxLenChat(int len); + void setLenDisplayLabelChat(QLabel* label); + void SetSendChatButton(QPushButton* button); + void updateDisplayChat(); + +private: + int maxlenchat = 235; + QLabel* lenDisplayLabelchat = nullptr; + QPushButton* sendChatButton = nullptr; +}; + + #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index d7caf5d..8471227 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -10,6 +10,43 @@ 779 + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + + + + 0 + 0 + 0 + + + + + + SilentDragonLite @@ -18,11 +55,11 @@ :/icons/res/icon.ico:/icons/res/icon.ico - - + + - 0 + 2 @@ -47,181 +84,184 @@ - + - - - - - - 0 - 0 - - - - Shielded - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - 0 - 0 - - - - Notarized - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - 0 - 0 - - - - Transparent - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - Qt::Horizontal - - - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Total - - - - - - - - 75 - true - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - color:red; - - - Your node is still syncing, balances may not be updated. - - - true - - - - - + 0 0 - - color: red; - - Some transactions are not yet confirmed. Balances may change. - - - true + Shielded - - - Qt::Vertical + + + - - - 20 - 40 - + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + + + + + + + + 0 + 0 + + + + Notarized + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 0 + 0 + + + + Transparent + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Total + + + + + + + + 75 + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + color:red; + + + Your node is still syncing, balances may not be updated. + + + true + + + + + + + + 0 + 0 + + + + color: red; + + + Some transactions are not yet confirmed. Balances may change. + + + true + + + + + + + Deposit Hush + + + + + + + Qt::Vertical + + + + 20 + 383 + + + + @@ -389,7 +429,7 @@ 0 0 1226 - 504 + 493 @@ -574,33 +614,6 @@ - - - - Recurring payment - - - - - - - Every month, starting 12-May-2012, for 6 payments - - - - - - - Edit Schedule - - - false - - - false - - - @@ -1324,6 +1337,443 @@ + + + HushChat + + + + + + + + QLayout::SetDefaultConstraint + + + 0 + + + 0 + + + 15 + + + + + + 51 + 51 + + + + + 51 + 51 + + + + + 100 + 0 + + + + Incoming contact request + + + false + + + + + + + :/icons/res/requestBlack.png:/icons/res/requestBlack.png + + + + 50 + 45 + + + + true + + + + + + + + 51 + 51 + + + + + 51 + 51 + + + + + 100 + 0 + + + + Add a new contact + + + + + + + :/icons/res/addContactBlack.png + + + + + 50 + 45 + + + + true + + + + + + + + 51 + 51 + + + + + 51 + 51 + + + + Get a new Address + + + + + + + :/icons/res/getAddrBlack.png:/icons/res/getAddrBlack.png + + + + 50 + 45 + + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 100 + 20 + + + + + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + <html><head/><body><p align="center"><span style=" font-weight:600; text-decoration: underline;">Contactlist</span></p></body></html> + + + + + + + + 0 + 0 + + + + true + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectItems + + + + + + + + + QLayout::SetDefaultConstraint + + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + <html><head/><body><p align="center"><span style=" font-weight:600;">Contact Name :</span></p></body></html> + + + + + + + + 75 + true + + + + <html><head/><body><p align="center"><br/></p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + Qt::NoFocus + + + The locks shows you the status of the message. Red lock = unconfirmed, green lock = min. 1 confirmations, orange lock = message is notarized + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + true + + + QAbstractItemView::NoEditTriggers + + + false + + + QAbstractItemView::DragOnly + + + Qt::IgnoreAction + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerItem + + + QListView::Snap + + + QListView::TopToBottom + + + QListView::Adjust + + + QListView::SinglePass + + + 0 + + + false + + + true + + + true + + + + + + + QLayout::SetDefaultConstraint + + + 0 + + + + + + 0 + 0 + + + + false + + + font: 11pt "Noto Color Emoji"; + + + QTextEdit::AutoNone + + + QTextEdit::WidgetWidth + + + 600 + + + false + + + Qt::TextEditorInteraction + + + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + false + + + + + + + :/icons/res/sendBlack.png + + + + + 50 + 49 + + + + false + + + true + + + + + + + + 0 + 0 + + + + QFrame::Sunken + + + 0 / 235 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + @@ -1509,6 +1959,11 @@ QLabel
fillediconlabel.h
+ + ChatMemoEdit + QTextEdit +
mainwindow.h
+
Address1 diff --git a/src/memodialog.ui b/src/memodialog.ui index 1c144e0..d365221 100644 --- a/src/memodialog.ui +++ b/src/memodialog.ui @@ -74,7 +74,7 @@ MemoEdit QPlainTextEdit -
memoedit.h
+
memoedit.h
diff --git a/src/mobileappconnector.ui b/src/mobileappconnector.ui index 9412c37..823a497 100644 --- a/src/mobileappconnector.ui +++ b/src/mobileappconnector.ui @@ -11,7 +11,7 @@ - Connect Mobile App + Mobile Connector App diff --git a/src/newseed.ui b/src/newseed.ui index ed5d9ae..7657542 100644 --- a/src/newseed.ui +++ b/src/newseed.ui @@ -6,15 +6,15 @@ 0 0 - 400 - 300 + 427 + 416 Form - + This is your new wallet's seed phrase. PLEASE BACK IT UP SECURELY. @@ -24,17 +24,7 @@ - - - - The seed phrase is the only way to restore the wallet. If you forget the seed phrase, THERE IS NO WAY TO RESTORE YOUR WALLET AND THE FUNDS in it - - - true - - - - + @@ -56,6 +46,16 @@ + + + + The seed phrase is the only way to restore the wallet. If you forget the seed phrase, THERE IS NO WAY TO RESTORE YOUR WALLET AND THE FUNDS in it + + + true + + + diff --git a/src/newwallet.ui b/src/newwallet.ui index 9cf34c5..9f1e76c 100644 --- a/src/newwallet.ui +++ b/src/newwallet.ui @@ -6,15 +6,29 @@ 0 0 - 400 - 300 + 437 + 381 - Form + Create New SDL Wallet - + + + + Confirm Passphrase: + + + + + + + QLineEdit::Password + + + + @@ -46,7 +60,14 @@ - + + + + QLineEdit::Password + + + + @@ -61,7 +82,7 @@ - Create a new Wallet + Create a new wallet @@ -78,6 +99,52 @@ + + + + Qt::Horizontal + + + + + + + Encryption Passphrase: + + + + + + + + 0 + 0 + + + + color: red; + + + <html><head/><body><p><span style=" font-style:italic;">Passphrase don't match</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-style:italic;">16 letters minimum</span></p></body></html> + + + diff --git a/src/recurringdialog.ui b/src/recurringdialog.ui index fbe26e6..6e53279 100644 --- a/src/recurringdialog.ui +++ b/src/recurringdialog.ui @@ -11,7 +11,7 @@ - Dialog + Reccuring Dialog diff --git a/src/recurringmultiple.ui b/src/recurringmultiple.ui index b895ecb..e7a41ca 100644 --- a/src/recurringmultiple.ui +++ b/src/recurringmultiple.ui @@ -11,7 +11,7 @@ - Dialog + Recurring Multiple Payments diff --git a/src/recurringpayments.ui b/src/recurringpayments.ui index 3874370..839fe73 100644 --- a/src/recurringpayments.ui +++ b/src/recurringpayments.ui @@ -11,7 +11,7 @@ - Payments + Reocurring Payments diff --git a/src/removeencryption.ui b/src/removeencryption.ui new file mode 100644 index 0000000..2194997 --- /dev/null +++ b/src/removeencryption.ui @@ -0,0 +1,175 @@ + + + removeencryption + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + + 400 + 300 + + + + + 400 + 300 + + + + Decrypt Your Wallet + + + + + + Qt::Horizontal + + + + + + + <html><head/><body><p><span style=" font-size:14pt; color:#ef2929;">WARNING:</span> If you remove your wallet.dat encryption, all your transactions and contacts are plaintext on disk!<br/><br/>Messages sent and received are always encrypted.</p></body></html> + + + Qt::AlignCenter + + + true + + + + + + + QLineEdit::Password + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-style:italic;">16 letters minimum</span></p></body></html> + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Encryption Passphrase: + + + + + + + Confirm Passphrase: + + + + + + + Qt::Horizontal + + + + + + + QLineEdit::Password + + + + + + + + 0 + 0 + + + + color: red; + + + <html><head/><body><p><span style=" font-style:italic;">Passphrase don't match</span></p></body></html> + + + Qt::AlignCenter + + + + + + + + + buttonBox + accepted() + removeencryption + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + removeencryption + close() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/requestContactDialog.ui b/src/requestContactDialog.ui new file mode 100644 index 0000000..76e904a --- /dev/null +++ b/src/requestContactDialog.ui @@ -0,0 +1,473 @@ + + + requestDialog + + + + 0 + 0 + 850 + 495 + + + + + 0 + 0 + + + + + 850 + 495 + + + + Incoming Contact Request + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Memo of the request</span></p></body></html> + + + + + + + + 500 + 231 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::NoEditTriggers + + + QListView::Adjust + + + 0 + + + false + + + true + + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Open requests</span></p></body></html> + + + + + + + + 0 + 0 + + + + + 225 + 190 + + + + true + + + QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + false + + + QAbstractItemView::SingleSelection + + + + + + + + 351 + 25 + + + + + 351 + 25 + + + + + + + + + 0 + 0 + + + + Request from: + + + + + + + + 0 + 0 + + + + + 0 + 25 + + + + 25 + + + + + + + + + + + + + + Add New Contact + + + false + + + + + + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Choose an avatar for your contact:</p></body></html> + + + + + + + + 0 + 0 + + + + My Zaddr: + + + + + + + + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + Cancel + + + false + + + false + + + + + + + + 106 + 25 + + + + + 106 + 25 + + + + + SDLogo + + + + :/icons/res/SDLogo.png + + + + + + Duke + + + + :/icons/res/Duke.png + + + + + + Denio + + + + :/icons/res/Denio.png + + + + + + Berg + + + + :/icons/res/Berg.png + + + + + + Sharpee + + + + :/icons/res/Sharpee.png + + + + + + Elsa + + + + :/icons/res/Elsa.png + + + + + + Yoda + + + + :/icons/res/Yoda.png + + + + + + Garfield + + + + :/icons/res/Garfield.png + + + + + + Snoopy + + + + :/icons/res/Snoopy.png + + + + + + Popey + + + + :/icons/res/Popey.png + + + + + + Pinguin + + + + :/icons/res/Pinguin.png + + + + + + Mickey + + + + :/icons/res/Mickey.png + + + + + + Stag + + + + :/icons/res/Stag.png + + + + + + + + + + 225 + 231 + + + + true + + + QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + false + + + QAbstractItemView::SingleSelection + + + + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Recently closed requests</span></p></body></html> + + + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-weight:600; text-decoration: underline;">Details of the request</span></p></body></html> + + + + + + + + 0 + 0 + + + + Give a Nickname: + + + + + + + + 351 + 25 + + + + + 351 + 25 + + + + + + + + + + cancel + clicked() + requestDialog + reject() + + + 339 + 482 + + + 505 + 251 + + + + + diff --git a/src/requestdialog.ui b/src/requestdialog.ui index 952f780..f12e3e6 100644 --- a/src/requestdialog.ui +++ b/src/requestdialog.ui @@ -14,9 +14,124 @@ Payment Request + + + + + + + + + + + Request From + + + + + + + The recipient will see this address in the "to" field when they pay your request. + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + + TextLabel + + + Qt::AlignCenter + + + + + + + + + + + + + + color: red; + + + + + + + + + + Memo + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 0 / 235 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + Amount USD + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + @@ -41,65 +156,13 @@ - - - - Qt::Horizontal - - - - - + + - Request From + Request payment from a Sapling address. You'll send a hush 0.0001 transaction to the address with a hush payment URI. The memo will be included in the transaction when the address pays you. - - - - - - My Address - - - - - - - color: red; - - - - - - - - - - Amount in - - - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - + + true @@ -119,20 +182,20 @@ - - - - Qt::Horizontal + + + + My Address - - - - - 0 - 0 - + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -146,83 +209,20 @@ - - + + + + Amount in + + + + + Qt::Horizontal - - - - The recipient will see this address in the "to" field when they pay your request. - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 0 / 512 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Amount USD - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Memo - - - - - - - TextLabel - - - Qt::AlignCenter - - - - - - - Request payment from a Sapling address. You'll send a hush 0.0001 transaction to the address with a hush payment URI. The memo will be included in the transaction when the address pays you. - - - true - - - diff --git a/src/restoreseed.ui b/src/restoreseed.ui index 9cc2b3f..64bd43f 100644 --- a/src/restoreseed.ui +++ b/src/restoreseed.ui @@ -11,7 +11,7 @@ - Form + Restore Wallet Seed diff --git a/src/sendtab.cpp b/src/sendtab.cpp index 3fc5510..7e21261 100644 --- a/src/sendtab.cpp +++ b/src/sendtab.cpp @@ -13,7 +13,7 @@ using json = nlohmann::json; void MainWindow::setupSendTab() { // Create the validator for send to/amount fields - amtValidator = new QRegExpValidator(QRegExp("[0-9]{0,8}\\.?[0-9]{0,8}")); + amtValidator = new QRegExpValidator(QRegExp("[0-9]{0,8}\\.?[0-9]{0,8}")); ui->Amount1->setValidator(amtValidator); @@ -35,14 +35,14 @@ void MainWindow::setupSendTab() { }); // The first Memo button - QObject::connect(ui->MemoBtn1, &QPushButton::clicked, [=] () { + QObject::connect(ui->MemoBtn1, &QPushButton::clicked, [=] () { this->memoButtonClicked(1); }); setMemoEnabled(1, false); - + // This is the damnest thing ever. If we do AddressBook::readFromStorage() directly, the whole file // doesn't get read. It needs to run in a timer after everything has finished to be able to read - // the file properly. + // the file properly. QTimer::singleShot(2000, [=]() { updateLabelsAutoComplete(); }); // The first address book button @@ -84,7 +84,7 @@ void MainWindow::setupSendTab() { ui->lblMinerFeeUSD->setText(fee.toDecimalAUDString()); } }); - ui->minerFeeAmt->setText(Settings::getMinerFee().toDecimalString()); + ui->minerFeeAmt->setText(Settings::getMinerFee().toDecimalString()); // Set up focus enter to set fees QObject::connect(ui->tabWidget, &QTabWidget::currentChanged, [=] (int pos) { @@ -123,13 +123,13 @@ void MainWindow::setupSendTab() { } else if (Settings::getInstance()->get_currency_name() == "AUD") { QString feeUSD = CAmount::fromDecimalString(txt).toDecimalAUDString(); ui->lblMinerFeeUSD->setText(feeUSD); - } + } } }); - + //Fees validator - feesValidator = new QRegExpValidator(QRegExp("[0-9]{0,8}\\.?[0-9]{0,8}")); + feesValidator = new QRegExpValidator(QRegExp("[0-9]{0,8}\\.?[0-9]{0,8}")); ui->minerFeeAmt->setValidator(feesValidator); // Font for the first Memo label @@ -138,9 +138,9 @@ void MainWindow::setupSendTab() { ui->MemoTxt1->setFont(f); // Recurring button - QObject::connect(ui->chkRecurring, &QCheckBox::stateChanged, [=] (int checked) { + /*QObject::connect(ui->chkRecurring, &QCheckBox::stateChanged, [=] (int checked) { if (checked) { - ui->btnRecurSchedule->setEnabled(true); + ui->btnRecurSchedule->setEnabled(true); // If this is the first time the button is checked, open the edit schedule dialog if (sendTxRecurringInfo == nullptr) { @@ -150,66 +150,66 @@ void MainWindow::setupSendTab() { ui->btnRecurSchedule->setEnabled(false); ui->lblRecurDesc->setText(""); } - }); + });*/ // Recurring schedule button - QObject::connect(ui->btnRecurSchedule, &QPushButton::clicked, this, &MainWindow::editSchedule); + // QObject::connect(ui->btnRecurSchedule, &QPushButton::clicked, this, &MainWindow::editSchedule); // Set the default state for the whole page clearSendForm(); } void MainWindow::disableRecurring() { - if (!Settings::getInstance()->isTestnet()) { +/* if (!Settings::getInstance()->isTestnet()) { ui->chkRecurring->setVisible(false); ui->chkRecurring->setEnabled(false); ui->btnRecurSchedule->setVisible(false); ui->btnRecurSchedule->setEnabled(false); - ui->action_Recurring_Payments->setVisible(false); - } + ui->action_Recurring_Payments->setVisible(false);*/ + // } } void MainWindow::editSchedule() { // Only on testnet for now if (!Settings::getInstance()->isTestnet()) { - QMessageBox::critical(this, "Not Supported yet", + QMessageBox::critical(this, "Not Supported yet", "Recurring payments are only supported on Testnet for now.", QMessageBox::Ok); return; } // Check to see that recurring payments are not selected when there are 2 or more addresses if (ui->sendToWidgets->children().size()-1 > 2) { - QMessageBox::critical(this, tr("Cannot support multiple addresses"), + QMessageBox::critical(this, tr("Cannot support multiple addresses"), tr("Recurring payments doesn't currently support multiple addresses"), QMessageBox::Ok); return; } // Open the edit schedule dialog - auto recurringInfo = Recurring::getInstance()->getNewRecurringFromTx(this, this, - createTxFromSendPage(), this->sendTxRecurringInfo); - if (recurringInfo == nullptr) { - // User pressed cancel. + // auto recurringInfo = Recurring::getInstance()->getNewRecurringFromTx(this, this, + // createTxFromSendPage(), this->sendTxRecurringInfo); + // if (recurringInfo == nullptr) { + // User pressed cancel. // If there is no existing recurring info, uncheck the recurring box - if (sendTxRecurringInfo == nullptr) { - ui->chkRecurring->setCheckState(Qt::Unchecked); - } - } - else { - delete this->sendTxRecurringInfo; + // if (sendTxRecurringInfo == nullptr) { + // ui->chkRecurring->setCheckState(Qt::Unchecked); + // } + // } + // else { + // delete this->sendTxRecurringInfo; - this->sendTxRecurringInfo = recurringInfo; - ui->lblRecurDesc->setText(recurringInfo->getScheduleDescription()); - } + // this->sendTxRecurringInfo = recurringInfo; + // ui->lblRecurDesc->setText(recurringInfo->getScheduleDescription()); + //} } void MainWindow::updateLabelsAutoComplete() { QList list; auto labels = AddressBook::getInstance()->getAllAddressLabels(); - + std::transform(labels.begin(), labels.end(), std::back_inserter(list), [=] (auto la) -> QString { - return la.first % "/" % la.second; + return la.getName() % "/" % la.getPartnerAddress(); }); - + delete labelCompleter; labelCompleter = new QCompleter(list, this); labelCompleter->setCaseSensitivity(Qt::CaseInsensitive); @@ -221,7 +221,7 @@ void MainWindow::updateLabelsAutoComplete() { } } - + void MainWindow::addAddressSection() { int itemNumber = ui->sendToWidgets->children().size() - 1; @@ -239,7 +239,7 @@ void MainWindow::addAddressSection() { horizontalLayout_12->addWidget(label_4); auto Address1 = new QLineEdit(verticalGroupBox); - Address1->setObjectName(QString("Address") % QString::number(itemNumber)); + Address1->setObjectName(QString("Address") % QString::number(itemNumber)); Address1->setPlaceholderText(tr("Address")); QObject::connect(Address1, &QLineEdit::textChanged, [=] (auto text) { this->addressChanged(itemNumber, text); @@ -261,16 +261,16 @@ void MainWindow::addAddressSection() { auto horizontalLayout_13 = new QHBoxLayout(); horizontalLayout_13->setSpacing(6); - + auto label_6 = new QLabel(verticalGroupBox); label_6->setText(tr("Amount")); horizontalLayout_13->addWidget(label_6); auto Amount1 = new QLineEdit(verticalGroupBox); - Amount1->setPlaceholderText(tr("Amount")); - Amount1->setObjectName(QString("Amount") % QString::number(itemNumber)); + Amount1->setPlaceholderText(tr("Amount")); + Amount1->setObjectName(QString("Amount") % QString::number(itemNumber)); Amount1->setBaseSize(QSize(200, 0)); - Amount1->setAlignment(Qt::AlignRight); + Amount1->setAlignment(Qt::AlignRight); // Create the validator for send to/amount fields Amount1->setValidator(amtValidator); @@ -281,7 +281,7 @@ void MainWindow::addAddressSection() { horizontalLayout_13->addWidget(Amount1); auto AmtUSD1 = new QLabel(verticalGroupBox); - AmtUSD1->setObjectName(QString("AmtUSD") % QString::number(itemNumber)); + AmtUSD1->setObjectName(QString("AmtUSD") % QString::number(itemNumber)); horizontalLayout_13->addWidget(AmtUSD1); auto horizontalSpacer_4 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); @@ -289,7 +289,7 @@ void MainWindow::addAddressSection() { auto MemoBtn1 = new QPushButton(verticalGroupBox); MemoBtn1->setObjectName(QString("MemoBtn") % QString::number(itemNumber)); - MemoBtn1->setText(tr("Memo")); + MemoBtn1->setText(tr("Memo")); // Connect Memo Clicked button QObject::connect(MemoBtn1, &QPushButton::clicked, [=] () { this->memoButtonClicked(itemNumber); @@ -307,24 +307,24 @@ void MainWindow::addAddressSection() { MemoTxt1->setWordWrap(true); sendAddressLayout->addWidget(MemoTxt1); - ui->sendToLayout->insertWidget(itemNumber-1, verticalGroupBox); + ui->sendToLayout->insertWidget(itemNumber-1, verticalGroupBox); // Disable recurring payments if a address section is added, since recurring payments // aren't supported for more than 1 address - delete sendTxRecurringInfo; - sendTxRecurringInfo = nullptr; - ui->lblRecurDesc->setText(""); - ui->chkRecurring->setChecked(false); - ui->chkRecurring->setEnabled(false); + // delete sendTxRecurringInfo; + // sendTxRecurringInfo = nullptr; + // ui->lblRecurDesc->setText(""); + // ui->chkRecurring->setChecked(false); + // ui->chkRecurring->setEnabled(false); // Set focus into the address Address1->setFocus(); // Delay the call to scroll to allow the scroll window to adjust - QTimer::singleShot(10, [=] () {ui->sendToScrollArea->ensureWidgetVisible(ui->addAddressButton);}); + QTimer::singleShot(10, [=] () {ui->sendToScrollArea->ensureWidgetVisible(ui->addAddressButton);}); } -void MainWindow::addressChanged(int itemNumber, const QString& text) { +void MainWindow::addressChanged(int itemNumber, const QString& text) { auto addr = AddressBook::addressFromAddressLabel(text); setMemoEnabled(itemNumber, Settings::isZAddress(addr)); } @@ -358,10 +358,10 @@ void MainWindow::amountChanged(int item, const QString& text) { } // If there is a recurring payment, update the info there as well - if (sendTxRecurringInfo != nullptr) { - Recurring::getInstance()->updateInfoWithTx(sendTxRecurringInfo, createTxFromSendPage()); - ui->lblRecurDesc->setText(sendTxRecurringInfo->getScheduleDescription()); - } + //if (sendTxRecurringInfo != nullptr) { + // Recurring::getInstance()->updateInfoWithTx(sendTxRecurringInfo, createTxFromSendPage()); + // ui->lblRecurDesc->setText(sendTxRecurringInfo->getScheduleDescription()); + // } } void MainWindow::setMemoEnabled(int number, bool enabled) { @@ -377,15 +377,15 @@ void MainWindow::setMemoEnabled(int number, bool enabled) { void MainWindow::memoButtonClicked(int number, bool includeReplyTo) { // Memos can only be used with zAddrs. So check that first - auto addr = ui->sendToWidgets->findChild(QString("Address") + QString::number(number)); - if (! Settings::isZAddress(AddressBook::addressFromAddressLabel(addr->text()))) { - QMessageBox msg(QMessageBox::Critical, tr("Memos can only be used with z-addresses"), - tr("The memo field can only be used with a z-address.\n") + addr->text() + tr("\ndoesn't look like a z-address"), - QMessageBox::Ok, this); + // auto addr = ui->sendToWidgets->findChild(QString("Address") + QString::number(number)); + //if (! Settings::isZAddress(AddressBook::addressFromAddressLabel(addr->text()))) { + // QMessageBox msg(QMessageBox::Critical, tr("Memos can only be used with z-addresses"), + // tr("The memo field can only be used with a z-address.\n") + addr->text() + tr("\ndoesn't look like a z-address"), + // QMessageBox::Ok, this); - msg.exec(); - return; - } + // msg.exec(); + // return; + // } // Get the current memo if it exists auto memoTxt = ui->sendToWidgets->findChild(QString("MemoTxt") + QString::number(number)); @@ -428,7 +428,7 @@ void MainWindow::memoButtonClicked(int number, bool includeReplyTo) { void MainWindow::clearSendForm() { // The last one is a spacer, so ignore that - int totalItems = ui->sendToWidgets->children().size() - 2; + int totalItems = ui->sendToWidgets->children().size() - 2; // Clear the first recipient fields auto addr = ui->sendToWidgets->findChild(QString("Address1")); @@ -451,43 +451,43 @@ void MainWindow::clearSendForm() { // Start the deletion after the first item, since we want to keep 1 send field there all there for (int i=1; i < totalItems; i++) { auto addressGroupBox = ui->sendToWidgets->findChild(QString("AddressGroupBox") % QString::number(i+1)); - + delete addressGroupBox; - } + } // Reset the recurring button if (Settings::getInstance()->isTestnet()) { - ui->chkRecurring->setEnabled(true); - } + // ui->chkRecurring->setEnabled(true); + } - ui->chkRecurring->setCheckState(Qt::Unchecked); - ui->btnRecurSchedule->setEnabled(false); - ui->lblRecurDesc->setText(""); - delete sendTxRecurringInfo; - sendTxRecurringInfo = nullptr; + // ui->chkRecurring->setCheckState(Qt::Unchecked); + // ui->btnRecurSchedule->setEnabled(false); + // ui->lblRecurDesc->setText(""); + // delete sendTxRecurringInfo; + // sendTxRecurringInfo = nullptr; } void MainWindow::maxAmountChecked(int checked) { if (checked == Qt::Checked) { ui->Amount1->setReadOnly(true); if (rpc == nullptr) return; - + // Calculate maximum amount CAmount sumAllAmounts; // Calculate all other amounts - int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that + int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that // Start counting the sum skipping the first one, because the MAX button is on the first one, and we don't - // want to include it in the sum. + // want to include it in the sum. for (int i=1; i < totalItems; i++) { auto amt = ui->sendToWidgets->findChild(QString("Amount") % QString::number(i+1)); sumAllAmounts = sumAllAmounts + CAmount::fromDecimalString(amt->text()); } sumAllAmounts = sumAllAmounts + Settings::getMinerFee(); - + auto maxamount = rpc->getModel()->getAvailableBalance() - sumAllAmounts; maxamount = (maxamount < 0) ? CAmount::fromqint64(0): maxamount; - + ui->Amount1->setText(maxamount.toDecimalString()); } else if (checked == Qt::Unchecked) { // Just remove the readonly part, don't change the content @@ -495,61 +495,61 @@ void MainWindow::maxAmountChecked(int checked) { } } -// Create a Tx from the current state of the send page. +// Create a Tx from the current state of the send page. Tx MainWindow::createTxFromSendPage() { Tx tx; // For each addr/amt in the sendTo tab - int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that + int totalItems = ui->sendToWidgets->children().size() - 2; // The last one is a spacer, so ignore that CAmount totalAmt; for (int i=0; i < totalItems; i++) { QString addr = ui->sendToWidgets->findChild(QString("Address") % QString::number(i+1))->text().trimmed(); - + // Remove label if it exists addr = AddressBook::addressFromAddressLabel(addr); // QString dustamt = "0"; QString amtStr = ui->sendToWidgets->findChild(QString("Amount") % QString::number(i+1))->text().trimmed(); if (amtStr.isEmpty()) { amtStr = "-1";; // The user didn't specify an amount - } + } bool ok; CAmount amt; - - + + // Make sure it parses amtStr.toDouble(&ok); if (!ok) { amt = CAmount::fromqint64(-1); - + } else { amt = CAmount::fromDecimalString(amtStr); totalAmt = totalAmt + amt; - + } - + QString memo = ui->sendToWidgets->findChild(QString("MemoTxt") % QString::number(i+1))->text().trimmed(); - - + + tx.toAddrs.push_back( ToFields{addr, amt, memo} ); - - + + } - + tx.fee = Settings::getMinerFee(); - + return tx; } bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { - // Function to split the address to make it easier to read. - // Split it into chunks of 4 chars. + // Function to split the address to make it easier to read. + // Split it into chunks of 4 chars. auto fnSplitAddressForWrap = [=] (const QString& a) -> QString { if (Settings::isTAddress(a)) return a; @@ -601,8 +601,8 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { delete confirm.sendToAddrs->findChild("labelMinerFee"); delete confirm.sendToAddrs->findChild("minerFee"); delete confirm.sendToAddrs->findChild("minerFeeUSD"); - - // For each addr/amt/memo, construct the JSON and also build the confirm dialog box + + // For each addr/amt/memo, construct the JSON and also build the confirm dialog box int row = 0; CAmount totalSpending; @@ -632,7 +632,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalUSDString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (EUR) } else if (Settings::getInstance()->get_currency_name() == "EUR") { @@ -640,7 +640,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalEURString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (BTC) } else if (Settings::getInstance()->get_currency_name() == "BTC") { @@ -648,7 +648,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalBTCString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (CNY) } else if (Settings::getInstance()->get_currency_name() == "CNY") { @@ -656,7 +656,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalCNYString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (RUB) } else if (Settings::getInstance()->get_currency_name() == "RUB") { @@ -664,7 +664,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalRUBString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (CAD) } else if (Settings::getInstance()->get_currency_name() == "CAD") { @@ -672,7 +672,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalCADString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (SGD) } else if (Settings::getInstance()->get_currency_name() == "SGD") { @@ -680,7 +680,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalSGDString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (CHF) } else if (Settings::getInstance()->get_currency_name() == "CHF") { @@ -688,7 +688,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalCHFString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (INR) } else if (Settings::getInstance()->get_currency_name() == "INR") { @@ -696,7 +696,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalINRString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (GBP) } else if (Settings::getInstance()->get_currency_name() == "GBP") { @@ -704,7 +704,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalGBPString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); // Amount (AUD) } else if (Settings::getInstance()->get_currency_name() == "AUD") { @@ -712,7 +712,7 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { AmtUSD->setObjectName(QString("AmtUSD") % QString::number(i + 1)); AmtUSD->setText(toAddr.amount.toDecimalAUDString()); AmtUSD->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter); - confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); + confirm.gridLayout->addWidget(AmtUSD, row, 2, 1, 1); } // Memo if (Settings::isZAddress(toAddr.addr) && !toAddr.memo.isEmpty()) { @@ -798,10 +798,10 @@ bool MainWindow::confirmTx(Tx tx, RecurringPaymentInfo* rpi) { // Syncing warning confirm.syncingWarning->setVisible(Settings::getInstance()->isSyncing()); - + // Show the dialog and submit it if the user confirms - return d.exec() == QDialog::Accepted; + return d.exec() == QDialog::Accepted; } // Send button clicked @@ -824,9 +824,9 @@ void MainWindow::sendButton() { } // Show a dialog to confirm the Tx - if (confirmTx(tx, sendTxRecurringInfo)) { - // If this is a recurring payment, save the hash so we can - // update the payment if it submits. + if (confirmTx(tx, sendTxRecurringInfo)) { + // If this is a recurring payment, save the hash so we can + // update the payment if it submits. QString recurringPaymentHash; // Recurring payments are enabled only if there is exactly 1 destination address. @@ -843,8 +843,18 @@ void MainWindow::sendButton() { auto d = new QDialog(this); auto connD = new Ui_ConnectionDialog(); connD->setupUi(d); - QPixmap logo(":/img/res/logobig.gif"); - connD->topIcon->setBasePixmap(logo.scaled(256, 256, Qt::KeepAspectRatio, Qt::SmoothTransformation)); + QMovie *movie1 = new QMovie(":/img/res/silentdragonlite-animated.gif");; + QMovie *movie2 = new QMovie(":/img/res/silentdragonlite-animated-dark.gif");; + auto theme = Settings::getInstance()->get_theme_name(); + if (theme == "Dark" || theme == "Midnight") { + movie2->setScaledSize(QSize(512,512)); + connD->topIcon->setMovie(movie2); + movie2->start(); + } else { + movie1->setScaledSize(QSize(512,512)); + connD->topIcon->setMovie(movie1); + movie1->start(); + } connD->status->setText(tr("Please wait...")); connD->statusDetail->setText(tr("Computing your transaction")); @@ -852,8 +862,8 @@ void MainWindow::sendButton() { d->show(); // And send the Tx - rpc->executeTransaction(tx, - [=] (QString txid) { + rpc->executeTransaction(tx, + [=] (QString txid) { ui->statusBar->showMessage(Settings::txidStatusMessage + " " + txid); connD->status->setText(tr("Done!")); @@ -868,40 +878,40 @@ void MainWindow::sendButton() { // And switch to the balances tab ui->tabWidget->setCurrentIndex(0); }); - + // Force a UI update so we get the unconfirmed Tx rpc->refresh(true); // If this was a recurring payment, update the payment with the info if (!recurringPaymentHash.isEmpty()) { // Since this is the send button payment, this is the first payment - Recurring::getInstance()->updatePaymentItem(recurringPaymentHash, 0, + Recurring::getInstance()->updatePaymentItem(recurringPaymentHash, 0, txid, "", PaymentStatus::COMPLETED); } }, // Errored out [=] (QString opid, QString errStr) { ui->statusBar->showMessage(QObject::tr(" Tx ") % opid % QObject::tr(" failed"), 15 * 1000); - + d->accept(); d->close(); delete connD; delete d; if (!opid.isEmpty()) - errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr; + errStr = QObject::tr("The transaction with id ") % opid % QObject::tr(" failed. The error was") + ":\n\n" + errStr; // If this was a recurring payment, update the payment with the failure if (!recurringPaymentHash.isEmpty()) { // Since this is the send button payment, this is the first payment - Recurring::getInstance()->updatePaymentItem(recurringPaymentHash, 0, - "", errStr, PaymentStatus::ERROR); - } + Recurring::getInstance()->updatePaymentItem(recurringPaymentHash, 0, + "", errStr, PaymentStatus::ERROR); + } - QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok); + QMessageBox::critical(this, QObject::tr("Transaction Error"), errStr, QMessageBox::Ok); } ); - } + } } QString MainWindow::doSendTxValidations(Tx tx) { @@ -915,7 +925,7 @@ QString MainWindow::doSendTxValidations(Tx tx) { } // This technically shouldn't be possible, but issue #62 seems to have discovered a bug - // somewhere, so just add a check to make sure. + // somewhere, so just add a check to make sure. if (toAddr.amount.toqint64() < 0) { return QString(tr("Amount for address '%1' is invalid!").arg(toAddr.addr)); } @@ -927,7 +937,7 @@ QString MainWindow::doSendTxValidations(Tx tx) { auto available = rpc->getModel()->getAvailableBalance(); if (available < total) { - return tr("Not enough available funds to send this transaction\n\nHave: %1\nNeed: %2\n\nNote: Funds need 5 confirmations before they can be spent") + return tr("Not enough available funds to send this transaction\n\nHave: %1\nNeed: %2\n\nNote: Funds need 1 confirmations before they can be spent") .arg(available.toDecimalhushString(), total.toDecimalhushString()); } @@ -937,4 +947,3 @@ QString MainWindow::doSendTxValidations(Tx tx) { void MainWindow::cancelButton() { clearSendForm(); } - diff --git a/src/settings.cpp b/src/settings.cpp index fbfb1cb..fd47f76 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -225,16 +225,12 @@ QString Settings::get_currency_name() { } void Settings::set_currency_name(QString currency_name) { - QSettings().setValue("options/currency_name", currency_name); - - - + QSettings().setValue("options/currency_name", currency_name); } - QString Settings::get_theme_name() { // Load from the QT Settings. - return QSettings().value("options/theme_name", false).toString(); + return QSettings().value("options/theme_name", "Dark").toString(); } void Settings::set_theme_name(QString theme_name) { @@ -242,6 +238,9 @@ void Settings::set_theme_name(QString theme_name) { } + + + //================================= // Static Stuff //================================= @@ -303,7 +302,7 @@ QString Settings::getDonationAddr() { if (Settings::getInstance()->isTestnet()) return "ztestsaplingXXX"; else - return "zs1kwp3h4rwz76zfqzmwqqextq696kndtjskg4fzc80l9ygfal4hchcsst83ua8tjwzzy9nja7v5rr"; + return "zs1fq9f7vg797qaeac9lyx0njyjmjg4w7m60hwq6lhyhvdcqltl5hdkm8vwx9cxy60ehuuz2x49jxt"; } diff --git a/src/settings.h b/src/settings.h index a917d67..c598369 100644 --- a/src/settings.h +++ b/src/settings.h @@ -67,6 +67,8 @@ public: void set_currency_name(QString currency_name); + + bool isSaplingActive(); void setZECPrice(double p) { ZECPrice = p; } diff --git a/src/settings.ui b/src/settings.ui index 419509c..57620e5 100644 --- a/src/settings.ui +++ b/src/settings.ui @@ -6,10 +6,28 @@ 0 0 - 733 - 539 + 590 + 443 + + + 0 + 0 + + + + + 590 + 443 + + + + + 590 + 443 + + Settings @@ -85,7 +103,7 @@ 80 110 - 80 + 111 25 @@ -97,22 +115,27 @@ - default + Dark - blue + Midnight - light + Light - dark + Blue + + + + + Default @@ -236,7 +259,7 @@ 80 150 - 80 + 111 25 diff --git a/src/startupencryption.ui b/src/startupencryption.ui new file mode 100644 index 0000000..963d2f0 --- /dev/null +++ b/src/startupencryption.ui @@ -0,0 +1,129 @@ + + + startup + + + + 0 + 0 + 400 + 177 + + + + + 0 + 0 + + + + + 400 + 177 + + + + + 400 + 177 + + + + SDL Startup Decryption + + + + + + <html><head/><body><p>If you have forgotten your passphrase, restore your wallet with your seed!</p></body></html> + + + Qt::AlignCenter + + + true + + + + + + + Qt::Horizontal + + + + + + + Encryption Passphrase: + + + + + + + + 0 + 25 + + + + QLineEdit::Password + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + startup + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + startup + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/version.h b/src/version.h index f3c7693..875d8ab 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define APP_VERSION "1.2.2" +#define APP_VERSION "1.3-Chat-Alpha"