File indexing completed on 2024-05-05 05:44:49
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 "kiosvn.h" 0021 #include "kdesvn-config.h" 0022 #include "kiolistener.h" 0023 0024 #include <QFile> 0025 #include <QTemporaryDir> 0026 #include <QTemporaryFile> 0027 0028 #include "helpers/kdesvn_debug.h" 0029 #include "helpers/sshagent.h" 0030 #include "helpers/stringhelper.h" 0031 #include "kdesvndinterface.h" 0032 #include "kio_macros.h" 0033 #include "settings/kdesvnsettings.h" 0034 #include "svnqt/client_commit_parameter.h" 0035 #include "svnqt/client_parameter.h" 0036 #include "svnqt/client_update_parameter.h" 0037 #include "svnqt/dirent.h" 0038 #include "svnqt/info_entry.h" 0039 #include "svnqt/status.h" 0040 #include "svnqt/svnqttypes.h" 0041 #include "svnqt/targets.h" 0042 #include "svnqt/url.h" 0043 0044 #include <kio_version.h> 0045 #include <klocalizedstring.h> 0046 #include <math.h> 0047 #include <netdb.h> 0048 #include <netinet/in.h> 0049 #include <stdlib.h> 0050 #include <sys/socket.h> 0051 #include <unistd.h> 0052 0053 namespace KIO 0054 { 0055 0056 class KioSvnData 0057 { 0058 public: 0059 explicit KioSvnData(kio_svnProtocol *); 0060 ~KioSvnData(); 0061 0062 void reInitClient(); 0063 void resetListener(); 0064 0065 KioListener m_Listener; 0066 bool first_done; 0067 bool dispProgress; 0068 bool dispWritten; 0069 svn::ContextP m_CurrentContext; 0070 svn::ClientP m_Svnclient; 0071 svn::Revision urlToRev(const QUrl &); 0072 QTime m_last; 0073 qulonglong m_Id; 0074 }; 0075 0076 KioSvnData::KioSvnData(kio_svnProtocol *par) 0077 : m_Listener(par) 0078 , first_done(false) 0079 , dispProgress(false) 0080 , dispWritten(false) 0081 , m_Svnclient(svn::Client::getobject(svn::ContextP())) 0082 , m_last(QTime::currentTime()) 0083 , m_Id(0) // null is an invalid id 0084 { 0085 reInitClient(); 0086 } 0087 0088 void KioSvnData::reInitClient() 0089 { 0090 if (first_done) { 0091 return; 0092 } 0093 SshAgent ag; 0094 ag.querySshAgent(); 0095 0096 first_done = true; 0097 m_CurrentContext = svn::ContextP(new svn::Context); 0098 m_CurrentContext->setListener(&m_Listener); 0099 m_Svnclient->setContext(m_CurrentContext); 0100 } 0101 0102 void KioSvnData::resetListener() 0103 { 0104 if (!first_done) { 0105 reInitClient(); 0106 } 0107 m_Listener.uncancel(); 0108 } 0109 0110 KioSvnData::~KioSvnData() 0111 { 0112 m_Listener.setCancel(true); 0113 /* wait a little bit */ 0114 sleep(1); 0115 m_CurrentContext->setListener(nullptr); 0116 } 0117 0118 svn::Revision KioSvnData::urlToRev(const QUrl &url) 0119 { 0120 const QList<QPair<QString, QString>> q = QUrlQuery(url).queryItems(); 0121 0122 /* we try to check if it is ssh and try to get a password for it */ 0123 const QString proto = url.scheme(); 0124 0125 if (proto.contains(QLatin1String("ssh"))) { 0126 SshAgent ag; 0127 ag.addSshIdentities(); 0128 } 0129 0130 svn::Revision rev = svn::Revision::UNDEFINED; 0131 typedef QPair<QString, QString> myStrPair; 0132 for (const myStrPair &p : q) { 0133 if (p.first == QLatin1String("rev")) { 0134 const QString v = p.second; 0135 svn::Revision tmp; 0136 m_Svnclient->url2Revision(v, rev, tmp); 0137 } 0138 } 0139 return rev; 0140 } 0141 0142 kio_svnProtocol::kio_svnProtocol(const QByteArray &pool_socket, const QByteArray &app_socket) 0143 : WorkerBase("kio_ksvn", pool_socket, app_socket) 0144 , StreamWrittenCb() 0145 { 0146 m_pData = new KioSvnData(this); 0147 m_pData->m_Id = reinterpret_cast<qulonglong>(this); 0148 } 0149 0150 kio_svnProtocol::~kio_svnProtocol() 0151 { 0152 unregisterFromDaemon(); 0153 delete m_pData; 0154 } 0155 0156 } 0157 0158 extern "C" { 0159 Q_DECL_EXPORT int kdemain(int argc, char **argv); 0160 } 0161 0162 int kdemain(int argc, char **argv) 0163 { 0164 QCoreApplication app(argc, argv); 0165 QCoreApplication::setApplicationName(QLatin1String("kio_ksvn")); 0166 qCDebug(KDESVN_LOG) << "*** Starting kio_ksvn " << Qt::endl; 0167 0168 if (argc != 4) { 0169 qCDebug(KDESVN_LOG) << "Usage: kio_ksvn protocol domain-socket1 domain-socket2" << Qt::endl; 0170 exit(-1); 0171 } 0172 KIO::kio_svnProtocol worker(argv[2], argv[3]); 0173 worker.dispatchLoop(); 0174 qCDebug(KDESVN_LOG) << "*** kio_ksvn Done" << Qt::endl; 0175 return 0; 0176 } 0177 0178 namespace KIO 0179 { 0180 0181 void kio_svnProtocol::listSendDirEntry(const svn::DirEntry &direntry) 0182 { 0183 const QDateTime dt(direntry.time().toQDateTime()); 0184 KIO::UDSEntry entry; 0185 if (direntry.name().isEmpty()) { 0186 qCDebug(KDESVN_LOG) << "Skipping empty entry!" << Qt::endl; 0187 return; 0188 } 0189 listEntry(createUDSEntry(direntry.name(), direntry.lastAuthor(), direntry.size(), direntry.kind() == svn_node_dir ? true : false, dt)); 0190 } 0191 0192 /*! 0193 \fn kio_svnProtocol::listDir (const QUrl&url) 0194 */ 0195 KIO::WorkerResult kio_svnProtocol::listDir(const QUrl &url) 0196 { 0197 qCDebug(KDESVN_LOG) << "kio_svn::listDir(const QUrl& url) : " << url.url() << Qt::endl; 0198 m_pData->resetListener(); 0199 svn::DirEntries dlist; 0200 svn::Revision rev = m_pData->urlToRev(url); 0201 if (rev == svn::Revision::UNDEFINED) { 0202 rev = svn::Revision::HEAD; 0203 } 0204 0205 try { 0206 // we ignoring the result 'cause it is done via kiolistener for a smoother insert of items. 0207 dlist = m_pData->m_Svnclient->list(makeSvnPath(url), rev, rev, svn::DepthImmediates, false); 0208 } catch (const svn::ClientException &e) { 0209 QString ex = e.msg(); 0210 qCDebug(KDESVN_LOG) << ex << Qt::endl; 0211 return extraError(KIO::ERR_WORKER_DEFINED, ex); 0212 } 0213 qCDebug(KDESVN_LOG) << "Listing finished" << Qt::endl; 0214 return KIO::WorkerResult::pass(); 0215 } 0216 0217 KIO::WorkerResult kio_svnProtocol::stat(const QUrl &url) 0218 { 0219 qCDebug(KDESVN_LOG) << "kio_svn::stat " << url << Qt::endl; 0220 m_pData->resetListener(); 0221 svn::Revision rev = m_pData->urlToRev(url); 0222 if (rev == svn::Revision::UNDEFINED) { 0223 rev = svn::Revision::HEAD; 0224 } 0225 svn::Revision peg = rev; 0226 bool dummy = false; 0227 svn::InfoEntries e; 0228 try { 0229 e = m_pData->m_Svnclient->info(makeSvnPath(url), svn::DepthEmpty, rev, peg); 0230 } catch (const svn::ClientException &e) { 0231 QString ex = e.msg(); 0232 qCDebug(KDESVN_LOG) << ex << Qt::endl; 0233 return extraError(KIO::ERR_WORKER_DEFINED, ex); 0234 } 0235 0236 if (e.isEmpty()) { 0237 dummy = true; 0238 } 0239 0240 KIO::UDSEntry entry; 0241 if (dummy) { 0242 entry = createUDSEntry(url.fileName(), QString(), 0, true, QDateTime()); 0243 } else { 0244 const QDateTime dt(e[0].cmtDate().toQDateTime()); 0245 if (e[0].kind() == svn_node_file) { 0246 entry = createUDSEntry(url.fileName(), QString(), 0, false, dt); 0247 } else { 0248 entry = createUDSEntry(url.fileName(), QString(), 0, true, dt); 0249 } 0250 } 0251 statEntry(entry); 0252 return KIO::WorkerResult::pass(); 0253 } 0254 0255 KIO::WorkerResult kio_svnProtocol::get(const QUrl &url) 0256 { 0257 if (m_pData->m_Listener.contextCancel()) { 0258 return KIO::WorkerResult::pass(); 0259 } 0260 svn::Revision rev = m_pData->urlToRev(url); 0261 if (rev == svn::Revision::UNDEFINED) { 0262 rev = svn::Revision::HEAD; 0263 } 0264 KioByteStream dstream(this, url.fileName()); 0265 try { 0266 const svn::Path path = makeSvnPath(url); 0267 svn::InfoEntries e; 0268 e = m_pData->m_Svnclient->info(path, svn::DepthEmpty, rev, rev); 0269 if (!e.isEmpty()) { 0270 totalSize(e.at(0).size()); 0271 } 0272 m_pData->m_Svnclient->cat(dstream, path, rev, rev); 0273 } catch (const svn::ClientException &e) { 0274 QString ex = e.msg(); 0275 // dolphin / Konqueror try to get the content without check if it is a folder when listing a folder 0276 // which results in a lot of error messages via kio notify 0277 if (e.apr_err() != SVN_ERR_CLIENT_IS_DIRECTORY) { 0278 return extraError(KIO::ERR_WORKER_DEFINED, QStringLiteral("Subversion error ") + ex); 0279 } 0280 return KIO::WorkerResult::pass(); 0281 } 0282 data(QByteArray()); // empty array means we're done sending the data 0283 return KIO::WorkerResult::pass(); 0284 } 0285 0286 KIO::WorkerResult kio_svnProtocol::mkdir(const QUrl &url, int) 0287 { 0288 qCDebug(KDESVN_LOG) << "kio_svn::mkdir " << url << Qt::endl; 0289 m_pData->resetListener(); 0290 svn::Revision rev = m_pData->urlToRev(url); 0291 if (rev == svn::Revision::UNDEFINED) { 0292 rev = svn::Revision::HEAD; 0293 } 0294 if (rev != svn::Revision::HEAD) { 0295 return extraError(KIO::ERR_WORKER_DEFINED, i18n("Can only write on HEAD revision.")); 0296 } 0297 m_pData->m_CurrentContext->setLogMessage(getDefaultLog()); 0298 try { 0299 m_pData->m_Svnclient->mkdir(makeSvnPath(url), getDefaultLog()); 0300 } catch (const svn::ClientException &e) { 0301 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0302 } 0303 return KIO::WorkerResult::pass(); 0304 } 0305 0306 KIO::WorkerResult kio_svnProtocol::mkdir(const QList<QUrl> &urls, int) 0307 { 0308 try { 0309 m_pData->m_Svnclient->mkdir(svn::Targets::fromUrlList(urls, svn::Targets::UrlConversion::PreferLocalPath), getDefaultLog()); 0310 } catch (const svn::ClientException &e) { 0311 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0312 } 0313 return KIO::WorkerResult::pass(); 0314 } 0315 0316 KIO::WorkerResult kio_svnProtocol::rename(const QUrl &src, const QUrl &target, KIO::JobFlags flags) 0317 { 0318 qCDebug(KDESVN_LOG) << "kio_svn::rename " << src << " to " << target << Qt::endl; 0319 m_pData->resetListener(); 0320 Q_UNUSED(flags); 0321 // bool force = flags&KIO::Overwrite; 0322 m_pData->m_CurrentContext->setLogMessage(getDefaultLog()); 0323 try { 0324 m_pData->m_Svnclient->move(svn::CopyParameter(makeSvnPath(src), makeSvnPath(target))); 0325 } catch (const svn::ClientException &e) { 0326 if (e.apr_err() == SVN_ERR_ENTRY_EXISTS) { 0327 return KIO::WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, e.msg()); 0328 } 0329 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0330 } 0331 notify(i18n("Renaming %1 to %2 successful", src.toDisplayString(), target.toDisplayString())); 0332 return KIO::WorkerResult::pass(); 0333 } 0334 0335 KIO::WorkerResult kio_svnProtocol::put(const QUrl &url, int permissions, KIO::JobFlags flags) 0336 { 0337 Q_UNUSED(permissions); 0338 m_pData->resetListener(); 0339 svn::Revision rev = m_pData->urlToRev(url); 0340 if (rev == svn::Revision::UNDEFINED) { 0341 rev = svn::Revision::HEAD; 0342 } 0343 if (rev != svn::Revision::HEAD) { 0344 return extraError(KIO::ERR_WORKER_DEFINED, i18n("Can only write on HEAD revision.")); 0345 } 0346 svn::Revision peg = rev; 0347 svn::InfoEntries e; 0348 bool exists = true; 0349 try { 0350 e = m_pData->m_Svnclient->info(makeSvnPath(url), svn::DepthEmpty, rev, peg); 0351 } catch (const svn::ClientException &e) { 0352 if (e.apr_err() == SVN_ERR_ENTRY_NOT_FOUND || e.apr_err() == SVN_ERR_RA_ILLEGAL_URL) { 0353 exists = false; 0354 } else { 0355 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0356 } 0357 } 0358 QSharedPointer<QFile> tmpfile; 0359 QSharedPointer<QTemporaryDir> _codir; 0360 if (exists) { 0361 if (flags & KIO::Overwrite) { 0362 if (!supportOverwrite()) { 0363 return extraError(KIO::ERR_WORKER_DEFINED, i18n("Overwriting existing items is disabled in settings.")); 0364 } 0365 _codir = QSharedPointer<QTemporaryDir>(new QTemporaryDir); 0366 _codir->setAutoRemove(true); 0367 svn::Path path = makeSvnPath(url); 0368 path.removeLast(); 0369 try { 0370 notify(i18n("Start checking out to temporary folder")); 0371 m_pData->dispWritten = true; 0372 registerToDaemon(); 0373 startOp(-1, i18n("Checking out %1", path.native())); 0374 svn::CheckoutParameter params; 0375 params.moduleName(path).destination(svn::Path(_codir->path())).revision(rev).peg(peg).depth(svn::DepthFiles); 0376 m_pData->m_Svnclient->checkout(params); 0377 } catch (const svn::ClientException &e) { 0378 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0379 } 0380 m_pData->dispWritten = false; 0381 stopOp(i18n("Temporary checkout done.")); 0382 tmpfile = QSharedPointer<QFile>(new QFile(_codir->path() + url.fileName())); 0383 tmpfile->open(QIODevice::ReadWrite | QIODevice::Truncate); 0384 } else { 0385 return extraError(KIO::ERR_FILE_ALREADY_EXIST, i18n("Could not write to existing item.")); 0386 } 0387 } else { 0388 QTemporaryFile *_tmpfile = new QTemporaryFile(); 0389 if (!_tmpfile->open()) { 0390 delete _tmpfile; 0391 return extraError(KIO::ERR_WORKER_DEFINED, i18n("Could not open temporary file")); 0392 } 0393 tmpfile = QSharedPointer<QFile>(_tmpfile); 0394 } 0395 int result = 0; 0396 QByteArray buffer; 0397 KIO::fileoffset_t processed_size = 0; 0398 do { 0399 dataReq(); 0400 result = readData(buffer); 0401 if (result > 0) { 0402 tmpfile->write(buffer); 0403 processed_size += result; 0404 processedSize(processed_size); 0405 } 0406 buffer.clear(); 0407 } while (result > 0); 0408 0409 tmpfile->flush(); 0410 0411 if (result != 0) { 0412 return KIO::WorkerResult::fail(KIO::ERR_ABORTED, i18n("Could not retrieve data for write.")); 0413 } 0414 0415 totalSize(processed_size); 0416 written(0); 0417 m_pData->dispWritten = true; 0418 registerToDaemon(); 0419 startOp(processed_size, i18n("Committing %1", makeSvnPath(url).path())); 0420 if (exists) { 0421 svn::CommitParameter commit_parameters; 0422 commit_parameters.targets(svn::Targets(tmpfile->fileName())).message(getDefaultLog()).depth(svn::DepthEmpty).keepLocks(false); 0423 try { 0424 m_pData->m_Svnclient->commit(commit_parameters); 0425 } catch (const svn::ClientException &e) { 0426 m_pData->dispWritten = false; 0427 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0428 } 0429 } else { 0430 try { 0431 m_pData->m_Svnclient->import(tmpfile->fileName(), svn::Url(makeSvnPath(url)), getDefaultLog(), svn::DepthEmpty, false, false); 0432 } catch (const svn::ClientException &e) { 0433 QString ex = e.msg(); 0434 stopOp(ex); 0435 m_pData->dispWritten = false; 0436 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0437 } 0438 } 0439 m_pData->dispWritten = false; 0440 0441 stopOp(i18n("Wrote %1 to repository", helpers::ByteToString(processed_size))); 0442 return KIO::WorkerResult::pass(); 0443 } 0444 0445 KIO::WorkerResult kio_svnProtocol::copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) 0446 { 0447 Q_UNUSED(permissions); 0448 Q_UNUSED(flags); 0449 // bool force = flags&KIO::Overwrite; 0450 m_pData->resetListener(); 0451 qCDebug(KDESVN_LOG) << "kio_svn::copy " << src << " to " << dest << Qt::endl; 0452 svn::Revision rev = m_pData->urlToRev(src); 0453 if (rev == svn::Revision::UNDEFINED) { 0454 rev = svn::Revision::HEAD; 0455 } 0456 m_pData->dispProgress = true; 0457 m_pData->m_CurrentContext->setLogMessage(getDefaultLog()); 0458 try { 0459 m_pData->m_Svnclient->copy(makeSvnPath(src), rev, makeSvnPath(dest)); 0460 } catch (const svn::ClientException &e) { 0461 qCDebug(KDESVN_LOG) << "kio_svn::copy aborted" << Qt::endl; 0462 if (e.apr_err() == SVN_ERR_ENTRY_EXISTS) { 0463 return KIO::WorkerResult::fail(KIO::ERR_DIR_ALREADY_EXIST, e.msg()); 0464 } 0465 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0466 } 0467 m_pData->dispProgress = false; 0468 qCDebug(KDESVN_LOG) << "kio_svn::copy finished" << Qt::endl; 0469 notify(i18n("Copied %1 to %2", makeSvnPath(src).path(), makeSvnPath(dest).path())); 0470 return KIO::WorkerResult::pass(); 0471 } 0472 0473 KIO::WorkerResult kio_svnProtocol::del(const QUrl &src, bool isfile) 0474 { 0475 Q_UNUSED(isfile); 0476 m_pData->resetListener(); 0477 qCDebug(KDESVN_LOG) << "kio_svn::del " << src << Qt::endl; 0478 // m_pData->reInitClient(); 0479 svn::Revision rev = m_pData->urlToRev(src); 0480 if (rev == svn::Revision::UNDEFINED) { 0481 rev = svn::Revision::HEAD; 0482 } 0483 if (rev != svn::Revision::HEAD) { 0484 return extraError(KIO::ERR_WORKER_DEFINED, i18n("Can only write on HEAD revision.")); 0485 } 0486 m_pData->m_CurrentContext->setLogMessage(getDefaultLog()); 0487 try { 0488 svn::Targets target(makeSvnPath(src)); 0489 m_pData->m_Svnclient->remove(target, false); 0490 } catch (const svn::ClientException &e) { 0491 qCDebug(KDESVN_LOG) << "kio_svn::del aborted" << Qt::endl; 0492 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0493 } 0494 qCDebug(KDESVN_LOG) << "kio_svn::del finished" << Qt::endl; 0495 return KIO::WorkerResult::pass(); 0496 } 0497 0498 bool kio_svnProtocol::getLogMsg(QString &t) 0499 { 0500 svn::CommitItemList _items; 0501 return m_pData->m_Listener.contextGetLogMessage(t, _items); 0502 } 0503 0504 bool kio_svnProtocol::checkWc(const svn::Path &localPath) const 0505 { 0506 m_pData->resetListener(); 0507 if (!localPath.isSet()) { 0508 return false; 0509 } 0510 svn::Revision peg(svn_opt_revision_unspecified); 0511 svn::Revision rev(svn_opt_revision_unspecified); 0512 svn::InfoEntries e; 0513 try { 0514 e = m_pData->m_Svnclient->info(localPath, svn::DepthEmpty, rev, peg); 0515 } catch (const svn::ClientException &e) { 0516 if (SVN_ERR_WC_NOT_DIRECTORY == e.apr_err()) { 0517 return false; 0518 } 0519 return true; 0520 } 0521 return false; 0522 } 0523 0524 svn::Path kio_svnProtocol::makeSvnPath(const QUrl &url) const 0525 { 0526 const QString scheme = svn::Url::transformProtokoll(url.scheme()); 0527 if (scheme == QLatin1String("file")) { 0528 const svn::Path path(url.toLocalFile()); 0529 if (checkWc(path)) { 0530 return path; 0531 } 0532 } 0533 if (url.path().isEmpty()) { 0534 throw svn::ClientException(QLatin1Char('\'') + url.url() + QLatin1String("' is not a valid subversion url")); 0535 } 0536 0537 QUrl tmpUrl(url); 0538 tmpUrl.setScheme(scheme); 0539 tmpUrl.setQuery(QString()); // svn doesn't know anything about queries (e.g ?rev=X) 0540 0541 return svn::Path(tmpUrl.toString(QUrl::NormalizePathSegments)); 0542 } 0543 0544 KIO::UDSEntry kio_svnProtocol::createUDSEntry(const QString &filename, const QString &user, long long int size, bool isdir, const QDateTime &mtime) 0545 { 0546 KIO::UDSEntry entry; 0547 entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); 0548 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, isdir ? S_IFDIR : S_IFREG); 0549 entry.fastInsert(KIO::UDSEntry::UDS_SIZE, size); 0550 entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, mtime.toSecsSinceEpoch()); 0551 entry.fastInsert(KIO::UDSEntry::UDS_USER, user); 0552 return entry; 0553 } 0554 0555 KIO::WorkerResult kio_svnProtocol::special(const QByteArray &data) 0556 { 0557 qCDebug(KDESVN_LOG) << "kio_svnProtocol::special" << Qt::endl; 0558 QByteArray tmpData(data); 0559 QDataStream stream(&tmpData, QIODevice::ReadOnly); 0560 m_pData->resetListener(); 0561 int tmp; 0562 stream >> tmp; 0563 qCDebug(KDESVN_LOG) << "kio_svnProtocol::special " << tmp << Qt::endl; 0564 switch (tmp) { 0565 case SVN_CHECKOUT: { 0566 QUrl repository, wc; 0567 int revnumber; 0568 QString revkind; 0569 stream >> repository; 0570 stream >> wc; 0571 stream >> revnumber; 0572 stream >> revkind; 0573 qCDebug(KDESVN_LOG) << "kio_svnProtocol CHECKOUT from " << repository.url() << " to " << wc.url() << " at " << revnumber << " or " << revkind 0574 << Qt::endl; 0575 return checkout(repository, wc, revnumber, revkind); 0576 } 0577 case SVN_UPDATE: { 0578 QUrl wc; 0579 int revnumber; 0580 QString revkind; 0581 stream >> wc; 0582 stream >> revnumber; 0583 stream >> revkind; 0584 qCDebug(KDESVN_LOG) << "kio_svnProtocol UPDATE " << wc.url() << " at " << revnumber << " or " << revkind << Qt::endl; 0585 return update(wc, revnumber, revkind); 0586 } 0587 case SVN_COMMIT: { 0588 QList<QUrl> wclist; 0589 while (!stream.atEnd()) { 0590 QUrl tmp; 0591 stream >> tmp; 0592 wclist << tmp; 0593 } 0594 qCDebug(KDESVN_LOG) << "kio_svnProtocol COMMIT" << Qt::endl; 0595 return commit(wclist); 0596 } 0597 case SVN_LOG: { 0598 qCDebug(KDESVN_LOG) << "kio_svnProtocol LOG" << Qt::endl; 0599 int revstart, revend; 0600 QString revkindstart, revkindend; 0601 QList<QUrl> targets; 0602 stream >> revstart; 0603 stream >> revkindstart; 0604 stream >> revend; 0605 stream >> revkindend; 0606 while (!stream.atEnd()) { 0607 QUrl tmp; 0608 stream >> tmp; 0609 targets << tmp; 0610 } 0611 return svnlog(revstart, revkindstart, revend, revkindend, targets); 0612 } 0613 case SVN_IMPORT: { 0614 QUrl wc, repos; 0615 stream >> repos; 0616 stream >> wc; 0617 qCDebug(KDESVN_LOG) << "kio_ksvnProtocol IMPORT" << Qt::endl; 0618 return import(repos, wc); 0619 } 0620 case SVN_ADD: { 0621 QUrl wc; 0622 qCDebug(KDESVN_LOG) << "kio_ksvnProtocol ADD" << Qt::endl; 0623 stream >> wc; 0624 return add(wc); 0625 } 0626 case SVN_DEL: { 0627 QList<QUrl> wclist; 0628 while (!stream.atEnd()) { 0629 QUrl tmp; 0630 stream >> tmp; 0631 wclist << tmp; 0632 } 0633 return wc_delete(wclist); 0634 } 0635 case SVN_REVERT: { 0636 QList<QUrl> wclist; 0637 while (!stream.atEnd()) { 0638 QUrl tmp; 0639 stream >> tmp; 0640 wclist << tmp; 0641 } 0642 qCDebug(KDESVN_LOG) << "kio_svnProtocol REVERT" << Qt::endl; 0643 return revert(wclist); 0644 } 0645 case SVN_STATUS: { 0646 QUrl wc; 0647 bool checkRepos = false; 0648 bool fullRecurse = false; 0649 stream >> wc; 0650 stream >> checkRepos; 0651 stream >> fullRecurse; 0652 qCDebug(KDESVN_LOG) << "kio_svnProtocol STATUS" << Qt::endl; 0653 return status(wc, checkRepos, fullRecurse); 0654 } 0655 case SVN_MKDIR: { 0656 QList<QUrl> list; 0657 stream >> list; 0658 qCDebug(KDESVN_LOG) << "kio_svnProtocol MKDIR" << Qt::endl; 0659 return this->mkdir(list, 0); 0660 } 0661 case SVN_RESOLVE: { 0662 QUrl url; 0663 bool recurse; 0664 stream >> url; 0665 stream >> recurse; 0666 qCDebug(KDESVN_LOG) << "kio_svnProtocol RESOLVE" << Qt::endl; 0667 return wc_resolve(url, recurse); 0668 } 0669 case SVN_SWITCH: { 0670 QUrl wc, url; 0671 bool recurse; 0672 int revnumber; 0673 QString revkind; 0674 stream >> wc; 0675 stream >> url; 0676 stream >> recurse; 0677 stream >> revnumber; 0678 stream >> revkind; 0679 qCDebug(KDESVN_LOG) << "kio_svnProtocol SWITCH" << Qt::endl; 0680 return wc_switch(wc, url, recurse, revnumber, revkind); 0681 } 0682 case SVN_DIFF: { 0683 QUrl url1, url2; 0684 int rev1, rev2; 0685 bool recurse; 0686 QString revkind1, revkind2; 0687 stream >> url1; 0688 stream >> url2; 0689 stream >> rev1; 0690 stream >> revkind1; 0691 stream >> rev2; 0692 stream >> revkind2; 0693 stream >> recurse; 0694 return diff(url1, url2, rev1, revkind1, rev2, revkind2, recurse); 0695 } 0696 default: { 0697 qCDebug(KDESVN_LOG) << "Unknown special" << Qt::endl; 0698 return KIO::WorkerResult::fail(); 0699 } 0700 } 0701 } 0702 0703 KIO::WorkerResult kio_svnProtocol::update(const QUrl &url, int revnumber, const QString &revkind) 0704 { 0705 svn::Revision where(revnumber, revkind); 0706 m_pData->resetListener(); 0707 /* update is always local - so make a path instead URI */ 0708 svn::Path p(url.path()); 0709 try { 0710 svn::Targets pathes(p.path()); 0711 // always update externals, too. (third last parameter) 0712 // no unversioned items allowed (second last parameter) 0713 // sticky depth (last parameter) 0714 svn::UpdateParameter _params; 0715 _params.targets(p.path()).revision(revnumber).depth(svn::DepthInfinity).ignore_externals(false).allow_unversioned(false).sticky_depth(true); 0716 m_pData->m_Svnclient->update(_params); 0717 } catch (const svn::ClientException &e) { 0718 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0719 } 0720 return KIO::WorkerResult::pass(); 0721 } 0722 0723 KIO::WorkerResult kio_svnProtocol::status(const QUrl &wc, bool cR, bool rec) 0724 { 0725 svn::StatusEntries dlist; 0726 svn::StatusParameter params(wc.path()); 0727 m_pData->resetListener(); 0728 try { 0729 dlist = m_pData->m_Svnclient->status( 0730 params.depth(rec ? svn::DepthInfinity : svn::DepthEmpty).all(false).update(cR).noIgnore(false).revision(svn::Revision::UNDEFINED)); 0731 } catch (const svn::ClientException &e) { 0732 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0733 } 0734 qCDebug(KDESVN_LOG) << "Status got " << dlist.count() << " entries." << Qt::endl; 0735 for (const svn::StatusPtr &s : qAsConst(dlist)) { 0736 if (!s) { 0737 continue; 0738 } 0739 const QString cntStr(QString::number(m_pData->m_Listener.counter()).rightJustified(10, QLatin1Char('0'))); 0740 // QDataStream stream(params, QIODevice::WriteOnly); 0741 setMetaData(cntStr + QLatin1String("path"), s->path()); 0742 setMetaData(cntStr + QLatin1String("node"), QString::number(s->nodeStatus())); 0743 setMetaData(cntStr + QLatin1String("text"), QString::number(s->textStatus())); 0744 setMetaData(cntStr + QLatin1String("prop"), QString::number(s->propStatus())); 0745 setMetaData(cntStr + QLatin1String("reptxt"), QString::number(s->reposTextStatus())); 0746 setMetaData(cntStr + QLatin1String("repprop"), QString::number(s->reposPropStatus())); 0747 setMetaData(cntStr + QLatin1String("rev"), QString::number(s->entry().cmtRev())); 0748 m_pData->m_Listener.incCounter(); 0749 } 0750 return KIO::WorkerResult::pass(); 0751 } 0752 0753 KIO::WorkerResult kio_svnProtocol::commit(const QList<QUrl> &urls) 0754 { 0755 /// @todo replace with direct call to kdesvn? 0756 QString msg; 0757 0758 CON_DBUS_VAL(KIO::WorkerResult::fail()); 0759 0760 QDBusReply<QStringList> res = kdesvndInterface.get_logmsg(); 0761 if (!res.isValid()) { 0762 qWarning() << "Unexpected reply type"; 0763 return KIO::WorkerResult::fail(); 0764 } 0765 QStringList lt = res; 0766 0767 if (lt.count() != 1) { 0768 msg = i18n("Wrong or missing log (may cancel pressed)."); 0769 qCDebug(KDESVN_LOG) << msg << Qt::endl; 0770 return KIO::WorkerResult::fail(); 0771 } 0772 msg = lt[0]; 0773 svn::Revision nnum = svn::Revision::UNDEFINED; 0774 svn::CommitParameter commit_parameters; 0775 commit_parameters.targets(svn::Targets::fromUrlList(urls, svn::Targets::UrlConversion::PreferLocalPath)) 0776 .message(msg) 0777 .depth(svn::DepthInfinity) 0778 .keepLocks(false); 0779 0780 try { 0781 nnum = m_pData->m_Svnclient->commit(commit_parameters); 0782 } catch (const svn::ClientException &e) { 0783 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0784 } 0785 for (const auto &url : urls) { 0786 QString userstring; 0787 if (nnum != svn::Revision::UNDEFINED) { 0788 userstring = i18n("Committed revision %1.", nnum.toString()); 0789 } else { 0790 userstring = i18n("Nothing to commit."); 0791 } 0792 const QString num(QString::number(m_pData->m_Listener.counter()).rightJustified(10, QLatin1Char('0'))); 0793 const QString zero(QStringLiteral("0")); 0794 setMetaData(num + QLatin1String("path"), url.path()); 0795 setMetaData(num + QLatin1String("action"), zero); 0796 setMetaData(num + QLatin1String("kind"), zero); 0797 setMetaData(num + QLatin1String("mime_t"), QString()); 0798 setMetaData(num + QLatin1String("content"), zero); 0799 setMetaData(num + QLatin1String("prop"), zero); 0800 setMetaData(num + QLatin1String("rev"), QString::number(nnum)); 0801 setMetaData(num + QLatin1String("string"), userstring); 0802 m_pData->m_Listener.incCounter(); 0803 } 0804 return KIO::WorkerResult::pass(); 0805 } 0806 0807 KIO::WorkerResult kio_svnProtocol::checkout(const QUrl &src, const QUrl &target, const int rev, const QString &revstring) 0808 { 0809 svn::Revision where(rev, revstring); 0810 try { 0811 svn::CheckoutParameter params; 0812 params.moduleName(makeSvnPath(src)).destination(target.path()).revision(where).peg(svn::Revision::UNDEFINED).depth(svn::DepthInfinity); 0813 m_pData->m_Svnclient->checkout(params); 0814 } catch (const svn::ClientException &e) { 0815 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0816 } 0817 return KIO::WorkerResult::pass(); 0818 } 0819 0820 KIO::WorkerResult kio_svnProtocol::svnlog(int revstart, const QString &revstringstart, int revend, const QString &revstringend, const QList<QUrl> &urls) 0821 { 0822 svn::Revision start(revstart, revstringstart); 0823 svn::Revision end(revend, revstringend); 0824 svn::LogParameter params; 0825 params.revisionRange(start, end).peg(svn::Revision::UNDEFINED).limit(0).discoverChangedPathes(true).strictNodeHistory(true); 0826 0827 for (const auto &url : urls) { 0828 svn::LogEntriesMap logs; 0829 try { 0830 m_pData->m_Svnclient->log(params.targets(makeSvnPath(url)), logs); 0831 } catch (const svn::ClientException &e) { 0832 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0833 } 0834 if (logs.isEmpty()) { 0835 const QString num(QString::number(m_pData->m_Listener.counter()).rightJustified(10, QLatin1Char('0'))); 0836 setMetaData(num + QStringLiteral("path"), url.path()); 0837 setMetaData(num + QStringLiteral("string"), i18n("Empty logs")); 0838 m_pData->m_Listener.incCounter(); 0839 continue; 0840 } 0841 0842 for (const svn::LogEntry &logEntry : qAsConst(logs)) { 0843 const QString num(QString::number(m_pData->m_Listener.counter()).rightJustified(10, QLatin1Char('0'))); 0844 setMetaData(num + QStringLiteral("path"), url.path()); 0845 setMetaData(num + QStringLiteral("rev"), QString::number(logEntry.revision)); 0846 setMetaData(num + QStringLiteral("author"), logEntry.author); 0847 setMetaData(num + QStringLiteral("logmessage"), logEntry.message); 0848 m_pData->m_Listener.incCounter(); 0849 for (const svn::LogChangePathEntry &logPathEntry : logEntry.changedPaths) { 0850 const QString num(QString::number(m_pData->m_Listener.counter()).rightJustified(10, QLatin1Char('0'))); 0851 setMetaData(num + QStringLiteral("rev"), QString::number(logEntry.revision)); 0852 setMetaData(num + QStringLiteral("path"), url.path()); 0853 setMetaData(num + QStringLiteral("loggedpath"), logPathEntry.path); 0854 setMetaData(num + QStringLiteral("loggedaction"), QString(QLatin1Char(logPathEntry.action))); 0855 setMetaData(num + QStringLiteral("loggedcopyfrompath"), logPathEntry.copyFromPath); 0856 setMetaData(num + QStringLiteral("loggedcopyfromrevision"), QString::number(logPathEntry.copyFromRevision)); 0857 m_pData->m_Listener.incCounter(); 0858 } 0859 } 0860 } 0861 return KIO::WorkerResult::pass(); 0862 } 0863 0864 KIO::WorkerResult kio_svnProtocol::revert(const QList<QUrl> &urls) 0865 { 0866 try { 0867 m_pData->m_Svnclient->revert(svn::Targets::fromUrlList(urls, svn::Targets::UrlConversion::PreferLocalPath), svn::DepthEmpty); 0868 } catch (const svn::ClientException &e) { 0869 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0870 } 0871 return KIO::WorkerResult::pass(); 0872 } 0873 0874 KIO::WorkerResult kio_svnProtocol::wc_switch(const QUrl &wc, const QUrl &target, bool rec, int rev, const QString &revstring) 0875 { 0876 svn::Revision where(rev, revstring); 0877 svn::Path wc_path(wc.path()); 0878 try { 0879 m_pData->m_Svnclient->doSwitch(wc_path, svn::Url(makeSvnPath(target)), where, rec ? svn::DepthInfinity : svn::DepthFiles); 0880 } catch (const svn::ClientException &e) { 0881 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0882 } 0883 return KIO::WorkerResult::pass(); 0884 } 0885 0886 KIO::WorkerResult kio_svnProtocol::diff(const QUrl &uri1, const QUrl &uri2, int rnum1, const QString &rstring1, int rnum2, const QString &rstring2, bool rec) 0887 { 0888 QByteArray ex; 0889 /// @todo read settings for diff (ignore contentype) 0890 try { 0891 const svn::Revision r1(rnum1, rstring1); 0892 const svn::Revision r2(rnum2, rstring2); 0893 const svn::Path u1 = makeSvnPath(uri1); 0894 const svn::Path u2 = makeSvnPath(uri2); 0895 QTemporaryDir tdir; 0896 qCDebug(KDESVN_LOG) << "kio_ksvn::diff : " << u1.path() << " at revision " << r1.toString() << " with " << u2.path() << " at revision " << r2.toString() 0897 << Qt::endl; 0898 svn::DiffParameter _opts; 0899 // no peg revision required 0900 _opts.path1(u1) 0901 .path2(u2) 0902 .tmpPath(tdir.path()) 0903 .rev1(r1) 0904 .rev2(r2) 0905 .ignoreContentType(false) 0906 .extra(svn::StringArray()) 0907 .depth(rec ? svn::DepthInfinity : svn::DepthEmpty) 0908 .ignoreAncestry(false) 0909 .noDiffDeleted(false) 0910 .relativeTo((u1.path() == u2.path() ? u1 : svn::Path())) 0911 .changeList(svn::StringArray()); 0912 0913 tdir.setAutoRemove(true); 0914 ex = m_pData->m_Svnclient->diff(_opts); 0915 } catch (const svn::ClientException &e) { 0916 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0917 } 0918 QString out = QString::fromUtf8(ex); 0919 const QString num(QString::number(m_pData->m_Listener.counter()).rightJustified(10, QLatin1Char('0'))); 0920 QTextStream stream(&out); 0921 while (!stream.atEnd()) { 0922 setMetaData(num + QStringLiteral("diffresult"), stream.readLine()); 0923 m_pData->m_Listener.incCounter(); 0924 } 0925 return KIO::WorkerResult::pass(); 0926 } 0927 0928 KIO::WorkerResult kio_svnProtocol::import(const QUrl &repos, const QUrl &wc) 0929 { 0930 try { 0931 const svn::Path target = makeSvnPath(repos); 0932 const QString path = wc.path(); 0933 m_pData->m_Svnclient->import(path, svn::Url(target), QString(), svn::DepthInfinity, false, false); 0934 } catch (const svn::ClientException &e) { 0935 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0936 } 0937 return KIO::WorkerResult::pass(); 0938 } 0939 0940 KIO::WorkerResult kio_svnProtocol::add(const QUrl &wc) 0941 { 0942 QString path = wc.path(); 0943 try { 0944 /* rec */ 0945 m_pData->m_Svnclient->add(svn::Path(path), svn::DepthInfinity); 0946 } catch (const svn::ClientException &e) { 0947 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0948 } 0949 return KIO::WorkerResult::pass(); 0950 } 0951 0952 KIO::WorkerResult kio_svnProtocol::wc_delete(const QList<QUrl> &urls) 0953 { 0954 try { 0955 m_pData->m_Svnclient->remove(svn::Targets::fromUrlList(urls, svn::Targets::UrlConversion::PreferLocalPath), false); 0956 } catch (const svn::ClientException &e) { 0957 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0958 } 0959 return KIO::WorkerResult::pass(); 0960 } 0961 0962 KIO::WorkerResult kio_svnProtocol::wc_resolve(const QUrl &url, bool recurse) 0963 { 0964 try { 0965 svn::Depth depth = recurse ? svn::DepthInfinity : svn::DepthEmpty; 0966 m_pData->m_Svnclient->resolve(url.path(), depth); 0967 } catch (const svn::ClientException &e) { 0968 return extraError(KIO::ERR_WORKER_DEFINED, e.msg()); 0969 } 0970 return KIO::WorkerResult::pass(); 0971 } 0972 0973 void kio_svnProtocol::streamWritten(const KIO::filesize_t current) 0974 { 0975 processedSize(current); 0976 } 0977 0978 void kio_svnProtocol::streamSendMime(const QMimeType &mt) 0979 { 0980 if (mt.isValid()) { 0981 mimeType(mt.name()); 0982 } 0983 } 0984 0985 void kio_svnProtocol::streamPushData(const QByteArray &streamData) 0986 { 0987 data(streamData); 0988 } 0989 0990 void kio_svnProtocol::contextProgress(long long int current, long long int max) 0991 { 0992 if (max > -1) { 0993 totalSize(KIO::filesize_t(max)); 0994 } 0995 0996 bool to_dbus = false; 0997 if (m_pData->dispProgress || m_pData->dispWritten || max > -1) { 0998 QTime now = QTime::currentTime(); 0999 if (m_pData->m_last.msecsTo(now) >= 90) { 1000 if (m_pData->dispProgress) { 1001 processedSize(KIO::filesize_t(current)); 1002 } else { 1003 written(current); 1004 to_dbus = useKioprogress(); 1005 } 1006 m_pData->m_last = now; 1007 } 1008 } 1009 if (to_dbus) { 1010 CON_DBUS; 1011 if (max > -1) { 1012 kdesvndInterface.maxTransferKioOperation(m_pData->m_Id, max); 1013 } 1014 kdesvndInterface.transferredKioOperation(m_pData->m_Id, current); 1015 } 1016 } 1017 1018 bool kio_svnProtocol::supportOverwrite() const 1019 { 1020 Kdesvnsettings::self()->load(); 1021 return Kdesvnsettings::kio_can_overwrite(); 1022 } 1023 1024 bool kio_svnProtocol::useKioprogress() const 1025 { 1026 Kdesvnsettings::self()->load(); 1027 return Kdesvnsettings::display_dockmsg(); 1028 } 1029 1030 /*! 1031 \fn kio_svnProtocol::getDefaultLog() 1032 */ 1033 QString kio_svnProtocol::getDefaultLog() 1034 { 1035 QString res; 1036 Kdesvnsettings::self()->load(); 1037 if (Kdesvnsettings::kio_use_standard_logmsg()) { 1038 res = Kdesvnsettings::kio_standard_logmsg(); 1039 } 1040 return res; 1041 } 1042 1043 void kio_svnProtocol::notify(const QString &text) 1044 { 1045 if (!useKioprogress()) { 1046 return; 1047 } 1048 CON_DBUS; 1049 kdesvndInterface.notifyKioOperation(text); 1050 } 1051 1052 KIO::WorkerResult kio_svnProtocol::extraError(int _errid, const QString &text) 1053 { 1054 const KIO::WorkerResult failResult = KIO::WorkerResult::fail(_errid, text); 1055 if (!text.isNull()) { 1056 CON_DBUS_VAL(failResult); 1057 kdesvndInterface.errorKioOperation(text); 1058 } 1059 return failResult; 1060 } 1061 1062 void kio_svnProtocol::registerToDaemon() 1063 { 1064 if (!useKioprogress()) { 1065 return; 1066 } 1067 CON_DBUS; 1068 kdesvndInterface.registerKioFeedback(m_pData->m_Id); 1069 } 1070 1071 void kio_svnProtocol::unregisterFromDaemon() 1072 { 1073 CON_DBUS; 1074 kdesvndInterface.unRegisterKioFeedback(m_pData->m_Id); 1075 } 1076 bool kio_svnProtocol::checkKioCancel() const 1077 { 1078 if (!useKioprogress()) { 1079 return false; 1080 } 1081 CON_DBUS_VAL(false); 1082 QDBusReply<bool> res = kdesvndInterface.canceldKioOperation(m_pData->m_Id); 1083 return res.isValid() ? res.value() : false; 1084 } 1085 1086 void kio_svnProtocol::startOp(qulonglong max, const QString &title) 1087 { 1088 if (!useKioprogress()) { 1089 return; 1090 } 1091 CON_DBUS; 1092 kdesvndInterface.maxTransferKioOperation(m_pData->m_Id, max); 1093 kdesvndInterface.titleKioOperation(m_pData->m_Id, title, title); 1094 kdesvndInterface.setKioStatus(m_pData->m_Id, 1, QString()); 1095 } 1096 1097 void kio_svnProtocol::stopOp(const QString &message) 1098 { 1099 if (!useKioprogress()) { 1100 return; 1101 } 1102 CON_DBUS; 1103 kdesvndInterface.setKioStatus(m_pData->m_Id, 0, message); 1104 unregisterFromDaemon(); 1105 } 1106 1107 } // namespace KIO 1108 1109 // Pseudo plugin class to embed metadata 1110 class KIOPluginForMetaData : public QObject 1111 { 1112 Q_OBJECT 1113 Q_PLUGIN_METADATA(IID "org.kde.kio.worker.svn" FILE "svn.json") 1114 }; 1115 1116 #include "kiosvn.moc"