1 /*
2 SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "svnjobbase.h"
8
9 #include <QStandardItemModel>
10
11 #include <KPasswordDialog>
12 #include <KLocalizedString>
13 #include <KMessageBox>
14
15 #include <ThreadWeaver/QObjectDecorator>
16
17 #include <interfaces/icore.h>
18 #include <interfaces/iplugincontroller.h>
19 #include <interfaces/iplugin.h>
20 #include <outputview/ioutputview.h>
21
22 #include "svninternaljobbase.h"
23 #include "svnssldialog.h"
24
SvnJobBase(KDevSvnPlugin * parent,KDevelop::OutputJob::OutputJobVerbosity verbosity)25 SvnJobBase::SvnJobBase( KDevSvnPlugin* parent, KDevelop::OutputJob::OutputJobVerbosity verbosity )
26 : VcsJob( parent, verbosity ), m_part( parent ),
27 m_status( KDevelop::VcsJob::JobNotStarted )
28 {
29 setCapabilities( KJob::Killable );
30 setTitle( QStringLiteral("Subversion") );
31 }
32
~SvnJobBase()33 SvnJobBase::~SvnJobBase()
34 {
35 }
36
startInternalJob()37 void SvnJobBase::startInternalJob()
38 {
39 auto job = internalJob();
40 connect( job.data(), &SvnInternalJobBase::failed,
41 this, &SvnJobBase::internalJobFailed, Qt::QueuedConnection );
42 connect( job.data(), &SvnInternalJobBase::done,
43 this, &SvnJobBase::internalJobDone, Qt::QueuedConnection );
44 connect( job.data(), &SvnInternalJobBase::started,
45 this, &SvnJobBase::internalJobStarted, Qt::QueuedConnection );
46 // add as shared pointer
47 // the signals "done" & "failed" are emitted when the queue and the executor still
48 // have and use a reference to the job, in the execution thread.
49 // As the this parent job will be deleted in the main/other thread
50 // (due to deleteLater() being called on it in the KJob::exec())
51 // and the ThreadWeaver queue will release the last reference to the passed
52 // JobInterface pointer only after the JobInterface::execute() method has been left,
53 // the internal threaded job thus needs to get shared memory management via the QSharedPointer.
54 m_part->jobQueue()->stream() << job;
55 }
56
doKill()57 bool SvnJobBase::doKill()
58 {
59 internalJob()->kill();
60 m_status = VcsJob::JobCanceled;
61 return true;
62 }
63
64
status() const65 KDevelop::VcsJob::JobStatus SvnJobBase::status() const
66 {
67 return m_status;
68 }
69
askForLogin(const QString & realm)70 void SvnJobBase::askForLogin( const QString& realm )
71 {
72 qCDebug(PLUGIN_SVN) << "login";
73 KPasswordDialog dlg( nullptr, KPasswordDialog::ShowUsernameLine | KPasswordDialog::ShowKeepPassword );
74 dlg.setPrompt( i18n("Enter Login for: %1", realm ) );
75 if (dlg.exec()) { // krazy:exclude=crashy
76 internalJob()->m_login_username = dlg.username();
77 internalJob()->m_login_password = dlg.password();
78 internalJob()->m_maySave = dlg.keepPassword();
79 } else {
80 internalJob()->m_login_username.clear();
81 internalJob()->m_login_password.clear();
82 }
83 internalJob()->m_guiSemaphore.release( 1 );
84 }
85
showNotification(const QString & path,const QString & msg)86 void SvnJobBase::showNotification( const QString& path, const QString& msg )
87 {
88 Q_UNUSED(path);
89 outputMessage(msg);
90 }
91
askForCommitMessage()92 void SvnJobBase::askForCommitMessage()
93 {
94 qCDebug(PLUGIN_SVN) << "commit msg";
95 internalJob()->m_guiSemaphore.release( 1 );
96 }
97
askForSslServerTrust(const QStringList & failures,const QString & host,const QString & print,const QString & from,const QString & until,const QString & issuer,const QString & realm)98 void SvnJobBase::askForSslServerTrust( const QStringList& failures, const QString& host,
99 const QString& print, const QString& from,
100 const QString& until, const QString& issuer,
101 const QString& realm )
102 {
103
104 qCDebug(PLUGIN_SVN) << "servertrust";
105 SvnSSLTrustDialog dlg;
106 dlg.setCertInfos( host, print, from, until, issuer, realm, failures );
107 if( dlg.exec() == QDialog::Accepted )
108 {
109 qCDebug(PLUGIN_SVN) << "accepted with:" << dlg.useTemporarily();
110 if( dlg.useTemporarily() )
111 {
112 internalJob()->m_trustAnswer = svn::ContextListener::ACCEPT_TEMPORARILY;
113 }else
114 {
115 internalJob()->m_trustAnswer = svn::ContextListener::ACCEPT_PERMANENTLY;
116 }
117 }else
118 {
119 qCDebug(PLUGIN_SVN) << "didn't accept";
120 internalJob()->m_trustAnswer = svn::ContextListener::DONT_ACCEPT;
121 }
122 internalJob()->m_guiSemaphore.release( 1 );
123 }
124
askForSslClientCert(const QString & realm)125 void SvnJobBase::askForSslClientCert( const QString& realm )
126 {
127 KMessageBox::information( nullptr, realm );
128 qCDebug(PLUGIN_SVN) << "clientrust";
129 internalJob()->m_guiSemaphore.release( 1 );
130 }
131
askForSslClientCertPassword(const QString &)132 void SvnJobBase::askForSslClientCertPassword( const QString& )
133 {
134 qCDebug(PLUGIN_SVN) << "clientpw";
135 internalJob()->m_guiSemaphore.release( 1 );
136 }
137
internalJobStarted()138 void SvnJobBase::internalJobStarted()
139 {
140 qCDebug(PLUGIN_SVN) << "job started" << static_cast<void*>(internalJob().data());
141 m_status = KDevelop::VcsJob::JobRunning;
142 }
143
internalJobDone()144 void SvnJobBase::internalJobDone()
145 {
146 qCDebug(PLUGIN_SVN) << "job done" << internalJob();
147 if ( m_status == VcsJob::JobFailed ) {
148 // see: https://bugs.kde.org/show_bug.cgi?id=273759
149 // this gets also called when the internal job failed
150 // then the emit result in internalJobFailed might trigger
151 // a nested event loop (i.e. error dialog)
152 // during that the internalJobDone gets called and triggers
153 // deleteLater and eventually deletes this job
154 // => havoc
155 //
156 // catching this state here works but I don't like it personally...
157 return;
158 }
159
160 outputMessage(i18n("Completed"));
161 if( m_status != VcsJob::JobCanceled ) {
162 m_status = KDevelop::VcsJob::JobSucceeded;
163 }
164
165 emitResult();
166 }
167
internalJobFailed()168 void SvnJobBase::internalJobFailed()
169 {
170 qCDebug(PLUGIN_SVN) << "job failed" << internalJob();
171
172 setError( 255 );
173 QString msg = internalJob()->errorMessage();
174 if( !msg.isEmpty() )
175 setErrorText( i18n( "Error executing Job:\n%1", msg ) );
176 outputMessage(errorText());
177 qCDebug(PLUGIN_SVN) << "Job failed";
178 if( m_status != VcsJob::JobCanceled )
179 {
180 m_status = KDevelop::VcsJob::JobFailed;
181 }
182
183 emitResult();
184 }
185
vcsPlugin() const186 KDevelop::IPlugin* SvnJobBase::vcsPlugin() const
187 {
188 return m_part;
189 }
190
outputMessage(const QString & message)191 void SvnJobBase::outputMessage(const QString& message)
192 {
193 if (!model()) return;
194 if (verbosity() == KDevelop::OutputJob::Silent) return;
195
196 auto *m = qobject_cast<QStandardItemModel*>(model());
197 QStandardItem *previous = m->item(m->rowCount()-1);
198 if (message == QLatin1String(".") && previous && previous->text().contains(QRegExp(QStringLiteral("\\.+"))))
199 previous->setText(previous->text() + message);
200 else
201 m->appendRow(new QStandardItem(message));
202 KDevelop::IPlugin* i = KDevelop::ICore::self()->pluginController()->pluginForExtension(QStringLiteral("org.kdevelop.IOutputView"));
203 if( i )
204 {
205 auto* view = i->extension<KDevelop::IOutputView>();
206 if( view )
207 {
208 view->raiseOutput( outputId() );
209 }
210 }
211 }
212
213
214