File indexing completed on 2024-05-12 17:16:10

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 "settings/kdesvnsettings.h"
0022 #include "ksvnwidgets/authdialogimpl.h"
0023 #include "ksvnwidgets/commitmsg_impl.h"
0024 #include "ksvnwidgets/ssltrustprompt.h"
0025 #include "ksvnwidgets/pwstorage.h"
0026 #include "helpers/kdesvn_debug.h"
0027 #include "fronthelpers/cursorstack.h"
0028 
0029 #include <KLocalizedString>
0030 #include <KMessageBox>
0031 #include <KPasswordDialog>
0032 
0033 #include <QFileDialog>
0034 #include <QTextStream>
0035 #include <QMutex>
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), m_CancelMutex(), noDialogs(false), m_updatedItems()
0054 {
0055 }
0056 
0057 CContextListenerData::~CContextListenerData()
0058 {
0059 }
0060 
0061 const int CContextListener::smax_actionstring = svn_wc_notify_failed_unlock + 1;
0062 
0063 const char *CContextListener::action_strings[] = {
0064     I18N_NOOP("Add to revision control"),
0065     I18N_NOOP("Copy"),
0066     I18N_NOOP("Delete"),
0067     I18N_NOOP("Restore missing"),
0068     I18N_NOOP("Revert"),
0069     I18N_NOOP("Revert failed"),
0070     I18N_NOOP("Resolved"),
0071     I18N_NOOP("Skip"),
0072     I18N_NOOP("Deleted"),
0073     I18N_NOOP("Added"),
0074     I18N_NOOP("Update"), //svn_wc_notify_update_update
0075     I18N_NOOP("Update complete"),
0076     I18N_NOOP("Update external module"),
0077     nullptr, // status completed - will not send is just noisy
0078     I18N_NOOP("Status on external"), //svn_wc_notify_status_external
0079     I18N_NOOP("Commit Modified"),
0080     I18N_NOOP("Commit Added"),
0081     I18N_NOOP("Commit Deleted"),
0082     I18N_NOOP("Commit Replaced"),
0083     nullptr, //tx delta -> making ticks instead
0084     nullptr, //svn_wc_notify_blame_revision - using ticks
0085     I18N_NOOP("Locking"),
0086     I18N_NOOP("Unlocked"),
0087     I18N_NOOP("Lock failed"),
0088     I18N_NOOP("Unlock failed")
0089 };
0090 
0091 const char *CContextListener::notify_state_strings[] = {
0092     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 
0103 QString CContextListener::NotifyAction(svn_wc_notify_action_t action)
0104 {
0105     if (action >= smax_actionstring || action < 0) {
0106         return QString();
0107     }
0108     return (action_strings[action] == nullptr) ? QString() : i18n(action_strings[action]);
0109 }
0110 
0111 QString CContextListener::NotifyState(svn_wc_notify_state_t state)
0112 {
0113     if (state > svn_wc_notify_state_conflicted || state < 0) {
0114         return QString();
0115     }
0116     return (notify_state_strings[state] == nullptr) ? QString() : i18n(notify_state_strings[state]);
0117 }
0118 
0119 CContextListener::CContextListener(QObject *parent)
0120     : QObject(parent)
0121     , svn::ContextListener()
0122     , m_Data(new CContextListenerData())
0123 {
0124 }
0125 
0126 CContextListener::~CContextListener()
0127 {
0128     disconnect();
0129     delete m_Data;
0130 }
0131 
0132 bool CContextListener::contextGetCachedLogin(const QString &realm, QString &username, QString &password)
0133 {
0134     PwStorage::self()->getCachedLogin(realm, username, password);
0135     return true;
0136 }
0137 
0138 bool CContextListener::contextGetSavedLogin(const QString &realm, QString &username, QString &password)
0139 {
0140     if (!Kdesvnsettings::passwords_in_wallet()) {
0141         return true;
0142     }
0143     emit waitShow(true);
0144     PwStorage::self()->getLogin(realm, username, password);
0145     PwStorage::self()->setCachedLogin(realm, username, password);
0146     emit waitShow(false);
0147     /* the return value isn't interesting to us... */
0148     return true;
0149 }
0150 
0151 bool CContextListener::contextGetLogin(
0152     const QString &realm,
0153     QString &username,
0154     QString &password,
0155     bool &maySave)
0156 {
0157     bool ret = false;
0158     maySave = false;
0159     emit waitShow(true);
0160     emit sendNotify(realm);
0161     QPointer<AuthDialogImpl> auth(new AuthDialogImpl(realm, username));
0162     if (auth->exec() == QDialog::Accepted) {
0163         username = auth->Username();
0164         password = auth->Password();
0165         maySave = (Kdesvnsettings::passwords_in_wallet() ? false : auth->maySave());
0166         if (Kdesvnsettings::passwords_in_wallet() && auth->maySave()) {
0167             PwStorage::self()->setLogin(realm, username, password);
0168         }
0169         if (Kdesvnsettings::use_password_cache()) {
0170             PwStorage::self()->setCachedLogin(realm, username, password);
0171         }
0172         ret = true;
0173     }
0174     delete auth;
0175     emit waitShow(false);
0176     return ret;
0177 }
0178 
0179 void CContextListener::contextNotify(const QString &aMsg)
0180 {
0181     if (aMsg.isEmpty()) {
0182         emit tickProgress();
0183     } else {
0184         emit sendNotify(aMsg);
0185     }
0186 }
0187 
0188 void CContextListener::contextNotify(const char *path,
0189                                      svn_wc_notify_action_t action,
0190                                      svn_node_kind_t /* kind */,
0191                                      const char *mime_type,
0192                                      svn_wc_notify_state_t content_state,
0193                                      svn_wc_notify_state_t prop_state,
0194                                      svn_revnum_t revision)
0195 {
0196     Q_UNUSED(mime_type);
0197     Q_UNUSED(prop_state);
0198 
0199     QString msg;
0200     QString aString = NotifyAction(action);
0201     extraNotify(QString::fromUtf8(path), action, revision);
0202     if (!aString.isEmpty()) {
0203         QTextStream ts(&msg, QIODevice::WriteOnly);
0204         ts << NotifyAction(action) << " " << QString::fromUtf8(path);
0205         if (revision > -1) {
0206             ts << " (Rev " << revision << ")";
0207         }
0208         aString = NotifyState(content_state);
0209         if (!aString.isEmpty()) {
0210             ts << "\n" << aString;
0211         }
0212     }
0213     contextNotify(msg);
0214 }
0215 
0216 void CContextListener::contextNotify(const svn_wc_notify_t *action)
0217 {
0218     if (!action) {
0219         return;
0220     }
0221 //    if (action->action<svn_wc_notify_locked) {
0222     contextNotify(action->path, action->action, action->kind, action->mime_type,
0223                   action->content_state, action->prop_state, action->revision);
0224 //        return;
0225 //    }
0226 //    QString aString = NotifyAction(action->action);
0227 }
0228 
0229 void CContextListener::sendTick()
0230 {
0231     emit tickProgress();
0232 }
0233 
0234 bool CContextListener::contextCancel()
0235 {
0236     {
0237         QMutexLocker lock(&(m_Data->m_CancelMutex));
0238         if (m_Data->m_cancelMe) {
0239             m_Data->m_cancelMe = false;
0240             return true;
0241         }
0242     }
0243     // otherwise deadlock!
0244     sendTick();
0245     return false;
0246 }
0247 
0248 bool CContextListener::contextGetLogMessage(QString &msg, const svn::CommitItemList &items)
0249 {
0250     bool isOk = false;
0251     emit waitShow(true);
0252     QString logMessage = Commitmsg_impl::getLogmessage(items, &isOk, nullptr, nullptr, nullptr);
0253     if (isOk) {
0254         msg = logMessage;
0255     }
0256     emit waitShow(false);
0257     return isOk;
0258 }
0259 
0260 svn::ContextListener::SslServerTrustAnswer CContextListener::contextSslServerTrustPrompt(
0261     const svn::ContextListener::SslServerTrustData &data , apr_uint32_t &acceptedFailures)
0262 {
0263     CursorStack cs(Qt::ArrowCursor);
0264 
0265     bool ok, saveit;
0266     emit waitShow(true);
0267     if (!SslTrustPrompt::sslTrust(
0268                 data.hostname,
0269                 data.fingerprint,
0270                 data.validFrom,
0271                 data.validUntil,
0272                 data.issuerDName,
0273                 data.realm,
0274                 failure2Strings(acceptedFailures),
0275                 &ok, &saveit)) {
0276         return DONT_ACCEPT;
0277     }
0278     emit waitShow(false);
0279     if (!saveit) {
0280         return ACCEPT_TEMPORARILY;
0281     }
0282     return ACCEPT_PERMANENTLY;
0283 }
0284 
0285 bool CContextListener::contextSslClientCertPrompt(QString &certFile)
0286 {
0287     qCDebug(KDESVN_LOG) << certFile << endl;
0288     emit waitShow(true);
0289     QString afile = QFileDialog::getOpenFileName(nullptr, i18n("Open a file with a #PKCS12 certificate"));
0290     emit waitShow(false);
0291     if (afile.isEmpty()) {
0292         return false;
0293     }
0294     certFile = afile;
0295     return true;
0296 }
0297 
0298 bool CContextListener::contextLoadSslClientCertPw(QString &password, const QString &realm)
0299 {
0300     PwStorage::self()->getCertPw(realm, password);
0301     return true;
0302 }
0303 
0304 bool CContextListener::contextSslClientCertPwPrompt(QString &password,
0305         const QString &realm, bool &maysave)
0306 {
0307     maysave = false;
0308     emit waitShow(true);
0309     QString npass;
0310     QPointer<KPasswordDialog> dlg(new KPasswordDialog(nullptr));
0311     dlg->setPrompt(i18n("Enter password for realm %1", realm));
0312     dlg->setWindowTitle(realm);
0313     int res = dlg->exec();
0314     if (res == QDialog::Accepted) {
0315         npass = dlg->password();
0316     }
0317     bool keepPw = (dlg ? dlg->keepPassword() : false);
0318     delete dlg;
0319 
0320     emit waitShow(false);
0321     if (res != QDialog::Accepted) {
0322         return false;
0323     }
0324     maysave = (Kdesvnsettings::passwords_in_wallet() ? false : keepPw);
0325     if (Kdesvnsettings::store_passwords() && keepPw) {
0326         PwStorage::self()->setCertPw(realm, password);
0327     }
0328     password = npass;
0329     return true;
0330 }
0331 
0332 void CContextListener::setCanceled(bool how)
0333 {
0334     QMutexLocker lock(&(m_Data->m_CancelMutex));
0335     m_Data->m_cancelMe = how;
0336 }
0337 
0338 QStringList CContextListener::failure2Strings(apr_uint32_t acceptedFailures)
0339 {
0340     QStringList res;
0341     if (acceptedFailures & SVN_AUTH_SSL_UNKNOWNCA) {
0342         res << i18n("The certificate is not issued by a trusted authority. Use the fingerprint to validate the certificate manually.");
0343     }
0344     if (acceptedFailures & SVN_AUTH_SSL_CNMISMATCH) {
0345         res << i18n("The certificate hostname does not match.");
0346     }
0347     if (acceptedFailures & SVN_AUTH_SSL_NOTYETVALID) {
0348         res << i18n("The certificate is not yet valid.");
0349     }
0350     if (acceptedFailures & SVN_AUTH_SSL_EXPIRED) {
0351         res << i18n("The certificate has expired.");
0352     }
0353     if (acceptedFailures & SVN_AUTH_SSL_OTHER) {
0354         res << i18n("The certificate has an unknown error.");
0355     }
0356     return res;
0357 }
0358 
0359 QString CContextListener::translate(const QString &what)
0360 {
0361     return i18n(what.toLocal8Bit());
0362 }
0363 
0364 /*!
0365     \fn CContextListener::contextProgress(long long int current, long long int max)
0366  */
0367 void CContextListener::contextProgress(long long int current, long long int max)
0368 {
0369     emit netProgress(current, max);
0370 }
0371 
0372 void CContextListener::maySavePlaintext(svn_boolean_t *may_save_plaintext, const QString &realmstring)
0373 {
0374     emit waitShow(true);
0375     if (may_save_plaintext) {
0376         QString question = i18n("%1\nReally store password as plain text?", realmstring);
0377         QString head = i18n("Save password");
0378         if (KMessageBox::questionYesNo(nullptr, question, head) == KMessageBox::Yes) {
0379             *may_save_plaintext = true;
0380         } else {
0381             *may_save_plaintext = false;
0382         }
0383     }
0384     emit waitShow(false);
0385 }
0386 
0387 const QStringList &CContextListener::updatedItems()const
0388 {
0389     return m_Data->m_updatedItems;
0390 }
0391 
0392 void CContextListener::cleanUpdatedItems()
0393 {
0394     m_Data->m_updatedItems.clear();
0395 }
0396 
0397 void CContextListener::extraNotify(const QString &path, svn_wc_notify_action_t action, svn_revnum_t revision)
0398 {
0399     Q_UNUSED(revision);
0400     switch (action) {
0401     case svn_wc_notify_update_update:
0402     case svn_wc_notify_update_add:
0403     case svn_wc_notify_update_delete:
0404         m_Data->m_updatedItems.append(path);
0405         break;
0406     default:
0407         break;
0408     }
0409 }