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 }