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