File indexing completed on 2024-04-21 04:57:28
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "kio_archivebase.h" 0008 #include <kio_archive_debug.h> 0009 0010 #include <errno.h> 0011 #include <stdlib.h> 0012 #include <sys/stat.h> 0013 0014 #include <K7Zip> 0015 #include <KAr> 0016 #include <KIO/StatJob> 0017 #include <KLocalizedString> 0018 #include <KTar> 0019 #include <KUser> 0020 #include <KZip> 0021 #include <kio_version.h> 0022 0023 #include <QDir> 0024 #include <QFile> 0025 #include <QMimeDatabase> 0026 #include <QMimeType> 0027 #include <QUrl> 0028 0029 #include <memory> 0030 0031 #ifdef Q_OS_WIN 0032 #define S_ISDIR(m) (((m & S_IFMT) == S_IFDIR)) 0033 #endif 0034 0035 using namespace KIO; 0036 0037 ArchiveProtocolBase::ArchiveProtocolBase(const QByteArray &proto, const QByteArray &pool, const QByteArray &app) 0038 : WorkerBase(proto, pool, app) 0039 { 0040 qCDebug(KIO_ARCHIVE_LOG); 0041 m_archiveFile = nullptr; 0042 } 0043 0044 ArchiveProtocolBase::~ArchiveProtocolBase() 0045 { 0046 delete m_archiveFile; 0047 } 0048 0049 bool ArchiveProtocolBase::checkNewFile(const QUrl &url, QString &path, KIO::Error &errorNum) 0050 { 0051 #ifndef Q_OS_WIN 0052 QString fullPath = url.path(); 0053 #else 0054 QString fullPath = url.path().remove(0, 1); 0055 #endif 0056 qCDebug(KIO_ARCHIVE_LOG) << fullPath; 0057 0058 // Are we already looking at that file ? 0059 if (m_archiveFile && m_archiveName == fullPath.left(m_archiveName.length())) { 0060 // Has it changed ? 0061 QT_STATBUF statbuf; 0062 if (QT_STAT(QFile::encodeName(m_archiveName).constData(), &statbuf) == 0) { 0063 if (m_mtime == statbuf.st_mtime) { 0064 path = fullPath.mid(m_archiveName.length()); 0065 qCDebug(KIO_ARCHIVE_LOG) << "returning" << path; 0066 return true; 0067 } 0068 } 0069 } 0070 qCDebug(KIO_ARCHIVE_LOG) << "Need to open a new file"; 0071 0072 // Close previous file 0073 if (m_archiveFile) { 0074 m_archiveFile->close(); 0075 delete m_archiveFile; 0076 m_archiveFile = nullptr; 0077 } 0078 0079 // Find where the tar file is in the full path 0080 int pos = 0; 0081 QString archiveFile; 0082 path.clear(); 0083 0084 if (!fullPath.isEmpty() && !fullPath.endsWith(QLatin1Char('/'))) 0085 fullPath += QLatin1Char('/'); 0086 0087 qCDebug(KIO_ARCHIVE_LOG) << "the full path is" << fullPath; 0088 QT_STATBUF statbuf; 0089 statbuf.st_mode = 0; // be sure to clear the directory bit 0090 while ((pos = fullPath.indexOf(QLatin1Char('/'), pos + 1)) != -1) { 0091 QString tryPath = fullPath.left(pos); 0092 qCDebug(KIO_ARCHIVE_LOG) << fullPath << "trying" << tryPath; 0093 if (QT_STAT(QFile::encodeName(tryPath).constData(), &statbuf) == -1) { 0094 if (errno == ENOENT) { 0095 // The current path is no longer part of the local filesystem. 0096 // Either we already have enough of the pathname, or we will 0097 // not get anything more useful. 0098 statbuf.st_mode = 0; // do not trust the result 0099 break; 0100 } 0101 0102 if (errno == EACCES) 0103 errorNum = KIO::ERR_ACCESS_DENIED; 0104 else 0105 errorNum = KIO::ERR_CANNOT_STAT; 0106 return false; 0107 } 0108 0109 if (!S_ISDIR(statbuf.st_mode)) { 0110 archiveFile = tryPath; 0111 m_mtime = statbuf.st_mtime; 0112 #ifdef Q_OS_WIN // st_uid and st_gid provides no information 0113 m_user.clear(); 0114 m_group.clear(); 0115 #else 0116 KUser user(statbuf.st_uid); 0117 m_user = user.loginName(); 0118 KUserGroup group(statbuf.st_gid); 0119 m_group = group.name(); 0120 #endif 0121 path = fullPath.mid(pos + 1); 0122 qCDebug(KIO_ARCHIVE_LOG).nospace() << "fullPath=" << fullPath << " path=" << path; 0123 if (path.length() > 1) { 0124 if (path.endsWith(QLatin1Char('/'))) 0125 path.chop(1); 0126 } else 0127 path = QStringLiteral("/"); 0128 qCDebug(KIO_ARCHIVE_LOG).nospace() << "Found. archiveFile=" << archiveFile << " path=" << path; 0129 break; 0130 } 0131 } 0132 if (archiveFile.isEmpty()) { 0133 qCDebug(KIO_ARCHIVE_LOG) << "not found"; 0134 if (S_ISDIR(statbuf.st_mode)) // Did the last stat() find a directory? 0135 { 0136 // Too bad, it is a directory, not an archive. 0137 qCDebug(KIO_ARCHIVE_LOG) << "Path is a directory, not an archive."; 0138 errorNum = KIO::ERR_IS_DIRECTORY; 0139 } else 0140 errorNum = KIO::ERR_DOES_NOT_EXIST; 0141 return false; 0142 } 0143 0144 // Open new file 0145 m_archiveFile = this->createArchive(url.scheme(), archiveFile); 0146 if (!m_archiveFile) { 0147 qCWarning(KIO_ARCHIVE_LOG) << "Protocol" << url.scheme() << "not supported by this IOWorker"; 0148 errorNum = KIO::ERR_UNSUPPORTED_PROTOCOL; 0149 return false; 0150 } 0151 0152 if (!m_archiveFile->open(QIODevice::ReadOnly)) { 0153 qCDebug(KIO_ARCHIVE_LOG) << "Opening" << archiveFile << "failed."; 0154 delete m_archiveFile; 0155 m_archiveFile = nullptr; 0156 errorNum = KIO::ERR_CANNOT_OPEN_FOR_READING; 0157 return false; 0158 } 0159 0160 m_archiveName = archiveFile; 0161 return true; 0162 } 0163 0164 uint ArchiveProtocolBase::computeArchiveDirSize(const KArchiveDirectory *dir) 0165 { 0166 // compute size of archive content 0167 uint totalSize = 0; 0168 const auto entries = dir->entries(); 0169 for (const auto &entryName : entries) { 0170 auto entry = dir->entry(entryName); 0171 if (entry->isFile()) { 0172 auto fileEntry = static_cast<const KArchiveFile *>(entry); 0173 totalSize += fileEntry->size(); 0174 } else if (entry->isDirectory()) { 0175 const auto dirEntry = static_cast<const KArchiveDirectory *>(entry); 0176 // recurse 0177 totalSize += computeArchiveDirSize(dirEntry); 0178 } 0179 } 0180 return totalSize; 0181 } 0182 0183 KIO::StatDetails ArchiveProtocolBase::getStatDetails() 0184 { 0185 // takes care of converting old metadata details to new StatDetails 0186 // TODO KF6 : remove legacy "details" code path 0187 KIO::StatDetails details; 0188 #if KIO_VERSION < QT_VERSION_CHECK(5, 240, 0) 0189 if (hasMetaData(QStringLiteral("statDetails"))) { 0190 #endif 0191 const QString statDetails = metaData(QStringLiteral("statDetails")); 0192 details = statDetails.isEmpty() ? KIO::StatDefaultDetails : static_cast<KIO::StatDetails>(statDetails.toInt()); 0193 #if KIO_VERSION < QT_VERSION_CHECK(5, 240, 0) 0194 } else { 0195 const QString sDetails = metaData(QStringLiteral("details")); 0196 // silence deprecation warning for KIO::detailsToStatDetails 0197 QT_WARNING_PUSH 0198 QT_WARNING_DISABLE_CLANG("-Wdeprecated-declarations") 0199 QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations") 0200 details = sDetails.isEmpty() ? KIO::StatDefaultDetails : KIO::detailsToStatDetails(sDetails.toInt()); 0201 QT_WARNING_POP 0202 } 0203 #endif 0204 return details; 0205 } 0206 0207 void ArchiveProtocolBase::createRootUDSEntry(KIO::UDSEntry &entry) 0208 { 0209 entry.clear(); 0210 entry.reserve(7); 0211 0212 auto path = m_archiveFile->fileName(); 0213 path = path.mid(path.lastIndexOf(QLatin1Char('/')) + 1); 0214 0215 entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); 0216 entry.fastInsert(KIO::UDSEntry::UDS_DISPLAY_NAME, path); 0217 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); 0218 entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, m_mtime); 0219 // entry.fastInsert( KIO::UDSEntry::UDS_ACCESS, 07777 ); // fake 'x' permissions, this is a pseudo-directory 0220 entry.fastInsert(KIO::UDSEntry::UDS_USER, m_user); 0221 entry.fastInsert(KIO::UDSEntry::UDS_GROUP, m_group); 0222 0223 QMimeDatabase db; 0224 QMimeType mt = db.mimeTypeForFile(m_archiveFile->fileName()); 0225 if (mt.isValid()) { 0226 entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, mt.name()); 0227 } 0228 } 0229 0230 void ArchiveProtocolBase::createUDSEntry(const KArchiveEntry *archiveEntry, UDSEntry &entry) 0231 { 0232 entry.clear(); 0233 0234 entry.reserve(8); 0235 entry.fastInsert(KIO::UDSEntry::UDS_NAME, archiveEntry->name()); 0236 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, archiveEntry->isFile() ? archiveEntry->permissions() & S_IFMT : S_IFDIR); // keep file type only 0237 if (archiveEntry->isFile()) { 0238 entry.fastInsert(KIO::UDSEntry::UDS_SIZE, ((KArchiveFile *)archiveEntry)->size()); 0239 } 0240 entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, archiveEntry->date().toSecsSinceEpoch()); 0241 entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, archiveEntry->permissions() & 07777); // keep permissions only 0242 entry.fastInsert(KIO::UDSEntry::UDS_USER, archiveEntry->user()); 0243 entry.fastInsert(KIO::UDSEntry::UDS_GROUP, archiveEntry->group()); 0244 entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, archiveEntry->symLinkTarget()); 0245 } 0246 0247 KIO::WorkerResult ArchiveProtocolBase::listDir(const QUrl &url) 0248 { 0249 qCDebug(KIO_ARCHIVE_LOG) << url.url(); 0250 0251 QString path; 0252 KIO::Error errorNum; 0253 if (!checkNewFile(url, path, errorNum)) { 0254 if (errorNum == KIO::ERR_CANNOT_OPEN_FOR_READING) { 0255 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format) 0256 // Therefore give a more specific error message 0257 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, 0258 i18n("Could not open the file, probably due to an unsupported file format.\n%1", url.toDisplayString())); 0259 } else if (errorNum != ERR_IS_DIRECTORY) { 0260 // We have any other error 0261 return KIO::WorkerResult::fail(errorNum, url.toDisplayString()); 0262 } 0263 // It's a real dir -> redirect 0264 QUrl redir = QUrl::fromLocalFile(url.path()); 0265 qCDebug(KIO_ARCHIVE_LOG) << "Ok, redirection to" << redir.url(); 0266 redirection(redir); 0267 // And let go of the tar file - for people who want to unmount a cdrom after that 0268 delete m_archiveFile; 0269 m_archiveFile = nullptr; 0270 return KIO::WorkerResult::pass(); 0271 } 0272 0273 if (path.isEmpty()) { 0274 QUrl redir; 0275 redir.setScheme(url.scheme()); 0276 qCDebug(KIO_ARCHIVE_LOG) << "url.path()=" << url.path(); 0277 redir.setPath(url.path() + QLatin1Char('/')); 0278 qCDebug(KIO_ARCHIVE_LOG) << "redirection" << redir.url(); 0279 redirection(redir); 0280 return KIO::WorkerResult::pass(); 0281 } 0282 0283 qCDebug(KIO_ARCHIVE_LOG) << "checkNewFile done"; 0284 const KArchiveDirectory *root = m_archiveFile->directory(); 0285 const KArchiveDirectory *dir; 0286 if (!path.isEmpty() && path != QLatin1String("/")) { 0287 qCDebug(KIO_ARCHIVE_LOG) << "Looking for entry" << path; 0288 const KArchiveEntry *e = root->entry(path); 0289 if (!e) { 0290 return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0291 } 0292 if (!e->isDirectory()) { 0293 return KIO::WorkerResult::fail(KIO::ERR_IS_FILE, url.toDisplayString()); 0294 } 0295 dir = (KArchiveDirectory *)e; 0296 } else { 0297 dir = root; 0298 } 0299 0300 const QStringList l = dir->entries(); 0301 totalSize(l.count()); 0302 0303 UDSEntry entry; 0304 if (!l.contains(QLatin1String("."))) { 0305 createRootUDSEntry(entry); 0306 listEntry(entry); 0307 } 0308 0309 QStringList::const_iterator it = l.begin(); 0310 for (; it != l.end(); ++it) { 0311 qCDebug(KIO_ARCHIVE_LOG) << (*it); 0312 const KArchiveEntry *archiveEntry = dir->entry((*it)); 0313 0314 createUDSEntry(archiveEntry, entry); 0315 0316 listEntry(entry); 0317 } 0318 0319 qCDebug(KIO_ARCHIVE_LOG) << "done"; 0320 return KIO::WorkerResult::pass(); 0321 } 0322 0323 KIO::WorkerResult ArchiveProtocolBase::stat(const QUrl &url) 0324 { 0325 QString path; 0326 UDSEntry entry; 0327 KIO::Error errorNum; 0328 if (!checkNewFile(url, path, errorNum)) { 0329 // We may be looking at a real directory - this happens 0330 // when pressing up after being in the root of an archive 0331 if (errorNum == KIO::ERR_CANNOT_OPEN_FOR_READING) { 0332 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format) 0333 // Therefore give a more specific error message 0334 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, 0335 i18n("Could not open the file, probably due to an unsupported file format.\n%1", url.toDisplayString())); 0336 } else if (errorNum != ERR_IS_DIRECTORY) { 0337 // We have any other error 0338 return KIO::WorkerResult::fail(errorNum, url.toDisplayString()); 0339 } 0340 entry.reserve(2); 0341 // Real directory. Return just enough information for KRun to work 0342 entry.fastInsert(KIO::UDSEntry::UDS_NAME, url.fileName()); 0343 qCDebug(KIO_ARCHIVE_LOG) << "returning name" << url.fileName(); 0344 0345 QT_STATBUF buff; 0346 #ifdef Q_OS_WIN 0347 QString fullPath = url.path().remove(0, 1); 0348 #else 0349 QString fullPath = url.path(); 0350 #endif 0351 0352 if (QT_STAT(QFile::encodeName(fullPath).constData(), &buff) == -1) { 0353 // Should not happen, as the file was already stated by checkNewFile 0354 return KIO::WorkerResult::fail(KIO::ERR_CANNOT_STAT, url.toDisplayString()); 0355 } 0356 0357 entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, buff.st_mode & S_IFMT); 0358 0359 statEntry(entry); 0360 0361 // And let go of the tar file - for people who want to unmount a cdrom after that 0362 delete m_archiveFile; 0363 m_archiveFile = nullptr; 0364 return KIO::WorkerResult::pass(); 0365 } 0366 0367 const KArchiveDirectory *root = m_archiveFile->directory(); 0368 const KArchiveEntry *archiveEntry; 0369 if (path.isEmpty()) { 0370 path = QStringLiteral("/"); 0371 archiveEntry = root; 0372 } else { 0373 archiveEntry = root->entry(path); 0374 } 0375 if (!archiveEntry) { 0376 return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0377 } 0378 0379 if (archiveEntry == root) { 0380 createRootUDSEntry(entry); 0381 } else { 0382 createUDSEntry(archiveEntry, entry); 0383 } 0384 0385 if (archiveEntry->isDirectory()) { 0386 auto details = getStatDetails(); 0387 if (details & KIO::StatRecursiveSize) { 0388 const auto directoryEntry = static_cast<const KArchiveDirectory *>(archiveEntry); 0389 entry.fastInsert(KIO::UDSEntry::UDS_RECURSIVE_SIZE, static_cast<long long>(computeArchiveDirSize(directoryEntry))); 0390 } 0391 } 0392 statEntry(entry); 0393 0394 return KIO::WorkerResult::pass(); 0395 } 0396 0397 KIO::WorkerResult ArchiveProtocolBase::get(const QUrl &url) 0398 { 0399 qCDebug(KIO_ARCHIVE_LOG) << url.url(); 0400 0401 QString path; 0402 KIO::Error errorNum; 0403 if (!checkNewFile(url, path, errorNum)) { 0404 if (errorNum == KIO::ERR_CANNOT_OPEN_FOR_READING) { 0405 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format) 0406 // Therefore give a more specific error message 0407 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, 0408 i18n("Could not open the file, probably due to an unsupported file format.\n%1", url.toDisplayString())); 0409 } else { 0410 // We have any other error 0411 return KIO::WorkerResult::fail(errorNum, url.toDisplayString()); 0412 } 0413 } 0414 0415 const KArchiveDirectory *root = m_archiveFile->directory(); 0416 const KArchiveEntry *archiveEntry = root->entry(path); 0417 0418 if (!archiveEntry) { 0419 return KIO::WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0420 } 0421 if (archiveEntry->isDirectory()) { 0422 return KIO::WorkerResult::fail(KIO::ERR_IS_DIRECTORY, url.toDisplayString()); 0423 } 0424 const KArchiveFile *archiveFileEntry = static_cast<const KArchiveFile *>(archiveEntry); 0425 if (!archiveEntry->symLinkTarget().isEmpty()) { 0426 const QString target = archiveEntry->symLinkTarget(); 0427 qCDebug(KIO_ARCHIVE_LOG) << "Redirection to" << target; 0428 const QUrl realURL = url.resolved(QUrl(target)); 0429 qCDebug(KIO_ARCHIVE_LOG) << "realURL=" << realURL; 0430 redirection(realURL); 0431 return KIO::WorkerResult::pass(); 0432 } 0433 0434 // qCDebug(KIO_ARCHIVE_LOG) << "Preparing to get the archive data"; 0435 0436 /* 0437 * The easy way would be to get the data by calling archiveFileEntry->data() 0438 * However this has drawbacks: 0439 * - the complete file must be read into the memory 0440 * - errors are skipped, resulting in an empty file 0441 */ 0442 0443 std::unique_ptr<QIODevice> io(archiveFileEntry->createDevice()); 0444 0445 if (!io) { 0446 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, 0447 i18n("The archive file could not be opened, perhaps because the format is unsupported.\n%1", url.toDisplayString())); 0448 } 0449 0450 if (!io->open(QIODevice::ReadOnly)) { 0451 return KIO::WorkerResult::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, url.toDisplayString()); 0452 } 0453 0454 totalSize(archiveFileEntry->size()); 0455 0456 // Size of a QIODevice read. It must be large enough so that the mime type check will not fail 0457 const qint64 maxSize = 0x100000; // 1MB 0458 0459 qint64 bufferSize = qMin(maxSize, archiveFileEntry->size()); 0460 QByteArray buffer; 0461 buffer.resize(bufferSize); 0462 if (buffer.isEmpty() && bufferSize > 0) { 0463 // Something went wrong 0464 return KIO::WorkerResult::fail(KIO::ERR_OUT_OF_MEMORY, url.toDisplayString()); 0465 } 0466 0467 bool firstRead = true; 0468 0469 // How much file do we still have to process? 0470 qint64 fileSize = archiveFileEntry->size(); 0471 KIO::filesize_t processed = 0; 0472 0473 while (!io->atEnd() && fileSize > 0) { 0474 if (!firstRead) { 0475 bufferSize = qMin(maxSize, fileSize); 0476 buffer.resize(bufferSize); 0477 } 0478 const qint64 read = io->read(buffer.data(), buffer.size()); // Avoid to use bufferSize here, in case something went wrong. 0479 if (read != bufferSize) { 0480 qCWarning(KIO_ARCHIVE_LOG) << "Read" << read << "bytes but expected" << bufferSize; 0481 return KIO::WorkerResult::fail(KIO::ERR_CANNOT_READ, url.toDisplayString()); 0482 } 0483 if (firstRead) { 0484 // We use the magic one the first data read 0485 // (As magic detection is about fixed positions, we can be sure that it is enough data.) 0486 QMimeDatabase db; 0487 QMimeType mime = db.mimeTypeForFileNameAndData(path, buffer); 0488 qCDebug(KIO_ARCHIVE_LOG) << "Emitting mimetype" << mime.name(); 0489 mimeType(mime.name()); 0490 firstRead = false; 0491 } 0492 data(buffer); 0493 processed += read; 0494 processedSize(processed); 0495 fileSize -= bufferSize; 0496 } 0497 io->close(); 0498 0499 data(QByteArray()); 0500 0501 return KIO::WorkerResult::pass(); 0502 } 0503 0504 /* 0505 In case someone wonders how the old filter stuff looked like : :) 0506 void TARProtocol::slotData(void *_p, int _len) 0507 { 0508 switch (m_cmd) { 0509 case CMD_PUT: 0510 assert(m_pFilter); 0511 m_pFilter->send(_p, _len); 0512 break; 0513 default: 0514 abort(); 0515 break; 0516 } 0517 } 0518 0519 void TARProtocol::slotDataEnd() 0520 { 0521 switch (m_cmd) { 0522 case CMD_PUT: 0523 assert(m_pFilter && m_pJob); 0524 m_pFilter->finish(); 0525 m_pJob->dataEnd(); 0526 m_cmd = CMD_NONE; 0527 break; 0528 default: 0529 abort(); 0530 break; 0531 } 0532 } 0533 0534 void TARProtocol::jobData(void *_p, int _len) 0535 { 0536 switch (m_cmd) { 0537 case CMD_GET: 0538 assert(m_pFilter); 0539 m_pFilter->send(_p, _len); 0540 break; 0541 case CMD_COPY: 0542 assert(m_pFilter); 0543 m_pFilter->send(_p, _len); 0544 break; 0545 default: 0546 abort(); 0547 } 0548 } 0549 0550 void TARProtocol::jobDataEnd() 0551 { 0552 switch (m_cmd) { 0553 case CMD_GET: 0554 assert(m_pFilter); 0555 m_pFilter->finish(); 0556 dataEnd(); 0557 break; 0558 case CMD_COPY: 0559 assert(m_pFilter); 0560 m_pFilter->finish(); 0561 m_pJob->dataEnd(); 0562 break; 0563 default: 0564 abort(); 0565 } 0566 } 0567 0568 void TARProtocol::filterData(void *_p, int _len) 0569 { 0570 debug("void TARProtocol::filterData"); 0571 switch (m_cmd) { 0572 case CMD_GET: 0573 data(_p, _len); 0574 break; 0575 case CMD_PUT: 0576 assert (m_pJob); 0577 m_pJob->data(_p, _len); 0578 break; 0579 case CMD_COPY: 0580 assert(m_pJob); 0581 m_pJob->data(_p, _len); 0582 break; 0583 default: 0584 abort(); 0585 } 0586 } 0587 */ 0588 0589 // kate: space-indent on; indent-width 4; replace-tabs on;