1 /***************************************************************************
2  *   Copyright (C) 2005-2009 by Rajko Albrecht  ral@alwins-world.de        *
3  *   http://kdesvn.alwins-world.de/                                        *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20 
21 #include "kdesvnd.h"
22 #include "kdesvn-config.h"
23 #include "kdesvnd_listener.h"
24 #include "ksvnwidgets/authdialogimpl.h"
25 #include "ksvnwidgets/ssltrustprompt.h"
26 #include "ksvnwidgets/commitmsg_impl.h"
27 #include "ksvnwidgets/pwstorage.h"
28 #include "helpers/kdesvn_debug.h"
29 
30 #include "settings/kdesvnsettings.h"
31 #include "svnqt/client.h"
32 #include "svnqt/revision.h"
33 #include "svnqt/status.h"
34 #include "svnqt/url.h"
35 #include "svnqt/svnqttypes.h"
36 #include "svnqt/client_parameter.h"
37 #include "helpers/ktranslateurl.h"
38 #include "helpers/stringhelper.h"
39 #include "kdesvndadaptor.h"
40 #include "ksvnjobview.h"
41 
42 #include <KIO/Global>
43 #include <KLocalizedString>
44 #include <KNotification>
45 #include <KPasswordDialog>
46 #include <KPluginFactory>
47 
48 #include <QFileDialog>
49 #include <QVariant>
50 #include <QDBusConnection>
51 #include <QApplication>
52 
53 K_PLUGIN_FACTORY_WITH_JSON(KdeSvndFactory,
54                            "kdesvnd.json",
55                            registerPlugin<kdesvnd>();
56                           )
57 
58 #define CHECK_KIO     if (!progressJobView.contains(kioid)) { \
59         return;\
60     }
61 
kdesvnd(QObject * parent,const QList<QVariant> &)62 kdesvnd::kdesvnd(QObject *parent, const QList<QVariant> &) : KDEDModule(parent),
63     m_uiserver(QStringLiteral("org.kde.JobViewServer"), QStringLiteral("/JobViewServer"), QDBusConnection::sessionBus())
64 {
65     m_Listener = new KdesvndListener(this);
66     new KdesvndAdaptor(this);
67 }
68 
~kdesvnd()69 kdesvnd::~kdesvnd()
70 {
71     delete m_Listener;
72 }
73 
getTopLevelActionMenu(const QStringList & urlList) const74 QStringList kdesvnd::getTopLevelActionMenu(const QStringList &urlList) const
75 {
76     // we get correct urls here
77     QList<QUrl> urls;
78     urls.reserve(urlList.size());
79     for (const QString &str : urlList) {
80         if (str.contains(QLatin1Char('@')))
81             urls += QUrl(str + QLatin1Char('@'));
82         else
83             urls += QUrl(str);
84     }
85 
86     return getActionMenu(urls, true);
87 }
88 
getActionMenu(const QStringList & urlList) const89 QStringList kdesvnd::getActionMenu(const QStringList &urlList) const
90 {
91     // we get correct urls here
92     QList<QUrl> urls;
93     urls.reserve(urlList.size());
94     for (const QString &str : urlList) {
95         if (str.contains(QLatin1Char('@')))
96             urls += QUrl(str + QLatin1Char('@'));
97         else
98             urls += QUrl(str);
99     }
100     return getActionMenu(urls, false);
101 }
102 
getActionMenu(const QList<QUrl> & list,bool toplevel) const103 QStringList kdesvnd::getActionMenu(const QList<QUrl> &list, bool toplevel) const
104 {
105     QStringList result;
106     Kdesvnsettings::self()->load();
107     if (Kdesvnsettings::no_konqueror_contextmenu() || list.isEmpty() ||
108             !list.at(0).isLocalFile() ||
109             (toplevel && Kdesvnsettings::no_konqueror_toplevelmenu())) {
110         return result;
111     }
112 
113     const bool itemIsWc = isWorkingCopy(list[0]);
114 
115     const QUrl _dir(list.at(0).adjusted(QUrl::RemoveFilename).adjusted(QUrl::StripTrailingSlash));
116     const bool parentIsWc = isWorkingCopy(_dir);
117 
118     bool itemIsRepository = false;
119     if (!parentIsWc && !itemIsWc) {
120         itemIsRepository = isRepository(list[0]);
121     }
122 
123     if (!itemIsWc) {
124         if (itemIsRepository) {
125             result << QStringLiteral("Export")
126                    << QStringLiteral("Checkout");
127         } else {
128             result << QStringLiteral("Exportto")
129                    << QStringLiteral("Checkoutto");
130         }
131     } else {
132         result << QStringLiteral("Update")
133                << QStringLiteral("Commit");
134     }
135 
136     if (!parentIsWc && !itemIsWc) {
137         if (itemIsRepository) {
138             result << QStringLiteral("Log");
139             if (!toplevel) {
140                 result << QStringLiteral("Info");
141                 const QUrl upUrl = KIO::upUrl(list.at(0));
142                 if (isRepository(upUrl)) {
143                     result << QStringLiteral("Blame")
144                            << QStringLiteral("Rename");
145                 }
146                 result << QStringLiteral("Tree");
147             }
148         }
149     } else if (!toplevel) {
150         if (!itemIsWc) {
151             result << QStringLiteral("Add");
152             return result;
153         }
154 
155         result << QStringLiteral("Log")
156                << QStringLiteral("Tree")
157                << QStringLiteral("Info")
158                << QStringLiteral("Diff")
159                << QStringLiteral("Rename")
160                << QStringLiteral("Revert");
161 
162         const QUrl url = list.at(0);
163         QFileInfo f(url.path());
164         if (f.isFile()) {
165             result << QStringLiteral("Blame");
166         }
167 
168         if (f.isDir()) {
169             result << QStringLiteral("Addnew");
170             result << QStringLiteral("Switch");
171         }
172     }
173     return result;
174 }
175 
getSingleActionMenu(const QString & what) const176 QStringList kdesvnd::getSingleActionMenu(const QString &what) const
177 {
178     QList<QUrl> l;
179     l.append(QUrl(what.contains(QLatin1Char('@')) ? what + QLatin1Char('@') : what));
180     return getActionMenu(l, false);
181 }
182 
get_saved_login(const QString & realm,const QString & user) const183 QStringList kdesvnd::get_saved_login(const QString &realm, const QString &user) const
184 {
185     Q_UNUSED(user);
186     QString username;
187     QString password;
188     PwStorage::self()->getLogin(realm, username, password);
189     QStringList res;
190     res.append(username);
191     res.append(password);
192     return res;
193 
194 }
195 
get_login(const QString & realm,const QString & user) const196 QStringList kdesvnd::get_login(const QString &realm, const QString &user) const
197 {
198     QPointer<AuthDialogImpl> auth(new AuthDialogImpl(realm, user));
199     QStringList res;
200     if (auth->exec() == QDialog::Accepted) {
201         res.append(auth->Username());
202         res.append(auth->Password());
203         if (auth->maySave()) {
204             res.append(QStringLiteral("true"));
205         } else {
206             res.append(QStringLiteral("false"));
207         }
208     }
209     delete auth;
210     return res;
211 }
212 
get_sslaccept(const QString & hostname,const QString & fingerprint,const QString & validFrom,const QString & validUntil,const QString & issuerDName,const QString & realm) const213 int kdesvnd::get_sslaccept(const QString &hostname,
214                            const QString &fingerprint,
215                            const QString &validFrom,
216                            const QString &validUntil,
217                            const QString &issuerDName,
218                            const QString &realm) const
219 {
220     bool ok, saveit;
221     if (!SslTrustPrompt::sslTrust(
222                 hostname,
223                 fingerprint,
224                 validFrom,
225                 validUntil,
226                 issuerDName,
227                 realm,
228                 QStringList(),
229                 &ok, &saveit)) {
230         return -1;
231     }
232     if (!saveit) {
233         return 0;
234     }
235     return 1;
236 }
237 
load_sslclientcertpw(const QString & realm) const238 QString kdesvnd::load_sslclientcertpw(const QString &realm) const
239 {
240     QString password;
241     if (!PwStorage::self()->getCertPw(realm, password)) {
242         return QString();
243     }
244     return password;
245 }
246 
get_sslclientcertpw(const QString & realm) const247 QStringList kdesvnd::get_sslclientcertpw(const QString &realm) const
248 {
249     QStringList resList;
250     QPointer<KPasswordDialog> dlg(new KPasswordDialog(nullptr, KPasswordDialog::DomainReadOnly | KPasswordDialog::ShowKeepPassword));
251     dlg->setDomain(realm);
252     dlg->setWindowTitle(i18nc("@title:window", "Enter Password for Realm %1", realm));
253     dlg->setKeepPassword(true);
254     if (dlg->exec() == KPasswordDialog::Accepted) {
255         resList.append(dlg->password());
256         if (dlg->keepPassword()) {
257             resList.append(QStringLiteral("true"));
258         } else {
259             resList.append(QStringLiteral("false"));
260         }
261     }
262     delete dlg;
263     return resList;
264 }
265 
get_sslclientcertfile() const266 QString kdesvnd::get_sslclientcertfile() const
267 {
268     return QFileDialog::getOpenFileName(nullptr, i18n("Open a file with a #PKCS12 certificate"));
269 }
270 
get_logmsg() const271 QStringList kdesvnd::get_logmsg() const
272 {
273     QStringList res;
274     bool ok;
275     QString logMessage = Commitmsg_impl::getLogmessage(&ok, nullptr, nullptr, nullptr);
276     if (ok) {
277         res.append(logMessage);
278     }
279     return res;
280 }
281 
cleanUrl(const QUrl & url)282 QString kdesvnd::cleanUrl(const QUrl &url)
283 {
284     return url.adjusted(QUrl::StripTrailingSlash|QUrl::NormalizePathSegments).path();
285 }
286 
287 /* just simple name check of course - no network access! */
isRepository(const QUrl & url) const288 bool kdesvnd::isRepository(const QUrl &url) const
289 {
290     QString proto = svn::Url::transformProtokoll(url.scheme());
291     if (proto == QLatin1String("file")) {
292         // local access - may a repository
293         svn::StatusParameter params(svn::Path(QLatin1String("file://") + cleanUrl(url)));
294         try {
295             m_Listener->m_Svnclient->status(params.depth(svn::DepthEmpty).all(false).update(false).noIgnore(false).revision(svn::Revision::HEAD));
296         } catch (const svn::ClientException &e) {
297             qCDebug(KDESVN_LOG) << e.msg() << endl;
298             return false;
299         }
300         return true;
301     }
302     return svn::Url::isValid(proto);
303 }
304 
isWorkingCopy(const QUrl & url) const305 bool kdesvnd::isWorkingCopy(const QUrl &url) const
306 {
307     if (url.isEmpty() || !url.isLocalFile() || url.scheme() != QLatin1String("file") || url.path() == QLatin1String("/")) {
308         return false;
309     }
310     svn::Revision peg(svn_opt_revision_unspecified);
311     svn::Revision rev(svn_opt_revision_unspecified);
312     svn::InfoEntries e;
313     try {
314         e = m_Listener->m_Svnclient->info(cleanUrl(url), svn::DepthEmpty, rev, peg);
315     } catch (const svn::ClientException &e) {
316         return false;
317     }
318     return true;
319 }
320 
canceldKioOperation(qulonglong kioid)321 bool kdesvnd::canceldKioOperation(qulonglong kioid)
322 {
323     if (!progressJobView.contains(kioid)) {
324         return false;
325     }
326     return progressJobView[kioid]->state() == KsvnJobView::CANCELD;
327 }
328 
maxTransferKioOperation(qulonglong kioid,qulonglong maxtransfer)329 void kdesvnd::maxTransferKioOperation(qulonglong kioid, qulonglong maxtransfer)
330 {
331     CHECK_KIO;
332     progressJobView[kioid]->setState(KsvnJobView::RUNNING);
333     progressJobView[kioid]->setTotal(maxtransfer);
334 }
335 
registerKioFeedback(qulonglong kioid)336 void kdesvnd::registerKioFeedback(qulonglong kioid)
337 {
338     if (progressJobView.contains(kioid)) {
339         return;
340     }
341     QDBusReply<QDBusObjectPath> reply = m_uiserver.requestView(qApp->applicationName(),
342                                                                qApp->applicationName(),
343                                                                0x0003);
344     if (reply.isValid()) {
345         KsvnJobView *jobView = new KsvnJobView(kioid, QStringLiteral("org.kde.JobViewServer"),
346                                                reply.value().path(),
347                                                QDBusConnection::sessionBus());
348         progressJobView.insert(kioid, jobView);
349         qCDebug(KDESVN_LOG) << "Register " << kioid << endl;
350     } else {
351         qCDebug(KDESVN_LOG) << "Could not register " << kioid << endl;
352     }
353 }
354 
titleKioOperation(qulonglong kioid,const QString & title,const QString & label)355 void kdesvnd::titleKioOperation(qulonglong kioid, const QString &title, const QString &label)
356 {
357     CHECK_KIO;
358     progressJobView[kioid]->setInfoMessage(title);
359     progressJobView[kioid]->setDescriptionField(0, i18n("Current task"), label);
360 }
361 
transferredKioOperation(qulonglong kioid,qulonglong transferred)362 void kdesvnd::transferredKioOperation(qulonglong kioid, qulonglong transferred)
363 {
364     CHECK_KIO;
365     if (progressJobView[kioid]->max() > -1) {
366         progressJobView[kioid]->setProcessedAmount(transferred, QStringLiteral("bytes"));
367         progressJobView[kioid]->setPercent(progressJobView[kioid]->percent(transferred));
368         progressJobView[kioid]->clearDescriptionField(1);
369     } else {
370         progressJobView[kioid]->setPercent(100.0);
371         progressJobView[kioid]->setDescriptionField(1, i18n("Current transfer"), helpers::ByteToString(transferred));
372     }
373 }
374 
unRegisterKioFeedback(qulonglong kioid)375 void kdesvnd::unRegisterKioFeedback(qulonglong kioid)
376 {
377     CHECK_KIO;
378     KsvnJobView *jobView = progressJobView.take(kioid);
379     delete jobView;
380     qCDebug(KDESVN_LOG) << "Removed " << kioid << endl;
381 }
382 
notifyKioOperation(const QString & text)383 void kdesvnd::notifyKioOperation(const QString &text)
384 {
385     KNotification::event(
386         QLatin1String("kdesvn-kio"), text,
387         QPixmap(), nullptr, KNotification::CloseOnTimeout,
388         QLatin1String("kdesvn"));
389 }
390 
errorKioOperation(const QString & text)391 void kdesvnd::errorKioOperation(const QString &text)
392 {
393     KNotification::event(
394         KNotification::Error, text,
395         QPixmap(), nullptr, KNotification::CloseOnTimeout
396     );
397 }
398 
setKioStatus(qulonglong kioid,int status,const QString & message)399 void kdesvnd::setKioStatus(qulonglong kioid, int status, const QString &message)
400 {
401     CHECK_KIO;
402     switch (status) {
403     case 0:
404         progressJobView[kioid]->setState(KsvnJobView::STOPPED);
405         progressJobView[kioid]->terminate(message);
406         break;
407     case 2:
408         progressJobView[kioid]->setState(KsvnJobView::CANCELD);
409         progressJobView[kioid]->terminate(message);
410         break;
411     case 1:
412         progressJobView[kioid]->setState(KsvnJobView::RUNNING);
413         progressJobView[kioid]->setSuspended(false);
414         break;
415     }
416 }
417 
418 #include "kdesvnd.moc"
419