File indexing completed on 2024-05-05 04:41:02

0001 /*
0002     SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "svnjobbase.h"
0008 
0009 #include <QStandardItemModel>
0010 
0011 #include <KPasswordDialog>
0012 #include <KLocalizedString>
0013 #include <KMessageBox>
0014 
0015 #include <ThreadWeaver/QObjectDecorator>
0016 
0017 #include <interfaces/icore.h>
0018 #include <interfaces/iplugincontroller.h>
0019 #include <interfaces/iplugin.h>
0020 #include <outputview/ioutputview.h>
0021 
0022 #include "svninternaljobbase.h"
0023 #include "svnssldialog.h"
0024 
0025 SvnJobBase::SvnJobBase( KDevSvnPlugin* parent, KDevelop::OutputJob::OutputJobVerbosity verbosity )
0026     : VcsJob( parent, verbosity ), m_part( parent ),
0027       m_status( KDevelop::VcsJob::JobNotStarted )
0028 {
0029     setCapabilities( KJob::Killable );
0030     setTitle( QStringLiteral("Subversion") );
0031 }
0032 
0033 SvnJobBase::~SvnJobBase()
0034 {
0035 }
0036 
0037 void SvnJobBase::startInternalJob()
0038 {
0039     auto job = internalJob();
0040     connect( job.data(), &SvnInternalJobBase::failed,
0041              this, &SvnJobBase::internalJobFailed, Qt::QueuedConnection );
0042     connect( job.data(), &SvnInternalJobBase::done,
0043              this, &SvnJobBase::internalJobDone, Qt::QueuedConnection );
0044     connect( job.data(), &SvnInternalJobBase::started,
0045              this, &SvnJobBase::internalJobStarted, Qt::QueuedConnection );
0046     // add as shared pointer
0047     // the signals "done" & "failed" are emitted when the queue and the executor still
0048     // have and use a reference to the job, in the execution thread.
0049     // As the this parent job will be deleted in the main/other thread
0050     // (due to deleteLater() being called on it in the KJob::exec())
0051     // and the ThreadWeaver queue will release the last reference to the passed
0052     // JobInterface pointer only after the JobInterface::execute() method has been left,
0053     // the internal threaded job thus needs to get shared memory management via the QSharedPointer.
0054     m_part->jobQueue()->stream() << job;
0055 }
0056 
0057 bool SvnJobBase::doKill()
0058 {
0059     internalJob()->kill();
0060     m_status = VcsJob::JobCanceled;
0061     return true;
0062 }
0063 
0064 
0065 KDevelop::VcsJob::JobStatus SvnJobBase::status() const
0066 {
0067     return m_status;
0068 }
0069 
0070 void SvnJobBase::askForLogin( const QString& realm )
0071 {
0072     qCDebug(PLUGIN_SVN) << "login";
0073     KPasswordDialog dlg( nullptr, KPasswordDialog::ShowUsernameLine | KPasswordDialog::ShowKeepPassword );
0074     dlg.setPrompt( i18n("Enter Login for: %1", realm ) );
0075     if (dlg.exec()) { // krazy:exclude=crashy
0076         internalJob()->m_login_username = dlg.username();
0077         internalJob()->m_login_password = dlg.password();
0078         internalJob()->m_maySave = dlg.keepPassword();
0079     } else {
0080         internalJob()->m_login_username.clear();
0081         internalJob()->m_login_password.clear();
0082     }
0083     internalJob()->m_guiSemaphore.release( 1 );
0084 }
0085 
0086 void SvnJobBase::showNotification( const QString& path, const QString& msg )
0087 {
0088     Q_UNUSED(path);
0089     outputMessage(msg);
0090 }
0091 
0092 void SvnJobBase::askForCommitMessage()
0093 {
0094     qCDebug(PLUGIN_SVN) << "commit msg";
0095     internalJob()->m_guiSemaphore.release( 1 );
0096 }
0097 
0098 void SvnJobBase::askForSslServerTrust( const QStringList& failures, const QString& host,
0099                                        const QString& print, const QString& from,
0100                                        const QString& until, const QString& issuer,
0101                                        const QString& realm )
0102 {
0103 
0104     qCDebug(PLUGIN_SVN) << "servertrust";
0105     SvnSSLTrustDialog dlg;
0106     dlg.setCertInfos( host, print, from, until, issuer, realm, failures );
0107     if( dlg.exec() == QDialog::Accepted )
0108     {
0109         qCDebug(PLUGIN_SVN) << "accepted with:" << dlg.useTemporarily();
0110         if( dlg.useTemporarily() )
0111         {
0112             internalJob()->m_trustAnswer = svn::ContextListener::ACCEPT_TEMPORARILY;
0113         }else
0114         {
0115         internalJob()->m_trustAnswer = svn::ContextListener::ACCEPT_PERMANENTLY;
0116         }
0117     }else
0118     {
0119         qCDebug(PLUGIN_SVN) << "didn't accept";
0120         internalJob()->m_trustAnswer = svn::ContextListener::DONT_ACCEPT;
0121     }
0122     internalJob()->m_guiSemaphore.release( 1 );
0123 }
0124 
0125 void SvnJobBase::askForSslClientCert( const QString& realm )
0126 {
0127     KMessageBox::information( nullptr, realm );
0128     qCDebug(PLUGIN_SVN) << "clientrust";
0129     internalJob()->m_guiSemaphore.release( 1 );
0130 }
0131 
0132 void SvnJobBase::askForSslClientCertPassword( const QString& )
0133 {
0134     qCDebug(PLUGIN_SVN) << "clientpw";
0135     internalJob()->m_guiSemaphore.release( 1 );
0136 }
0137 
0138 void SvnJobBase::internalJobStarted()
0139 {
0140     qCDebug(PLUGIN_SVN)  << "job started" << static_cast<void*>(internalJob().data());
0141     m_status = KDevelop::VcsJob::JobRunning;
0142 }
0143 
0144 void SvnJobBase::internalJobDone()
0145 {
0146     qCDebug(PLUGIN_SVN) << "job done" << internalJob();
0147     if ( m_status == VcsJob::JobFailed ) {
0148         // see: https://bugs.kde.org/show_bug.cgi?id=273759
0149         // this gets also called when the internal job failed
0150         // then the emit result in internalJobFailed might trigger
0151         // a nested event loop (i.e. error dialog)
0152         // during that the internalJobDone gets called and triggers
0153         // deleteLater and eventually deletes this job
0154         // => havoc
0155         //
0156         // catching this state here works but I don't like it personally...
0157         return;
0158     }
0159 
0160     outputMessage(i18n("Completed"));
0161     if( m_status != VcsJob::JobCanceled ) {
0162         m_status = KDevelop::VcsJob::JobSucceeded;
0163     }
0164 
0165     emitResult();
0166 }
0167 
0168 void SvnJobBase::internalJobFailed()
0169 {
0170     qCDebug(PLUGIN_SVN) << "job failed" << internalJob();
0171 
0172     setError( 255 );
0173     QString msg = internalJob()->errorMessage();
0174     if( !msg.isEmpty() )
0175         setErrorText( i18n( "Error executing Job:\n%1", msg ) );
0176     outputMessage(errorText());
0177     qCDebug(PLUGIN_SVN) << "Job failed";
0178     if( m_status != VcsJob::JobCanceled )
0179     {
0180         m_status = KDevelop::VcsJob::JobFailed;
0181     }
0182 
0183     emitResult();
0184 }
0185 
0186 KDevelop::IPlugin* SvnJobBase::vcsPlugin() const
0187 {
0188     return m_part;
0189 }
0190 
0191 void SvnJobBase::outputMessage(const QString& message)
0192 {
0193     if (!model()) return;
0194     if (verbosity() == KDevelop::OutputJob::Silent) return;
0195 
0196     auto *m = qobject_cast<QStandardItemModel*>(model());
0197     QStandardItem *previous = m->item(m->rowCount()-1);
0198     if (message == QLatin1String(".") && previous && previous->text().contains(QRegExp(QStringLiteral("\\.+"))))
0199         previous->setText(previous->text() + message);
0200     else
0201         m->appendRow(new QStandardItem(message));
0202     KDevelop::IPlugin* i = KDevelop::ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IOutputView"));
0203     if( i )
0204     {
0205         auto* view = i->extension<KDevelop::IOutputView>();
0206         if( view )
0207         {
0208             view->raiseOutput( outputId() );
0209         }
0210     }
0211 }
0212 
0213 #include "moc_svnjobbase.cpp"