1 /*****************************************************************************
2  * Copyright (C) 2003 Rafi Yanai <krusader@users.sf.net>                     *
3  * Copyright (C) 2003 Shie Erlich <krusader@users.sf.net>                    *
4  * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org]              *
5  *                                                                           *
6  * This file is part of Krusader [https://krusader.org].                     *
7  *                                                                           *
8  * Krusader is free software: you can redistribute it and/or modify          *
9  * it under the terms of the GNU General Public License as published by      *
10  * the Free Software Foundation, either version 2 of the License, or         *
11  * (at your option) any later version.                                       *
12  *                                                                           *
13  * Krusader is distributed in the hope that it will be useful,               *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
16  * GNU General Public License for more details.                              *
17  *                                                                           *
18  * You should have received a copy of the GNU General Public License         *
19  * along with Krusader.  If not, see [http://www.gnu.org/licenses/].         *
20  *****************************************************************************/
21 
22 #include "krarc.h"
23 #include "../krusader/defaults.h"
24 
25 // QtCore
26 #include <QByteArray>
27 #include <QDebug>
28 #include <QDir>
29 #include <QFile>
30 #include <QFileInfo>
31 #include <QMimeDatabase>
32 #include <QMimeType>
33 #include <QRegExp>
34 #include <QTemporaryFile>
35 #include <QTextCodec>
36 #include <qplatformdefs.h>
37 
38 #include <KArchive/KTar>
39 #include <KCoreAddons/KProcess>
40 #include <KI18n/KLocalizedString>
41 #include <KIO/Job>
42 #include <KIOCore/KFileItem>
43 
44 #include <kio_version.h>
45 
46 #include <errno.h>
47 #include "../krusader/compat.h"
48 
49 #define MAX_IPC_SIZE           (1024*32)
50 #define TRIES_WITH_PASSWORDS   3
51 
52 using namespace KIO;
53 extern "C"
54 {
55 
56 #ifdef KRARC_ENABLED
57     /* This codec is for being able to handle files which encoding differs from the current locale.
58      *
59      * Unfortunately QProcess requires QString parameters for arguments which are encoded to Local8Bit
60      * If we want to use unzip with ISO-8852-2 when the current locale is UTF-8, it will cause problems.
61      *
62      * Workaround:
63      *  1. encode the QString to QByteArray ( according to the selected remote encoding, ISO-8852-2 )
64      *  2. encode QByteArray to QString again
65      *     unicode 0xE000-0xF7FF is for private use
66      *     the byte array is mapped to 0xE000-0xE0FF unicodes
67      *  3. KrArcCodec maps 0xE000-0xE0FF to 0x0000-0x00FF, while calls the default encoding routine
68      *     for other unicodes.
69      */
70 
71     class KrArcCodec : public QTextCodec
72     {
73     public:
KrArcCodec(QTextCodec * codec)74         KrArcCodec(QTextCodec * codec) : originalCodec(codec) {}
~KrArcCodec()75         virtual ~KrArcCodec() {}
76 
name() const77         virtual QByteArray name() const Q_DECL_OVERRIDE {
78             return  originalCodec->name();
79         }
aliases() const80         virtual QList<QByteArray> aliases() const Q_DECL_OVERRIDE {
81             return originalCodec->aliases();
82         }
mibEnum() const83         virtual int mibEnum() const Q_DECL_OVERRIDE {
84             return  originalCodec->mibEnum();
85         }
86 
87     protected:
convertToUnicode(const char * in,int length,ConverterState * state) const88         virtual QString convertToUnicode(const char *in, int length, ConverterState *state) const Q_DECL_OVERRIDE {
89             return originalCodec->toUnicode(in, length, state);
90         }
convertFromUnicode(const QChar * in,int length,ConverterState * state) const91         virtual QByteArray convertFromUnicode(const QChar *in, int length, ConverterState *state) const Q_DECL_OVERRIDE {
92             // the QByteArray is embedded into the unicode charset (QProcess hell)
93             QByteArray result;
94             for (int i = 0; i != length; i++) {
95                 if (((in[ i ].unicode()) & 0xFF00) == 0xE000)    // map 0xE000-0xE0FF to 0x0000-0x00FF
96                     result.append((char)(in[ i ].unicode() & 0xFF));
97                 else
98                     result.append(originalCodec->fromUnicode(in + i, 1, state));
99             }
100             return result;
101         }
102 
103     private:
104         QTextCodec * originalCodec;
105     } *krArcCodec;
106 
107 #define SET_KRCODEC    QTextCodec *origCodec = QTextCodec::codecForLocale(); \
108     QTextCodec::setCodecForLocale( krArcCodec );
109 #define RESET_KRCODEC  QTextCodec::setCodecForLocale( origCodec );
110 
111 #endif // KRARC_ENABLED
112 
113     class DummySlave : public KIO::SlaveBase
114     {
115     public:
DummySlave(const QByteArray & pool_socket,const QByteArray & app_socket)116         DummySlave(const QByteArray &pool_socket, const QByteArray &app_socket) :
117                 SlaveBase("kio_krarc", pool_socket, app_socket) {
118             error((int)ERR_SLAVE_DEFINED, QString("krarc is disabled."));
119         }
120     };
121 
kdemain(int argc,char ** argv)122     int Q_DECL_EXPORT kdemain(int argc, char **argv) {
123         if (argc != 4) {
124             qWarning() << "Usage: kio_krarc  protocol domain-socket1 domain-socket2" << endl;
125             exit(-1);
126         }
127 
128 #ifdef KRARC_ENABLED
129         kio_krarcProtocol slave(argv[2], argv[3]);
130 #else
131         DummySlave slave(argv[2], argv[3]);
132 #endif
133 
134         slave.dispatchLoop();
135 
136         return 0;
137     }
138 
139 } // extern "C"
140 
141 #ifdef KRARC_ENABLED
kio_krarcProtocol(const QByteArray & pool_socket,const QByteArray & app_socket)142 kio_krarcProtocol::kio_krarcProtocol(const QByteArray &pool_socket, const QByteArray &app_socket)
143         : SlaveBase("kio_krarc", pool_socket, app_socket), archiveChanged(true), arcFile(0L), extArcReady(false),
144         password(QString()), krConf("krusaderrc"), codec(0)
145 {
146     KRFUNC;
147     confGrp = KConfigGroup(&krConf, "Dependencies");
148 
149     KConfigGroup group(&krConf, "General");
150     QString tmpDirPath = group.readEntry("Temp Directory", _TempDirectory);
151     QDir tmpDir(tmpDirPath);
152     if(!tmpDir.exists()) {
153         for (int i = 1 ; i != -1 ; i = tmpDirPath.indexOf('/', i + 1))
154             QDir().mkdir(tmpDirPath.left(i));
155         QDir().mkdir(tmpDirPath);
156     }
157 
158     arcTempDir = tmpDirPath + DIR_SEPARATOR;
159     QString dirName = "krArc" + QDateTime::currentDateTime().toString(Qt::ISODate);
160     dirName.replace(QRegExp(":"), "_");
161     tmpDir.mkdir(dirName);
162     arcTempDir = arcTempDir + dirName + DIR_SEPARATOR;
163 
164     krArcCodec = new KrArcCodec(QTextCodec::codecForLocale());
165 }
166 
167 /* ---------------------------------------------------------------------------------- */
~kio_krarcProtocol()168 kio_krarcProtocol::~kio_krarcProtocol()
169 {
170     KRFUNC;
171     // delete the temp directory
172     KProcess proc;
173     proc << fullPathName("rm") << "-rf" << arcTempDir;
174     proc.start();
175     proc.waitForFinished();
176 }
177 
178 /* ---------------------------------------------------------------------------------- */
179 
checkWriteSupport()180 bool kio_krarcProtocol::checkWriteSupport()
181 {
182     KRFUNC;
183     krConf.reparseConfiguration();
184     if (KConfigGroup(&krConf, "kio_krarc").readEntry("EnableWrite", false))
185         return true;
186     else {
187         error(ERR_UNSUPPORTED_ACTION,
188               i18n("krarc: write support is disabled.\n"
189                    "You can enable it on the 'Archives' page in Konfigurator."));
190         return false;
191     }
192 }
193 
receivedData(KProcess *,QByteArray & d)194 void kio_krarcProtocol::receivedData(KProcess *, QByteArray &d)
195 {
196     KRFUNC;
197     QByteArray buf(d);
198     data(buf);
199     processedSize(d.length());
200     decompressedLen += d.length();
201 }
202 
mkdir(const QUrl & url,int permissions)203 void kio_krarcProtocol::mkdir(const QUrl &url, int permissions)
204 {
205     KRFUNC;
206     const QString path = getPath(url);
207     KRDEBUG(path);
208 
209     if (!checkWriteSupport())
210         return;
211 
212     // In case of KIO::mkpath call there is a mkdir call for every path element.
213     // Therefore the path all the way up to our archive needs to be checked for existence
214     // and reported as success.
215     if (QDir().exists(path)) {
216         finished();
217         return;
218     }
219 
220     if (!setArcFile(url)) {
221         error(ERR_CANNOT_ENTER_DIRECTORY, path);
222         return;
223     }
224 
225     if (newArchiveURL && !initDirDict(url)) {
226         error(ERR_CANNOT_ENTER_DIRECTORY, path);
227         return;
228     }
229 
230     if (putCmd.isEmpty()) {
231         error(ERR_UNSUPPORTED_ACTION,
232               i18n("Creating directories is not supported with %1 archives", arcType));
233         return;
234     }
235 
236     const QString arcFilePath = getPath(arcFile->url());
237 
238     if (arcType == "arj" || arcType == "lha") {
239         QString arcDir = path.mid(arcFilePath.length());
240         if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR;
241 
242         if (dirDict.find(arcDir) == dirDict.end())
243             addNewDir(arcDir);
244         finished();
245         return;
246     }
247 
248     QString arcDir  = findArcDirectory(url);
249     QString tempDir = arcDir.mid(1) + path.mid(path.lastIndexOf(DIR_SEPARATOR) + 1);
250     if (tempDir.right(1) != DIR_SEPARATOR) tempDir = tempDir + DIR_SEPARATOR;
251 
252     if (permissions == -1) permissions = 0777;  //set default permissions
253 
254     QByteArray arcTempDirEnc = arcTempDir.toLocal8Bit();
255     for (int i = 0;i < tempDir.length() && i >= 0; i = tempDir.indexOf(DIR_SEPARATOR, i + 1)) {
256         QByteArray newDirs = encodeString(tempDir.left(i));
257         newDirs.prepend(arcTempDirEnc);
258         QT_MKDIR(newDirs, permissions);
259     }
260 
261     if (tempDir.endsWith(DIR_SEPARATOR))
262         tempDir.truncate(tempDir.length() - 1);
263 
264     // pack the directory
265     KrLinecountingProcess proc;
266     proc << putCmd << arcFilePath << localeEncodedString(tempDir);
267     infoMessage(i18n("Creating %1...", url.fileName()));
268     QDir::setCurrent(arcTempDir);
269 
270     SET_KRCODEC
271     proc.start();
272     RESET_KRCODEC
273 
274     proc.waitForFinished();
275 
276     // delete the temp directory
277     QDir().rmdir(arcTempDir);
278 
279     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()))  {
280         error(ERR_COULD_NOT_WRITE, path + "\n\n" + proc.getErrorMsg());
281         return;
282     }
283 
284     //  force a refresh of archive information
285     initDirDict(url, true);
286     finished();
287 }
288 
put(const QUrl & url,int permissions,KIO::JobFlags flags)289 void kio_krarcProtocol::put(const QUrl &url, int permissions, KIO::JobFlags flags)
290 {
291     KRFUNC;
292     KRDEBUG(getPath(url));
293 
294     if (!checkWriteSupport())
295         return;
296 
297     bool overwrite = !!(flags & KIO::Overwrite);
298     bool resume = !!(flags & KIO::Resume);
299 
300     if (!setArcFile(url)) {
301         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
302         return;
303     }
304     if (newArchiveURL && !initDirDict(url)) {
305         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
306         return;
307     }
308 
309     if (putCmd.isEmpty()) {
310         error(ERR_UNSUPPORTED_ACTION,
311               i18n("Writing to %1 archives is not supported", arcType));
312         return;
313     }
314     if (!overwrite && findFileEntry(url)) {
315         error(ERR_FILE_ALREADY_EXIST, getPath(url));
316         return;
317     }
318 
319     QString arcDir  = findArcDirectory(url);
320     if (arcDir.isEmpty())
321         KRDEBUG("arcDir is empty.");
322 
323     QString tempFile = arcDir.mid(1) + getPath(url).mid(getPath(url).lastIndexOf(DIR_SEPARATOR) + 1);
324     QString tempDir  = arcDir.mid(1);
325     if (tempDir.right(1) != DIR_SEPARATOR) tempDir = tempDir + DIR_SEPARATOR;
326 
327     if (permissions == -1) permissions = 0777;  //set default permissions
328 
329     QByteArray arcTempDirEnc = arcTempDir.toLocal8Bit();
330     for (int i = 0;i < tempDir.length() && i >= 0; i = tempDir.indexOf(DIR_SEPARATOR, i + 1)) {
331         QByteArray newDirs = encodeString(tempDir.left(i));
332         newDirs.prepend(arcTempDirEnc);
333         QT_MKDIR(newDirs, 0755);
334     }
335 
336     int fd;
337     if (resume) {
338         QByteArray ba = encodeString(tempFile);
339         ba.prepend(arcTempDirEnc);
340         fd = QT_OPEN(ba, O_RDWR);    // append if resuming
341         QT_LSEEK(fd, 0, SEEK_END); // Seek to end
342     } else {
343         // WABA: Make sure that we keep writing permissions ourselves,
344         // otherwise we can be in for a surprise on NFS.
345         mode_t initialMode;
346         if (permissions != -1)
347             initialMode = permissions | S_IWUSR | S_IRUSR;
348         else
349             initialMode = 0666;
350 
351         QByteArray ba = encodeString(tempFile);
352         ba.prepend(arcTempDirEnc);
353         fd = QT_OPEN(ba, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
354     }
355 
356     QByteArray buffer;
357     int readResult;
358     bool isIncomplete = false;
359     do {
360         dataReq();
361         readResult = readData(buffer);
362         auto bytesWritten = ::write(fd, buffer.data(), buffer.size());
363         if (bytesWritten < buffer.size()) {
364             isIncomplete = true;
365             break;
366         }
367     } while (readResult > 0);
368     ::close(fd);
369 
370     if (isIncomplete) {
371         error(ERR_COULD_NOT_WRITE, getPath(url));
372         return;
373     }
374 
375     // pack the file
376     KrLinecountingProcess proc;
377     proc << putCmd << getPath(arcFile->url()) << localeEncodedString(tempFile);
378     infoMessage(i18n("Packing %1...", url.fileName()));
379     QDir::setCurrent(arcTempDir);
380 
381     SET_KRCODEC
382     proc.start();
383     RESET_KRCODEC
384 
385     proc.waitForFinished();
386 
387     // remove the files
388     QDir().rmdir(arcTempDir);
389 
390     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()))  {
391         error(ERR_COULD_NOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg());
392         return;
393     }
394     //  force a refresh of archive information
395     initDirDict(url, true);
396     finished();
397 }
398 
get(const QUrl & url)399 void kio_krarcProtocol::get(const QUrl &url)
400 {
401     KRFUNC;
402     get(url, TRIES_WITH_PASSWORDS);
403 }
404 
get(const QUrl & url,int tries)405 void kio_krarcProtocol::get(const QUrl &url, int tries)
406 {
407     KRFUNC;
408     KRDEBUG(getPath(url));
409     bool decompressToFile = false;
410 
411     if (!setArcFile(url)) {
412         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
413         return;
414     }
415     if (newArchiveURL && !initDirDict(url)) {
416         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
417         return;
418     }
419 
420     if (getCmd.isEmpty()) {
421         error(ERR_UNSUPPORTED_ACTION,
422               i18n("Retrieving data from %1 archives is not supported", arcType));
423         return;
424     }
425     UDSEntry* entry = findFileEntry(url);
426     if (!entry) {
427         error(KIO::ERR_DOES_NOT_EXIST, getPath(url));
428         return;
429     }
430     if (KFileItem(*entry, url).isDir()) {
431         error(KIO::ERR_IS_DIRECTORY, getPath(url));
432         return;
433     }
434     KIO::filesize_t expectedSize = KFileItem(*entry, url).size();
435     // for RPM files extract the cpio file first
436     if (!extArcReady && arcType == "rpm") {
437         KrLinecountingProcess cpio;
438         cpio << "rpm2cpio" << getPath(arcFile->url(), QUrl::StripTrailingSlash);
439         cpio.setStandardOutputFile(arcTempDir + "contents.cpio");
440 
441         cpio.start();
442         cpio.waitForFinished();
443 
444         if (cpio.exitStatus() != QProcess::NormalExit || !checkStatus(cpio.exitCode())) {
445             error(ERR_COULD_NOT_READ, getPath(url) + "\n\n" + cpio.getErrorMsg());
446             return;
447         }
448         extArcReady = true;
449     }
450     // for DEB files extract the tar file first
451     if (!extArcReady && arcType == "deb") {
452         KrLinecountingProcess dpkg;
453         dpkg << cmd << "--fsys-tarfile" << getPath(arcFile->url(), QUrl::StripTrailingSlash);
454         dpkg.setStandardOutputFile(arcTempDir + "contents.cpio");
455 
456         dpkg.start();
457         dpkg.waitForFinished();
458 
459         if (dpkg.exitStatus() != QProcess::NormalExit || !checkStatus(dpkg.exitCode())) {
460             error(ERR_COULD_NOT_READ, getPath(url) + "\n\n" + dpkg.getErrorMsg());
461             return;
462         }
463         extArcReady = true;
464     }
465 
466     // Use the external unpacker to unpack the file
467     QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1);
468     KrLinecountingProcess proc;
469     if (extArcReady) {
470         proc << getCmd << arcTempDir + "contents.cpio" << '*' + localeEncodedString(file);
471     } else if (arcType == "arj" || arcType == "ace" || arcType == "7z") {
472         proc << getCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash) << localeEncodedString(file);
473         if (arcType == "ace" && QFile("/dev/ptmx").exists())    // Don't remove, unace crashes if missing!!!
474             proc.setStandardInputFile("/dev/ptmx");
475         file = url.fileName();
476         decompressToFile = true;
477     } else {
478         decompressedLen = 0;
479         // Determine the mimetype of the file to be retrieved, and emit it.
480         // This is mandatory in all slaves (for KRun/BrowserRun to work).
481         QMimeDatabase db;
482         QMimeType mt = db.mimeTypeForFile(arcTempDir + file);
483         if (mt.isValid())
484             emit mimeType(mt.name());
485 
486         QString escapedFilename = file;
487         if(arcType == "zip") // left bracket needs to be escaped
488             escapedFilename.replace('[', "[[]");
489         proc << getCmd << getPath(arcFile->url());
490         if (arcType != "gzip" && arcType != "bzip2" && arcType != "lzma" && arcType != "xz") proc << localeEncodedString(escapedFilename);
491         connect(&proc, SIGNAL(newOutputData(KProcess *, QByteArray &)),
492                 this, SLOT(receivedData(KProcess *, QByteArray &)));
493         proc.setMerge(false);
494     }
495     infoMessage(i18n("Unpacking %1...", url.fileName()));
496     // change the working directory to our arcTempDir
497     QDir::setCurrent(arcTempDir);
498 
499     SET_KRCODEC
500     proc.setTextModeEnabled(false);
501     proc.start();
502     RESET_KRCODEC
503 
504     proc.waitForFinished();
505 
506     if (!extArcReady && !decompressToFile) {
507         if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()) || (arcType != "bzip2" && arcType != "lzma" && arcType != "xz" && expectedSize != decompressedLen)) {
508             if (encrypted && tries) {
509                 invalidatePassword();
510                 get(url, tries - 1);
511                 return;
512             }
513             error(KIO::ERR_ACCESS_DENIED, getPath(url) + "\n\n" + proc.getErrorMsg());
514             return;
515         }
516     } else {
517         if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()) || !QFileInfo(arcTempDir + file).exists()) {
518             if (decompressToFile)
519                 QFile(arcTempDir + file).remove();
520             if (encrypted && tries) {
521                 invalidatePassword();
522                 get(url, tries - 1);
523                 return;
524             }
525             error(KIO::ERR_ACCESS_DENIED, getPath(url));
526             return;
527         }
528         // the following block is ripped from KDE file KIO::Slave
529         // $Id: krarc.cpp,v 1.43 2007/01/13 13:39:51 ckarai Exp $
530         QByteArray _path(QFile::encodeName(arcTempDir + file));
531         QT_STATBUF buff;
532         if (QT_LSTAT(_path.data(), &buff) == -1) {
533             if (errno == EACCES)
534                 error(KIO::ERR_ACCESS_DENIED, getPath(url));
535             else
536                 error(KIO::ERR_DOES_NOT_EXIST, getPath(url));
537             return;
538         }
539         if (S_ISDIR(buff.st_mode)) {
540             error(KIO::ERR_IS_DIRECTORY, getPath(url));
541             return;
542         }
543         if (!S_ISREG(buff.st_mode)) {
544             error(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url));
545             return;
546         }
547         int fd = QT_OPEN(_path.data(), O_RDONLY);
548         if (fd < 0) {
549             error(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url));
550             return;
551         }
552         // Determine the mimetype of the file to be retrieved, and emit it.
553         // This is mandatory in all slaves (for KRun/BrowserRun to work).
554         QMimeDatabase db;
555         QMimeType mt = db.mimeTypeForFile(arcTempDir + file);
556         if (mt.isValid())
557             emit mimeType(mt.name());
558 
559         KIO::filesize_t processed_size = 0;
560 
561         QString resumeOffset = metaData("resume");
562         if (!resumeOffset.isEmpty()) {
563             bool ok;
564             KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok);
565             if (ok && (offset > 0) && (offset < buff.st_size)) {
566                 if (QT_LSEEK(fd, offset, SEEK_SET) == offset) {
567                     canResume();
568                     processed_size = offset;
569                 }
570             }
571         }
572 
573         totalSize(buff.st_size);
574 
575         char buffer[ MAX_IPC_SIZE ];
576         while (1) {
577             int n = ::read(fd, buffer, MAX_IPC_SIZE);
578             if (n == -1) {
579                 if (errno == EINTR)
580                     continue;
581                 error(KIO::ERR_COULD_NOT_READ, getPath(url));
582                 ::close(fd);
583                 return;
584             }
585             if (n == 0)
586                 break; // Finished
587 
588             {
589                 QByteArray array = QByteArray::fromRawData(buffer, n);
590                 data(array);
591             }
592 
593             processed_size += n;
594         }
595 
596         data(QByteArray());
597         ::close(fd);
598         processedSize(buff.st_size);
599         finished();
600 
601         if (decompressToFile)
602             QFile(arcTempDir + file).remove();
603         return;
604     }
605     // send empty buffer to mark EOF
606     data(QByteArray());
607     finished();
608 }
609 
del(QUrl const & url,bool isFile)610 void kio_krarcProtocol::del(QUrl const & url, bool isFile)
611 {
612     KRFUNC;
613     KRDEBUG(getPath(url));
614 
615     if (!checkWriteSupport())
616         return;
617 
618     if (!setArcFile(url)) {
619         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
620         return;
621     }
622     if (newArchiveURL && !initDirDict(url)) {
623         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
624         return;
625     }
626 
627     if (delCmd.isEmpty()) {
628         error(ERR_UNSUPPORTED_ACTION,
629               i18n("Deleting files from %1 archives is not supported", arcType));
630         return;
631     }
632     if (!findFileEntry(url)) {
633         if ((arcType != "arj" && arcType != "lha") || isFile) {
634             error(KIO::ERR_DOES_NOT_EXIST, getPath(url));
635             return;
636         }
637     }
638 
639     QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1);
640     if (!isFile && file.right(1) != DIR_SEPARATOR) {
641         if (arcType == "zip") file = file + DIR_SEPARATOR;
642     }
643     KrLinecountingProcess proc;
644     proc << delCmd << getPath(arcFile->url()) << localeEncodedString(file);
645     infoMessage(i18n("Deleting %1...", url.fileName()));
646 
647     SET_KRCODEC
648     proc.start();
649     RESET_KRCODEC
650 
651     proc.waitForFinished();
652     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()))  {
653         error(ERR_COULD_NOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg());
654         return;
655     }
656     //  force a refresh of archive information
657     initDirDict(url, true);
658     finished();
659 }
660 
stat(const QUrl & url)661 void kio_krarcProtocol::stat(const QUrl &url)
662 {
663     KRFUNC;
664     KRDEBUG(getPath(url));
665     if (!setArcFile(url)) {
666         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
667         return;
668     }
669     if (newArchiveURL && !initDirDict(url)) {
670         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
671         return;
672     }
673 
674     if (listCmd.isEmpty()) {
675         error(ERR_UNSUPPORTED_ACTION,
676               i18n("Accessing files is not supported with %1 archives", arcType));
677         return;
678     }
679     QString path = getPath(url, QUrl::StripTrailingSlash);
680     QUrl newUrl = url;
681 
682     // but treat the archive itself as the archive root
683     if (path == getPath(arcFile->url(), QUrl::StripTrailingSlash)) {
684         newUrl.setPath(path + DIR_SEPARATOR);
685         path = getPath(newUrl);
686     }
687     // we might be stating a real file
688     if (QFileInfo(path).exists()) {
689         QT_STATBUF buff;
690         QT_STAT(path.toLocal8Bit(), &buff);
691         QString mime;
692         QMimeDatabase db;
693         QMimeType result = db.mimeTypeForFile(path);
694         if (result.isValid())
695             mime = result.name();
696         statEntry(KFileItem(QUrl::fromLocalFile(path), mime, buff.st_mode).entry());
697         finished();
698         return;
699     }
700     UDSEntry* entry = findFileEntry(newUrl);
701     if (entry) {
702         statEntry(*entry);
703         finished();
704     } else error(KIO::ERR_DOES_NOT_EXIST, path);
705 }
706 
copy(const QUrl & url,const QUrl & dest,int,KIO::JobFlags flags)707 void kio_krarcProtocol::copy(const QUrl &url, const QUrl &dest, int, KIO::JobFlags flags)
708 {
709     KRDEBUG("source: " << url.path() << " dest: " << dest.path());
710 
711     if (!checkWriteSupport())
712         return;
713 
714     bool overwrite = !!(flags & KIO::Overwrite);
715 
716     // KDE HACK: opening the password dlg in copy causes error for the COPY, and further problems
717     // that's why encrypted files are not allowed to copy
718     if (!encrypted && dest.isLocalFile())
719         do {
720             if (url.fileName() != dest.fileName())
721                 break;
722 
723             if (QTextCodec::codecForLocale()->name() != codec->name())
724                 break;
725 
726             //the file exists and we don't want to overwrite
727             if ((!overwrite) && (QFile(getPath(dest)).exists())) {
728                 error((int)ERR_FILE_ALREADY_EXIST, QString(QFile::encodeName(getPath(dest))));
729                 return;
730             };
731 
732             if (!setArcFile(url)) {
733                 error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
734                 return;
735             }
736             if (newArchiveURL && !initDirDict(url)) {
737                 error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
738                 return;
739             }
740 
741             UDSEntry* entry = findFileEntry(url);
742             if (copyCmd.isEmpty() || !entry)
743                 break;
744 
745             QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1);
746 
747             QString destDir = getPath(dest, QUrl::StripTrailingSlash);
748             if (!QDir(destDir).exists()) {
749                 int ndx = destDir.lastIndexOf(DIR_SEPARATOR_CHAR);
750                 if (ndx != -1)
751                     destDir.truncate(ndx + 1);
752             }
753 
754             QDir::setCurrent(destDir);
755 
756             QString escapedFilename = file;
757             if(arcType == "zip") {
758                 // left bracket needs to be escaped
759                 escapedFilename.replace('[', "[[]");
760             }
761 
762             KrLinecountingProcess proc;
763             proc << copyCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash) << escapedFilename;
764             if (arcType == "ace" && QFile("/dev/ptmx").exists())    // Don't remove, unace crashes if missing!!!
765                 proc.setStandardInputFile("/dev/ptmx");
766             proc.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect
767 
768             infoMessage(i18n("Unpacking %1...", url.fileName()));
769             proc.start();
770             proc.waitForFinished();
771             if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()))  {
772                 error(KIO::ERR_COULD_NOT_WRITE, getPath(dest, QUrl::StripTrailingSlash) + "\n\n" + proc.getErrorMsg());
773                 return;
774             }
775             if (!QFileInfo(getPath(dest, QUrl::StripTrailingSlash)).exists()) {
776                 error(KIO::ERR_COULD_NOT_WRITE, getPath(dest, QUrl::StripTrailingSlash));
777                 return;
778             }
779 
780             processedSize(KFileItem(*entry, url).size());
781             finished();
782             QDir::setCurrent(QDir::rootPath());   /* for being able to umount devices after copying*/
783             return;
784         } while (0);
785 
786     if (encrypted)
787         KRDEBUG("ERROR: " << url.path() << " is encrypted.");
788     if (!dest.isLocalFile())
789         KRDEBUG("ERROR: " << url.path() << " is not a local file.");
790 
791     // CMD_COPY is no more in KF5 - replaced with 74 value (as stated in kio/src/core/commands_p.h, which could be found in cgit.kde.org/kio.git/tree)
792     error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, 74));
793 }
794 
rename(const QUrl & src,const QUrl & dest,KIO::JobFlags flags)795 void kio_krarcProtocol::rename(const QUrl& src, const QUrl& dest, KIO::JobFlags flags)
796 {
797     Q_UNUSED(flags);
798 
799     KRDEBUG("renaming from: " << src.path() << " to: " << dest.path());
800     KRDEBUG("command: " << arcPath);
801 
802     if (!checkWriteSupport()) {
803         return;
804     }
805 
806     if (renCmd.isEmpty()) {
807         error(KIO::ERR_CANNOT_RENAME, src.fileName());
808         return;
809     }
810 
811     if (src.fileName() == dest.fileName()) {
812         return;
813     }
814 
815     KrLinecountingProcess proc;
816     proc << renCmd << arcPath << src.path().replace(arcPath + '/', "") << dest.path().replace(arcPath + '/', "");
817     proc.start();
818     proc.waitForFinished();
819 
820     if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()))  {
821         error(KIO::ERR_CANNOT_RENAME, src.fileName());
822         return;
823     }
824 
825     finished();
826 }
827 
listDir(const QUrl & url)828 void kio_krarcProtocol::listDir(const QUrl &url)
829 {
830     KRFUNC;
831     KRDEBUG(getPath(url));
832     if (!setArcFile(url)) {
833         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
834         return;
835     }
836     if (listCmd.isEmpty()) {
837         error(ERR_UNSUPPORTED_ACTION,
838               i18n("Listing directories is not supported for %1 archives", arcType));
839         return;
840     }
841     QString path = getPath(url);
842     if (path.right(1) != DIR_SEPARATOR) path = path + DIR_SEPARATOR;
843 
844     // it might be a real dir !
845     if (QFileInfo(path).exists()) {
846         if (QFileInfo(path).isDir()) {
847             QUrl redir;
848             redir.setPath(getPath(url));
849             redirection(redir);
850             finished();
851         } else { // maybe it's an archive !
852             error(ERR_IS_FILE, path);
853         }
854         return;
855     }
856     if (!initDirDict(url)) {
857         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
858         return;
859     }
860     QString arcDir = path.mid(getPath(arcFile->url()).length());
861     arcDir.truncate(arcDir.lastIndexOf(DIR_SEPARATOR));
862     if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR;
863 
864     if (dirDict.find(arcDir) == dirDict.end()) {
865         error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url));
866         return;
867     }
868     UDSEntryList* dirList = dirDict[ arcDir ];
869     totalSize(dirList->size());
870     listEntries(*dirList);
871     finished();
872 }
873 
setArcFile(const QUrl & url)874 bool kio_krarcProtocol::setArcFile(const QUrl &url)
875 {
876     KRFUNC;
877     KRDEBUG(url.fileName());
878     QString path = getPath(url);
879     time_t currTime = time(0);
880     archiveChanged = true;
881     newArchiveURL = true;
882     // is the file already set ?
883     if (arcFile && getPath(arcFile->url(), QUrl::StripTrailingSlash) == path.left(getPath(arcFile->url(), QUrl::StripTrailingSlash).length())) {
884         newArchiveURL = false;
885         // Has it changed ?
886         KFileItem* newArcFile = new KFileItem(arcFile->url(), QString(), arcFile->mode());
887         if (metaData("Charset") != currentCharset || !newArcFile->cmp(*arcFile)) {
888             currentCharset = metaData("Charset");
889 
890             codec = QTextCodec::codecForName(currentCharset.toLatin1());
891             if (codec == 0)
892                 codec = QTextCodec::codecForMib(4 /* latin-1 */);
893 
894             delete arcFile;
895             password.clear();
896             extArcReady = false;
897             arcFile = newArcFile;
898         } else { // same old file
899             delete newArcFile;
900             archiveChanged = false;
901             if (encrypted && password.isNull())
902                 initArcParameters();
903         }
904     } else { // it's a new file...
905         extArcReady = false;
906 
907         // new archive file means new dirDict, too
908         dirDict.clear();
909 
910         if (arcFile) {
911             delete arcFile;
912             password.clear();
913             arcFile = 0L;
914         }
915         QString newPath = path;
916         if (newPath.right(1) != DIR_SEPARATOR) newPath = newPath + DIR_SEPARATOR;
917         for (int pos = 0; pos >= 0; pos = newPath.indexOf(DIR_SEPARATOR, pos + 1)) {
918             QFileInfo qfi(newPath.left(pos));
919             if (qfi.exists() && !qfi.isDir()) {
920                 QT_STATBUF stat_p;
921                 QT_LSTAT(newPath.left(pos).toLocal8Bit(), &stat_p);
922                 arcFile = new KFileItem(QUrl::fromLocalFile(newPath.left(pos)), QString(), stat_p.st_mode);
923                 break;
924             }
925         }
926         if (!arcFile) {
927             // KRDEBUG("ERROR: " << path << " does not exist.");
928             error(ERR_DOES_NOT_EXIST, path);
929             return false; // file not found
930         }
931         currentCharset = metaData("Charset");
932 
933         codec = QTextCodec::codecForName(currentCharset.toLatin1());
934         if (codec == 0)
935             codec = QTextCodec::codecForMib(4 /* latin-1 */);
936     }
937 
938     /* FIX: file change can only be detected if the timestamp between the two consequent
939        changes is more than 1s. If the archive is continuously changing (check: move files
940        inside the archive), krarc may erronously think, that the archive file is unchanged,
941        because the timestamp is the same as the previous one. This situation can only occur
942        if the modification time equals with the current time. While this condition is true,
943        we can say, that the archive is changing, so content reread is always necessary
944        during that period. */
945     if (archiveChanging)
946         archiveChanged = true;
947     archiveChanging = (currTime == (time_t)arcFile->time(KFileItem::ModificationTime).toTime_t());
948 
949     arcPath = getPath(arcFile->url(), QUrl::StripTrailingSlash);
950     arcType = detectArchive(encrypted, arcPath);
951 
952     if (arcType == "tbz")
953         arcType = "bzip2";
954     else if (arcType == "tgz")
955         arcType = "gzip";
956     else if (arcType == "tlz")
957         arcType = "lzma";
958     else if (arcType == "txz")
959         arcType = "xz";
960 
961     if (arcType.isEmpty()) {
962         arcType = arcFile->mimetype();
963         arcType = getShortTypeFromMime(arcType);
964         if (arcType == "jar")
965             arcType = "zip";
966     }
967 
968     return initArcParameters();
969 }
970 
initDirDict(const QUrl & url,bool forced)971 bool kio_krarcProtocol::initDirDict(const QUrl &url, bool forced)
972 {
973     KRFUNC;
974     KRDEBUG(getPath(url));
975     // set the archive location
976     //if( !setArcFile(getPath(url)) ) return false;
977     // no need to rescan the archive if it's not changed
978         // KRDEBUG("achiveChanged: " << archiveChanged << " forced: " << forced);
979     if (!archiveChanged && !forced) {
980         // KRDEBUG("doing nothing.");
981         return true;
982     }
983 
984     extArcReady = false;
985 
986     if (!setArcFile(url))
987         return false;   /* if the archive was changed refresh the file information */
988 
989     // write the temp file
990     KrLinecountingProcess proc;
991     QTemporaryFile temp;
992 
993     // parse the temp file
994     if (!temp.open()) {
995         error(ERR_COULD_NOT_READ, temp.fileName());
996         return false;
997     }
998 
999     if (arcType != "bzip2" && arcType != "lzma" && arcType != "xz") {
1000         if (arcType == "rpm") {
1001             proc << listCmd << arcPath;
1002             proc.setStandardOutputFile(temp.fileName());
1003         } else {
1004             proc << listCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash);
1005             proc.setStandardOutputFile(temp.fileName());
1006         }
1007         if (arcType == "ace" && QFile("/dev/ptmx").exists())    // Don't remove, unace crashes if missing!!!
1008             proc.setStandardInputFile("/dev/ptmx");
1009 
1010         proc.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect
1011         proc.start();
1012         proc.waitForFinished();
1013         if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) return false;
1014     }
1015     // clear the dir dictionary
1016 
1017     QHashIterator< QString, KIO::UDSEntryList *> lit(dirDict);
1018     while (lit.hasNext())
1019         delete lit.next().value();
1020     dirDict.clear();
1021 
1022     // add the "/" directory
1023     UDSEntryList* root = new UDSEntryList();
1024     dirDict.insert(DIR_SEPARATOR, root);
1025     // and the "/" UDSEntry
1026     UDSEntry entry;
1027     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_NAME, ".");
1028     mode_t mode = parsePermString("drwxr-xr-x");
1029     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT);   // keep file type only
1030     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_ACCESS, mode & 07777);   // keep permissions only
1031 
1032     root->append(entry);
1033 
1034     if (arcType == "bzip2" || arcType == "lzma" || arcType == "xz")
1035         abort();
1036 
1037     char buf[1000];
1038     QString line;
1039 
1040     int lineNo = 0;
1041     bool invalidLine = false;
1042     // the rar list is started with a ------ line.
1043     if (arcType == "rar" || arcType == "arj" || arcType == "lha" || arcType == "7z") {
1044         while (temp.readLine(buf, 1000) != -1) {
1045             line = decodeString(buf);
1046             if (line.startsWith(QLatin1String("----------"))) break;
1047         }
1048     }
1049     while (temp.readLine(buf, 1000) != -1) {
1050         line = decodeString(buf);
1051         if (arcType == "rar") {
1052             // the rar list is ended with a ------ line.
1053             if (line.startsWith(QLatin1String("----------"))) {
1054                 invalidLine = !invalidLine;
1055                 break;
1056             }
1057             if (invalidLine)
1058                 continue;
1059             else {
1060                 if (line[0] == '*') // encrypted archives starts with '*'
1061                     line[0] = ' ';
1062             }
1063         }
1064         if (arcType == "ace") {
1065             // the ace list begins with a number.
1066             if (!line[0].isDigit()) continue;
1067         }
1068         if (arcType == "arj") {
1069             // the arj list is ended with a ------ line.
1070             if (line.startsWith(QLatin1String("----------"))) {
1071                 invalidLine = !invalidLine;
1072                 continue;
1073             }
1074             if (invalidLine)
1075                 continue;
1076             else {
1077                 temp.readLine(buf, 1000);
1078                 line = line + decodeString(buf);
1079                 temp.readLine(buf, 1000);
1080                 line = line + decodeString(buf);
1081                 temp.readLine(buf, 1000);
1082                 line = line + decodeString(buf);
1083             }
1084         }
1085         if (arcType == "lha" || arcType == "7z") {
1086             // the arj list is ended with a ------ line.
1087             if (line.startsWith(QLatin1String("----------"))) break;
1088         }
1089         parseLine(lineNo++, line.trimmed());
1090     }
1091     // close and delete our file
1092     temp.close();
1093 
1094     archiveChanged = false;
1095     // KRDEBUG("done.");
1096     return true;
1097 }
1098 
findArcDirectory(const QUrl & url)1099 QString kio_krarcProtocol::findArcDirectory(const QUrl &url)
1100 {
1101     KRFUNC;
1102     KRDEBUG(url.fileName());
1103 
1104     QString path = getPath(url);
1105     if (path.right(1) == DIR_SEPARATOR) path.truncate(path.length() - 1);
1106 
1107     if (!initDirDict(url)) {
1108         return QString();
1109     }
1110     QString arcDir = path.mid(getPath(arcFile->url()).length());
1111     arcDir.truncate(arcDir.lastIndexOf(DIR_SEPARATOR));
1112     if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR;
1113 
1114     return arcDir;
1115 }
1116 
findFileEntry(const QUrl & url)1117 UDSEntry* kio_krarcProtocol::findFileEntry(const QUrl &url)
1118 {
1119     KRFUNC;
1120     QString arcDir = findArcDirectory(url);
1121     if (arcDir.isEmpty()) return 0;
1122 
1123     QHash<QString, KIO::UDSEntryList *>::iterator itef = dirDict.find(arcDir);
1124     if (itef == dirDict.end())
1125         return 0;
1126     UDSEntryList* dirList = itef.value();
1127 
1128     QString name = getPath(url);
1129     if (getPath(arcFile->url(), QUrl::StripTrailingSlash) == getPath(url, QUrl::StripTrailingSlash)) name = '.'; // the '/' case
1130     else {
1131         if (name.right(1) == DIR_SEPARATOR) name.truncate(name.length() - 1);
1132         name = name.mid(name.lastIndexOf(DIR_SEPARATOR) + 1);
1133     }
1134 
1135     UDSEntryList::iterator entry;
1136 
1137     for (entry = dirList->begin(); entry != dirList->end(); ++entry) {
1138         if ((entry->contains(KIO::UDSEntry::UDS_NAME)) &&
1139                 (entry->stringValue(KIO::UDSEntry::UDS_NAME) == name))
1140             return &(*entry);
1141     }
1142     return 0;
1143 }
1144 
nextWord(QString & s,char d)1145 QString kio_krarcProtocol::nextWord(QString &s, char d)
1146 {
1147     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
1148     s = s.trimmed();
1149     int j = s.indexOf(d, 0);
1150     QString temp = s.left(j); // find the leftmost word.
1151     s.remove(0, j);
1152     return temp;
1153 }
1154 
parsePermString(QString perm)1155 mode_t kio_krarcProtocol::parsePermString(QString perm)
1156 {
1157     KRFUNC;
1158     mode_t mode = 0;
1159     // file type
1160     if (perm[0] == 'd') mode |= S_IFDIR;
1161 #ifndef Q_WS_WIN
1162     if (perm[0] == 'l') mode |= S_IFLNK;
1163 #endif
1164     if (perm[0] == '-') mode |= S_IFREG;
1165     // owner permissions
1166     if (perm[1] != '-') mode |= S_IRUSR;
1167     if (perm[2] != '-') mode |= S_IWUSR;
1168     if (perm[3] != '-') mode |= S_IXUSR;
1169 #ifndef Q_WS_WIN
1170     // group permissions
1171     if (perm[4] != '-') mode |= S_IRGRP;
1172     if (perm[5] != '-') mode |= S_IWGRP;
1173     if (perm[6] != '-') mode |= S_IXGRP;
1174     // other permissions
1175     if (perm[7] != '-') mode |= S_IROTH;
1176     if (perm[8] != '-') mode |= S_IWOTH;
1177     if (perm[9] != '-') mode |= S_IXOTH;
1178 #endif
1179     return mode;
1180 }
1181 
addNewDir(QString path)1182 UDSEntryList* kio_krarcProtocol::addNewDir(QString path)
1183 {
1184     KRFUNC;
1185     UDSEntryList* dir;
1186 
1187     // check if the current dir exists
1188     QHash<QString, KIO::UDSEntryList *>::iterator itef = dirDict.find(path);
1189     if (itef != dirDict.end())
1190         return itef.value();
1191 
1192     // set dir to the parent dir
1193     dir = addNewDir(path.left(path.lastIndexOf(DIR_SEPARATOR, -2) + 1));
1194 
1195     // add a new entry in the parent dir
1196     QString name = path.mid(path.lastIndexOf(DIR_SEPARATOR, -2) + 1);
1197     name = name.left(name.length() - 1);
1198 
1199     if (name == "." || name == "..") { // entries with these names wouldn't be displayed
1200         // don't translate since this is an internal error
1201         QString err = QString("Cannot handle path: ") + path;
1202         // KRDEBUG("ERROR: " << err);
1203         error(KIO::ERR_INTERNAL, err);
1204         exit();
1205     }
1206 
1207     UDSEntry entry;
1208     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_NAME, name);
1209     mode_t mode = parsePermString("drwxr-xr-x");
1210     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT);   // keep file type only
1211     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_ACCESS, mode & 07777);   // keep permissions only
1212     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_SIZE, 0);
1213     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_MODIFICATION_TIME, arcFile->time(KFileItem::ModificationTime).toTime_t());
1214 
1215     dir->append(entry);
1216 
1217     // create a new directory entry and add it..
1218     dir = new UDSEntryList();
1219     dirDict.insert(path, dir);
1220 
1221     return dir;
1222 }
1223 
parseLine(int lineNo,QString line)1224 void kio_krarcProtocol::parseLine(int lineNo, QString line)
1225 {
1226     KRFUNC;
1227     UDSEntryList* dir;
1228     UDSEntry entry;
1229 
1230     QString owner;
1231     QString group;
1232     QString symlinkDest;
1233     QString perm;
1234     mode_t mode          = 0666;
1235     size_t size          = 0;
1236     time_t time          = ::time(0);
1237     QString fullName;
1238 
1239     if (arcType == "zip") {
1240         // permissions
1241         perm = nextWord(line);
1242         // ignore the next 2 fields
1243         nextWord(line); nextWord(line);
1244         // size
1245         size = nextWord(line).toLong();
1246         // ignore the next 2 fields
1247         nextWord(line);nextWord(line);
1248         // date & time
1249         QString d = nextWord(line);
1250         QDate qdate(d.mid(0, 4).toInt(), d.mid(4, 2).toInt(), d.mid(6, 2).toInt());
1251         QTime qtime(d.mid(9, 2).toInt(), d.mid(11, 2).toInt(), d.mid(13, 2).toInt());
1252         time = QDateTime(qdate, qtime).toTime_t();
1253         // full name
1254         fullName = nextWord(line, '\n');
1255 
1256         if (perm.length() != 10)
1257             perm = (perm.at(0) == 'd' || fullName.endsWith(DIR_SEPARATOR)) ? "drwxr-xr-x" : "-rw-r--r--" ;
1258         mode = parsePermString(perm);
1259     }
1260     if (arcType == "rar") {
1261         // permissions
1262         perm = nextWord(line);
1263         // size
1264         size = nextWord(line).toLong();
1265         // ignore the next 2 fields : packed size and compression ration
1266         nextWord(line); nextWord(line);
1267         // date & time
1268         QString d = nextWord(line);
1269         int year = 1900 + d.mid(6, 2).toInt();
1270         if (year < 1930)
1271             year += 100;
1272         QDate qdate(year, d.mid(3, 2).toInt(), d.mid(0, 2).toInt());
1273         QString t = nextWord(line);
1274         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0);
1275         time = QDateTime(qdate, qtime).toTime_t();
1276         // checksum : ignored
1277         nextWord(line);
1278         // full name
1279         fullName = nextWord(line, '\n');
1280 
1281         if (perm.length() == 7) { // windows rar permission format
1282             bool isDir  = (perm.at(1).toLower() == 'd');
1283             bool isReadOnly = (perm.at(2).toLower() == 'r');
1284 
1285             perm = isDir ? "drwxr-xr-x" : "-rw-r--r--";
1286 
1287             if (isReadOnly)
1288                 perm[ 2 ] = '-';
1289         }
1290 
1291         if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ;
1292         mode = parsePermString(perm);
1293     }
1294     if (arcType == "arj") {
1295         nextWord(line);
1296         // full name
1297         fullName = nextWord(line, '\n');
1298         // ignore the next 2 fields
1299         nextWord(line); nextWord(line);
1300         // size
1301         size = nextWord(line).toLong();
1302         // ignore the next 2 fields
1303         nextWord(line); nextWord(line);
1304         // date & time
1305         QString d = nextWord(line);
1306         int year = 1900 + d.mid(0, 2).toInt();
1307         if (year < 1930) year += 100;
1308         QDate qdate(year, d.mid(3, 2).toInt(), d.mid(6, 2).toInt());
1309         QString t = nextWord(line);
1310         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0);
1311         time = QDateTime(qdate, qtime).toTime_t();
1312         // permissions
1313         perm = nextWord(line);
1314         if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ;
1315         mode = parsePermString(perm);
1316     }
1317     if (arcType == "rpm") {
1318         // full name
1319         fullName = nextWord(line);
1320         // size
1321         size = nextWord(line).toULong();
1322         // date & time
1323         time = nextWord(line).toULong();
1324         // next field is md5sum, ignore it
1325         nextWord(line);
1326         // permissions
1327         mode = nextWord(line).toULong(0, 8);
1328         // Owner & Group
1329         owner = nextWord(line);
1330         group = nextWord(line);
1331         // symlink destination
1332 #ifndef Q_WS_WIN
1333         if (S_ISLNK(mode)) {
1334             // ignore the next 3 fields
1335             nextWord(line); nextWord(line); nextWord(line);
1336             symlinkDest = nextWord(line);
1337         }
1338 #endif
1339     }
1340     if (arcType == "gzip") {
1341         if (!lineNo) return;  //ignore the first line
1342         // first field is uncompressed size - ignore it
1343         nextWord(line);
1344         // size
1345         size = nextWord(line).toULong();
1346         // ignore the next field
1347         nextWord(line);
1348         // full name
1349         fullName = nextWord(line);
1350         fullName = fullName.mid(fullName.lastIndexOf(DIR_SEPARATOR) + 1);
1351     }
1352     if (arcType == "lzma") {
1353         fullName = arcFile->name();
1354         if (fullName.endsWith(QLatin1String("lzma"))) {
1355             fullName.truncate(fullName.length() - 5);
1356         }
1357         mode = arcFile->mode();
1358         size = arcFile->size();
1359     }
1360     if (arcType == "xz") {
1361         fullName = arcFile->name();
1362         if (fullName.endsWith(QLatin1String("xz"))) {
1363             fullName.truncate(fullName.length() - 3);
1364         }
1365         mode = arcFile->mode();
1366         size = arcFile->size();
1367     }
1368     if (arcType == "bzip2") {
1369         // There is no way to list bzip2 files, so we take our information from
1370         // the archive itself...
1371         fullName = arcFile->name();
1372         if (fullName.endsWith(QLatin1String("bz2"))) {
1373             fullName.truncate(fullName.length() - 4);
1374         }
1375         mode = arcFile->mode();
1376         size = arcFile->size();
1377     }
1378     if (arcType == "lha") {
1379         // permissions
1380         perm = nextWord(line);
1381         if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ;
1382         mode = parsePermString(perm);
1383         // ignore the next field
1384         nextWord(line);
1385         // size
1386         size = nextWord(line).toLong();
1387         // ignore the next field
1388         nextWord(line);
1389         // date & time
1390         int month = (QString("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec").split(',')).indexOf(nextWord(line)) + 1;
1391         int day = nextWord(line).toInt();
1392         int year = QDate::currentDate().year();
1393         QString third = nextWord(line);
1394         QTime qtime;
1395 
1396         if (third.contains(":"))
1397             qtime = QTime::fromString(third);
1398         else
1399             year = third.toInt();
1400 
1401         QDate qdate(year, month, day);
1402 
1403         time = QDateTime(qdate, qtime).toTime_t();
1404         // full name
1405         fullName = nextWord(line, '\n');
1406     }
1407     if (arcType == "ace") {
1408         // date & time
1409         QString d = nextWord(line);
1410         int year = 1900 + d.mid(6, 2).toInt();
1411         if (year < 1930) year += 100;
1412         QDate qdate(year, d.mid(3, 2).toInt(), d.mid(0, 2).toInt());
1413         QString t = nextWord(line);
1414         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0);
1415         time = QDateTime(qdate, qtime).toTime_t();
1416         // ignore the next field
1417         nextWord(line);
1418         // size
1419         size = nextWord(line).toLong();
1420         // ignore the next field
1421         nextWord(line);
1422         // full name
1423         fullName = nextWord(line, '\n');
1424         if (fullName[ 0 ] == '*')  // encrypted archives starts with '*'
1425             fullName = fullName.mid(1);
1426     }
1427     if (arcType == "deb") {
1428         // permissions
1429         perm = nextWord(line);
1430         mode = parsePermString(perm);
1431         // Owner & Group
1432         owner = nextWord(line, DIR_SEPARATOR_CHAR);
1433         group = nextWord(line).mid(1);
1434         // size
1435         size = nextWord(line).toLong();
1436         // date & time
1437         QString d = nextWord(line);
1438         QDate qdate(d.mid(0, 4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt());
1439         QString t = nextWord(line);
1440         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0);
1441         time = QDateTime(qdate, qtime).toTime_t();
1442         // full name
1443         fullName = nextWord(line, '\n').mid(1);
1444         //if ( fullName.right( 1 ) == "/" ) return;
1445         if (fullName.contains("->")) {
1446             symlinkDest = fullName.mid(fullName.indexOf("->") + 2);
1447             fullName = fullName.left(fullName.indexOf("->") - 1);
1448         }
1449     }
1450     if (arcType == "7z") {
1451         // date & time
1452         QString d = nextWord(line);
1453         QDate qdate(d.mid(0, 4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt());
1454         QString t = nextWord(line);
1455         QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), t.mid(6, 2).toInt());
1456         time = QDateTime(qdate, qtime).toTime_t();
1457 
1458         // permissions
1459         perm = nextWord(line);
1460         bool isDir  = (perm.at(0).toLower() == 'd');
1461         bool isReadOnly = (perm.at(1).toLower() == 'r');
1462         perm = isDir ? "drwxr-xr-x" : "-rw-r--r--";
1463         if (isReadOnly)
1464             perm[ 2 ] = '-';
1465 
1466         mode = parsePermString(perm);
1467 
1468         // size
1469         size = nextWord(line).toLong();
1470 
1471         // ignore the next 15 characters
1472         line = line.mid(15);
1473 
1474         // full name
1475         fullName = nextWord(line, '\n');
1476     }
1477 
1478     if (fullName.right(1) == DIR_SEPARATOR) fullName = fullName.left(fullName.length() - 1);
1479     if (!fullName.startsWith(DIR_SEPARATOR)) fullName = DIR_SEPARATOR + fullName;
1480     QString path = fullName.left(fullName.lastIndexOf(DIR_SEPARATOR) + 1);
1481     // set/create the directory UDSEntryList
1482     QHash<QString, KIO::UDSEntryList *>::iterator itef = dirDict.find(path);
1483     if (itef == dirDict.end())
1484         dir = addNewDir(path);
1485     else
1486         dir = itef.value();
1487 
1488     QString name = fullName.mid(fullName.lastIndexOf(DIR_SEPARATOR) + 1);
1489     // file name
1490     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_NAME, name);
1491     // file type
1492     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT);   // keep file type only
1493     // file permissions
1494     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_ACCESS, mode & 07777);   // keep permissions only
1495     // file size
1496     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_SIZE, size);
1497     // modification time
1498     entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_MODIFICATION_TIME, time);
1499     // link destination
1500     if (!symlinkDest.isEmpty()) {
1501         entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_LINK_DEST, symlinkDest);
1502     }
1503     if (S_ISDIR(mode)) {
1504         fullName = fullName + DIR_SEPARATOR;
1505         if (dirDict.find(fullName) == dirDict.end())
1506             dirDict.insert(fullName, new UDSEntryList());
1507         else {
1508             // try to overwrite an existing entry
1509             UDSEntryList::iterator entryIt;
1510 
1511             for (entryIt = dir->begin(); entryIt != dir->end(); ++entryIt) {
1512                 if (entryIt->contains(KIO::UDSEntry::UDS_NAME) &&
1513                         entryIt->stringValue(KIO::UDSEntry::UDS_NAME) == name) {
1514                     entryIt->UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_MODIFICATION_TIME, time);
1515                     entryIt->UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_ACCESS, mode);
1516                     return;
1517                 }
1518             }
1519             return; // there is already an entry for this directory
1520         }
1521     }
1522 
1523     // multi volume archives can add a file twice, use only one
1524     UDSEntryList::iterator dirEntryIt;
1525 
1526     for (dirEntryIt = dir->begin(); dirEntryIt != dir->end(); ++dirEntryIt)
1527         if (dirEntryIt->contains(KIO::UDSEntry::UDS_NAME) &&
1528                 dirEntryIt->stringValue(KIO::UDSEntry::UDS_NAME) == name)
1529             return;
1530 
1531     dir->append(entry);
1532 }
1533 
initArcParameters()1534 bool kio_krarcProtocol::initArcParameters()
1535 {
1536     KRFUNC;
1537     KRDEBUG("arcType: " << arcType);
1538 
1539     noencoding = false;
1540 
1541     cmd.clear();
1542     listCmd = QStringList();
1543     getCmd  = QStringList();
1544     copyCmd = QStringList();
1545     delCmd  = QStringList();
1546     putCmd  = QStringList();
1547     renCmd  = QStringList();
1548 
1549     if (arcType == "zip") {
1550         noencoding = true;
1551         cmd     = fullPathName("unzip");
1552         listCmd << fullPathName("unzip") << "-ZTs-z-t-h";
1553         getCmd  << fullPathName("unzip") << "-p";
1554         copyCmd << fullPathName("unzip") << "-jo";
1555 
1556         if (QStandardPaths::findExecutable(QStringLiteral("zip")).isEmpty()) {
1557             delCmd  = QStringList();
1558             putCmd  = QStringList();
1559         } else {
1560             delCmd  << fullPathName("zip") << "-d";
1561             putCmd  << fullPathName("zip") << "-ry";
1562         }
1563 
1564         if (!QStandardPaths::findExecutable(QStringLiteral("7za")).isEmpty()) {
1565             renCmd  << fullPathName("7za") << "rn";
1566         }
1567 
1568         if (!getPassword().isEmpty()) {
1569             getCmd  << "-P" << password;
1570             copyCmd << "-P" << password;
1571             putCmd  << "-P" << password;
1572         }
1573     } else if (arcType == "rar") {
1574         noencoding = true;
1575         if (QStandardPaths::findExecutable(QStringLiteral("rar")).isEmpty()) {
1576             cmd     = fullPathName("unrar");
1577             listCmd << fullPathName("unrar") << "-c-" << "-v" << "v";
1578             getCmd  << fullPathName("unrar") << "p" << "-ierr" << "-idp" << "-c-" << "-y";
1579             copyCmd << fullPathName("unrar") << "e" << "-y";
1580             delCmd  = QStringList();
1581             putCmd  = QStringList();
1582         } else {
1583             cmd     = fullPathName("rar");
1584             listCmd << fullPathName("rar") << "-c-" << "-v" << "v";
1585             getCmd  << fullPathName("rar") << "p" << "-ierr" << "-idp" << "-c-" << "-y";
1586             copyCmd << fullPathName("rar") << "e" << "-y";
1587             delCmd  << fullPathName("rar") << "d";
1588             putCmd  << fullPathName("rar") << "-r" << "a";
1589         }
1590         if (!getPassword().isEmpty()) {
1591             getCmd  << QString("-p%1").arg(password);
1592             listCmd << QString("-p%1").arg(password);
1593             copyCmd << QString("-p%1").arg(password);
1594             if (!putCmd.isEmpty()) {
1595                 putCmd << QString("-p%1").arg(password);
1596                 delCmd << QString("-p%1").arg(password);
1597             }
1598         }
1599     } else if (arcType == "rpm") {
1600         cmd     = fullPathName("rpm");
1601         listCmd << fullPathName("rpm") << "--dump" << "-lpq";
1602         getCmd  << fullPathName("cpio") << "--force-local" << "--no-absolute-filenames" << "-iuvdF";
1603         delCmd  = QStringList();
1604         putCmd  = QStringList();
1605         copyCmd = QStringList();
1606     } else if (arcType == "gzip") {
1607         cmd     = fullPathName("gzip");
1608         listCmd << fullPathName("gzip") << "-l";
1609         getCmd  << fullPathName("gzip") << "-dc";
1610         copyCmd = QStringList();
1611         delCmd  = QStringList();
1612         putCmd  = QStringList();
1613     } else if (arcType == "bzip2") {
1614         cmd     = fullPathName("bzip2");
1615         listCmd << fullPathName("bzip2");
1616         getCmd  << fullPathName("bzip2") << "-dc";
1617         copyCmd = QStringList();
1618         delCmd  = QStringList();
1619         putCmd  = QStringList();
1620     } else if (arcType == "lzma") {
1621         cmd     = fullPathName("lzma");
1622         listCmd << fullPathName("lzma");
1623         getCmd  << fullPathName("lzma") << "-dc";
1624         copyCmd = QStringList();
1625         delCmd  = QStringList();
1626         putCmd  = QStringList();
1627     } else if (arcType == "xz") {
1628         cmd     = fullPathName("xz");
1629         listCmd << fullPathName("xz");
1630         getCmd  << fullPathName("xz") << "-dc";
1631         copyCmd = QStringList();
1632         delCmd  = QStringList();
1633         putCmd  = QStringList();
1634     } else if (arcType == "arj") {
1635         cmd     = fullPathName("arj");
1636         listCmd << fullPathName("arj") << "v" << "-y" << "-v";
1637         getCmd  << fullPathName("arj") << "-jyov" << "-v" << "e";
1638         copyCmd << fullPathName("arj") << "-jyov" << "-v" << "e";
1639         delCmd  << fullPathName("arj") << "d";
1640         putCmd  << fullPathName("arj") << "-r" << "a";
1641         if (!getPassword().isEmpty()) {
1642             getCmd  << QString("-g%1").arg(password);
1643             copyCmd << QString("-g%1").arg(password);
1644             putCmd  << QString("-g%1").arg(password);
1645         }
1646     } else if (arcType == "lha") {
1647         cmd     = fullPathName("lha");
1648         listCmd << fullPathName("lha") << "l";
1649         getCmd  << fullPathName("lha") << "pq";
1650         copyCmd << fullPathName("lha") << "eif";
1651         delCmd  << fullPathName("lha") << "d";
1652         putCmd  << fullPathName("lha") << "a";
1653     } else if (arcType == "ace") {
1654         cmd     = fullPathName("unace");
1655         listCmd << fullPathName("unace") << "v";
1656         getCmd  << fullPathName("unace") << "e" << "-o";
1657         copyCmd << fullPathName("unace") << "e" << "-o";
1658         delCmd  = QStringList();
1659         putCmd  = QStringList();
1660         if (!getPassword().isEmpty()) {
1661             getCmd  << QString("-p%1").arg(password);
1662             copyCmd << QString("-p%1").arg(password);
1663         }
1664     } else if (arcType == "deb") {
1665         cmd = fullPathName("dpkg");
1666         listCmd << fullPathName("dpkg") << "-c";
1667         getCmd  << fullPathName("tar") << "xvf";
1668         copyCmd = QStringList();
1669         delCmd  = QStringList();
1670         putCmd  = QStringList();
1671     } else if (arcType == "7z") {
1672         noencoding = true;
1673         cmd = fullPathName("7z");
1674         if (QStandardPaths::findExecutable(cmd).isEmpty())
1675             cmd = fullPathName("7za");
1676 
1677         listCmd << cmd << "l" << "-y";
1678         getCmd  << cmd << "e" << "-y";
1679         copyCmd << cmd << "e" << "-y";
1680         delCmd  << cmd << "d" << "-y";
1681         putCmd  << cmd << "a" << "-y";
1682         renCmd  << cmd << "rn";
1683         if (!getPassword().isEmpty()) {
1684             getCmd  << QString("-p%1").arg(password);
1685             listCmd << QString("-p%1").arg(password);
1686             copyCmd << QString("-p%1").arg(password);
1687             if (!putCmd.isEmpty()) {
1688                 putCmd << QString("-p%1").arg(password);
1689                 delCmd << QString("-p%1").arg(password);
1690             }
1691         }
1692     }
1693     // checking if it's an absolute path
1694 #ifdef Q_WS_WIN
1695     if (cmd.length() > 2 && cmd[ 0 ].isLetter() && cmd[ 1 ] == ':')
1696         return true;
1697 #else
1698     if (cmd.startsWith(DIR_SEPARATOR))
1699         return true;
1700 #endif
1701     if (QStandardPaths::findExecutable(cmd).isEmpty()) {
1702         error(KIO::ERR_CANNOT_LAUNCH_PROCESS,
1703               cmd +
1704               i18n("\nMake sure that the %1 binary is installed properly on your system.", cmd));
1705         KRDEBUG("Failed to find cmd: " << cmd);
1706         return false;
1707     }
1708     return true;
1709 }
1710 
checkStatus(int exitCode)1711 bool kio_krarcProtocol::checkStatus(int exitCode)
1712 {
1713     KRFUNC;
1714     KRDEBUG(exitCode);
1715     return KrArcBaseManager::checkStatus(arcType, exitCode);
1716 }
1717 
checkIf7zIsEncrypted(bool & encrypted,QString fileName)1718 void kio_krarcProtocol::checkIf7zIsEncrypted(bool &encrypted, QString fileName)
1719 {
1720     KRFUNC;
1721     if (encryptedArchPath == fileName)
1722         encrypted = true;
1723     else {  // we try to find whether the 7z archive is encrypted
1724         // this is hard as the headers are also compressed
1725         QString tester = fullPathName("7z");
1726         if (QStandardPaths::findExecutable(tester).isEmpty()) {
1727             KRDEBUG("A 7z program was not found");
1728             tester = fullPathName("7za");
1729             if (QStandardPaths::findExecutable(tester).isEmpty()) {
1730                 KRDEBUG("A 7za program was not found");
1731                 return;
1732             }
1733         }
1734 
1735         QString testCmd = tester + " t -y ";
1736         lastData = encryptedArchPath = "";
1737 
1738         KrLinecountingProcess proc;
1739         proc << testCmd << fileName;
1740         connect(&proc, SIGNAL(newOutputData(KProcess *, QByteArray &)),
1741                 this, SLOT(checkOutputForPassword(KProcess *, QByteArray &)));
1742         proc.start();
1743         proc.waitForFinished();
1744         encrypted = this->encrypted;
1745 
1746         if (encrypted)
1747             encryptedArchPath = fileName;
1748     }
1749 }
1750 
checkOutputForPassword(KProcess * proc,QByteArray & buf)1751 void kio_krarcProtocol::checkOutputForPassword(KProcess * proc, QByteArray & buf)
1752 {
1753     KRFUNC;
1754     QString data =  QString(buf);
1755 
1756     QString checkable = lastData + data;
1757 
1758     QStringList lines = checkable.split('\n');
1759     lastData = lines[ lines.count() - 1 ];
1760     for (int i = 0; i != lines.count(); i++) {
1761         QString line = lines[ i ].trimmed().toLower();
1762         int ndx = line.indexOf("testing");
1763         if (ndx >= 0)
1764             line.truncate(ndx);
1765         if (line.isEmpty())
1766             continue;
1767 
1768         if (line.contains("password") && line.contains("enter")) {
1769             KRDEBUG("Encrypted 7z archive found!");
1770             encrypted = true;
1771             proc->kill();
1772             return;
1773         }
1774     }
1775 }
1776 
invalidatePassword()1777 void kio_krarcProtocol::invalidatePassword()
1778 {
1779     KRFUNC;
1780     KRDEBUG(getPath(arcFile->url(), QUrl::StripTrailingSlash) + DIR_SEPARATOR);
1781 
1782     if (!encrypted)
1783         return;
1784 
1785     KIO::AuthInfo authInfo;
1786     authInfo.caption = i18n("Krarc Password Dialog");
1787     authInfo.username = "archive";
1788     authInfo.readOnly = true;
1789     authInfo.keepPassword = true;
1790     authInfo.verifyPath = true;
1791     QString fileName = getPath(arcFile->url(), QUrl::StripTrailingSlash);
1792     authInfo.url = QUrl::fromLocalFile(ROOT_DIR);
1793     authInfo.url.setHost(fileName /*.replace('/','_')*/);
1794     authInfo.url.setScheme("krarc");
1795 
1796     password.clear();
1797 
1798     cacheAuthentication(authInfo);
1799 }
1800 
getPassword()1801 QString kio_krarcProtocol::getPassword()
1802 {
1803     KRFUNC;
1804     KRDEBUG("Encrypted: " << encrypted);
1805 
1806     if (!password.isNull())
1807         return password;
1808     if (!encrypted)
1809         return (password = "");
1810 
1811     KIO::AuthInfo authInfo;
1812     authInfo.caption = i18n("Krarc Password Dialog");
1813     authInfo.username = "archive";
1814     authInfo.readOnly = true;
1815     authInfo.keepPassword = true;
1816     authInfo.verifyPath = true;
1817     QString fileName = getPath(arcFile->url(), QUrl::StripTrailingSlash);
1818     authInfo.url = QUrl::fromLocalFile(ROOT_DIR);
1819     authInfo.url.setHost(fileName /*.replace('/','_')*/);
1820     authInfo.url.setScheme("krarc");
1821 
1822     if (checkCachedAuthentication(authInfo) && !authInfo.password.isNull()) {
1823         KRDEBUG(authInfo.password);
1824         return (password = authInfo.password);
1825     }
1826 
1827     authInfo.password.clear();
1828 
1829 #if KIO_VERSION_MINOR >= 24
1830     int errCode = openPasswordDialogV2(authInfo, i18n("Accessing the file requires a password."));
1831     if (!errCode && !authInfo.password.isNull()) {
1832 #else
1833     if (openPasswordDialog(authInfo, i18n("Accessing the file requires a password.")) && !authInfo.password.isNull()) {
1834 #endif
1835         KRDEBUG(authInfo.password);
1836         return (password = authInfo.password);
1837 #if KIO_VERSION_MINOR >= 24
1838     } else {
1839         error(errCode, QString());
1840 #endif
1841     }
1842 
1843     KRDEBUG(password);
1844     return password;
1845 }
1846 
1847 QString kio_krarcProtocol::detectFullPathName(QString name)
1848 {
1849     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
1850     KRDEBUG(name);
1851 
1852     name = name + EXEC_SUFFIX;
1853     QStringList path = QString::fromLocal8Bit(qgetenv("PATH")).split(':');
1854 
1855     for (QStringList::Iterator it = path.begin(); it != path.end(); ++it) {
1856         if (QDir(*it).exists(name)) {
1857             QString dir = *it;
1858             if (!dir.endsWith(DIR_SEPARATOR))
1859                 dir += DIR_SEPARATOR;
1860 
1861             return dir + name;
1862         }
1863     }
1864     return name;
1865 }
1866 
1867 QString kio_krarcProtocol::fullPathName(QString name)
1868 {
1869     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
1870     KRDEBUG(name);
1871 
1872     QString supposedName = confGrp.readEntry(name, QString());
1873     if (supposedName.isEmpty())
1874         supposedName = detectFullPathName(name);
1875     return supposedName;
1876 }
1877 
1878 QString kio_krarcProtocol::localeEncodedString(QString str)
1879 {
1880     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
1881     if (noencoding)
1882         return str;
1883 
1884     QByteArray array = codec->fromUnicode(str);
1885 
1886     // encoding the byte array to QString, mapping 0x0000-0x00FF to 0xE000-0xE0FF
1887     // see KrArcCodec for more explanation
1888     int size = array.size();
1889     QString result;
1890 
1891     const char *data = array.data();
1892     for (int i = 0; i != size; i++) {
1893         unsigned short ch = (((int)data[ i ]) & 0xFF) + 0xE000;   // user defined character
1894         result.append(QChar(ch));
1895     }
1896     return result;
1897 }
1898 
1899 QByteArray kio_krarcProtocol::encodeString(QString str)
1900 {
1901     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
1902     if (noencoding)
1903         return QTextCodec::codecForLocale()->fromUnicode(str);
1904     return codec->fromUnicode(str);
1905 }
1906 
1907 QString kio_krarcProtocol::decodeString(char * buf)
1908 {
1909     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
1910     if (noencoding)
1911         return QTextCodec::codecForLocale()->toUnicode(buf);
1912     return codec->toUnicode(buf);
1913 }
1914 
1915 QString kio_krarcProtocol::getPath(const QUrl &url, QUrl::FormattingOptions options)
1916 {
1917     // Note: KRFUNC was not used here in order to avoid filling the log with too much information
1918     QString path = url.adjusted(options).path();
1919     REPLACE_DIR_SEP2(path);
1920 
1921 #ifdef Q_WS_WIN
1922     if (path.startsWith(DIR_SEPARATOR)) {
1923         int p = 1;
1924         while (p < path.length() && path[ p ] == DIR_SEPARATOR_CHAR)
1925             p++;
1926         /* /C:/Folder */
1927         if (p + 2 <= path.length() && path[ p ].isLetter() && path[ p + 1 ] == ':') {
1928             path = path.mid(p);
1929         }
1930     }
1931 #endif
1932     return path;
1933 }
1934 
1935 #endif // KRARC_ENABLED
1936