diff --git a/Logger.cpp b/Logger.cpp index f2e34552..ad356bcb 100644 --- a/Logger.cpp +++ b/Logger.cpp @@ -34,6 +34,7 @@ #include #include "Logger.h" +#include "src/qt/TailsOS.h" #include "wallet/api/wallet2_api.h" // default log path by OS (should be writable) @@ -66,6 +67,9 @@ const QString getLogPath(const QString logPath) { const QFileInfo fi(logPath); + if(TailsOS::detect() && TailsOS::usePersistence) + return QDir::homePath() + "/Persistent/Monero/logs/" + defaultLogName; + if(!logPath.isEmpty() && !fi.isDir()) return fi.absoluteFilePath(); else { diff --git a/images/tails-grey.png b/images/tails-grey.png new file mode 100644 index 00000000..b2e18e42 Binary files /dev/null and b/images/tails-grey.png differ diff --git a/main.cpp b/main.cpp index a41b3c75..939b78f2 100644 --- a/main.cpp +++ b/main.cpp @@ -64,7 +64,7 @@ #include "MainApp.h" #include "qt/ipc.h" #include "qt/utils.h" -#include "qt/mime.h" +#include "src/qt/TailsOS.h" #include "src/qt/KeysFiles.h" #include "src/qt/MoneroSettings.h" #include "qt/prices.h" @@ -83,6 +83,7 @@ bool isAndroid = false; bool isWindows = false; bool isMac = false; bool isLinux = false; +bool isTails = false; bool isDesktop = false; bool isOpenGL = true; @@ -102,6 +103,7 @@ int main(int argc, char *argv[]) bool isWindows = true; #elif defined(Q_OS_LINUX) bool isLinux = true; + bool isTails = TailsOS::detect(); #elif defined(Q_OS_MAC) bool isMac = true; #endif @@ -123,25 +125,40 @@ int main(int argc, char *argv[]) // qDebug() << "High DPI auto scaling - enabled"; //#endif + MainApp app(argc, argv); + + app.setApplicationName("monero-core"); + app.setOrganizationDomain("getmonero.org"); + app.setOrganizationName("monero-project"); + + // Ask to enable Tails OS persistence mode, it affects: + // - Log file location + // - QML Settings file location (monero-core.conf) + // - Default wallets path + // Target directory is: ~/Persistent/Monero + if (isTails) { + if (!TailsOS::detectDataPersistence()) + TailsOS::showDataPersistenceDisabledWarning(); + else + TailsOS::askPersistence(); + } + QString moneroAccountsDir; #if defined(Q_OS_WIN) || defined(Q_OS_IOS) QStringList moneroAccountsRootDir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation); #else QStringList moneroAccountsRootDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation); #endif - if (!moneroAccountsRootDir.empty()) { + + if(isTails && TailsOS::usePersistence){ + moneroAccountsDir = QDir::homePath() + "/Persistent/Monero/wallets"; + } else if (!moneroAccountsRootDir.empty()) { moneroAccountsDir = moneroAccountsRootDir.at(0) + "/Monero/wallets"; } else { qCritical() << "Error: accounts root directory could not be set"; return 1; } - MainApp app(argc, argv); - - app.setApplicationName("monero-core"); - app.setOrganizationDomain("getmonero.org"); - app.setOrganizationName("monero-project"); - #if defined(Q_OS_LINUX) if (isDesktop) app.setWindowIcon(QIcon(":/images/appicon.ico")); #endif @@ -174,9 +191,8 @@ int main(int argc, char *argv[]) } qWarning().noquote() << "app startd" << "(log: " + logPath + ")"; -#ifdef Q_OS_LINUX + // Desktop entry registerXdgMime(app); -#endif IPC *ipc = new IPC(&app); QStringList posArgs = parser.positionalArguments(); @@ -317,6 +333,8 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty("walletLogPath", logPath); + engine.rootContext()->setContextProperty("tailsUsePersistence", TailsOS::usePersistence); + // Exclude daemon manager from IOS #ifndef Q_OS_IOS const QStringList arguments = (QStringList) QCoreApplication::arguments().at(0); @@ -330,7 +348,7 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty("isIOS", isIOS); engine.rootContext()->setContextProperty("isAndroid", isAndroid); engine.rootContext()->setContextProperty("isOpenGL", isOpenGL); - engine.rootContext()->setContextProperty("isLinux", isLinux); + engine.rootContext()->setContextProperty("isTails", isTails); engine.rootContext()->setContextProperty("screenWidth", geo.width()); engine.rootContext()->setContextProperty("screenHeight", geo.height()); @@ -354,6 +372,7 @@ int main(int argc, char *argv[]) accountName = "My monero Account"; engine.rootContext()->setContextProperty("defaultAccountName", accountName); + engine.rootContext()->setContextProperty("homePath", QDir::homePath()); engine.rootContext()->setContextProperty("applicationDirectory", QApplication::applicationDirPath()); engine.rootContext()->setContextProperty("idealThreadCount", QThread::idealThreadCount()); diff --git a/main.qml b/main.qml index 1655456c..25536f79 100644 --- a/main.qml +++ b/main.qml @@ -1349,6 +1349,12 @@ ApplicationWindow { MoneroSettings { id: persistentSettings + fileName: { + if(isTails && tailsUsePersistence) + return homePath + "/Persistent/Monero/monero-core.conf"; + return ""; + } + property string language property string locale property string account_name diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index d832ee12..97286b05 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -65,12 +65,12 @@ HEADERS += \ MainApp.h \ src/qt/FutureScheduler.h \ src/qt/ipc.h \ - src/qt/mime.h \ src/qt/KeysFiles.h \ src/qt/utils.h \ src/qt/prices.h \ src/qt/macoshelper.h \ src/qt/MoneroSettings.h + src/qt/TailsOS.h SOURCES += main.cpp \ filter.cpp \ @@ -101,11 +101,11 @@ SOURCES += main.cpp \ MainApp.cpp \ src/qt/FutureScheduler.cpp \ src/qt/ipc.cpp \ - src/qt/mime.cpp \ src/qt/KeysFiles.cpp \ src/qt/utils.cpp \ src/qt/prices.cpp \ - src/qt/MoneroSettings.cpp + src/qt/MoneroSettings.cpp \ + src/qt/TailsOS.cpp CONFIG(DISABLE_PASS_STRENGTH_METER) { HEADERS -= src/zxcvbn-c/zxcvbn.h diff --git a/pages/settings/SettingsInfo.qml b/pages/settings/SettingsInfo.qml index 938bd122..80e6a129 100644 --- a/pages/settings/SettingsInfo.qml +++ b/pages/settings/SettingsInfo.qml @@ -326,6 +326,41 @@ Rectangle { font.pixelSize: 14 text: isOpenGL ? "OpenGL" : "Low graphics mode" } + + Rectangle { + visible: isTails + height: 1 + Layout.topMargin: 2 + Layout.bottomMargin: 2 + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + Rectangle { + visible: isTails + height: 1 + Layout.topMargin: 2 + Layout.bottomMargin: 2 + Layout.fillWidth: true + color: MoneroComponents.Style.dividerColor + opacity: MoneroComponents.Style.dividerOpacity + } + + MoneroComponents.TextBlock { + visible: isTails + Layout.fillWidth: true + font.pixelSize: 14 + text: qsTr("Tails: ") + translationManager.emptyString + } + + MoneroComponents.TextBlock { + visible: isTails + Layout.fillWidth: true + color: MoneroComponents.Style.dimmedFontColor + font.pixelSize: 14 + text: tailsUsePersistence ? qsTr("persistent") + translationManager.emptyString : qsTr("persistence disabled") + translationManager.emptyString; + } } // Copy info to clipboard diff --git a/qml.qrc b/qml.qrc index 3bced000..29f68f1a 100644 --- a/qml.qrc +++ b/qml.qrc @@ -251,5 +251,6 @@ images/copy.svg images/edit.svg images/arrow-right-in-circle-outline-medium-white.svg + images/tails-grey.png diff --git a/src/qt/TailsOS.cpp b/src/qt/TailsOS.cpp new file mode 100644 index 00000000..df2fa92b --- /dev/null +++ b/src/qt/TailsOS.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#include "TailsOS.h" +#include "utils.h" + +bool TailsOS::usePersistence = false; +QString TailsOS::tailsPathData = QString("/live/persistence/TailsData_unlocked/"); + +bool TailsOS::detect() +{ + if (!fileExists("/etc/os-release")) + return false; + + QByteArray data = fileOpen("/etc/os-release"); + QRegularExpression re("TAILS_PRODUCT_NAME=\"Tails\""); + QRegularExpressionMatch os_match = re.match(data); + bool matched = os_match.hasMatch(); + +#ifdef QT_DEBUG + if (matched) + qDebug() << "Tails OS detected"; +#endif + + return matched; +} + +bool TailsOS::detectDataPersistence() +{ + return QDir(QDir::homePath() + "/Persistent").exists(); +} + +bool TailsOS::detectDotPersistence() +{ + return QDir(tailsPathData + "dotfiles").exists(); +} + +void TailsOS::showDataPersistenceDisabledWarning() +{ + QMessageBox msgBox; + msgBox.setText(QObject::tr("Warning: persistence disabled")); + msgBox.setWindowTitle(QObject::tr("Warning: persistence disabled")); + msgBox.setInformativeText( + QObject::tr("Monero GUI has detected that Tails persistence is " + "currently disabled. Any configurations you make inside " + "the Monero GUI will not be saved." + "\n\n" + "In addition, make sure to not save your wallet on the " + "filesystem, as it will be lost at shutdown." + "\n\n" + "To enable Tails persistence, setup an encrypted volume " + "and restart Tails. To gain a startup menu item, " + "enable the Tails \"dotfiles\" feature.")); + + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setIconPixmap(QPixmap(":/images/tails-grey.png")); + msgBox.exec(); +} + +void TailsOS::askPersistence() +{ + QMessageBox msgBox; + msgBox.setWindowTitle(QObject::tr("Monero GUI")); + msgBox.setText(QObject::tr("Use Tails persistence?")); + msgBox.setInformativeText( + QObject::tr("Persist wallet files and configuration on the encrypted volume?" + "\n\n" + "In addition, you can enable Tails dotfiles persistence " + "to gain a start menu entry.\n")); + + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + msgBox.setIconPixmap(QPixmap(":/images/tails-grey.png")); + TailsOS::usePersistence = (msgBox.exec() == QMessageBox::Yes); +} + +void TailsOS::persistXdgMime(QString filePath, QString data) +{ + QFileInfo file(filePath); + QString tailsPath = tailsPathData + "dotfiles/.local/share/applications/"; + + // write to persistent volume +#ifdef QT_DEBUG + qDebug() << "Writing xdg mime: " << tailsPath + file.fileName(); +#endif + + QDir().mkpath(tailsPath); // ensure directory exists + fileWrite(tailsPath + file.fileName(), data); + + // write to current session +#ifdef QT_DEBUG + qDebug() << "Writing xdg mime: " << file.filePath(); +#endif + + QDir().mkpath(file.path()); // ensure directory exists + fileWrite(file.filePath(), data); +} diff --git a/src/qt/TailsOS.h b/src/qt/TailsOS.h new file mode 100644 index 00000000..05d5ebd1 --- /dev/null +++ b/src/qt/TailsOS.h @@ -0,0 +1,23 @@ +#ifndef TAILSOS_H +#define TAILSOS_H + +#include + + +class TailsOS +{ +public: + TailsOS(); + static bool detect(); + static bool detectDataPersistence(); + static bool detectDotPersistence(); + + static void showDataPersistenceDisabledWarning(); + static void askPersistence(); + static void persistXdgMime(QString filePath, QString data); + + static bool usePersistence; + static QString tailsPathData; +}; + +#endif // TAILSOS_H diff --git a/src/qt/mime.cpp b/src/qt/mime.cpp deleted file mode 100644 index 6daf4de6..00000000 --- a/src/qt/mime.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2014-2019, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include -#include - -#include "mime.h" -#include "utils.h" - -void registerXdgMime(QApplication &app){ - // MacOS handled via Info.plist - // Windows handled in the installer by rbrunner7 - - QString xdg = QString( - "[Desktop Entry]\n" - "Name=Monero GUI\n" - "GenericName=Monero-GUI\n" - "X-GNOME-FullName=Monero-GUI\n" - "Comment=Monero GUI\n" - "Keywords=Monero;\n" - "Exec=%1 %u\n" - "Terminal=false\n" - "Type=Application\n" - "Icon=monero\n" - "Categories=Network;GNOME;Qt;\n" - "MimeType=x-scheme-handler/monero;x-scheme-handler/moneroseed\n" - "StartupNotify=true\n" - "X-GNOME-Bugzilla-Bugzilla=GNOME\n" - "X-GNOME-UsesNotifications=true\n" - ).arg(app.applicationFilePath()); - - QString appPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); - QString filePath = QString("%1/monero-gui.desktop").arg(appPath); - - qDebug() << QString("Writing %1").arg(filePath); - QFile file(filePath); - if(file.open(QIODevice::WriteOnly)){ - QTextStream out(&file); out << xdg << endl; - file.close(); - } - else - file.close(); -} diff --git a/src/qt/mime.h b/src/qt/mime.h deleted file mode 100644 index b79d43a3..00000000 --- a/src/qt/mime.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2014-2019, The Monero Project -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, are -// permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of -// conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list -// of conditions and the following disclaimer in the documentation and/or other -// materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be -// used to endorse or promote products derived from this software without specific -// prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY -// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL -// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF -// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef MIME_H -#define MIME_H - -#include - -void registerXdgMime(QApplication &app); - -#endif // MIME_H diff --git a/src/qt/utils.cpp b/src/qt/utils.cpp index 2407a50e..4418a050 100644 --- a/src/qt/utils.cpp +++ b/src/qt/utils.cpp @@ -27,15 +27,35 @@ // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include "src/qt/TailsOS.h" #include "utils.h" bool fileExists(QString path) { QFileInfo check_file(path); - if (check_file.exists() && check_file.isFile()) + return check_file.exists() && check_file.isFile(); +} + +QByteArray fileOpen(QString path) { + QFile file(path); + if(!file.open(QFile::ReadOnly | QFile::Text)) + return QByteArray(); + + QByteArray data = file.readAll(); + file.close(); + return data; +} + +bool fileWrite(QString path, QString data) { + QFile file(path); + if(file.open(QIODevice::WriteOnly)){ + QTextStream out(&file); out << data << endl; + file.close(); return true; - else - return false; + } + + return false; } QString getAccountName(){ @@ -47,6 +67,53 @@ QString getAccountName(){ return accountName; } +QString xdgMime(QApplication &app){ + return QString( + "[Desktop Entry]\n" + "Name=Monero GUI\n" + "GenericName=Monero-GUI\n" + "X-GNOME-FullName=Monero-GUI\n" + "Comment=Monero GUI\n" + "Keywords=Monero;\n" + "Exec=%1 %u\n" + "Terminal=false\n" + "Type=Application\n" + "Icon=monero\n" + "Categories=Network;GNOME;Qt;\n" + "MimeType=x-scheme-handler/monero;x-scheme-handler/moneroseed\n" + "StartupNotify=true\n" + "X-GNOME-Bugzilla-Bugzilla=GNOME\n" + "X-GNOME-UsesNotifications=true\n" + ).arg(app.applicationFilePath()); +} + +void registerXdgMime(QApplication &app){ +#ifdef Q_OS_LINUX + // Register desktop entry + // - MacOS handled via Info.plist + // - Windows handled in the installer by rbrunner7 + // - Linux written to `QStandardPaths::ApplicationsLocation` + // - Tails written to persistent dotfiles + QString mime = xdgMime(app); + QString appPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); + QString filePath = QString("%1/monero-gui.desktop").arg(appPath); + + if (TailsOS::detect() && TailsOS::detectDotPersistence() && TailsOS::usePersistence) { + TailsOS::persistXdgMime(filePath, mime); + return; + } + + QFileInfo file(filePath); + QDir().mkpath(file.path()); // ensure directory exists + +#ifdef QT_DEBUG + qDebug() << "Writing xdg mime: " << filePath; +#endif + + fileWrite(filePath, mime); +#endif +} + QString randomUserAgent(){ QStringList urand; urand << "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1" diff --git a/src/qt/utils.h b/src/qt/utils.h index c18ffdc1..f48dff97 100644 --- a/src/qt/utils.h +++ b/src/qt/utils.h @@ -31,9 +31,14 @@ #include #include +#include bool fileExists(QString path); +QByteArray fileOpen(QString path); +bool fileWrite(QString path, QString data); QString getAccountName(); +QString xdgMime(QApplication &app); +void registerXdgMime(QApplication &app); const static QRegExp reURI = QRegExp("^\\w+:\\/\\/([\\w+\\-?\\-_\\-=\\-&]+)"); QString randomUserAgent();