diff options
Diffstat (limited to 'krArc/krarc.cpp')
-rw-r--r-- | krArc/krarc.cpp | 1668 |
1 files changed, 1668 insertions, 0 deletions
diff --git a/krArc/krarc.cpp b/krArc/krarc.cpp new file mode 100644 index 0000000..663236a --- /dev/null +++ b/krArc/krarc.cpp @@ -0,0 +1,1668 @@ +/*************************************************************************** + krarc.cpp + ------------------- + begin : Sat Jun 14 14:42:49 IDT 2003 + copyright : (C) 2003 by Rafi Yanai & Shie Erlich + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <stdlib.h> + +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qregexp.h> +#include <qdir.h> + +#include <kfileitem.h> +#include <kdebug.h> +#include <kmessagebox.h> +#include <kinstance.h> +#include <klocale.h> +#include <kurl.h> +#include <ktempfile.h> +#include <klargefile.h> +#include <kstandarddirs.h> +#include <kio/job.h> +#include <ktar.h> + +#include <iostream> +#include "krarc.h" + +#define MAX_IPC_SIZE (1024*32) +#define TRIES_WITH_PASSWORDS 3 + +#if 0 +#define KRDEBUG(X...) do{ \ + QFile f("/tmp/debug"); \ + f.open(IO_WriteOnly | IO_Append); \ + QTextStream stream( &f ); \ + stream << "Pid:" << (int)getpid() << " " <<__FUNCTION__<<"(" <<__LINE__<<"): "; \ + stream << X << endl; \ + f.close(); \ +} while(0); +#else +#define KRDEBUG(X...) +#endif + +using namespace KIO; +extern "C" { + +int kdemain( int argc, char **argv ){ + KInstance instance( "kio_krarc" ); + + if (argc != 4) { + kdWarning() << "Usage: kio_krarc protocol domain-socket1 domain-socket2" << endl; + exit(-1); + } + + kio_krarcProtocol slave(argv[2], argv[3]); + slave.dispatchLoop(); + + return 0; +} + +} // extern "C" + +kio_krarcProtocol::kio_krarcProtocol(const QCString &pool_socket, const QCString &app_socket) + : SlaveBase("kio_krarc", pool_socket, app_socket), archiveChanged(true), arcFile(0L),extArcReady(false), + password(QString::null) { + + krConfig = new KConfig( "krusaderrc" ); + krConfig->setGroup( "Dependencies" ); + + dirDict.setAutoDelete(true); + + arcTempDir = locateLocal("tmp",QString::null); + QString dirName = "krArc"+QDateTime::currentDateTime().toString(Qt::ISODate); + dirName.replace(QRegExp(":"),"_"); + QDir(arcTempDir).mkdir(dirName); + arcTempDir = arcTempDir+dirName+"/"; +} + +/* ---------------------------------------------------------------------------------- */ +kio_krarcProtocol::~kio_krarcProtocol(){ + // delete the temp directory + KrShellProcess proc; + proc << "rm -rf "<< arcTempDir; + proc.start(KProcess::Block); +} + +/* ---------------------------------------------------------------------------------- */ +void kio_krarcProtocol::receivedData(KProcess*,char* buf,int len){ + QByteArray d(len); + d.setRawData(buf,len); + data(d); + d.resetRawData(buf,len); + processedSize(len); + decompressedLen += len; +} + +void kio_krarcProtocol::mkdir(const KURL& url,int permissions){ + KRDEBUG(url.path()); + + if( !setArcFile( url ) ) { + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + if( newArchiveURL && !initDirDict(url) ){ + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + + if( putCmd.isEmpty() ){ + error(ERR_UNSUPPORTED_ACTION, + i18n("Creating directories is not supported with %1 archives").arg(arcType) ); + return; + } + + if( arcType == "arj" || arcType == "lha" ) { + QString arcDir = url.path().mid(arcFile->url().path().length()); + if( arcDir.right(1) != "/") arcDir = arcDir+"/"; + + if( dirDict.find( arcDir ) == 0 ) + addNewDir( arcDir ); + finished(); + return; + } + + //QString tmpDir = arcTempDir+url.path(); + QString arcDir = findArcDirectory(url); + QString tmpDir = arcTempDir + arcDir.mid(1) + url.path().mid(url.path().findRev("/")+1); + if( tmpDir.right(1) != "/" ) tmpDir = tmpDir+"/"; + + if( permissions == -1 ) permissions = 0777; //set default permissions + for( unsigned int i=arcTempDir.length();i<tmpDir.length(); i=tmpDir.find("/",i+1)){ + ::mkdir(tmpDir.left(i).local8Bit(),permissions); + } + + if( tmpDir.endsWith( "/" ) ) + tmpDir.truncate( tmpDir.length() - 1 ); + + // pack the directory + KrShellProcess proc; + proc << putCmd << convertName( arcFile->url().path() ) + " " << convertFileName( tmpDir.mid(arcTempDir.length()) ); + infoMessage(i18n("Creating %1 ...").arg( url.fileName() ) ); + QDir::setCurrent(arcTempDir); + proc.start(KProcess::Block,KProcess::AllOutput); + + // delete the temp directory + QDir().rmdir(arcTempDir); + + if( !proc.normalExit() || !checkStatus( proc.exitStatus() ) ) { + error(ERR_COULD_NOT_WRITE,url.path() + "\n\n" + proc.getErrorMsg() ); + return; + } + + // force a refresh of archive information + initDirDict(url,true); + finished(); +} + +void kio_krarcProtocol::put(const KURL& url,int permissions,bool overwrite,bool resume){ + KRDEBUG(url.path()); + if( !setArcFile( url ) ) { + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + if( newArchiveURL && !initDirDict(url) ){ + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + + if( putCmd.isEmpty() ){ + error(ERR_UNSUPPORTED_ACTION, + i18n("Writing to %1 archives is not supported").arg(arcType) ); + return; + } + if( !overwrite && findFileEntry(url) ){ + error( ERR_FILE_ALREADY_EXIST,url.path() ); + return; + } + + QString arcDir = findArcDirectory(url); + QString tmpFile = arcTempDir + arcDir.mid(1) + url.path().mid(url.path().findRev("/")+1); + + QString tmpDir = arcTempDir+arcDir.mid(1)+"/"; + for( unsigned int i=arcTempDir.length();i<tmpDir.length(); i=tmpDir.find("/",i+1)){ + QDir("/").mkdir(tmpDir.left(i)); + } + int fd; + if ( resume ) { + fd = KDE_open( tmpFile.local8Bit(), O_RDWR ); // append if resuming + KDE_lseek(fd, 0, SEEK_END); // Seek to end + } else { + // WABA: Make sure that we keep writing permissions ourselves, + // otherwise we can be in for a surprise on NFS. + mode_t initialMode; + if ( permissions != -1) + initialMode = permissions | S_IWUSR | S_IRUSR; + else + initialMode = 0666; + + fd = KDE_open(tmpFile.local8Bit(), O_CREAT | O_TRUNC | O_WRONLY, initialMode); + } + QByteArray buffer; + int readResult; + do{ + dataReq(); + readResult = readData(buffer); + write(fd,buffer.data(),buffer.size()); + } while( readResult > 0 ); + close(fd); + // pack the file + KrShellProcess proc; + proc << putCmd << convertName( arcFile->url().path() )+ " " <<convertFileName( tmpFile.mid(arcTempDir.length()) ); + infoMessage(i18n("Packing %1 ...").arg( url.fileName() ) ); + QDir::setCurrent(arcTempDir); + proc.start(KProcess::Block,KProcess::AllOutput); + // remove the file + QFile::remove(tmpFile); + + if( !proc.normalExit() || !checkStatus( proc.exitStatus() ) ) { + error(ERR_COULD_NOT_WRITE,url.path() + "\n\n" + proc.getErrorMsg() ); + return; + } + // force a refresh of archive information + initDirDict(url,true); + finished(); +} + +void kio_krarcProtocol::get(const KURL& url ){ + get( url, TRIES_WITH_PASSWORDS ); +} + +void kio_krarcProtocol::get(const KURL& url, int tries ){ + bool decompressToFile = false; + KRDEBUG(url.path()); + + if( !setArcFile( url ) ) { + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + if( newArchiveURL && !initDirDict(url) ){ + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + + if( getCmd.isEmpty() ){ + error(ERR_UNSUPPORTED_ACTION, + i18n("Retrieving data from %1 archives is not supported").arg(arcType) ); + return; + } + UDSEntry* entry = findFileEntry(url); + if( !entry ){ + error(KIO::ERR_DOES_NOT_EXIST,url.path()); + return; + } + if(KFileItem(*entry,url).isDir()){ + error(KIO::ERR_IS_DIRECTORY,url.path()); + return; + } + KIO::filesize_t expectedSize = KFileItem(*entry,url).size(); + // for RPM files extract the cpio file first + if( !extArcReady && arcType == "rpm"){ + KrShellProcess cpio; + cpio << "rpm2cpio" << convertName( arcFile->url().path(-1) ) << " > " << arcTempDir+"contents.cpio"; + cpio.start(KProcess::Block,KProcess::AllOutput); + if( !cpio.normalExit() || cpio.exitStatus() != 0 ) { + error(ERR_COULD_NOT_READ,url.path() + "\n\n" + cpio.getErrorMsg() ); + return; + } + extArcReady = true; + } + // for DEB files extract the tar file first + if ( !extArcReady && arcType == "deb" ) { + KrShellProcess dpkg; + dpkg << cmd + " --fsys-tarfile" << convertName( arcFile->url().path( -1 ) ) << " > " << arcTempDir + "contents.cpio"; + dpkg.start( KProcess::Block, KProcess::AllOutput ); + if( !dpkg.normalExit() || dpkg.exitStatus() != 0 ) { + error(ERR_COULD_NOT_READ,url.path() + "\n\n" + dpkg.getErrorMsg() ); + return; + } + extArcReady = true; + } + + // Use the external unpacker to unpack the file + QString file = url.path().mid(arcFile->url().path().length()+1); + KrShellProcess proc; + if( extArcReady ){ + proc << getCmd << arcTempDir+"contents.cpio " << convertName( "*"+file ); + } else if( arcType == "arj" || arcType == "ace" || arcType == "7z" ) { + proc << getCmd << convertName( arcFile->url().path(-1) )+ " " << convertFileName( file ); + if( arcType == "ace" && QFile( "/dev/ptmx" ).exists() ) // Don't remove, unace crashes if missing!!! + proc << "<" << "/dev/ptmx"; + file = url.fileName(); + decompressToFile = true; + } else { + decompressedLen = 0; + // Determine the mimetype of the file to be retrieved, and emit it. + // This is mandatory in all slaves (for KRun/BrowserRun to work). + KMimeType::Ptr mt = KMimeType::findByURL( arcTempDir+file, 0, false /* NOT local URL */ ); + emit mimeType( mt->name() ); + proc << getCmd << convertName( arcFile->url().path() )+" "; + if( arcType != "gzip" && arcType != "bzip2" ) proc << convertFileName( file ); + connect(&proc,SIGNAL(receivedStdout(KProcess*,char*,int)), + this,SLOT(receivedData(KProcess*,char*,int)) ); + } + infoMessage(i18n("Unpacking %1 ...").arg( url.fileName() ) ); + // change the working directory to our arcTempDir + QDir::setCurrent(arcTempDir); + proc.start(KProcess::Block,KProcess::AllOutput); + + if( !extArcReady && !decompressToFile ) { + if( !proc.normalExit() || !checkStatus( proc.exitStatus() ) || ( arcType != "bzip2" && expectedSize != decompressedLen ) ) { + if( encrypted && tries ) { + invalidatePassword(); + get( url, tries - 1 ); + return; + } + error( KIO::ERR_ACCESS_DENIED, url.path() + "\n\n" + proc.getErrorMsg() ); + return; + } + } + else{ + if( !proc.normalExit() || !checkStatus( proc.exitStatus() ) || !QFileInfo( arcTempDir+file ).exists() ) { + if( decompressToFile ) + QFile(arcTempDir+file).remove(); + if( encrypted && tries ) { + invalidatePassword(); + get( url, tries - 1 ); + return; + } + error( KIO::ERR_ACCESS_DENIED, url.path() ); + return; + } + // the follwing block is ripped from KDE file KIO::Slave + // $Id: krarc.cpp,v 1.43 2007/01/13 13:39:51 ckarai Exp $ + QCString _path( QFile::encodeName(arcTempDir+file) ); + KDE_struct_stat buff; + if( KDE_lstat( _path.data(), &buff ) == -1 ) { + if ( errno == EACCES ) + error( KIO::ERR_ACCESS_DENIED, url.path() ); + else + error( KIO::ERR_DOES_NOT_EXIST, url.path() ); + return; + } + if ( S_ISDIR( buff.st_mode ) ) { + error( KIO::ERR_IS_DIRECTORY, url.path() ); + return; + } + if ( !S_ISREG(buff.st_mode) ) { + error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() ); + return; + } + int fd = KDE_open( _path.data(), O_RDONLY ); + if ( fd < 0 ) { + error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.path() ); + return; + } + // Determine the mimetype of the file to be retrieved, and emit it. + // This is mandatory in all slaves (for KRun/BrowserRun to work). + KMimeType::Ptr mt = KMimeType::findByURL( arcTempDir+file, buff.st_mode, true /* local URL */ ); + emit mimeType( mt->name() ); + + KIO::filesize_t processed_size = 0; + + QString resumeOffset = metaData("resume"); + if ( !resumeOffset.isEmpty() ){ + bool ok; +#if QT_VERSION >= 0x030200 + KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok); +#else + KIO::fileoffset_t offset = resumeOffset.toULong(&ok); +#endif + if (ok && (offset > 0) && (offset < buff.st_size)){ + if (KDE_lseek(fd, offset, SEEK_SET) == offset){ + canResume (); + processed_size = offset; + } + } + } + + totalSize( buff.st_size ); + + char buffer[ MAX_IPC_SIZE ]; + QByteArray array; + while( 1 ){ + int n = ::read( fd, buffer, MAX_IPC_SIZE ); + if (n == -1){ + if (errno == EINTR) + continue; + error( KIO::ERR_COULD_NOT_READ, url.path()); + close(fd); + return; + } + if (n == 0) + break; // Finished + + array.setRawData(buffer, n); + data( array ); + array.resetRawData(buffer, n); + + processed_size += n; + } + + data( QByteArray() ); + close( fd ); + processedSize( buff.st_size ); + finished(); + + if( decompressToFile ) + QFile(arcTempDir+file).remove(); + return; + } + // send empty buffer to mark EOF + data(QByteArray()); + finished(); +} + +void kio_krarcProtocol::del(KURL const & url, bool isFile){ + KRDEBUG(url.path()); + + if( !setArcFile( url ) ) { + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + if( newArchiveURL && !initDirDict(url) ){ + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + + if( delCmd.isEmpty() ){ + error(ERR_UNSUPPORTED_ACTION, + i18n("Deleting files from %1 archives is not supported").arg(arcType) ); + return; + } + if( !findFileEntry(url) ){ + if( ( arcType != "arj" && arcType != "lha" ) || isFile ) { + error(KIO::ERR_DOES_NOT_EXIST,url.path()); + return; + } + } + + QString file = url.path().mid(arcFile->url().path().length()+1); + if( !isFile && file.right(1) != "/" ) { + if(arcType == "zip") file = file + "/"; + } + KrShellProcess proc; + proc << delCmd << convertName( arcFile->url().path() )+" " << convertFileName( file ); + infoMessage(i18n("Deleting %1 ...").arg( url.fileName() ) ); + proc.start(KProcess::Block, KProcess::AllOutput); + if( !proc.normalExit() || !checkStatus( proc.exitStatus() ) ) { + error(ERR_COULD_NOT_WRITE,url.path() + "\n\n" + proc.getErrorMsg() ); + return; + } + // force a refresh of archive information + initDirDict(url,true); + finished(); +} + +void kio_krarcProtocol::stat( const KURL & url ){ + KRDEBUG(url.path()); + if( !setArcFile( url ) ) { + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + if( newArchiveURL && !initDirDict(url) ){ + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + + if( listCmd.isEmpty() ){ + error(ERR_UNSUPPORTED_ACTION, + i18n("Accessing files is not supported with the %1 archives").arg(arcType) ); + return; + } + QString path = url.path(-1); + KURL newUrl = url; + + // but treat the archive itself as the archive root + if( path == arcFile->url().path(-1) ){ + newUrl.setPath(path+"/"); + path = newUrl.path(); + } + // we might be stating a real file + if( QFileInfo(path).exists() ){ + KDE_struct_stat buff; + KDE_stat( path.local8Bit(), &buff ); + QString mime = KMimeType::findByPath(path,buff.st_mode)->name(); + statEntry(KFileItem(path,mime,buff.st_mode).entry()); + finished(); + return; + } + UDSEntry* entry = findFileEntry(newUrl); + if( entry ){ + statEntry( *entry ); + finished(); + } else error( KIO::ERR_DOES_NOT_EXIST, path ); +} + +void kio_krarcProtocol::copy (const KURL &url, const KURL &dest, int, bool overwrite) { + KRDEBUG(url.path()); + + // KDE HACK: opening the password dlg in copy causes error for the COPY, and further problems + // that's why encrypted files are not allowed to copy + if( !encrypted && dest.isLocalFile() ) + do { + if( url.fileName() != dest.fileName() ) + break; + + //the file exists and we don't want to overwrite + if ((!overwrite) && ( QFile( dest.path() ).exists() ) ) { + error(ERR_FILE_ALREADY_EXIST, QFile::encodeName(dest.path()) ); + return; + }; + + if( !setArcFile( url ) ) { + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + if( newArchiveURL && !initDirDict(url) ){ + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + + UDSEntry* entry = findFileEntry(url); + if( copyCmd.isEmpty() || !entry ) + break; + + QString file = url.path().mid(arcFile->url().path().length()+1); + + QString destDir = dest.path( -1 ); + if( !QDir( destDir ).exists() ) { + int ndx = destDir.findRev( '/' ); + if( ndx != -1 ) + destDir.truncate( ndx+1 ); + } + + QDir::setCurrent( destDir.local8Bit() ); + + KrShellProcess proc; + proc << copyCmd << convertName( arcFile->url().path(-1) )+" " << convertFileName( file ); + if( arcType == "ace" && QFile( "/dev/ptmx" ).exists() ) // Don't remove, unace crashes if missing!!! + proc << "<" << "/dev/ptmx"; + + infoMessage(i18n("Unpacking %1 ...").arg( url.fileName() ) ); + proc.start(KProcess::Block, KProcess::AllOutput); + if( !proc.normalExit() || !checkStatus( proc.exitStatus() ) ) { + error(KIO::ERR_COULD_NOT_WRITE, dest.path(-1) + "\n\n" + proc.getErrorMsg() ); + return; + } + if( !QFileInfo( dest.path(-1) ).exists() ) { + error( KIO::ERR_COULD_NOT_WRITE, dest.path(-1) ); + return; + } + + processedSize( KFileItem(*entry,url).size() ); + finished(); + QDir::setCurrent( "/" ); /* for being able to umount devices after copying*/ + return; + }while( 0 ); + + error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_COPY)); +} + +void kio_krarcProtocol::listDir(const KURL& url){ + KRDEBUG(url.path()); + if( !setArcFile( url ) ) { + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + if( listCmd.isEmpty() ){ + error(ERR_UNSUPPORTED_ACTION, + i18n("Listing directories is not supported for %1 archives").arg(arcType) ); + return; + } + QString path = url.path(); + if( path.right(1) != "/" ) path = path+"/"; + + // it might be a real dir ! + if( QFileInfo(path).exists() ){ + if( QFileInfo(path).isDir() ){ + KURL redir; + redir.setPath( url.path() ); + redirection(redir); + finished(); + } else { // maybe it's an archive ! + error(ERR_IS_FILE,path); + } + return; + } + if( !initDirDict(url) ){ + error( ERR_CANNOT_ENTER_DIRECTORY, url.path()); + return; + } + QString arcDir = path.mid(arcFile->url().path().length()); + arcDir.truncate(arcDir.findRev("/")); + if(arcDir.right(1) != "/") arcDir = arcDir+"/"; + + UDSEntryList* dirList = dirDict.find(arcDir); + if( dirList == 0 ){ + error(ERR_CANNOT_ENTER_DIRECTORY,url.path()); + return; + } + totalSize(dirList->size()); + listEntries(*dirList); + finished(); +} + +bool kio_krarcProtocol::setArcFile(const KURL& url){ + QString path = url.path(); + time_t currTime = time( 0 ); + archiveChanged = true; + newArchiveURL = true; + // is the file already set ? + if( arcFile && arcFile->url().path(-1) == path.left(arcFile->url().path(-1).length()) ){ + newArchiveURL = false; + // Has it changed ? + KFileItem* newArcFile = new KFileItem(arcFile->url(),QString::null,arcFile->mode()); + if( !newArcFile->cmp( *arcFile ) ){ + delete arcFile; + password = QString::null; + extArcReady = false; + arcFile = newArcFile; + } else { // same old file + delete newArcFile; + archiveChanged = false; + if( encrypted && password.isNull() ) + initArcParameters(); + } + } else { // it's a new file... + extArcReady = false; + if( arcFile ){ + delete arcFile; + password = QString::null; + arcFile = 0L; + } + QString newPath = path; + if(newPath.right(1) != "/") newPath = newPath+"/"; + for(int pos=0; pos >= 0; pos = newPath.find("/",pos+1)){ + QFileInfo qfi(newPath.left(pos)); + if( qfi.exists() && !qfi.isDir() ){ + KDE_struct_stat stat_p; + KDE_lstat(newPath.left(pos).local8Bit(),&stat_p); + arcFile = new KFileItem(KURL::fromPathOrURL( newPath.left(pos) ),QString::null,stat_p.st_mode); + break; + } + } + if( !arcFile ){ + error( ERR_DOES_NOT_EXIST,path ); + return false; // file not found + } + } + + /* FIX: file change can only be detected if the timestamp between the two consequent + changes is more than 1s. If the archive is continuously changing (check: move files + inside the archive), krarc may erronously think, that the archive file is unchanged, + because the timestamp is the same as the previous one. This situation can only occur + if the modification time equals with the current time. While this condition is true, + we can say, that the archive is changing, so content reread is always necessary + during that period. */ + if( archiveChanging ) + archiveChanged = true; + archiveChanging = ( currTime == arcFile->time( UDS_MODIFICATION_TIME ) ); + + arcPath = arcFile->url().path(-1); + arcType = detectArchive( encrypted, arcPath ); + + if( arcType == "tbz" ) + arcType = "bzip2"; + else if( arcType == "tgz" ) + arcType = "gzip"; + + if( arcType.isEmpty() ) { + arcType = arcFile->mimetype(); + arcType = arcType.mid(arcType.findRev("-")+1); + + if( arcType == "jar" ) + arcType = "zip"; + } + + return initArcParameters(); +} + +bool kio_krarcProtocol::initDirDict(const KURL&url, bool forced){ + // set the archive location + //if( !setArcFile(url.path()) ) return false; + // no need to rescan the archive if it's not changed + if( !archiveChanged && !forced ) return true; + extArcReady = false; + + if( !setArcFile( url ) ) + return false; /* if the archive was changed refresh the file information */ + + // write the temp file + KrShellProcess proc; + KTempFile temp( QString::null, "tmp" ); + temp.setAutoDelete(true); + if( arcType != "bzip2" ){ + if( arcType == "rpm" ) + proc << listCmd << convertName( arcPath ) <<" > " << temp.name(); + else + proc << listCmd << convertName( arcFile->url().path(-1) ) <<" > " << temp.name(); + if( arcType == "ace" && QFile( "/dev/ptmx" ).exists() ) // Don't remove, unace crashes if missing!!! + proc << "<" << "/dev/ptmx"; + proc.start(KProcess::Block,KProcess::AllOutput); + if( !proc.normalExit() || !checkStatus( proc.exitStatus() ) ) return false; + } + // clear the dir dictionary + dirDict.clear(); + + // add the "/" directory + UDSEntryList* root = new UDSEntryList(); + dirDict.insert("/",root); + // and the "/" UDSEntry + UDSEntry entry; + UDSAtom atom; + atom.m_uds = UDS_NAME; + atom.m_str = "."; + entry.append(atom); + mode_t mode = parsePermString("drwxr-xr-x"); + atom.m_uds = UDS_FILE_TYPE; + atom.m_long = mode & S_IFMT; // keep file type only + entry.append( atom ); + atom.m_uds = UDS_ACCESS; + atom.m_long = mode & 07777; // keep permissions only + entry.append( atom ); + + root->append(entry); + + if( arcType == "bzip2" ){ + KRDEBUG("Got me here..."); + parseLine(0,"",temp.file()); + return true; + } + + // parse the temp file + temp.file()->open(IO_ReadOnly); + char buf[1000]; + QString line; + + int lineNo = 0; + bool invalidLine = false; + // the rar list is started with a ------ line. + if(arcType == "rar" || arcType == "arj" || arcType == "lha" || arcType == "7z" ){ + while(temp.file()->readLine(buf,1000) != -1){ + line = QString::fromLocal8Bit(buf); + if( line.startsWith("----------") ) break; + } + } + while(temp.file()->readLine(buf,1000) != -1) { + line = QString::fromLocal8Bit(buf); + if( arcType == "rar" ) { + // the rar list is ended with a ------ line. + if( line.startsWith("----------") ) { + invalidLine = !invalidLine; + continue; + } + if( invalidLine ) + continue; + else{ + temp.file()->readLine(buf,1000); + line = line+QString::fromLocal8Bit(buf); + if( line[0]=='*' ) // encrypted archives starts with '*' + line[0]=' '; + } + } + if( arcType == "ace" ) { + // the ace list begins with a number. + if( !line[0].isDigit() ) continue; + } + if( arcType == "arj" ) { + // the arj list is ended with a ------ line. + if( line.startsWith("----------") ) { + invalidLine = !invalidLine; + continue; + } + if( invalidLine ) + continue; + else { + temp.file()->readLine(buf,1000); + line = line+QString::fromLocal8Bit(buf); + temp.file()->readLine(buf,1000); + line = line+QString::fromLocal8Bit(buf); + temp.file()->readLine(buf,1000); + line = line+QString::fromLocal8Bit(buf); + } + } + if( arcType == "lha" || arcType == "7z" ) { + // the arj list is ended with a ------ line. + if( line.startsWith("----------") ) break; + } + parseLine(lineNo++,line.stripWhiteSpace(),temp.file()); + } + // close and delete our file + temp.file()->close(); + + archiveChanged = false; + return true; +} + +QString kio_krarcProtocol::findArcDirectory(const KURL& url){ + QString path = url.path(); + if( path.right(1) == "/" ) path.truncate(path.length()-1); + + if( !initDirDict(url) ){ + return QString::null; + } + QString arcDir = path.mid(arcFile->url().path().length()); + arcDir.truncate(arcDir.findRev("/")); + if(arcDir.right(1) != "/") arcDir = arcDir+"/"; + + return arcDir; +} + +UDSEntry* kio_krarcProtocol::findFileEntry(const KURL& url){ + QString arcDir = findArcDirectory(url); + if( arcDir.isEmpty() ) return 0; + + UDSEntryList* dirList = dirDict.find(arcDir); + if( !dirList ){ + return 0; + } + QString name = url.path(); + if( arcFile->url().path(-1) == url.path(-1) ) name = "."; // the "/" case + else{ + if( name.right(1) == "/" ) name.truncate(name.length()-1); + name = name.mid(name.findRev("/")+1); + } + + UDSEntryList::iterator entry; + UDSEntry::iterator atom; + + for ( entry = dirList->begin(); entry != dirList->end(); ++entry ){ + for( atom = (*entry).begin(); atom != (*entry).end(); ++atom ){ + if( (*atom).m_uds == UDS_NAME ){ + if((*atom).m_str == name){ + return &(*entry); + } else break; + } + } + } + return 0; +} + +QString kio_krarcProtocol::nextWord(QString &s,char d) { + s=s.stripWhiteSpace(); + int j=s.find(d,0); + QString temp=s.left(j); // find the leftmost word. + s.remove(0,j); + return temp; +} + +mode_t kio_krarcProtocol::parsePermString(QString perm){ + mode_t mode=0; + // file type + if(perm[0] == 'd') mode |= S_IFDIR; + if(perm[0] == 'l') mode |= S_IFLNK; + if(perm[0] == '-') mode |= S_IFREG; + // owner permissions + if(perm[1] != '-') mode |= S_IRUSR; + if(perm[2] != '-') mode |= S_IWUSR; + if(perm[3] != '-') mode |= S_IXUSR; + // group permissions + if(perm[4] != '-') mode |= S_IRGRP; + if(perm[5] != '-') mode |= S_IWGRP; + if(perm[6] != '-') mode |= S_IXGRP; + // other permissions + if(perm[7] != '-') mode |= S_IROTH; + if(perm[8] != '-') mode |= S_IWOTH; + if(perm[9] != '-') mode |= S_IXOTH; + + return mode; +} + +UDSEntryList* kio_krarcProtocol::addNewDir(QString path){ + UDSEntryList* dir; + + // check if the current dir exists + dir = dirDict.find(path); + if(dir != 0) return dir; // dir exists- return it ! + + // set dir to the parent dir + dir = addNewDir(path.left(path.findRev("/",-2)+1)); + + // add a new entry in the parent dir + QString name = path.mid(path.findRev("/",-2)+1); + name = name.left(name.length()-1); + + UDSEntry entry; + UDSAtom atom; + atom.m_uds = UDS_NAME; + atom.m_str = name; + entry.append(atom); + + mode_t mode = parsePermString("drwxr-xr-x"); + + atom.m_uds = UDS_FILE_TYPE; + atom.m_long = mode & S_IFMT; // keep file type only + entry.append( atom ); + + atom.m_uds = UDS_ACCESS; + atom.m_long = mode & 07777; // keep permissions only + entry.append( atom ); + + atom.m_uds = UDS_SIZE; + atom.m_long = 0; + entry.append( atom ); + + atom.m_uds = UDS_MODIFICATION_TIME; + atom.m_long = arcFile->time(UDS_MODIFICATION_TIME); + entry.append( atom ); + + dir->append(entry); + + // create a new directory entry and add it.. + dir = new UDSEntryList(); + dirDict.insert(path,dir); + + return dir; +} + +void kio_krarcProtocol::parseLine(int lineNo, QString line, QFile*) { + UDSEntryList* dir; + UDSEntry entry; + UDSAtom atom; + + QString owner = QString::null; + QString group = QString::null; + QString symlinkDest = QString::null; + QString perm = QString::null; + mode_t mode = 0666; + size_t size = 0; + time_t time = ::time(0); + QString fullName = QString::null; + + if(arcType == "zip"){ + // permissions + perm = nextWord(line); + // ignore the next 2 fields + nextWord(line); nextWord(line); + // size + size = nextWord(line).toLong(); + // ignore the next 2 fields + nextWord(line);nextWord(line); + // date & time + QString d = nextWord(line); + QDate qdate(d.mid(0,4).toInt(),d.mid(4,2).toInt(),d.mid(6,2).toInt()); + QTime qtime(d.mid(9,2).toInt(),d.mid(11,2).toInt(),d.mid(13,2).toInt()); + time = QDateTime(qdate,qtime).toTime_t(); + // full name + fullName = nextWord(line,'\n'); + + if(perm.length() != 10) + perm = (perm.at(0)=='d' || fullName.endsWith( "/" )) ? "drwxr-xr-x" : "-rw-r--r--" ; + mode = parsePermString(perm); + } + if(arcType == "rar") { + // full name + fullName = nextWord(line,'\n'); + // size + size = nextWord(line).toLong(); + // ignore the next 2 fields + nextWord(line); nextWord(line); + // date & time + QString d = nextWord(line); + int year = 1900 + d.mid(6,2).toInt(); + if( year < 1930 ) year+=100; + QDate qdate( year, d.mid(3,2).toInt(), d.mid(0,2).toInt() ); + QString t = nextWord(line); + QTime qtime(t.mid(0,2).toInt(),t.mid(3,2).toInt(),0); + time = QDateTime(qdate,qtime).toTime_t(); + // permissions + perm = nextWord(line); + + if( perm.length() == 7 ) // windows rar permission format + { + bool isDir = ( perm.at(1).lower() == 'd' ); + bool isReadOnly = ( perm.at(2).lower() == 'r' ); + + perm = isDir ? "drwxr-xr-x" : "-rw-r--r--"; + + if( isReadOnly ) + perm.at( 2 ) = '-'; + } + + if(perm.length() != 10) perm = (perm.at(0)=='d')? "drwxr-xr-x" : "-rw-r--r--" ; + mode = parsePermString(perm); + } + if(arcType == "arj"){ + nextWord(line); + // full name + fullName = nextWord(line,'\n'); + // ignore the next 2 fields + nextWord(line); nextWord(line); + // size + size = nextWord(line).toLong(); + // ignore the next 2 fields + nextWord(line); nextWord(line); + // date & time + QString d = nextWord(line); + int year = 1900 + d.mid(0,2).toInt(); + if( year < 1930 ) year+=100; + QDate qdate( year, d.mid(3,2).toInt(), d.mid(6,2).toInt() ); + QString t = nextWord(line); + QTime qtime(t.mid(0,2).toInt(),t.mid(3,2).toInt(),0); + time = QDateTime(qdate,qtime).toTime_t(); + // permissions + perm = nextWord(line); + if(perm.length() != 10) perm = (perm.at(0)=='d')? "drwxr-xr-x" : "-rw-r--r--" ; + mode = parsePermString(perm); + } + if(arcType == "rpm"){ + // full name + fullName = nextWord(line); + // size + size = nextWord(line).toULong(); + // date & time + time = nextWord(line).toULong(); + // next field is md5sum, ignore it + nextWord(line); + // permissions + mode = nextWord(line).toULong(0,8); + // Owner & Group + owner = nextWord(line); + group = nextWord(line); + // symlink destination + if( S_ISLNK(mode) ){ + // ignore the next 3 fields + nextWord(line); nextWord(line); nextWord(line); + symlinkDest = nextWord(line); + } + } + if( arcType == "gzip" ){ + if( !lineNo ) return; //ignore the first line + // first field is uncompressed size - ignore it + nextWord(line); + // size + size = nextWord(line).toULong(); + // ignore the next field + nextWord(line); + // full name + fullName = nextWord(line); + fullName = fullName.mid(fullName.findRev("/")+1); + } + if( arcType == "bzip2" ){ + // There is no way to list bzip2 files, so we take our information from + // the archive itself... + fullName = arcFile->name(); + if( fullName.endsWith("bz2") ) fullName.truncate(fullName.length()-4); + mode = arcFile->mode(); + size = arcFile->size(); + } + if(arcType == "lha"){ + // permissions + perm = nextWord(line); + if(perm.length() != 10) perm = (perm.at(0)=='d')? "drwxr-xr-x" : "-rw-r--r--" ; + mode = parsePermString(perm); + // ignore the next field + nextWord(line); + // size + size = nextWord(line).toLong(); + // ignore the next field + nextWord(line); + // date & time + int month = (QStringList::split(',', "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec")).findIndex( nextWord(line) ) + 1; + int day = nextWord(line).toInt(); + int year = QDate::currentDate().year(); + QString third = nextWord(line); + QTime qtime; + + if( third.contains(":" ) ) + qtime = QTime::fromString( third ); + else + year = third.toInt(); + + QDate qdate(year, month, day ); + + time = QDateTime(qdate, qtime).toTime_t(); + // full name + fullName = nextWord(line,'\n'); + } + if(arcType == "ace"){ + // date & time + QString d = nextWord(line); + int year = 1900 + d.mid(6,2).toInt(); + if( year < 1930 ) year+=100; + QDate qdate( year, d.mid(3,2).toInt(), d.mid(0,2).toInt() ); + QString t = nextWord(line); + QTime qtime(t.mid(0,2).toInt(),t.mid(3,2).toInt(),0); + time = QDateTime(qdate,qtime).toTime_t(); + // ignore the next field + nextWord(line); + // size + size = nextWord(line).toLong(); + // ignore the next field + nextWord(line); + // full name + fullName = nextWord(line,'\n'); + if( fullName[ 0 ] == '*' ) // encrypted archives starts with '*' + fullName = fullName.mid( 1 ); + } + if( arcType == "deb" ){ + // permissions + perm = nextWord( line ); + mode = parsePermString( perm ); + // Owner & Group + owner = nextWord( line,'/' ); + group = nextWord( line ).mid(1); + // size + size = nextWord( line ).toLong(); + // date & time + QString d = nextWord( line ); + QDate qdate( d.mid( 0, 4 ).toInt(), d.mid( 5, 2 ).toInt(), d.mid( 8, 2 ).toInt() ); + QString t = nextWord( line ); + QTime qtime( t.mid( 0, 2 ).toInt(), t.mid( 3, 2 ).toInt(), 0 ); + time = QDateTime( qdate, qtime ).toTime_t(); + // full name + fullName = nextWord( line, '\n' ).mid( 1 ); + //if ( fullName.right( 1 ) == "/" ) return; + if( fullName.contains("->") ){ + symlinkDest = fullName.mid(fullName.find("->")+2); + fullName = fullName.left(fullName.find("->")-1); + } + } + if(arcType == "7z"){ + // date & time + QString d = nextWord(line); + QDate qdate( d.mid(0,4).toInt(), d.mid(5,2).toInt(), d.mid(8,2).toInt() ); + QString t = nextWord(line); + QTime qtime(t.mid(0,2).toInt(),t.mid(3,2).toInt(),t.mid(6,2).toInt() ); + time = QDateTime(qdate,qtime).toTime_t(); + + // permissions + perm = nextWord(line); + bool isDir = ( perm.at(0).lower() == 'd' ); + bool isReadOnly = ( perm.at(1).lower() == 'r' ); + perm = isDir ? "drwxr-xr-x" : "-rw-r--r--"; + if( isReadOnly ) + perm.at( 2 ) = '-'; + + mode = parsePermString(perm); + + // size + size = nextWord(line).toLong(); + + // ignore the next 15 characters + line = line.mid( 15 ); + + // full name + fullName = nextWord(line,'\n'); + } + + if( fullName.right(1) == "/" ) fullName = fullName.left(fullName.length()-1); + if( !fullName.startsWith("/") ) fullName = "/"+fullName; + QString path = fullName.left(fullName.findRev("/")+1); + // set/create the directory UDSEntryList + dir = dirDict.find(path); + if(dir == 0) dir = addNewDir(path); + QString name = fullName.mid(fullName.findRev("/")+1); + // file name + atom.m_uds = UDS_NAME; + atom.m_str = name; + entry.append(atom); + // file type + atom.m_uds = UDS_FILE_TYPE; + atom.m_long = mode & S_IFMT; // keep file type only + entry.append( atom ); + // file permissions + atom.m_uds = UDS_ACCESS; + atom.m_long = mode & 07777; // keep permissions only + entry.append( atom ); + // file size + atom.m_uds = UDS_SIZE; + atom.m_long = size; + entry.append( atom ); + // modification time + atom.m_uds = UDS_MODIFICATION_TIME; + atom.m_long = time; + entry.append( atom ); + // link destination + if( !symlinkDest.isEmpty() ){ + atom.m_uds = UDS_LINK_DEST; + atom.m_str = symlinkDest; + entry.append( atom ); + } + if( S_ISDIR(mode) ){ + fullName=fullName+"/"; + if(dirDict.find(fullName) == 0) + dirDict.insert(fullName,new UDSEntryList()); + else { + // try to overwrite an existing entry + UDSEntryList::iterator entryIt; + UDSEntry::iterator atomIt; + + for ( entryIt = dir->begin(); entryIt != dir->end(); ++entryIt ) + for( atomIt = (*entryIt).begin(); atomIt != (*entryIt).end(); ++atomIt ) + if( (*atomIt).m_uds == UDS_NAME ) + if((*atomIt).m_str == name) { + for( atomIt = (*entryIt).begin(); atomIt != (*entryIt).end(); ++atomIt ) { + switch( (*atomIt).m_uds ) { + case UDS_MODIFICATION_TIME: + (*atomIt).m_long = time; + break; + case UDS_ACCESS: + (*atomIt).m_long = mode & 07777; + break; + } + } + return; + } + return; // there is alreay an entry for this directory + } + } + + // multi volume archives can add a file twice, use only one + UDSEntryList::iterator dirEntryIt; + UDSEntry::iterator dirAtomIt; + for ( dirEntryIt = dir->begin(); dirEntryIt != dir->end(); ++dirEntryIt ) + for( dirAtomIt = (*dirEntryIt).begin(); dirAtomIt != (*dirEntryIt).end(); ++dirAtomIt ) + if( (*dirAtomIt).m_uds == UDS_NAME && (*dirAtomIt).m_str == name ) + return; + + dir->append(entry); +} + +bool kio_krarcProtocol::initArcParameters() { + KRDEBUG("arcType: "<<arcType); + + if(arcType == "zip"){ + cmd = fullPathName( "unzip" ); + listCmd = fullPathName( "unzip" ) + " -ZTs-z-t-h "; + getCmd = fullPathName( "unzip" ) + " -p "; + copyCmd = fullPathName( "unzip" ) + " -jo "; + + if( KStandardDirs::findExe( "zip" ).isEmpty() ) { + delCmd = QString::null; + putCmd = QString::null; + } else { + delCmd = fullPathName( "zip" ) + " -d "; + putCmd = fullPathName( "zip" ) + " -ry "; + } + + if( !getPassword().isEmpty() ) { + getCmd += "-P '"+password+"' "; + copyCmd += "-P '"+password+"' "; + putCmd += "-P '"+password+"' "; + } + } else if (arcType == "rar") { + if( KStandardDirs::findExe( "rar" ).isEmpty() ) { + cmd = fullPathName( "unrar" ); + listCmd = fullPathName( "unrar" ) + " -c- -v v "; + getCmd = fullPathName( "unrar" ) + " p -ierr -idp -c- -y "; + copyCmd = fullPathName( "unrar" ) + " e -y "; + delCmd = QString::null; + putCmd = QString::null; + } else { + cmd = fullPathName( "rar" ); + listCmd = fullPathName( "rar" ) + " -c- -v v "; + getCmd = fullPathName( "rar" ) + " p -ierr -idp -c- -y "; + copyCmd = fullPathName( "rar" ) + " e -y "; + delCmd = fullPathName( "rar" ) + " d "; + putCmd = fullPathName( "rar" ) + " -r a "; + } + if( !getPassword().isEmpty() ) { + getCmd += "-p'"+password+"' "; + listCmd += "-p'"+password+"' "; + copyCmd += "-p'"+password+"' "; + if( !putCmd.isEmpty() ) { + putCmd += "-p'"+password+"' "; + delCmd += "-p'"+password+"' "; + } + } + } else if(arcType == "rpm") { + cmd = fullPathName( "rpm" ); + listCmd = fullPathName( "rpm" ) + " --dump -lpq "; + getCmd = fullPathName( "cpio" ) + " --force-local --no-absolute-filenames -iuvdF"; + delCmd = QString::null; + putCmd = QString::null; + copyCmd = QString::null; + } else if(arcType == "gzip") { + cmd = fullPathName( "gzip" ); + listCmd = fullPathName( "gzip" ) + " -l"; + getCmd = fullPathName( "gzip" ) + " -dc"; + copyCmd = QString::null; + delCmd = QString::null; + putCmd = QString::null; + } else if(arcType == "bzip2") { + cmd = fullPathName( "bzip2" ); + listCmd = fullPathName( "bzip2" ); + getCmd = fullPathName( "bzip2" ) + " -dc"; + copyCmd = QString::null; + delCmd = QString::null; + putCmd = QString::null; + } else if(arcType == "arj"){ + cmd = fullPathName( "arj" ); + listCmd = fullPathName( "arj" ) + " v -y -v "; + getCmd = fullPathName( "arj" ) + " -jyov -v e "; + copyCmd = fullPathName( "arj" ) + " -jyov -v e "; + delCmd = fullPathName( "arj" ) + " d "; + putCmd = fullPathName( "arj" ) + " -r a "; + if( !getPassword().isEmpty() ) { + getCmd += "-g'"+password+"' "; + copyCmd += "-g'"+password+"' "; + putCmd += "-g'"+password+"' "; + } + } else if(arcType == "lha") { + cmd = fullPathName( "lha" ); + listCmd = fullPathName( "lha" ) + " l "; + getCmd = fullPathName( "lha" ) + " pq "; + copyCmd = fullPathName( "lha" ) + " eif "; + delCmd = fullPathName( "lha" ) + " d "; + putCmd = fullPathName( "lha" ) + " a "; + } else if(arcType == "ace") { + cmd = fullPathName( "unace" ); + listCmd = fullPathName( "unace" ) + " v"; + getCmd = fullPathName( "unace" ) + " e -o "; + copyCmd = fullPathName( "unace" ) + " e -o "; + delCmd = QString::null; + putCmd = QString::null; + if( !getPassword().isEmpty() ) { + getCmd += "-p'"+password+"' "; + copyCmd += "-p'"+password+"' "; + } + } else if ( arcType == "deb" ) { + cmd = fullPathName("dpkg"); + listCmd = fullPathName("dpkg")+" -c"; + getCmd = fullPathName("tar")+" xvf "; + copyCmd = QString::null; + delCmd = QString::null; + putCmd = QString::null; + } else if (arcType == "7z") { + cmd = fullPathName( "7z" ); + if( KStandardDirs::findExe(cmd).isEmpty() ) + cmd = fullPathName( "7za" ); + + listCmd = cmd + " l -y "; + getCmd = cmd + " e -y "; + copyCmd = cmd + " e -y "; + delCmd = cmd + " d -y "; + putCmd = cmd + " a -y "; + if( !getPassword().isEmpty() ) { + getCmd += "-p'"+password+"' "; + listCmd += "-p'"+password+"' "; + copyCmd += "-p'"+password+"' "; + if( !putCmd.isEmpty() ) { + putCmd += "-p'"+password+"' "; + delCmd += "-p'"+password+"' "; + } + } + } + else { + cmd = QString::null; + listCmd = QString::null; + getCmd = QString::null; + copyCmd = QString::null; + delCmd = QString::null; + putCmd = QString::null; + } + + if( KStandardDirs::findExe(cmd).isEmpty() ){ + error( KIO::ERR_CANNOT_LAUNCH_PROCESS, + cmd+ + i18n("\nMake sure that the %1 binary are installed properly on your system.").arg(cmd)); + KRDEBUG("Failed to find cmd: " << cmd); + return false; + } + return true; +} + +bool kio_krarcProtocol::checkStatus( int exitCode ) { + KRDEBUG( exitCode ); + + if( arcType == "zip" || arcType == "rar" || arcType == "7z" ) + return exitCode == 0 || exitCode == 1; + else if( arcType == "ace" || arcType == "bzip2" || arcType == "lha" || arcType == "rpm" || arcType == "arj" ) + return exitCode == 0; + else if( arcType == "gzip" ) + return exitCode == 0 || exitCode == 2; + else + return exitCode == 0; +} + +struct AutoDetectParams { + QString type; + int location; + QString detectionString; +}; + +QString kio_krarcProtocol::detectArchive( bool &encrypted, QString fileName ) { + static AutoDetectParams autoDetectParams[] = {{"zip", 0, "PK\x03\x04"}, + {"rar", 0, "Rar!\x1a" }, + {"arj", 0, "\x60\xea" }, + {"rpm", 0, "\xed\xab\xee\xdb"}, + {"ace", 7, "**ACE**" }, + {"bzip2",0, "\x42\x5a\x68\x39\x31" }, + {"gzip", 0, "\x1f\x8b"}, + {"deb", 0, "!<arch>\ndebian-binary " }, + {"7z", 0, "7z\xbc\xaf\x27\x1c" } }; + static int autoDetectElems = sizeof( autoDetectParams ) / sizeof( AutoDetectParams ); + + encrypted = false; + + QFile arcFile( fileName ); + if ( arcFile.open( IO_ReadOnly ) ) { + char buffer[ 1024 ]; + long sizeMax = arcFile.readBlock( buffer, sizeof( buffer ) ); + arcFile.close(); + + for( int i=0; i < autoDetectElems; i++ ) { + QString detectionString = autoDetectParams[ i ].detectionString; + int location = autoDetectParams[ i ].location; + + int endPtr = detectionString.length() + location; + if( endPtr > sizeMax ) + continue; + + unsigned int j=0; + for(; j != detectionString.length(); j++ ) { + if( detectionString[ j ] == '?' ) + continue; + if( buffer[ location + j ] != detectionString[ j ] ) + break; + } + + if( j == detectionString.length() ) { + QString type = autoDetectParams[ i ].type; + if( type == "bzip2" || type == "gzip" ) { + KTar tapeArchive( fileName ); + if( tapeArchive.open( IO_ReadOnly ) ) { + tapeArchive.close(); + if( type == "bzip2" ) + type = "tbz"; + else + type = "tgz"; + } + } + else if( type == "zip" ) + encrypted = (buffer[6] & 1); + else if( type == "arj" ) { + if( sizeMax > 4 ) { + long headerSize = ((unsigned char *)buffer)[ 2 ] + 256*((unsigned char *)buffer)[ 3 ]; + long fileHeader = headerSize + 10; + if( fileHeader + 9 < sizeMax && buffer[ fileHeader ] == (char)0x60 && buffer[ fileHeader + 1 ] == (char)0xea ) + encrypted = (buffer[ fileHeader + 8 ] & 1 ); + } + } + else if( type == "rar" ) { + if( sizeMax > 13 && buffer[ 9 ] == (char)0x73 ) { + if( buffer[ 10 ] & 0x80 ) { // the header is encrypted? + encrypted = true; + } else { + long offset = 7; + long mainHeaderSize = ((unsigned char *)buffer)[ offset+5 ] + 256*((unsigned char *)buffer)[ offset+6 ]; + offset += mainHeaderSize; + while( offset + 10 < sizeMax ) { + long headerSize = ((unsigned char *)buffer)[ offset+5 ] + 256*((unsigned char *)buffer)[ offset+6 ]; + bool isDir = (buffer[ offset+7 ] == '\0' ) && (buffer[ offset+8 ] == '\0' ) && + (buffer[ offset+9 ] == '\0' ) && (buffer[ offset+10 ] == '\0' ); + + if( buffer[ offset + 2 ] != (char)0x74 ) + break; + if( !isDir ) { + encrypted = ( buffer[ offset + 3 ] & 4 ) != 0; + break; + } + offset += headerSize; + } + } + } + } + else if( type == "ace" ) { + long offset = 0; + long mainHeaderSize = ((unsigned char *)buffer)[ offset+2 ] + 256*((unsigned char *)buffer)[ offset+3 ] + 4; + offset += mainHeaderSize; + while( offset + 10 < sizeMax ) { + long headerSize = ((unsigned char *)buffer)[ offset+2 ] + 256*((unsigned char *)buffer)[ offset+3 ] + 4; + bool isDir = (buffer[ offset+11 ] == '\0' ) && (buffer[ offset+12 ] == '\0' ) && + (buffer[ offset+13 ] == '\0' ) && (buffer[ offset+14 ] == '\0' ); + + if( buffer[ offset + 4 ] != (char)0x01 ) + break; + if( !isDir ) { + encrypted = ( buffer[ offset + 6 ] & 64 ) != 0; + break; + } + offset += headerSize; + } + } + else if( type == "7z" ) { + if( encryptedArchPath == fileName ) + encrypted = true; + else { // we try to find whether the 7z archive is encrypted + // this is hard as the headers are also compresseds + QString tester = fullPathName( "7z" ); + if( KStandardDirs::findExe( tester ).isEmpty() ) { + tester = fullPathName( "7za" ); + if( KStandardDirs::findExe( tester ).isEmpty() ) { + return type; + } + } + + QString testCmd = tester + " t -y "; + lastData = encryptedArchPath = ""; + + KrShellProcess proc; + proc << testCmd << convertName( fileName ); + connect( &proc, SIGNAL( receivedStdout(KProcess*,char*,int) ), + this, SLOT( checkOutputForPassword( KProcess*,char*,int ) ) ); + proc.start(KProcess::Block,KProcess::AllOutput); + encrypted = this->encrypted; + + if( encrypted ) + encryptedArchPath = fileName; + } + } + return type; + } + } + + if( sizeMax >= 512 ) { + /* checking if it's a tar file */ + unsigned checksum = 32*8; + char chksum[ 9 ]; + for( int i=0; i != 512; i++ ) + checksum += ((unsigned char *)buffer)[ i ]; + for( int i=148; i != 156; i++ ) + checksum -= ((unsigned char *)buffer)[ i ]; + sprintf( chksum, "0%o", checksum ); + if( !memcmp( buffer + 148, chksum, strlen( chksum ) ) ) { + int k = strlen( chksum ); + for(; k < 8; k++ ) + if( buffer[148+k] != 0 && buffer[148+k] != 32 ) + break; + if( k==8 ) + return "tar"; + } + } + } + return QString::null; +} + +void kio_krarcProtocol::checkOutputForPassword( KProcess *proc,char *buf,int len ) { + QByteArray d(len); + d.setRawData(buf,len); + QString data = QString( d ); + d.resetRawData(buf,len); + + QString checkable = lastData + data; + + QStringList lines = QStringList::split( '\n', checkable ); + lastData = lines[ lines.count() - 1 ]; + for( unsigned i=0; i != lines.count(); i++ ) { + QString line = lines[ i ].stripWhiteSpace().lower(); + int ndx = line.find( "testing" ); + if( ndx >=0 ) + line.truncate( ndx ); + if( line.isEmpty() ) + continue; + + if( line.contains( "password" ) && line.contains( "enter" ) ) { + KRDEBUG( "Encrypted 7z archive found!" ); + encrypted = true; + proc->kill(); + } + } +} + +void kio_krarcProtocol::invalidatePassword() { + KRDEBUG( arcFile->url().path(-1) + "/" ); + + if( !encrypted ) + return; + + KIO::AuthInfo authInfo; + authInfo.caption= i18n( "Krarc Password Dialog" ); + authInfo.username= "archive"; + authInfo.readOnly = true; + authInfo.keepPassword = true; + authInfo.verifyPath = true; + QString fileName = arcFile->url().path(-1); + authInfo.url = KURL::fromPathOrURL( "/" ); + authInfo.url.setHost( fileName /*.replace('/','_')*/ ); + authInfo.url.setProtocol( "krarc" ); + + password = QString::null; + + cacheAuthentication( authInfo ); +} + +QString kio_krarcProtocol::getPassword() { + KRDEBUG( encrypted ); + + if( !password.isNull() ) + return password; + if( !encrypted ) + return (password = "" ); + + KIO::AuthInfo authInfo; + authInfo.caption= i18n( "Krarc Password Dialog" ); + authInfo.username= "archive"; + authInfo.readOnly = true; + authInfo.keepPassword = true; + authInfo.verifyPath = true; + QString fileName = arcFile->url().path(-1); + authInfo.url = KURL::fromPathOrURL( "/" ); + authInfo.url.setHost( fileName /*.replace('/','_')*/ ); + authInfo.url.setProtocol( "krarc" ); + + if( checkCachedAuthentication( authInfo ) && !authInfo.password.isNull() ) { + KRDEBUG( authInfo.password ); + return ( password = authInfo.password ); + } + + authInfo.password = QString::null; + + if ( openPassDlg( authInfo, i18n("Accessing the file requires password.") ) && !authInfo.password.isNull() ) { + KRDEBUG( authInfo.password ); + return ( password = authInfo.password ); + } + + KRDEBUG( password ); + return password; +} + +QString kio_krarcProtocol::fullPathName( QString name ) { + QString supposedName = krConfig->readEntry( name, name ); + if( supposedName.isEmpty() ) + supposedName = name; + return escape( supposedName ); +} + +QString kio_krarcProtocol::convertFileName( QString name ) { + if( arcType == "zip" ) + name = name.replace( "[", "[[]" ); + return convertName( name ); +} + +QString kio_krarcProtocol::convertName( QString name ) { + if( !name.contains( '\'' ) ) + return "'" + name + "'"; + if( !name.contains( '"' ) && !name.contains( '$' ) ) + return "\"" + name + "\""; + return escape( name ); +} + +QString kio_krarcProtocol::escape( QString name ) { + const QString evilstuff = "\\\"'`()[]{}!?;$&<>| "; // stuff that should get escaped + + for ( unsigned int i = 0; i < evilstuff.length(); ++i ) + name.replace( evilstuff[ i ], ('\\' + evilstuff[ i ]) ); + + return name; +} + +#include "krarc.moc" |