@@ -35,6 +35,7 @@ const QString CredentialsManagement::INVALID_DOMAIN_TEXT =
3535 tr (" The following domains are invalid or private: <b><ul><li>%1</li></ul></b>They are not saved." );
3636const QString CredentialsManagement::INVALID_INPUT_STYLE =
3737 " border: 2px solid red" ;
38+ const QString CredentialsManagement::TOTP_CONFIRMATION = tr(" Confirm TOTP" );
3839
3940CredentialsManagement::CredentialsManagement (QWidget *parent) :
4041 QWidget(parent), ui(new Ui::CredentialsManagement), m_pAddedLoginItem(nullptr )
@@ -256,6 +257,7 @@ void CredentialsManagement::setWsClient(WSClient *c)
256257 connect (wsClient, &WSClient::memMgmtModeChanged, this , &CredentialsManagement::checkDeviceType);
257258 connect (wsClient, &WSClient::advancedMenuChanged, this , &CredentialsManagement::checkDeviceType);
258259 connect (wsClient, &WSClient::deviceConnected, this , &CredentialsManagement::checkDeviceType);
260+ connect (wsClient, &WSClient::memMgmtModeChanged, this , &CredentialsManagement::handleTOTPQR);
259261 connect (wsClient, &WSClient::advancedMenuChanged, this , &CredentialsManagement::handleAdvancedModeChange);
260262 handleAdvancedModeChange (wsClient->get_advancedMenu ());
261263 connect (wsClient, &WSClient::displayUserCategories, this ,
@@ -597,6 +599,7 @@ void CredentialsManagement::saveSelectedTOTP()
597599 if (pLoginItem != nullptr ) {
598600 m_pCredModel->setTOTP (srcIndex, m_pTOTPCred->getSecretKey (), m_pTOTPCred->getTimeStep (), m_pTOTPCred->getCodeSize ());
599601 credentialDataChanged ();
602+ updateLoginDescription (pLoginItem);
600603 }
601604 }
602605}
@@ -677,6 +680,14 @@ void CredentialsManagement::saveChanges()
677680 emit wantSaveMemMode ();
678681}
679682
683+ void CredentialsManagement::onMainWindowActivated ()
684+ {
685+ if (wsClient->get_memMgmtMode () && wsClient->isMPBLE ())
686+ {
687+ onClipboardDataChanged ();
688+ }
689+ }
690+
680691void CredentialsManagement::keyPressEvent (QKeyEvent *event)
681692{
682693 QWidget::keyPressEvent (event);
@@ -1565,6 +1576,117 @@ QString CredentialsManagement::getFirstDomain(TreeItem *pItem) const
15651576 return Common::getFirstDomain (multDomains);
15661577}
15671578
1579+ void CredentialsManagement::addCredAndTOTP (const QString &service, TOTPReader::TOTPResult res)
1580+ {
1581+ m_pCredModel->addCredential (service,
1582+ res.login ,
1583+ " " );
1584+ QModelIndex serviceIdx = m_pCredModel->getServiceIndexByName (service);
1585+ auto *pServiceItem = m_pCredModel->getServiceItemByIndex (serviceIdx);
1586+ auto * pLoginItem = pServiceItem->findLoginByName (res.login );
1587+ QModelIndex loginIndex = m_pCredModelFilter->getProxyIndexFromItem (pLoginItem);
1588+ ui->credentialTreeView ->selectionModel ()->setCurrentIndex (loginIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1589+
1590+ pLoginItem->setTOTPCredential (res.secret , res.period , res.digits );
1591+ pLoginItem->setTotpTimeStep (res.period );
1592+ pLoginItem->setTotpCodeSize (res.digits );
1593+ pLoginItem->setTOTPDeleted (false );
1594+ credentialDataChanged ();
1595+ updateLoginDescription (pLoginItem);
1596+ }
1597+
1598+ void CredentialsManagement::processTOTPQR (TOTPReader::TOTPResult res)
1599+ {
1600+ if (!res.isValid )
1601+ {
1602+ return ;
1603+ }
1604+
1605+ bool matchService = false ;
1606+ QModelIndex serviceIdx = m_pCredModel->getServiceIndexByName (res.service );
1607+ if (!serviceIdx.isValid ())
1608+ {
1609+ ParseDomain parsedService{res.service };
1610+ if (parsedService.isWebsite ())
1611+ {
1612+ // e.g.: TOTP is for test.com, there is an existing test service.
1613+ serviceIdx = m_pCredModel->getServiceIndexByName (parsedService.domain ());
1614+ }
1615+ else
1616+ {
1617+ // e.g.: TOTP is for test, there is an existing test.com service.
1618+ serviceIdx = m_pCredModel->getServiceIndexByNamePart (res.service );
1619+ }
1620+ matchService = serviceIdx.isValid ();
1621+ }
1622+
1623+ auto *pServiceItem = m_pCredModel->getServiceItemByIndex (serviceIdx);
1624+ if (nullptr != pServiceItem)
1625+ {
1626+ if (matchService)
1627+ {
1628+ // Found service match, confirm if want to add
1629+ auto response = QMessageBox::information (this , TOTP_CONFIRMATION,
1630+ tr (" Do you want to add TOTP for <b>%1</b> service?" ).arg (pServiceItem->name ()),
1631+ QMessageBox::Yes|QMessageBox::No);
1632+ if (QMessageBox::No == response)
1633+ {
1634+ return ;
1635+ }
1636+ }
1637+
1638+ auto * pLoginItem = pServiceItem->findLoginByName (res.login );
1639+ if (nullptr != pLoginItem)
1640+ {
1641+ QString credName = pLoginItem->getDisplayName ();
1642+ QModelIndex loginIndex = m_pCredModelFilter->getProxyIndexFromItem (pLoginItem);
1643+ ui->credentialTreeView ->selectionModel ()->setCurrentIndex (loginIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
1644+ QString totpMessage = " " ;
1645+ if (pLoginItem->totpCodeSize () != 0 )
1646+ {
1647+ // TOTP already exist, confirm if user wants to overwrite
1648+ totpMessage = tr (" There is TOTP saved for credential %1\n Do you want to overwrite TOTP information?" );
1649+ }
1650+ else
1651+ {
1652+ totpMessage = tr (" Do you want to set TOTP information for %1?" );
1653+ }
1654+ auto response = QMessageBox::information (this , TOTP_CONFIRMATION,
1655+ totpMessage.arg (credName),
1656+ QMessageBox::Yes|QMessageBox::No);
1657+ if (QMessageBox::Yes == response)
1658+ {
1659+ pLoginItem->setTOTPCredential (res.secret , res.period , res.digits );
1660+ pLoginItem->setTotpTimeStep (res.period );
1661+ pLoginItem->setTotpCodeSize (res.digits );
1662+ pLoginItem->setTOTPDeleted (false );
1663+ credentialDataChanged ();
1664+ updateLoginDescription (pLoginItem);
1665+ }
1666+ }
1667+ else
1668+ {
1669+ auto response = QMessageBox::information (this , TOTP_CONFIRMATION,
1670+ tr (" Do you want to create <b>%1</b> login and add TOTP for <b>%2</b> service?" ).arg (res.login , pServiceItem->name ()),
1671+ QMessageBox::Yes|QMessageBox::No);
1672+ if (QMessageBox::Yes == response)
1673+ {
1674+ addCredAndTOTP (pServiceItem->name (), res);
1675+ }
1676+ }
1677+ }
1678+ else
1679+ {
1680+ auto response = QMessageBox::information (this , TOTP_CONFIRMATION,
1681+ tr (" <b>%1</b> service does not exist.\n Do you want to create service with <b>%2</b> login and add TOTP for it?" ).arg (res.service , res.login ),
1682+ QMessageBox::Yes|QMessageBox::No);
1683+ if (QMessageBox::Yes == response)
1684+ {
1685+ addCredAndTOTP (res.service , res);
1686+ }
1687+ }
1688+ }
1689+
15681690void CredentialsManagement::on_toolButtonFavFilter_clicked ()
15691691{
15701692 bool favFilter = m_pCredModelFilter->switchFavFilter ();
@@ -1728,7 +1850,7 @@ void CredentialsManagement::on_pushButtonLinkTo_clicked()
17281850 LoginItem *pLoginItem = m_pCredModel->getLoginItemByIndex (srcIndex);
17291851 if (pLoginItem != nullptr )
17301852 {
1731- linkToName = " < " + pLoginItem->parentItem ()-> name () + " / " + pLoginItem-> name () + " > " ;
1853+ linkToName = pLoginItem->getDisplayName () ;
17321854 m_credentialLinkedAddr = pLoginItem->address ();
17331855 if (m_linkingMode == LinkingMode::NEW_CREDENTIAL)
17341856 {
@@ -1745,7 +1867,7 @@ void CredentialsManagement::on_pushButtonLinkTo_clicked()
17451867 LoginItem *pLoginItem = m_pCredModel->getLoginItemByIndex (srcIndex);
17461868 if (nullptr != pLoginItem)
17471869 {
1748- QString linkName = " < " + pLoginItem->parentItem ()-> name () + " / " + pLoginItem-> name () + " > " ;
1870+ QString linkName = pLoginItem->getDisplayName () ;
17491871 int ret = QMessageBox::warning (this , " Credential link" ,
17501872 tr (" %1 will use %2 password.\n "
17511873 " Do you want to confirm?" ).arg (linkName).arg (linkToName),
@@ -1854,3 +1976,50 @@ void CredentialsManagement::onTreeViewContextMenuRequested(const QPoint& pos)
18541976 }
18551977 }
18561978}
1979+
1980+ void CredentialsManagement::handleTOTPQR (bool isMMM)
1981+ {
1982+ if (!wsClient->isMPBLE ())
1983+ {
1984+ return ;
1985+ }
1986+
1987+ QClipboard *clipboard = QGuiApplication::clipboard ();
1988+ if (isMMM)
1989+ {
1990+ connect (clipboard, &QClipboard::dataChanged, this , &CredentialsManagement::onClipboardDataChanged);
1991+ // When MMM is populated trigger a check for clipboard if QR TOTP image is available
1992+ QTimer::singleShot (QR_PROCESSING_TIMEOUT, this , [this ](){ onClipboardDataChanged (); });
1993+ }
1994+ else
1995+ {
1996+ disconnect (clipboard, &QClipboard::dataChanged, 0 , 0 );
1997+ }
1998+ }
1999+
2000+ void CredentialsManagement::onClipboardDataChanged ()
2001+ {
2002+ TOTPReader::TOTPResult res;
2003+ QClipboard *clipboard = QGuiApplication::clipboard ();
2004+
2005+ QImage clipImage = clipboard->image ();
2006+ static QImage lastImage;
2007+ if (clipImage == lastImage)
2008+ {
2009+ // Only process image from clipboard one time.
2010+ return ;
2011+ }
2012+ else
2013+ {
2014+ lastImage = clipImage;
2015+ }
2016+
2017+ if (!clipImage.isNull () && !m_processingQRImage)
2018+ {
2019+ m_processingQRImage = true ;
2020+ res = TOTPReader::getQRCodeResult (clipImage);
2021+ // For image dataChanged is triggered twice, prevent double process with this workaround
2022+ QTimer::singleShot (QR_PROCESSING_TIMEOUT, this , [this ](){m_processingQRImage = false ; });
2023+ }
2024+ processTOTPQR (res);
2025+ }
0 commit comments