LCOV - code coverage report
Current view: top level - src - qtpass.cpp (source / functions) Coverage Total Hit
Test: .lcov.total Lines: 0.0 % 240 0
Test Date: 2026-03-23 21:55:57 Functions: 0.0 % 23 0

            Line data    Source code
       1              : // SPDX-FileCopyrightText: 2016 Anne Jan Brouwer
       2              : // SPDX-License-Identifier: GPL-3.0-or-later
       3              : #include "qtpass.h"
       4              : #include "mainwindow.h"
       5              : #include "qtpasssettings.h"
       6              : #include "util.h"
       7              : #include <QApplication>
       8              : #include <QClipboard>
       9              : #include <QDialog>
      10              : #include <QLabel>
      11              : #include <QPixmap>
      12              : #include <QVBoxLayout>
      13              : 
      14              : #ifndef Q_OS_WIN
      15              : #include <QInputDialog>
      16              : #include <QLineEdit>
      17              : #include <utility>
      18              : #else
      19              : #define WIN32_LEAN_AND_MEAN /*_KILLING_MACHINE*/
      20              : #define WIN32_EXTRA_LEAN
      21              : #include <windows.h>
      22              : #include <winnetwk.h>
      23              : #undef DELETE
      24              : #endif
      25              : 
      26              : #ifdef QT_DEBUG
      27              : #include "debughelper.h"
      28              : #endif
      29              : 
      30            0 : QtPass::QtPass(MainWindow *mainWindow)
      31            0 :     : m_mainWindow(mainWindow), freshStart(true) {
      32            0 :   setClipboardTimer();
      33            0 :   clearClipboardTimer.setSingleShot(true);
      34            0 :   connect(&clearClipboardTimer, &QTimer::timeout, this,
      35            0 :           &QtPass::clearClipboard);
      36              : 
      37              : #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
      38              : #pragma GCC diagnostic push
      39              : #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
      40              : #endif
      41            0 :   QObject::connect(qApp, &QApplication::aboutToQuit, this,
      42            0 :                    &QtPass::clearClipboard);
      43              : #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
      44              : #pragma GCC diagnostic pop
      45              : #endif
      46              : 
      47            0 :   setMainWindow();
      48            0 : }
      49              : 
      50              : /**
      51              :  * @brief QtPass::~QtPass destroy!
      52              :  */
      53            0 : QtPass::~QtPass() {
      54              : #ifdef Q_OS_WIN
      55              :   if (QtPassSettings::isUseWebDav())
      56              :     WNetCancelConnection2A(QtPassSettings::getPassStore().toUtf8().constData(),
      57              :                            0, 1);
      58              : #else
      59            0 :   if (fusedav.state() == QProcess::Running) {
      60            0 :     fusedav.terminate();
      61            0 :     fusedav.waitForFinished(2000);
      62              :   }
      63              : #endif
      64            0 : }
      65              : 
      66              : /**
      67              :  * @brief QtPass::init make sure we are ready to go as soon as
      68              :  * possible
      69              :  */
      70            0 : auto QtPass::init() -> bool {
      71            0 :   QString passStore = QtPassSettings::getPassStore(Util::findPasswordStore());
      72            0 :   QtPassSettings::setPassStore(passStore);
      73              : 
      74            0 :   QtPassSettings::initExecutables();
      75              : 
      76            0 :   QString version = QtPassSettings::getVersion();
      77              :   // dbg()<< version;
      78              : 
      79              :   // Config updates
      80            0 :   if (version.isEmpty()) {
      81              : #ifdef QT_DEBUG
      82              :     dbg() << "assuming fresh install";
      83              : #endif
      84              : 
      85            0 :     if (QtPassSettings::getAutoclearSeconds() < 5) {
      86            0 :       QtPassSettings::setAutoclearSeconds(10);
      87              :     }
      88            0 :     if (QtPassSettings::getAutoclearPanelSeconds() < 5) {
      89            0 :       QtPassSettings::setAutoclearPanelSeconds(10);
      90              :     }
      91            0 :     if (!QtPassSettings::getPwgenExecutable().isEmpty()) {
      92            0 :       QtPassSettings::setUsePwgen(true);
      93              :     } else {
      94            0 :       QtPassSettings::setUsePwgen(false);
      95              :     }
      96            0 :     QtPassSettings::setPassTemplate("login\nurl");
      97              :   } else {
      98              :     // QStringList ver = version.split(".");
      99              :     // dbg()<< ver;
     100              :     // if (ver[0] == "0" && ver[1] == "8") {
     101              :     //// upgrade to 0.9
     102              :     // }
     103            0 :     if (QtPassSettings::getPassTemplate().isEmpty()) {
     104            0 :       QtPassSettings::setPassTemplate("login\nurl");
     105              :     }
     106              :   }
     107              : 
     108            0 :   QtPassSettings::setVersion(VERSION);
     109              : 
     110            0 :   if (Util::checkConfig()) {
     111            0 :     m_mainWindow->config();
     112            0 :     if (freshStart && Util::checkConfig()) {
     113              :       return false;
     114              :     }
     115              :   }
     116              : 
     117              :   // TODO(annejan): this needs to be before we try to access the store,
     118              :   // but it would be better to do it after the Window is shown,
     119              :   // as the long delay it can cause is irritating otherwise.
     120            0 :   if (QtPassSettings::isUseWebDav()) {
     121            0 :     mountWebDav();
     122              :   }
     123              : 
     124            0 :   freshStart = false;
     125              :   // startupPhase = false;
     126            0 :   return true;
     127              : }
     128              : 
     129            0 : void QtPass::setMainWindow() {
     130            0 :   m_mainWindow->restoreWindow();
     131              : 
     132            0 :   fusedav.setParent(m_mainWindow);
     133              : 
     134              :   // TODO(bezet): this should be reconnected dynamically when pass changes
     135            0 :   connectPassSignalHandlers(QtPassSettings::getRealPass());
     136            0 :   connectPassSignalHandlers(QtPassSettings::getImitatePass());
     137              : 
     138            0 :   connect(m_mainWindow, &MainWindow::passShowHandlerFinished, this,
     139            0 :           &QtPass::passShowHandlerFinished);
     140              : 
     141              :   // only for ipass
     142            0 :   connect(QtPassSettings::getImitatePass(), &ImitatePass::startReencryptPath,
     143            0 :           m_mainWindow, &MainWindow::startReencryptPath);
     144            0 :   connect(QtPassSettings::getImitatePass(), &ImitatePass::endReencryptPath,
     145            0 :           m_mainWindow, &MainWindow::endReencryptPath);
     146              : 
     147            0 :   connect(m_mainWindow, &MainWindow::passGitInitNeeded, [=]() -> void {
     148              : #ifdef QT_DEBUG
     149              :     dbg() << "Pass git init called";
     150              : #endif
     151            0 :     QtPassSettings::getPass()->GitInit();
     152            0 :   });
     153              : 
     154            0 :   connect(m_mainWindow, &MainWindow::generateGPGKeyPair, m_mainWindow,
     155            0 :           [=](const QString &batch) -> void {
     156            0 :             QtPassSettings::getPass()->GenerateGPGKeys(batch);
     157            0 :             m_mainWindow->showStatusMessage(tr("Generating GPG key pair"),
     158              :                                             60000);
     159            0 :           });
     160            0 : }
     161              : 
     162            0 : void QtPass::connectPassSignalHandlers(Pass *pass) {
     163            0 :   connect(pass, &Pass::error, this, &QtPass::processError);
     164            0 :   connect(pass, &Pass::processErrorExit, this, &QtPass::processErrorExit);
     165            0 :   connect(pass, &Pass::critical, m_mainWindow, &MainWindow::critical);
     166            0 :   connect(pass, &Pass::startingExecuteWrapper, m_mainWindow,
     167            0 :           &MainWindow::executeWrapperStarted);
     168            0 :   connect(pass, &Pass::statusMsg, m_mainWindow, &MainWindow::showStatusMessage);
     169            0 :   connect(pass, &Pass::finishedShow, m_mainWindow,
     170            0 :           &MainWindow::passShowHandler);
     171            0 :   connect(pass, &Pass::finishedOtpGenerate, m_mainWindow,
     172            0 :           &MainWindow::passOtpHandler);
     173              : 
     174            0 :   connect(pass, &Pass::finishedGitInit, this, &QtPass::passStoreChanged);
     175            0 :   connect(pass, &Pass::finishedGitPull, this, &QtPass::processFinished);
     176            0 :   connect(pass, &Pass::finishedGitPush, this, &QtPass::processFinished);
     177            0 :   connect(pass, &Pass::finishedInsert, this, &QtPass::finishedInsert);
     178            0 :   connect(pass, &Pass::finishedRemove, this, &QtPass::passStoreChanged);
     179            0 :   connect(pass, &Pass::finishedInit, this, &QtPass::passStoreChanged);
     180            0 :   connect(pass, &Pass::finishedMove, this, &QtPass::passStoreChanged);
     181            0 :   connect(pass, &Pass::finishedCopy, this, &QtPass::passStoreChanged);
     182            0 :   connect(pass, &Pass::finishedGenerateGPGKeys, this,
     183            0 :           &QtPass::onKeyGenerationComplete);
     184            0 : }
     185              : 
     186              : /**
     187              :  * @brief QtPass::mountWebDav is some scary voodoo magic
     188              :  */
     189            0 : void QtPass::mountWebDav() {
     190              : #ifdef Q_OS_WIN
     191              :   char dst[20] = {0};
     192              :   NETRESOURCEA netres;
     193              :   memset(&netres, 0, sizeof(netres));
     194              :   netres.dwType = RESOURCETYPE_DISK;
     195              :   netres.lpLocalName = 0;
     196              :   netres.lpRemoteName = QtPassSettings::getWebDavUrl().toUtf8().data();
     197              :   DWORD size = sizeof(dst);
     198              :   DWORD r = WNetUseConnectionA(
     199              :       reinterpret_cast<HWND>(m_mainWindow->effectiveWinId()), &netres,
     200              :       QtPassSettings::getWebDavPassword().toUtf8().constData(),
     201              :       QtPassSettings::getWebDavUser().toUtf8().constData(),
     202              :       CONNECT_TEMPORARY | CONNECT_INTERACTIVE | CONNECT_REDIRECT, dst, &size,
     203              :       0);
     204              :   if (r == NO_ERROR) {
     205              :     QtPassSettings::setPassStore(dst);
     206              :   } else {
     207              :     char message[256] = {0};
     208              :     FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, r, 0, message,
     209              :                    sizeof(message), 0);
     210              :     m_mainWindow->flashText(tr("Failed to connect WebDAV:\n") + message +
     211              :                                 " (0x" + QString::number(r, 16) + ")",
     212              :                             true);
     213              :   }
     214              : #else
     215            0 :   fusedav.start("fusedav", QStringList()
     216            0 :                                << "-o"
     217            0 :                                << "nonempty"
     218            0 :                                << "-u"
     219            0 :                                << "\"" + QtPassSettings::getWebDavUser() + "\""
     220            0 :                                << QtPassSettings::getWebDavUrl()
     221            0 :                                << "\"" + QtPassSettings::getPassStore() + "\"");
     222            0 :   fusedav.waitForStarted();
     223            0 :   if (fusedav.state() == QProcess::Running) {
     224            0 :     QString pwd = QtPassSettings::getWebDavPassword();
     225            0 :     bool ok = true;
     226            0 :     if (pwd.isEmpty()) {
     227            0 :       pwd = QInputDialog::getText(m_mainWindow, tr("QtPass WebDAV password"),
     228            0 :                                   tr("Enter password to connect to WebDAV:"),
     229            0 :                                   QLineEdit::Password, "", &ok);
     230              :     }
     231            0 :     if (ok && !pwd.isEmpty()) {
     232            0 :       fusedav.write(pwd.toUtf8() + '\n');
     233            0 :       fusedav.closeWriteChannel();
     234            0 :       fusedav.waitForFinished(2000);
     235              :     } else {
     236            0 :       fusedav.terminate();
     237              :     }
     238              :   }
     239            0 :   QString error = fusedav.readAllStandardError();
     240            0 :   int prompt = error.indexOf("Password:");
     241            0 :   if (prompt >= 0) {
     242            0 :     error.remove(0, prompt + 10);
     243              :   }
     244            0 :   if (fusedav.state() != QProcess::Running) {
     245            0 :     error = tr("fusedav exited unexpectedly\n") + error;
     246              :   }
     247            0 :   if (error.size() > 0) {
     248            0 :     m_mainWindow->flashText(
     249            0 :         tr("Failed to start fusedav to connect WebDAV:\n") + error, true);
     250              :   }
     251              : #endif
     252            0 : }
     253              : 
     254              : /**
     255              :  * @brief QtPass::processError something went wrong
     256              :  * @param error
     257              :  */
     258            0 : void QtPass::processError(QProcess::ProcessError error) {
     259            0 :   QString errorString;
     260            0 :   switch (error) {
     261            0 :   case QProcess::FailedToStart:
     262            0 :     errorString = tr("QProcess::FailedToStart");
     263            0 :     break;
     264            0 :   case QProcess::Crashed:
     265            0 :     errorString = tr("QProcess::Crashed");
     266            0 :     break;
     267            0 :   case QProcess::Timedout:
     268            0 :     errorString = tr("QProcess::Timedout");
     269            0 :     break;
     270            0 :   case QProcess::ReadError:
     271            0 :     errorString = tr("QProcess::ReadError");
     272            0 :     break;
     273            0 :   case QProcess::WriteError:
     274            0 :     errorString = tr("QProcess::WriteError");
     275            0 :     break;
     276            0 :   case QProcess::UnknownError:
     277            0 :     errorString = tr("QProcess::UnknownError");
     278            0 :     break;
     279              :   }
     280              : 
     281            0 :   m_mainWindow->flashText(errorString, true);
     282            0 :   m_mainWindow->setUiElementsEnabled(true);
     283            0 : }
     284              : 
     285            0 : void QtPass::processErrorExit(int exitCode, const QString &p_error) {
     286            0 :   if (!p_error.isEmpty()) {
     287            0 :     QString output;
     288            0 :     QString error = p_error.toHtmlEscaped();
     289            0 :     if (exitCode == 0) {
     290              :       //  https://github.com/IJHack/qtpass/issues/111
     291            0 :       output = "<span style=\"color: darkgray;\">" + error + "</span><br />";
     292              :     } else {
     293            0 :       output = "<span style=\"color: red;\">" + error + "</span><br />";
     294              :     }
     295              : 
     296            0 :     output.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
     297            0 :     output.replace(QStringLiteral("\n"), "<br />");
     298              : 
     299            0 :     m_mainWindow->flashText(output, false, true);
     300              :   }
     301              : 
     302            0 :   m_mainWindow->setUiElementsEnabled(true);
     303            0 : }
     304              : 
     305              : /**
     306              :  * @brief QtPass::processFinished background process has finished
     307              :  * @param exitCode
     308              :  * @param exitStatus
     309              :  * @param output    stdout from a process
     310              :  * @param errout    stderr from a process
     311              :  */
     312            0 : void QtPass::processFinished(const QString &p_output, const QString &p_errout) {
     313            0 :   showInTextBrowser(p_output);
     314              :   //    Sometimes there is error output even with 0 exit code, which is
     315              :   //    assumed in this function
     316            0 :   processErrorExit(0, p_errout);
     317              : 
     318            0 :   m_mainWindow->setUiElementsEnabled(true);
     319            0 : }
     320              : 
     321            0 : void QtPass::passStoreChanged(const QString &p_out, const QString &p_err) {
     322            0 :   processFinished(p_out, p_err);
     323            0 :   doGitPush();
     324            0 : }
     325              : 
     326            0 : void QtPass::finishedInsert(const QString &p_output, const QString &p_errout) {
     327            0 :   processFinished(p_output, p_errout);
     328            0 :   doGitPush();
     329            0 :   m_mainWindow->on_treeView_clicked(m_mainWindow->getCurrentTreeViewIndex());
     330            0 : }
     331              : 
     332            0 : void QtPass::onKeyGenerationComplete(const QString &p_output,
     333              :                                      const QString &p_errout) {
     334            0 :   if (nullptr != m_mainWindow->getKeygenDialog()) {
     335              : #ifdef QT_DEBUG
     336              :     qDebug() << "Keygen Done";
     337              : #endif
     338              : 
     339            0 :     m_mainWindow->cleanKeygenDialog();
     340              :     // TODO(annejan): some sanity checking ?
     341              :   }
     342              : 
     343            0 :   processFinished(p_output, p_errout);
     344            0 : }
     345              : 
     346            0 : void QtPass::passShowHandlerFinished(QString output) {
     347            0 :   showInTextBrowser(std::move(output));
     348            0 : }
     349              : 
     350            0 : void QtPass::showInTextBrowser(QString output, const QString &prefix,
     351              :                                const QString &postfix) {
     352            0 :   output = output.toHtmlEscaped();
     353              : 
     354            0 :   output.replace(Util::protocolRegex(), R"(<a href="\1">\1</a>)");
     355            0 :   output.replace(QStringLiteral("\n"), "<br />");
     356            0 :   output = prefix + output + postfix;
     357              : 
     358            0 :   m_mainWindow->flashText(output, false, true);
     359            0 : }
     360              : 
     361            0 : void QtPass::doGitPush() {
     362            0 :   if (QtPassSettings::isAutoPush()) {
     363            0 :     m_mainWindow->onPush();
     364              :   }
     365            0 : }
     366              : 
     367            0 : void QtPass::setClippedText(const QString &password, const QString &p_output) {
     368            0 :   if (QtPassSettings::getClipBoardType() != Enums::CLIPBOARD_NEVER &&
     369              :       !p_output.isEmpty()) {
     370            0 :     clippedText = password;
     371            0 :     if (QtPassSettings::getClipBoardType() == Enums::CLIPBOARD_ALWAYS) {
     372            0 :       copyTextToClipboard(password);
     373              :     }
     374              :   }
     375            0 : }
     376            0 : void QtPass::clearClippedText() { clippedText = ""; }
     377              : 
     378            0 : void QtPass::setClipboardTimer() {
     379            0 :   clearClipboardTimer.setInterval(MS_PER_SECOND *
     380            0 :                                   QtPassSettings::getAutoclearSeconds());
     381            0 : }
     382              : 
     383              : /**
     384              :  * @brief MainWindow::clearClipboard remove clipboard contents.
     385              :  */
     386            0 : void QtPass::clearClipboard() {
     387            0 :   QClipboard *clipboard = QApplication::clipboard();
     388              :   bool cleared = false;
     389            0 :   if (this->clippedText == clipboard->text(QClipboard::Selection)) {
     390            0 :     clipboard->clear(QClipboard::Selection);
     391            0 :     clipboard->setText(QString(""), QClipboard::Selection);
     392              :     cleared = true;
     393              :   }
     394            0 :   if (this->clippedText == clipboard->text(QClipboard::Clipboard)) {
     395            0 :     clipboard->clear(QClipboard::Clipboard);
     396              :     cleared = true;
     397              :   }
     398            0 :   if (cleared) {
     399            0 :     m_mainWindow->showStatusMessage(tr("Clipboard cleared"));
     400              :   } else {
     401            0 :     m_mainWindow->showStatusMessage(tr("Clipboard not cleared"));
     402              :   }
     403              : 
     404            0 :   clippedText.clear();
     405            0 : }
     406              : 
     407              : /**
     408              :  * @brief MainWindow::copyTextToClipboard copies text to your clipboard
     409              :  * @param text
     410              :  */
     411            0 : void QtPass::copyTextToClipboard(const QString &text) {
     412            0 :   QClipboard *clip = QApplication::clipboard();
     413            0 :   if (!QtPassSettings::isUseSelection()) {
     414            0 :     clip->setText(text, QClipboard::Clipboard);
     415              :   } else {
     416            0 :     clip->setText(text, QClipboard::Selection);
     417              :   }
     418              : 
     419            0 :   clippedText = text;
     420            0 :   m_mainWindow->showStatusMessage(tr("Copied to clipboard"));
     421            0 :   if (QtPassSettings::isUseAutoclear()) {
     422            0 :     clearClipboardTimer.start();
     423              :   }
     424            0 : }
     425              : 
     426              : /**
     427              :  * @brief displays the text as qrcode
     428              :  * @param text
     429              :  */
     430            0 : void QtPass::showTextAsQRCode(const QString &text) {
     431            0 :   QProcess qrencode;
     432            0 :   qrencode.start(QtPassSettings::getQrencodeExecutable("/usr/bin/qrencode"),
     433            0 :                  QStringList() << "-o-"
     434            0 :                                << "-tPNG");
     435            0 :   qrencode.write(text.toUtf8());
     436            0 :   qrencode.closeWriteChannel();
     437            0 :   qrencode.waitForFinished();
     438            0 :   QByteArray output(qrencode.readAllStandardOutput());
     439              : 
     440            0 :   if (qrencode.exitStatus() || qrencode.exitCode()) {
     441            0 :     QString error(qrencode.readAllStandardError());
     442            0 :     m_mainWindow->showStatusMessage(error);
     443              :   } else {
     444            0 :     QPixmap image;
     445            0 :     image.loadFromData(output, "PNG");
     446              : 
     447            0 :     auto *popup = new QDialog(nullptr, Qt::Popup | Qt::FramelessWindowHint);
     448            0 :     auto *layout = new QVBoxLayout;
     449            0 :     auto *popupLabel = new QLabel();
     450            0 :     layout->addWidget(popupLabel);
     451            0 :     popupLabel->setPixmap(image);
     452            0 :     popupLabel->setScaledContents(true);
     453            0 :     popupLabel->show();
     454            0 :     popup->setLayout(layout);
     455            0 :     popup->move(QCursor::pos());
     456            0 :     popup->exec();
     457            0 :   }
     458            0 : }
        

Generated by: LCOV version 2.4-beta