File indexing completed on 2024-04-28 05:41:59

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2009 by Rajko Albrecht                             *
0003  *   ral@alwins-world.de                                                   *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
0019  ***************************************************************************/
0020 #include "ccontextlistener.h"
0021 #include "fronthelpers/cursorstack.h"
0022 #include "helpers/kdesvn_debug.h"
0023 #include "ksvnwidgets/authdialogimpl.h"
0024 #include "ksvnwidgets/commitmsg_impl.h"
0025 #include "ksvnwidgets/pwstorage.h"
0026 #include "ksvnwidgets/ssltrustprompt.h"
0027 #include "settings/kdesvnsettings.h"
0028 
0029 #include <KLocalizedString>
0030 #include <KMessageBox>
0031 #include <KPasswordDialog>
0032 
0033 #include <QFileDialog>
0034 #include <QMutex>
0035 #include <QTextStream>
0036 
0037 class CContextListenerData
0038 {
0039 public:
0040     CContextListenerData();
0041     virtual ~CContextListenerData();
0042 
0043     // data
0044     bool m_cancelMe;
0045     QMutex m_CancelMutex;
0046 
0047     bool noDialogs;
0048 
0049     QStringList m_updatedItems;
0050 };
0051 
0052 CContextListenerData::CContextListenerData()
0053     : m_cancelMe(false)
0054     , m_CancelMutex()
0055     , noDialogs(false)
0056     , m_updatedItems()
0057 {
0058 }
0059 
0060 CContextListenerData::~CContextListenerData()
0061 {
0062 }
0063 
0064 const int CContextListener::smax_actionstring = svn_wc_notify_failed_unlock + 1;
0065 
0066 const char *CContextListener::action_strings[] = {I18N_NOOP("Add to revision control"),
0067                                                   I18N_NOOP("Copy"),
0068                                                   I18N_NOOP("Delete"),
0069                                                   I18N_NOOP("Restore missing"),
0070                                                   I18N_NOOP("Revert"),
0071                                                   I18N_NOOP("Revert failed"),
0072                                                   I18N_NOOP("Resolved"),
0073                                                   I18N_NOOP("Skip"),
0074                                                   I18N_NOOP("Deleted"),
0075                                                   I18N_NOOP("Added"),
0076                                                   I18N_NOOP("Update"), // svn_wc_notify_update_update
0077                                                   I18N_NOOP("Update complete"),
0078                                                   I18N_NOOP("Update external module"),
0079                                                   nullptr, // status completed - will not send is just noisy
0080                                                   I18N_NOOP("Status on external"), // svn_wc_notify_status_external
0081                                                   I18N_NOOP("Commit Modified"),
0082                                                   I18N_NOOP("Commit Added"),
0083                                                   I18N_NOOP("Commit Deleted"),
0084                                                   I18N_NOOP("Commit Replaced"),
0085                                                   nullptr, // tx delta -> making ticks instead
0086                                                   nullptr, // svn_wc_notify_blame_revision - using ticks
0087                                                   I18N_NOOP("Locking"),
0088                                                   I18N_NOOP("Unlocked"),
0089                                                   I18N_NOOP("Lock failed"),
0090                                                   I18N_NOOP("Unlock failed")};
0091 
0092 const char *CContextListener::notify_state_strings[] = {nullptr, // = 0
0093                                                         nullptr,
0094                                                         I18N_NOOP("unchanged"),
0095                                                         I18N_NOOP("item wasn't present"),
0096                                                         I18N_NOOP("unversioned item obstructed work"),
0097                                                         // I18N_NOOP("Pristine state was modified."), // should send a signal with path instead of message?
0098                                                         nullptr,
0099                                                         I18N_NOOP("Modified state had mods merged in."),
0100                                                         I18N_NOOP("Modified state got conflicting mods.")};
0101 
0102 QString CContextListener::NotifyAction(svn_wc_notify_action_t action)
0103 {
0104     if (action >= smax_actionstring || action < 0) {
0105         return QString();
0106     }
0107     return (action_strings[action] == nullptr) ? QString() : i18n(action_strings[action]);
0108 }
0109 
0110 QString CContextListener::NotifyState(svn_wc_notify_state_t state)
0111 {
0112     if (state > svn_wc_notify_state_conflicted || state < 0) {
0113         return QString();
0114     }
0115     return (notify_state_strings[state] == nullptr) ? QString() : i18n(notify_state_strings[state]);
0116 }
0117 
0118 CContextListener::CContextListener(QObject *parent)
0119     : QObject(parent)
0120     , svn::ContextListener()
0121     , m_Data(new CContextListenerData())
0122 {
0123 }
0124 
0125 CContextListener::~CContextListener()
0126 {
0127     disconnect();
0128     delete m_Data;
0129 }
0130 
0131 bool CContextListener::contextGetCachedLogin(const QString &realm, QString &username, QString &password)
0132 {
0133     PwStorage::self()->getCachedLogin(realm, username, password);
0134     return true;
0135 }
0136 
0137 bool CContextListener::contextGetSavedLogin(const QString &realm, QString &username, QString &password)
0138 {
0139     if (!Kdesvnsettings::passwords_in_wallet()) {
0140         return true;
0141     }
0142     emit waitShow(true);
0143     PwStorage::self()->getLogin(realm, username, password);
0144     PwStorage::self()->setCachedLogin(realm, username, password);
0145     emit waitShow(false);
0146     /* the return value isn't interesting to us... */
0147     return true;
0148 }
0149 
0150 bool CContextListener::contextGetLogin(const QString &realm, QString &username, QString &password, bool &maySave)
0151 {
0152     bool ret = false;
0153     maySave = false;
0154     emit waitShow(true);
0155     emit sendNotify(realm);
0156     QPointer<AuthDialogImpl> auth(new AuthDialogImpl(realm, username));
0157     if (auth->exec() == QDialog::Accepted) {
0158         username = auth->Username();
0159         password = auth->Password();
0160         maySave = (Kdesvnsettings::passwords_in_wallet() ? false : auth->maySave());
0161         if (Kdesvnsettings::passwords_in_wallet() && auth->maySave()) {
0162             PwStorage::self()->setLogin(realm, username, password);
0163         }
0164         if (Kdesvnsettings::use_password_cache()) {
0165             PwStorage::self()->setCachedLogin(realm, username, password);
0166         }
0167         ret = true;
0168     }
0169     delete auth;
0170     emit waitShow(false);
0171     return ret;
0172 }
0173 
0174 void CContextListener::contextNotify(const QString &aMsg)
0175 {
0176     if (aMsg.isEmpty()) {
0177         emit tickProgress();
0178     } else {
0179         emit sendNotify(aMsg);
0180     }
0181 }
0182 
0183 void CContextListener::contextNotify(const char *path,
0184                                      svn_wc_notify_action_t action,
0185                                      svn_node_kind_t /* kind */,
0186                                      const char *mime_type,
0187                                      svn_wc_notify_state_t content_state,
0188                                      svn_wc_notify_state_t prop_state,
0189                                      svn_revnum_t revision)
0190 {
0191     Q_UNUSED(mime_type);
0192     Q_UNUSED(prop_state);
0193 
0194     QString msg;
0195     QString aString = NotifyAction(action);
0196     extraNotify(QString::fromUtf8(path), action, revision);
0197     if (!aString.isEmpty()) {
0198         QTextStream ts(&msg, QIODevice::WriteOnly);
0199         ts << NotifyAction(action) << " " << QString::fromUtf8(path);
0200         if (revision > -1) {
0201             ts << " (Rev " << revision << ")";
0202         }
0203         aString = NotifyState(content_state);
0204         if (!aString.isEmpty()) {
0205             ts << "\n" << aString;
0206         }
0207     }
0208     contextNotify(msg);
0209 }
0210 
0211 void CContextListener::contextNotify(const svn_wc_notify_t *action)
0212 {
0213     if (!action) {
0214         return;
0215     }
0216     //    if (action->action<svn_wc_notify_locked) {
0217     contextNotify(action->path, action->action, action->kind, action->mime_type, action->content_state, action->prop_state, action->revision);
0218     //        return;
0219     //    }
0220     //    QString aString = NotifyAction(action->action);
0221 }
0222 
0223 void CContextListener::sendTick()
0224 {
0225     emit tickProgress();
0226 }
0227 
0228 bool CContextListener::contextCancel()
0229 {
0230     {
0231         QMutexLocker lock(&(m_Data->m_CancelMutex));
0232         if (m_Data->m_cancelMe) {
0233             m_Data->m_cancelMe = false;
0234             return true;
0235         }
0236     }
0237     // otherwise deadlock!
0238     sendTick();
0239     return false;
0240 }
0241 
0242 bool CContextListener::contextGetLogMessage(QString &msg, const svn::CommitItemList &items)
0243 {
0244     bool isOk = false;
0245     emit waitShow(true);
0246     QString logMessage = Commitmsg_impl::getLogmessage(items, &isOk, nullptr, nullptr, nullptr);
0247     if (isOk) {
0248         msg = logMessage;
0249     }
0250     emit waitShow(false);
0251     return isOk;
0252 }
0253 
0254 svn::ContextListener::SslServerTrustAnswer CContextListener::contextSslServerTrustPrompt(const svn::ContextListener::SslServerTrustData &data,
0255                                                                                          apr_uint32_t &acceptedFailures)
0256 {
0257     CursorStack cs(Qt::ArrowCursor);
0258 
0259     bool ok, saveit;
0260     emit waitShow(true);
0261     if (!SslTrustPrompt::sslTrust(data.hostname,
0262                                   data.fingerprint,
0263                                   data.validFrom,
0264                                   data.validUntil,
0265                                   data.issuerDName,
0266                                   data.realm,
0267                                   failure2Strings(acceptedFailures),
0268                                   &ok,
0269                                   &saveit)) {
0270         return DONT_ACCEPT;
0271     }
0272     emit waitShow(false);
0273     if (!saveit) {
0274         return ACCEPT_TEMPORARILY;
0275     }
0276     return ACCEPT_PERMANENTLY;
0277 }
0278 
0279 bool CContextListener::contextSslClientCertPrompt(QString &certFile)
0280 {
0281     qCDebug(KDESVN_LOG) << certFile << Qt::endl;
0282     emit waitShow(true);
0283     QString afile = QFileDialog::getOpenFileName(nullptr, i18n("Open a file with a #PKCS12 certificate"));
0284     emit waitShow(false);
0285     if (afile.isEmpty()) {
0286         return false;
0287     }
0288     certFile = afile;
0289     return true;
0290 }
0291 
0292 bool CContextListener::contextLoadSslClientCertPw(QString &password, const QString &realm)
0293 {
0294     PwStorage::self()->getCertPw(realm, password);
0295     return true;
0296 }
0297 
0298 bool CContextListener::contextSslClientCertPwPrompt(QString &password, const QString &realm, bool &maysave)
0299 {
0300     maysave = false;
0301     emit waitShow(true);
0302     QString npass;
0303     QPointer<KPasswordDialog> dlg(new KPasswordDialog(nullptr));
0304     dlg->setPrompt(i18n("Enter password for realm %1", realm));
0305     dlg->setWindowTitle(realm);
0306     int res = dlg->exec();
0307     if (res == QDialog::Accepted) {
0308         npass = dlg->password();
0309     }
0310     bool keepPw = (dlg ? dlg->keepPassword() : false);
0311     delete dlg;
0312 
0313     emit waitShow(false);
0314     if (res != QDialog::Accepted) {
0315         return false;
0316     }
0317     maysave = (Kdesvnsettings::passwords_in_wallet() ? false : keepPw);
0318     if (Kdesvnsettings::store_passwords() && keepPw) {
0319         PwStorage::self()->setCertPw(realm, password);
0320     }
0321     password = npass;
0322     return true;
0323 }
0324 
0325 void CContextListener::setCanceled(bool how)
0326 {
0327     QMutexLocker lock(&(m_Data->m_CancelMutex));
0328     m_Data->m_cancelMe = how;
0329 }
0330 
0331 QStringList CContextListener::failure2Strings(apr_uint32_t acceptedFailures)
0332 {
0333     QStringList res;
0334     if (acceptedFailures & SVN_AUTH_SSL_UNKNOWNCA) {
0335         res << i18n("The certificate is not issued by a trusted authority. Use the fingerprint to validate the certificate manually.");
0336     }
0337     if (acceptedFailures & SVN_AUTH_SSL_CNMISMATCH) {
0338         res << i18n("The certificate hostname does not match.");
0339     }
0340     if (acceptedFailures & SVN_AUTH_SSL_NOTYETVALID) {
0341         res << i18n("The certificate is not yet valid.");
0342     }
0343     if (acceptedFailures & SVN_AUTH_SSL_EXPIRED) {
0344         res << i18n("The certificate has expired.");
0345     }
0346     if (acceptedFailures & SVN_AUTH_SSL_OTHER) {
0347         res << i18n("The certificate has an unknown error.");
0348     }
0349     return res;
0350 }
0351 
0352 QString CContextListener::translate(const QString &what)
0353 {
0354     return i18n(what.toLocal8Bit());
0355 }
0356 
0357 /*!
0358     \fn CContextListener::contextProgress(long long int current, long long int max)
0359  */
0360 void CContextListener::contextProgress(long long int current, long long int max)
0361 {
0362     emit netProgress(current, max);
0363 }
0364 
0365 void CContextListener::maySavePlaintext(svn_boolean_t *may_save_plaintext, const QString &realmstring)
0366 {
0367     emit waitShow(true);
0368     if (may_save_plaintext) {
0369         QString question = i18n("%1\nReally store password as plain text?", realmstring);
0370         QString head = i18n("Save password");
0371         KGuiItem contButton(i18nc("@action:button", "Store in Plain Text"));
0372         if (KMessageBox::warningContinueCancel(nullptr, question, head, contButton) == KMessageBox::Continue) {
0373             *may_save_plaintext = true;
0374         } else {
0375             *may_save_plaintext = false;
0376         }
0377     }
0378     emit waitShow(false);
0379 }
0380 
0381 const QStringList &CContextListener::updatedItems() const
0382 {
0383     return m_Data->m_updatedItems;
0384 }
0385 
0386 void CContextListener::cleanUpdatedItems()
0387 {
0388     m_Data->m_updatedItems.clear();
0389 }
0390 
0391 void CContextListener::extraNotify(const QString &path, svn_wc_notify_action_t action, svn_revnum_t revision)
0392 {
0393     Q_UNUSED(revision);
0394     switch (action) {
0395     case svn_wc_notify_update_update:
0396     case svn_wc_notify_update_add:
0397     case svn_wc_notify_update_delete:
0398         m_Data->m_updatedItems.append(path);
0399         break;
0400     default:
0401         break;
0402     }
0403 }
0404 
0405 #include "moc_ccontextlistener.cpp"