diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef1fafb..af10bb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,28 +12,46 @@ jobs: strategy: matrix: os: [ ubuntu-16.04, ubuntu-18.04, macos-latest, windows-latest ] - build-opts: [ "-DFORCE_ANSI=ON", "-DFORCE_ANSI=OFF" ] + build: [ cli-force-ansi-on, cli-force-ansi-off, gui ] + include: + - build: cli-force-ansi-on + cmake-opts: -DBUILD_COMPONENT_CLI=ON -DBUILD_COMPONENT_GUI=OFF -DFORCE_ANSI=ON + - build: cli-force-ansi-off + cmake-opts: -DBUILD_COMPONENT_CLI=ON -DBUILD_COMPONENT_GUI=OFF -DFORCE_ANSI=ON + - build: gui + cmake-opts: -DBUILD_COMPONENT_CLI=OFF -DBUILD_COMPONENT_GUI=ON - name: ${{matrix.os}} (${{matrix.build-opts}}) + + name: ${{matrix.os}} (${{matrix.build}}) runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v1 - - name: Dependancies + - name: OpenSSL OSX + if: runner.os == 'macOS' shell: bash - run: | - if [ "$RUNNER_OS" == "macOS" ]; then - brew install openssl - fi + run: brew install openssl + + - name: Qt - Ubuntu + if: matrix.build == 'gui' && runner.os == 'Linux' + shell: bash + run: sudo apt-get install qt5-default + + - name: Qt - Windows/Mac + if: matrix.build == 'gui' && runner.os != 'Linux' + uses: jurplel/install-qt-action@v2 + with: + version: '5.9' - name: Configure shell: bash run: | + OPTS="${{matrix.cmake-opts}}" if [ "$RUNNER_OS" == "macOS" ]; then - SSL_OPTS="-D OPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1" + OPTS="-D OPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1 ${OPTS}" fi - cmake ${SSL_OPTS} ${{matrix.build-opts}} -B build + cmake ${OPTS} -B build - name: Build shell: bash @@ -42,5 +60,5 @@ jobs: - name: Upload artifact uses: actions/upload-artifact@v1 with: - name: ${{matrix.os}}_${{matrix.build-opts}}-build + name: ${{matrix.os}}_${{matrix.build}}-build path: build diff --git a/CMakeLists.txt b/CMakeLists.txt index 7eac1f0..1c48bb2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,18 @@ project(eosio-keygen HOMEPAGE_URL "https://github.com/eosswedenorg/eosio-keygen" ) set( PROJECT_MAINTAINER "Henrik Hautakoski ") +set( PROJECT_LICENSE_FILE ${CMAKE_CURRENT_LIST_DIR}/LICENSE ) + +# -------------------------------- +# Options +# -------------------------------- + +option(BUILD_COMPONENT_CLI "Build CLI Component" ON) +option(BUILD_COMPONENT_GUI "Build GUI Component (Qt5)" OFF) + +if (NOT BUILD_COMPONENT_CLI AND NOT BUILD_COMPONENT_GUI) + message(FATAL_ERROR "Atleast one of BUILD_COMPONENT_GUI,BUILD_COMPONENT_CLI must be set to ON") +endif() # -------------------------------- # CMake settings @@ -64,18 +76,25 @@ configure_file(config.hpp.in "${PROJECT_BINARY_DIR}/config.hpp" @ONLY) include_directories(${PROJECT_BINARY_DIR}) # -------------------------------- -# Subdirectories +# Components # -------------------------------- add_subdirectory( common ) -add_subdirectory( cli ) + +if (BUILD_COMPONENT_CLI) + add_subdirectory( cli ) +endif() + +if (BUILD_COMPONENT_GUI) + add_subdirectory( gui ) +endif() # -------------------------------- # Install # -------------------------------- # Readme and license -install(FILES README.md LICENSE +install(FILES README.md ${PROJECT_LICENSE_FILE} DESTINATION ${CMAKE_INSTALL_SHAREDIR}) # -------------------------------- @@ -83,7 +102,7 @@ install(FILES README.md LICENSE # -------------------------------- if (CPACK_GENERATOR MATCHES "^[Nn][Ss][Ii][Ss]$") - set( CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_LIST_DIR}/LICENSE ) + set( CPACK_RESOURCE_FILE_LICENSE ${PROJECT_LICENSE_FILE} ) endif() set( CPACK_DEBIAN_PACKAGE_PRIORITY "optional" ) diff --git a/config.hpp.in b/config.hpp.in index ff5de16..a1b4356 100644 --- a/config.hpp.in +++ b/config.hpp.in @@ -31,4 +31,7 @@ #define CONFIG_SHARE_PATH "@CMAKE_INSTALL_DATADIR@/@CMAKE_PROJECT_NAME@" #define CONFIG_SHARE_FULL_PATH "@CMAKE_INSTALL_FULL_DATADIR@/@CMAKE_PROJECT_NAME@" +#define CONFIG_DICT_PATH "@CMAKE_INSTALL_DATADIR@/@CMAKE_PROJECT_NAME@/dict" +#define CONFIG_DICT_FULL_PATH "@CMAKE_INSTALL_FULL_DATADIR@/@CMAKE_PROJECT_NAME@/dict" + #endif /* EOSIOKEYGEN_CONFIG_H */ diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt new file mode 100644 index 0000000..3a32d08 --- /dev/null +++ b/gui/CMakeLists.txt @@ -0,0 +1,48 @@ + +# Qt5 needs MOC,RCC and UIC +set( CMAKE_AUTOMOC ON ) +set( CMAKE_AUTORCC ON ) +set( CMAKE_AUTOUIC ON ) + +# Autogenerate about config file + +file( READ ${PROJECT_LICENSE_FILE} GUI_ABOUT_LICENSE ) + +string(REGEX REPLACE "^([^\n]+)" "

\\1

" GUI_ABOUT_LICENSE ${GUI_ABOUT_LICENSE}) +string(REGEX REPLACE "\n\n([^\n]+)" "

\\1

" GUI_ABOUT_LICENSE ${GUI_ABOUT_LICENSE}) +string(REGEX REPLACE "\n" "" GUI_ABOUT_LICENSE ${GUI_ABOUT_LICENSE}) +string(REGEX REPLACE "\<(.+)\>" "- \\1" GUI_ABOUT_AUTHOR ${PROJECT_MAINTAINER}) + +configure_file(gui_about.h.in "${CMAKE_CURRENT_BINARY_DIR}/gui_about.h" @ONLY ESCAPE_QUOTES) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +# -------------------------------- +# Program +# -------------------------------- + +set( COMPONENT_NAME ${CMAKE_PROJECT_NAME}-gui) + +set( PROGRAM_EXE ${COMPONENT_NAME} ) + +set( PROGRAM_SRC + main.cpp + MainWindow.cpp + GenerateWindow.cpp + SearchWindow.cpp + MultiSelect.cpp + helpers.cpp +) + +add_executable( ${PROGRAM_EXE} ${PROGRAM_SRC} ) + +# Libraries +find_package( Qt5 COMPONENTS Concurrent Core Widgets REQUIRED ) +target_link_libraries( ${PROGRAM_EXE} Qt5::Concurrent Qt5::Core Qt5::Widgets common ) + +# -------------------------------- +# Install +# -------------------------------- + +install(TARGETS ${PROGRAM_EXE} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + COMPONENT ${COMPONENT_NAME}) diff --git a/gui/GenerateWindow.cpp b/gui/GenerateWindow.cpp new file mode 100644 index 0000000..898281c --- /dev/null +++ b/gui/GenerateWindow.cpp @@ -0,0 +1,119 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include "GenerateWindow.hpp" + +void _initKeyWidget(QLineEdit& w) { + w.setFixedWidth(450); + w.setReadOnly(true); +} + +void _initKeyCopyButton(QPushButton& btn, const QIcon& icon) { + btn.setFixedWidth(32); + btn.setIcon(icon); +} + +GenerateWindow::GenerateWindow(QWidget *parent) : +QWidget (parent), +m_btn_gen ("Generate"), +m_btn_copy_both ("Copy keys") +{ + QIcon copy_icon = QIcon::fromTheme("edit-copy"); + QGridLayout* layout; + + _initKeyWidget(m_pub); + _initKeyWidget(m_priv); + + _initKeyCopyButton(m_btn_copy_pub, copy_icon); + _initKeyCopyButton(m_btn_copy_priv, copy_icon); + + // Layout + layout = new QGridLayout(); + layout->setAlignment(Qt::AlignCenter); + + // Stretch first and last column to make the widgets horizontally centered. + layout->setColumnStretch(0, 1); + layout->setColumnStretch(4, 1); + + // Public key row + layout->addWidget(new QLabel("Public:"), 0, 1, Qt::AlignRight); + layout->addWidget(&m_pub, 0, 2); + layout->addWidget(&m_btn_copy_pub, 0, 3); + + // Private key row + + layout->addWidget(new QLabel("Private:"), 1, 1, Qt::AlignRight); + layout->addWidget(&m_priv, 1, 2); + layout->addWidget(&m_btn_copy_priv, 1, 3); + + // Bottom row + + m_btn_copy_both.setFixedWidth(80); + + layout->addWidget(&m_btn_gen, 2, 2); + layout->addWidget(&m_btn_copy_both, 2, 3); + + setLayout(layout); + + // Connections + connect(&m_btn_gen, SIGNAL(released()), this, SLOT(generate_key())); + connect(&m_btn_copy_both, SIGNAL(released()), this, SLOT(copy_both_keys())); + connect(&m_btn_copy_pub, SIGNAL(released()), this, SLOT(copy_pub_key())); + connect(&m_btn_copy_priv, SIGNAL(released()), this, SLOT(copy_priv_key())); +} + +void GenerateWindow::generate_key() +{ + std::string pubstr, privstr; + struct libeosio::ec_keypair pair; + + libeosio::ec_generate_key(&pair); + + m_pub.setText(QString::fromStdString(libeosio::wif_pub_encode(pair.pub))); + m_priv.setText(QString::fromStdString(libeosio::wif_priv_encode(pair.secret))); +} + +void GenerateWindow::copy_both_keys() +{ + QClipboard *clipboard = QGuiApplication::clipboard(); + clipboard->setText(m_pub.text() + "\n" + m_priv.text()); +} + +void GenerateWindow::copy_pub_key() +{ + QClipboard *clipboard = QGuiApplication::clipboard(); + clipboard->setText(m_pub.text()); +} + +void GenerateWindow::copy_priv_key() +{ + QClipboard *clipboard = QGuiApplication::clipboard(); + clipboard->setText(m_priv.text()); +} diff --git a/gui/GenerateWindow.hpp b/gui/GenerateWindow.hpp new file mode 100644 index 0000000..da2a110 --- /dev/null +++ b/gui/GenerateWindow.hpp @@ -0,0 +1,62 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef GENERATE_WINDOW_H +#define GENERATE_WINDOW_H + +#include +#include +#include + +class GenerateWindow : public QWidget +{ + Q_OBJECT +public: + GenerateWindow(QWidget *parent = 0); + +public slots: + + // Genereate a new key. + void generate_key(); + + // Copy both keys to clipboard + void copy_both_keys(); + + // copy public key to clipboard + void copy_pub_key(); + + // copy private key to clipboard + void copy_priv_key(); + +protected: + + QLineEdit m_pub; + QLineEdit m_priv; + + QPushButton m_btn_gen; + QPushButton m_btn_copy_both; + QPushButton m_btn_copy_priv; + QPushButton m_btn_copy_pub; +}; + +#endif /* GENERATE_WINDOW_H */ diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp new file mode 100644 index 0000000..ce2d579 --- /dev/null +++ b/gui/MainWindow.cpp @@ -0,0 +1,63 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include +#include "gui_about.h" +#include "GenerateWindow.hpp" +#include "SearchWindow.hpp" +#include "MainWindow.hpp" + +MainWindow::MainWindow(QWidget *parent) : +QMainWindow (parent) +{ + // Create sub windows and stacked widget. + m_stacked = new QStackedWidget(); + m_stacked->addWidget(new GenerateWindow()); + m_stacked->addWidget(new SearchWindow()); + + setCentralWidget(m_stacked); + + // Menu bar. + + menuBar()->addAction("Generate", this, SLOT(switchToGenerate())); + menuBar()->addAction("Search", this, SLOT(switchToSearch())); + menuBar()->addAction("About", this, SLOT(showAbout())); +} + +void MainWindow::switchToGenerate() +{ + m_stacked->setCurrentIndex(0); +} + +void MainWindow::switchToSearch() +{ + m_stacked->setCurrentIndex(1); +} + +void MainWindow::showAbout() +{ + QMessageBox::about(this, EOSIOKEYGEN_GUI_ABOUT_TITLE, EOSIOKEYGEN_GUI_ABOUT_TEXT); +} diff --git a/gui/MainWindow.hpp b/gui/MainWindow.hpp new file mode 100644 index 0000000..704fa1f --- /dev/null +++ b/gui/MainWindow.hpp @@ -0,0 +1,52 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MAIN_WINDOW_H +#define MAIN_WINDOW_H + +#include + +class QStackedWidget; + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(QWidget *parent = 0); + +private slots : + + // Switch to generate window. + void switchToGenerate(); + + // Switch to search window. + void switchToSearch(); + + void showAbout(); + +private : + + QStackedWidget* m_stacked; +}; + +#endif /* MAIN_WINDOW_H */ diff --git a/gui/MultiSelect.cpp b/gui/MultiSelect.cpp new file mode 100644 index 0000000..bccf803 --- /dev/null +++ b/gui/MultiSelect.cpp @@ -0,0 +1,150 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include +#include +#include "MultiSelect.hpp" + +MultiSelect::MultiSelect(const QString& text, bool user_can_add, QWidget *parent) : +QPushButton (text + ": none", parent) +{ + QPushButton* btn; + QVBoxLayout* layout; + + m_prefix = text; + + // Dialog + m_dialog = new QDialog(this); + + // Dialog Widgets + m_list = new QListWidget(); + btn = new QPushButton("Select"); + + // Dialog layout + layout = new QVBoxLayout(m_dialog); + layout->addWidget(m_list); + layout->addWidget(btn); + + // Connections + QObject::connect(btn, SIGNAL(clicked()), m_dialog, SLOT(accept())); + QObject::connect(m_dialog, SIGNAL(accepted()), this, SLOT(selectionConfirmed())); + QObject::connect(m_list, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(listItemClicked(QListWidgetItem*))); + + // Configured to let users add items. provide button and signal. + if (user_can_add) { + btn = new QPushButton("Add"); + layout->addWidget(btn); + QObject::connect(btn, SIGNAL(clicked()), this, SLOT(addBtnClicked())); + } +} + +void MultiSelect::addItem(const QString& text, bool checked) +{ + QListWidgetItem* item = new QListWidgetItem(text, m_list); + item->setFlags(Qt::ItemIsEnabled); + item->setCheckState(checked ? Qt::Checked : Qt::Unchecked); +} + +void MultiSelect::addItems(const QStringList& list, bool checked) +{ + QStringList::const_iterator it; + + for(it = list.cbegin(); it != list.cend(); it++) { + addItem(*it, checked); + } +} + +void MultiSelect::clearItems() +{ + m_list->clear(); +} + +QStringList MultiSelect::getSelectedItems() const +{ + QListWidgetItem* item; + QStringList ret; + + // Cannot use m_list->selectedItems() as that function only fetches + // _selected_ and not _checked_ items (there is a difference). + for(int i = 0; i < m_list->count(); i++) { + item = m_list->item(i); + // Include in list if checked. + if (item->checkState() == Qt::Checked) { + ret << item->text(); + } + } + return ret; +} + +void MultiSelect::selectionConfirmed() +{ + QStringList selected = getSelectedItems(); + + // Update the text for this widget to + // reflect the selected objects. + QString txt = m_prefix + ": "; + + // Have more than one item. show number of items. + if (selected.count() > 1) { + txt += QString::number(selected.count()) + " selected"; + } + // Just one, we can show the text. + else if (selected.count() == 1) { + txt += selected.at(0); + } + else { + txt += "none"; + } + + setText(txt); + + // Emit the selectionChanged signal with the updated list + emit selectionChanged(selected); +} + +void MultiSelect::listItemClicked(QListWidgetItem *item) +{ + // toggle state when user clicks. + bool checked = item->checkState() == Qt::Checked; + item->setCheckState(checked ? Qt::Unchecked : Qt::Checked); +} + +void MultiSelect::addBtnClicked() +{ + // Just emit addNewItem event. + emit addNewItem(); +} + +void MultiSelect::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) { + // Show select dialog. + m_dialog->show(); + } else { + // pass on other buttons to base class + QPushButton::mousePressEvent(event); + } +} diff --git a/gui/MultiSelect.hpp b/gui/MultiSelect.hpp new file mode 100644 index 0000000..84ed207 --- /dev/null +++ b/gui/MultiSelect.hpp @@ -0,0 +1,96 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef MULTI_SELECT_H +#define MULTI_SELECT_H + +#include +#include +#include + +class QMouseEvent; +class QPushButton; +class QListWidget; +class QListWidgetItem; +class QDialog; +class MultiSelectDialog; + +// +// MultiSelect implements multi selection of items using +// a push button and a dialog with a list of checkbox items. +// +class MultiSelect : public QPushButton +{ + Q_OBJECT +public: + MultiSelect(const QString& text, bool user_add_item = false, QWidget *parent = 0); + + // Items. + + void addItem(const QString& text, bool checked = false); + + void addItems(const QStringList& list, bool checked = false); + + void clearItems(); + + // Get a list of currently selected items. + QStringList getSelectedItems() const; + +signals: + + // This signal is emitted whenever the user has made a new selection. + void selectionChanged(QStringList selected); + + // This signal is emitted whenever the user clicks the "Add" button. + // NOTE: Will only be emitted if `user_add_item` has been set to `true` in the constructor. + void addNewItem(); + +private slots : + + // Called when the dialog is accepted. + void selectionConfirmed(); + + // Called when a list item is clicked on. + void listItemClicked(QListWidgetItem *item); + + // Called when the add button is clicked on. + void addBtnClicked(); + +protected : + + // Event handlers + void mousePressEvent(QMouseEvent *e) override; + +private : + + // Prefix to show on the button before value. + QString m_prefix; + + // List of items + QListWidget* m_list; + + // Dialog to show if this widget are clicked on. + QDialog* m_dialog; +}; + +#endif /* MULTI_SELECT_H */ diff --git a/gui/SearchWindow.cpp b/gui/SearchWindow.cpp new file mode 100644 index 0000000..3b962d5 --- /dev/null +++ b/gui/SearchWindow.cpp @@ -0,0 +1,268 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "helpers.hpp" +#include "SearchWindow.hpp" + +SearchWindow::SearchWindow(QWidget *parent, Qt::WindowFlags flags) : +QWidget (parent, flags), +m_status ("status"), +m_leet_cb ("L33t"), +m_dict_lang ("Dictionary Language"), +m_dict_file ("Dictionary File", true), +m_btn_exec ("Search"), +m_btn_clear ("Clear") +{ + setMinimumSize(600, 400); + + // Monospaced font + QFont f_mono("monospace"); + + // Output + m_output.setFont(f_mono); + m_output.setReadOnly(true); + + // Layout + // ------------------------ + setLayout(&m_layout); + m_layout.setColumnStretch(0, 10); + m_layout.setColumnStretch(1, 10); + + // First row. + m_dict_lang.addItems(get_files(CONFIG_DICT_FULL_PATH)); + m_layout.addWidget(&m_dict_lang, 0, 0); + m_layout.addWidget(&m_dict_file, 0, 1); + + m_layout.addWidget(&m_leet_cb, 0, 2); + + m_num_threads.setValue((int) eoskeygen::KeySearch::max_threads()); + m_num_threads.setRange(1, (int) eoskeygen::KeySearch::max_threads()); + m_num_threads.setSuffix(" Threads"); + m_layout.addWidget(&m_num_threads, 0, 3); + + m_num_results.setValue(10); + m_num_results.setRange(1, 99); + m_num_results.setSuffix(" Results"); + m_layout.addWidget(&m_num_results, 0, 4); + + // Second row. + m_layout.addWidget(&m_status, 1, 0, 1, 3); + m_layout.addWidget(&m_txt_search, 1, 0, 1, 3); + m_layout.addWidget(&m_btn_exec, 1, 3); + m_layout.addWidget(&m_btn_clear, 1, 4); + + // Third row. + m_layout.addWidget(&m_output, 2, 0, 1, 0); + + // Search + // ------------------------ + + m_ksearch.setCallback(this); + + initSignals(); + + // Focus search field. + m_txt_search.setFocus(); +} + +void SearchWindow::initSignals() +{ + // Buttons + connect(&m_btn_exec, SIGNAL(released()), this, SLOT(search())); + connect(&m_btn_clear, SIGNAL(released()), &m_output, SLOT(clear())); + + // Worker Thread + connect(&m_worker, SIGNAL(started()), this, SLOT(searchStarted())); + connect(&m_worker, SIGNAL(finished()), this, SLOT(searchFinished())); + + connect(this, SIGNAL(addOutput(QString)), this, SLOT(output(QString))); + + connect(&m_dict_file, SIGNAL(addNewItem()), this, SLOT(langFileAdd())); +} + +void SearchWindow::loadDictionaries() +{ + QStringList list; + eoskeygen::Dictionary tmpDict; + std::string base_path(CONFIG_DICT_FULL_PATH); + + // Clear dictionary first. + m_dict.clear(); + + // Go through all selected languages. + list = m_dict_lang.getSelectedItems(); + for(QStringList::const_iterator it = list.cbegin(); it != list.cend(); it++) { + + // Load and add them to dictionary. + tmpDict.loadFromFile(base_path + "/" + it->toStdString()); + m_dict.add(tmpDict); + } + + // Go through all selected files. + list = m_dict_file.getSelectedItems(); + for(QStringList::const_iterator it = list.cbegin(); it != list.cend(); it++) { + + // Load and add them to dictionary. + tmpDict.loadFromFile(it->toStdString()); + m_dict.add(tmpDict); + } +} + +void SearchWindow::onResult(const struct libeosio::ec_keypair* key, const struct eoskeygen::KeySearch::result& result) +{ + int pos = (int) result.pos; + int len = (int) result.len; + QString pub = QString::fromStdString(libeosio::wif_pub_encode(key->pub)); + QString mid = pub.mid(pos, len); + QString left = pub.left(pos); + QString right = pub.mid(pos + len, pub.size() - pos); + eoskeygen::Dictionary::search_result_t dict_res = m_dict.search(pub.toStdString()); + + QString out = "Public: " + pub.left(3); + for(int i = 3; i < pub.length(); ) { + + if (i == pos) { + out += "" + pub.mid(pos, len) + ""; + i += len; + continue; + } + + // Look in the dictionary. + auto dp = dict_res.find(i); + if (dp != dict_res.end()) { + int p = (int) dp->first; + int l = (int) dp->second; + out += "" + pub.mid(p, l) + ""; + i += l; + continue; + } + + out += pub[i++]; + } + + out += "
Private: " + QString::fromStdString(libeosio::wif_priv_encode(key->secret)); + + // As this function could be called from a non-gui thread. we use signals. + emit addOutput("

" + out + "

"); +} + +// -------------------- +// Slots +// -------------------- + +void SearchWindow::search() +{ + if (m_worker.isRunning()) { + return; + } + + const std::string& input = m_txt_search.text().toLocal8Bit().constData(); + eoskeygen::strlist_t list; + + if (m_leet_cb.isChecked()) { + list = eoskeygen::l33twords(input); + } else { + list = eoskeygen::strlist::splitw(input); + } + + // Validate that we atleast got something to search for. + if (list.size() < 1 || (list.size() == 1 && list[0] == "")) { + QMessageBox::warning( this, + "Empty search field.", + "You must specify atleast one search string" ); + return; + } + + loadDictionaries(); + + m_ksearch.clear(); + m_ksearch.addList(list); + m_ksearch.setThreadCount(m_num_threads.value()); + + QFuture future = QtConcurrent::run(m_ksearch, &eoskeygen::KeySearch::find, m_num_results.value()); + m_worker.setFuture(future); + + m_status.setText("Searching for: " + QString::fromStdString(eoskeygen::strlist::join(list, ", "))); +} + +void SearchWindow::output(const std::string& html) +{ + output(QString::fromStdString(html)); +} + +void SearchWindow::output(const QString& html) +{ + if (m_output.toPlainText().size()) { + m_output.setHtml(m_output.toHtml() + html); + } else { + m_output.setHtml(html); + } + + // Force scrollbar to the bottom. + m_output.verticalScrollBar()->setValue(m_output.verticalScrollBar()->maximum()); +} + +void SearchWindow::langFileAdd() +{ + QStringList files = QFileDialog::getOpenFileNames(this, + "Select one or more language files"); + + m_dict_file.addItems(files, true); +} + +void SearchWindow::searchStarted() +{ + m_txt_search.setEnabled(false); + m_txt_search.setHidden(true); + m_dict_lang.setEnabled(false); + m_dict_file.setEnabled(false); + m_leet_cb.setEnabled(false); + m_btn_exec.setEnabled(false); + m_btn_clear.setEnabled(false); + m_num_threads.setEnabled(false); + m_num_results.setEnabled(false); +} + +void SearchWindow::searchFinished() +{ + m_txt_search.setEnabled(true); + m_txt_search.setHidden(false); + m_dict_lang.setEnabled(true); + m_dict_file.setEnabled(true); + m_leet_cb.setEnabled(true); + m_btn_exec.setEnabled(true); + m_btn_clear.setEnabled(true); + m_num_threads.setEnabled(true); + m_num_results.setEnabled(true); +} diff --git a/gui/SearchWindow.hpp b/gui/SearchWindow.hpp new file mode 100644 index 0000000..49bc41b --- /dev/null +++ b/gui/SearchWindow.hpp @@ -0,0 +1,115 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef SEARCH_WINDOW_H +#define SEARCH_WINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "MultiSelect.hpp" + +class SearchWindow : public QWidget, public eoskeygen::IKeySearchResult +{ + Q_OBJECT +public: + explicit SearchWindow(QWidget *parent = 0, Qt::WindowFlags flags = Qt::WindowFlags()); + + void onResult(const struct libeosio::ec_keypair* key, const struct eoskeygen::KeySearch::result& result); + +private : + void initSignals(); + + void loadDictionaries(); + +private slots: + + // Start a search + void search(); + + // Output html to screen. + void output(const std::string& html); + void output(const QString& html); + + // Called when a search is started. + void searchStarted(); + + // Called when a search is done. + void searchFinished(); + + // Called when a new language file should be added + void langFileAdd(); + +signals: + void addOutput(const QString& line); + +private: + + // Search worker thread. + QFutureWatcher m_worker; + + eoskeygen::KeySearch m_ksearch; + + eoskeygen::Dictionary m_dict; + + // Widgets + // ---------------- + + // Status text. + QLabel m_status; + + // Search input. + QLineEdit m_txt_search; + + // Number of Threads. + QSpinBox m_num_threads; + + // Number of Results + QSpinBox m_num_results; + + QCheckBox m_leet_cb; + + MultiSelect m_dict_lang; + + MultiSelect m_dict_file; + + // Buttons + QPushButton m_btn_exec; + QPushButton m_btn_clear; + + // Text output. + QTextEdit m_output; + + // Gui Layout. + QGridLayout m_layout; +}; + +#endif /* SEARCH_WINDOW_H */ diff --git a/gui/gui_about.h.in b/gui/gui_about.h.in new file mode 100644 index 0000000..1f6ab9f --- /dev/null +++ b/gui/gui_about.h.in @@ -0,0 +1,37 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef EOSIOKEYGEN_GUI_ABOUT_H +#define EOSIOKEYGEN_GUI_ABOUT_H + +#define EOSIOKEYGEN_GUI_ABOUT_TITLE "@PROJECT_NAME@ - About" + +#define EOSIOKEYGEN_GUI_ABOUT_TEXT \ + "

@PROJECT_NAME@ - v@PROJECT_VERSION@

" \ + "

@PROJECT_DESCRIPTION@

" \ + "

@PROJECT_HOMEPAGE_URL@

" \ + "@GUI_ABOUT_LICENSE@" \ + "

Author

" \ + "

@GUI_ABOUT_AUTHOR@

" + +#endif /* EOSIOKEYGEN_CONFIG_ABOUT_H */ diff --git a/gui/helpers.cpp b/gui/helpers.cpp new file mode 100644 index 0000000..d1574d4 --- /dev/null +++ b/gui/helpers.cpp @@ -0,0 +1,42 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "helpers.hpp" + +QStringList get_files(const QDir& directory) { + + QFileInfoList list; + QStringList ret; + + list = directory.entryInfoList(QDir::Files); + for (int i = 0; i < list.size(); ++i) { + QFileInfo info = list.at(i); + ret << info.fileName(); + } + return ret; +} + +QStringList get_files(const QString& directory) +{ + return get_files(QDir(directory)); +} diff --git a/gui/helpers.hpp b/gui/helpers.hpp new file mode 100644 index 0000000..ccb5218 --- /dev/null +++ b/gui/helpers.hpp @@ -0,0 +1,38 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef HELPERS_H +#define HELPERS_H + +#include +#include +#include + +// +// Get a list of files for a given directory. +// NOTE: only filenames are returned. relative to directory. +// +QStringList get_files(const QDir& directory); +QStringList get_files(const QString& directory); + +#endif /* HELPERS_H */ diff --git a/gui/main.cpp b/gui/main.cpp new file mode 100644 index 0000000..ecd14e6 --- /dev/null +++ b/gui/main.cpp @@ -0,0 +1,36 @@ +/** + * MIT License + * + * Copyright (c) 2020 EOS Sw/eden + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include "MainWindow.hpp" + +int main(int argc, char **argv) { + + QApplication app(argc, argv); + + MainWindow window; + + window.show(); + + return app.exec(); +}