summaryrefslogtreecommitdiffstats
path: root/kioslave/tar
diff options
context:
space:
mode:
Diffstat (limited to 'kioslave/tar')
-rw-r--r--kioslave/tar/Makefile.am24
-rw-r--r--kioslave/tar/ar.protocol12
-rw-r--r--kioslave/tar/ktartest.cpp201
-rw-r--r--kioslave/tar/tar.cc623
-rw-r--r--kioslave/tar/tar.h55
-rw-r--r--kioslave/tar/tar.protocol12
-rw-r--r--kioslave/tar/zip.protocol12
7 files changed, 939 insertions, 0 deletions
diff --git a/kioslave/tar/Makefile.am b/kioslave/tar/Makefile.am
new file mode 100644
index 000000000..fb4f4713a
--- /dev/null
+++ b/kioslave/tar/Makefile.am
@@ -0,0 +1,24 @@
+## Makefile.am of kdebase/kioslave/tar
+
+INCLUDES = $(all_includes)
+AM_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kio_tar.la
+
+kio_tar_la_SOURCES = tar.cc
+kio_tar_la_LIBADD = $(LIB_KSYCOCA)
+kio_tar_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+check_PROGRAMS = ktartest
+
+ktartest_SOURCES = ktartest.cpp
+ktartest_LDADD = $(LIB_KSYCOCA)
+
+noinst_HEADERS = tar.h
+
+kdelnk_DATA = tar.protocol ar.protocol zip.protocol
+kdelnkdir = $(kde_servicesdir)
+
+messages:
+ $(XGETTEXT) *.cc -o $(podir)/kio_tar.pot
diff --git a/kioslave/tar/ar.protocol b/kioslave/tar/ar.protocol
new file mode 100644
index 000000000..7bdc3e746
--- /dev/null
+++ b/kioslave/tar/ar.protocol
@@ -0,0 +1,12 @@
+[Protocol]
+exec=kio_tar
+protocol=ar
+mimetype=application/x-archive
+input=filesystem
+output=filesystem
+listing=Name,Type,Size,Date,Access,Owner,Group,Link
+reading=true
+source=true
+DocPath=kioslave/ar.html
+Icon=ar
+Class=:local
diff --git a/kioslave/tar/ktartest.cpp b/kioslave/tar/ktartest.cpp
new file mode 100644
index 000000000..a82e6dbcd
--- /dev/null
+++ b/kioslave/tar/ktartest.cpp
@@ -0,0 +1,201 @@
+#include "ktar.h"
+#include <stdio.h>
+#include <qfile.h>
+#include <kinstance.h>
+#include <kdebug.h>
+
+void recursive_print( const KTarDirectory * dir, const QString & path )
+{
+ QStringList l = dir->entries();
+ QStringList::Iterator it = l.begin();
+ for( ; it != l.end(); ++it )
+ {
+ const KTarEntry* entry = dir->entry( (*it) );
+ printf("mode=%07o %s %s %s%s\n", entry->permissions(), entry->user().latin1(), entry->group().latin1(), path.latin1(), (*it).latin1());
+ if (entry->isDirectory())
+ recursive_print( (KTarDirectory *)entry, path+(*it)+"/" );
+ }
+}
+
+static void usage()
+{
+ printf("\n"
+ " Usage :\n"
+ " ./ktartest list /path/to/existing_file.tar.gz tests listing an existing tar.gz\n"
+ " ./ktartest get /path/to/existing_file.tar.gz filename tests extracting a file.\n"
+ " ./ktartest readwrite newfile.tar.gz will create the tar.gz, then close and reopen it.\n"
+ " ./ktartest maxlength newfile.tar.gz tests the maximum filename length allowed.\n"
+ " ./ktartest bytearray /path/to/existing_file.tar.gz tests KTarData\n");
+}
+
+int main( int argc, char** argv )
+{
+ if (argc < 3)
+ {
+ usage();
+ return 1;
+ }
+ KInstance instance("ktartest");
+
+ QString command = argv[1];
+ kdDebug() << "main: command=" << command << endl;
+ if ( command == "list" )
+ {
+ KTarGz tar( argv[2] );
+
+ if ( !tar.open( IO_ReadOnly ) )
+ {
+ printf("Could not open %s for reading\n", argv[1] );
+ return 1;
+ }
+
+ const KTarDirectory* dir = tar.directory();
+
+ //printf("calling recursive_print\n");
+ recursive_print( dir, "" );
+ //printf("recursive_print called\n");
+
+ tar.close();
+
+ return 0;
+ }
+ else if ( command == "get" )
+ {
+ if ( argc != 4 )
+ {
+ usage();
+ return 1;
+ }
+
+ KTarGz tar( argv[2] );
+
+ if ( !tar.open( IO_ReadOnly ) )
+ {
+ printf("Could not open %s for reading\n", argv[1] );
+ return 1;
+ }
+
+ const KTarDirectory* dir = tar.directory();
+
+ const KTarEntry* e = dir->entry( argv[3] );
+ Q_ASSERT( e && e->isFile() );
+ const KTarFile* f = (KTarFile*)e;
+
+ QByteArray arr( f->data() );
+ printf("SIZE=%i\n",arr.size() );
+ QString str( arr );
+ printf("DATA=%s\n", str.latin1());
+
+ /*
+ // This is what KGzipDev::readAll could do, if QIODevice::readAll was virtual....
+ QByteArray array(1024);
+ int n;
+ while ( ( n = dev.readBlock( array.data(), array.size() ) ) )
+ {
+ kdDebug() << "readBlock returned " << n << endl << endl;
+ QCString s(array,n+1); // Terminate with 0 before printing
+ printf("%s", s.data());
+ }
+ dev.close();
+ */
+
+
+ tar.close();
+ }
+ else if (command == "readwrite" )
+ {
+ kdDebug() << " --- readwrite --- " << endl;
+ KTarGz tar( argv[2] );
+
+ if ( !tar.open( IO_WriteOnly ) )
+ {
+ printf("Could not open %s for writing\n", argv[1]);
+ return 1;
+ }
+
+ tar.writeFile( "empty", "weis", "users", 0, "" );
+ tar.writeFile( "test1", "weis", "users", 5, "Hallo" );
+ tar.writeFile( "test2", "weis", "users", 8, "Hallo Du" );
+ tar.writeFile( "mydir/test3", "weis", "users", 13, "Noch so einer" );
+ tar.writeFile( "my/dir/test3", "dfaure", "hackers", 29, "I don't speak German (David)" );
+
+#define SIZE1 100
+ // Now a medium file : 100 null bytes
+ char medium[ SIZE1 ];
+ memset( medium, 0, SIZE1 );
+ tar.writeFile( "mediumfile", "user", "group", SIZE1, medium );
+ // Another one, with an absolute path
+ tar.writeFile( "/dir/subdir/mediumfile2", "user", "group", SIZE1, medium );
+
+ // Now a huge file : 20000 null bytes
+ int n = 20000;
+ char * huge = new char[ n ];
+ memset( huge, 0, n );
+ tar.writeFile( "hugefile", "user", "group", n, huge );
+ delete [] huge;
+
+ tar.close();
+
+ printf("-----------------------\n");
+
+ if ( !tar.open( IO_ReadOnly ) )
+ {
+ printf("Could not open %s for reading\n", argv[1] );
+ return 1;
+ }
+
+ const KTarDirectory* dir = tar.directory();
+ recursive_print(dir, "");
+
+ const KTarEntry* e = dir->entry( "mydir/test3" );
+ Q_ASSERT( e && e->isFile() );
+ const KTarFile* f = (KTarFile*)e;
+
+ QByteArray arr( f->data() );
+ printf("SIZE=%i\n",arr.size() );
+ QString str( arr );
+ printf("DATA=%s\n", str.latin1());
+
+ tar.close();
+
+ return 0;
+ }
+ else if ( command == "maxlength" )
+ {
+ KTarGz tar( argv[2] );
+
+ if ( !tar.open( IO_WriteOnly ) )
+ {
+ printf("Could not open %s for writing\n", argv[1]);
+ return 1;
+ }
+ // Generate long filenames of each possible length bigger than 98...
+ for (int i = 98; i < 500 ; i++ )
+ {
+ QString str, num;
+ str.fill( 'a', i-10 );
+ num.setNum( i );
+ num = num.rightJustify( 10, '0' );
+ tar.writeFile( str+num, "testu", "testg", 3, "hum" );
+ }
+ // Result of this test : it fails at 482 (instead of 154 previously).
+ // Ok, I think we can do with that :)
+ tar.close();
+ printf("Now run 'tar tvzf %s'\n", argv[2]);
+ return 0;
+ }
+ else if ( command == "bytearray" )
+ {
+ QFile file( argv[2] );
+ if ( !file.open( IO_ReadOnly ) )
+ return 1;
+ KTarGz tar( &file );
+ tar.open( IO_ReadOnly );
+ const KTarDirectory* dir = tar.directory();
+ recursive_print( dir, "" );
+ return 0;
+ }
+ else
+ printf("Unknown command\n");
+}
+
diff --git a/kioslave/tar/tar.cc b/kioslave/tar/tar.cc
new file mode 100644
index 000000000..bfe909367
--- /dev/null
+++ b/kioslave/tar/tar.cc
@@ -0,0 +1,623 @@
+#include <config.h>
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <qfile.h>
+
+#include <kglobal.h>
+#include <kurl.h>
+#include <kdebug.h>
+#include <kinstance.h>
+#include <ktar.h>
+#include <kzip.h>
+#include <kar.h>
+#include <kmimemagic.h>
+#include <klocale.h>
+#include <kde_file.h>
+#include <kio/global.h>
+#include <kremoteencoding.h>
+
+#include <errno.h> // to be removed
+
+#include "tar.h"
+
+using namespace KIO;
+
+extern "C" { int KDE_EXPORT kdemain(int argc, char **argv); }
+
+int kdemain( int argc, char **argv )
+{
+ KInstance instance( "kio_tar" );
+
+ kdDebug(7109) << "Starting " << getpid() << endl;
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_tar protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+
+ ArchiveProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ kdDebug(7109) << "Done" << endl;
+ return 0;
+}
+
+ArchiveProtocol::ArchiveProtocol( const QCString &pool, const QCString &app ) : SlaveBase( "tar", pool, app )
+{
+ kdDebug( 7109 ) << "ArchiveProtocol::ArchiveProtocol" << endl;
+ m_archiveFile = 0L;
+}
+
+ArchiveProtocol::~ArchiveProtocol()
+{
+ delete m_archiveFile;
+}
+
+bool ArchiveProtocol::checkNewFile( const KURL & url, QString & path, KIO::Error& errorNum )
+{
+ QString fullPath = url.path();
+ kdDebug(7109) << "ArchiveProtocol::checkNewFile " << fullPath << endl;
+
+
+ // Are we already looking at that file ?
+ if ( m_archiveFile && m_archiveName == fullPath.left(m_archiveName.length()) )
+ {
+ // Has it changed ?
+ KDE_struct_stat statbuf;
+ if ( KDE_stat( QFile::encodeName( m_archiveName ), &statbuf ) == 0 )
+ {
+ if ( m_mtime == statbuf.st_mtime )
+ {
+ path = fullPath.mid( m_archiveName.length() );
+ kdDebug(7109) << "ArchiveProtocol::checkNewFile returning " << path << endl;
+ return true;
+ }
+ }
+ }
+ kdDebug(7109) << "Need to open a new file" << endl;
+
+ // Close previous file
+ if ( m_archiveFile )
+ {
+ m_archiveFile->close();
+ delete m_archiveFile;
+ m_archiveFile = 0L;
+ }
+
+ // Find where the tar file is in the full path
+ int pos = 0;
+ QString archiveFile;
+ path = QString::null;
+
+ int len = fullPath.length();
+ if ( len != 0 && fullPath[ len - 1 ] != '/' )
+ fullPath += '/';
+
+ kdDebug(7109) << "the full path is " << fullPath << endl;
+ KDE_struct_stat statbuf;
+ statbuf.st_mode = 0; // be sure to clear the directory bit
+ while ( (pos=fullPath.find( '/', pos+1 )) != -1 )
+ {
+ QString tryPath = fullPath.left( pos );
+ kdDebug(7109) << fullPath << " trying " << tryPath << endl;
+ if ( KDE_stat( QFile::encodeName(tryPath), &statbuf ) == -1 )
+ {
+ // We are not in the file system anymore, either we have already enough data or we will never get any useful data anymore
+ break;
+ }
+ if ( !S_ISDIR(statbuf.st_mode) )
+ {
+ archiveFile = tryPath;
+ m_mtime = statbuf.st_mtime;
+ path = fullPath.mid( pos + 1 );
+ kdDebug(7109) << "fullPath=" << fullPath << " path=" << path << endl;
+ len = path.length();
+ if ( len > 1 )
+ {
+ if ( path[ len - 1 ] == '/' )
+ path.truncate( len - 1 );
+ }
+ else
+ path = QString::fromLatin1("/");
+ kdDebug(7109) << "Found. archiveFile=" << archiveFile << " path=" << path << endl;
+ break;
+ }
+ }
+ if ( archiveFile.isEmpty() )
+ {
+ kdDebug(7109) << "ArchiveProtocol::checkNewFile: not found" << endl;
+ if ( S_ISDIR(statbuf.st_mode) ) // Was the last stat about a directory?
+ {
+ // Too bad, it is a directory, not an archive.
+ kdDebug(7109) << "Path is a directory, not an archive." << endl;
+ errorNum = KIO::ERR_IS_DIRECTORY;
+ }
+ else
+ errorNum = KIO::ERR_DOES_NOT_EXIST;
+ return false;
+ }
+
+ // Open new file
+ if ( url.protocol() == "tar" ) {
+ kdDebug(7109) << "Opening KTar on " << archiveFile << endl;
+ m_archiveFile = new KTar( archiveFile );
+ } else if ( url.protocol() == "ar" ) {
+ kdDebug(7109) << "Opening KAr on " << archiveFile << endl;
+ m_archiveFile = new KAr( archiveFile );
+ } else if ( url.protocol() == "zip" ) {
+ kdDebug(7109) << "Opening KZip on " << archiveFile << endl;
+ m_archiveFile = new KZip( archiveFile );
+ } else {
+ kdWarning(7109) << "Protocol " << url.protocol() << " not supported by this IOSlave" << endl;
+ errorNum = KIO::ERR_UNSUPPORTED_PROTOCOL;
+ return false;
+ }
+
+ if ( !m_archiveFile->open( IO_ReadOnly ) )
+ {
+ kdDebug(7109) << "Opening " << archiveFile << "failed." << endl;
+ delete m_archiveFile;
+ m_archiveFile = 0L;
+ errorNum = KIO::ERR_CANNOT_OPEN_FOR_READING;
+ return false;
+ }
+
+ m_archiveName = archiveFile;
+ return true;
+}
+
+
+void ArchiveProtocol::createUDSEntry( const KArchiveEntry * archiveEntry, UDSEntry & entry )
+{
+ UDSAtom atom;
+ entry.clear();
+ atom.m_uds = UDS_NAME;
+ atom.m_str = remoteEncoding()->decode(archiveEntry->name().local8Bit());
+ entry.append(atom);
+
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = archiveEntry->permissions() & S_IFMT; // keep file type only
+ entry.append( atom );
+
+ atom.m_uds = UDS_SIZE;
+ atom.m_long = archiveEntry->isFile() ? ((KArchiveFile *)archiveEntry)->size() : 0L ;
+ entry.append( atom );
+
+ atom.m_uds = UDS_MODIFICATION_TIME;
+ atom.m_long = archiveEntry->date();
+ entry.append( atom );
+
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = archiveEntry->permissions() & 07777; // keep permissions only
+ entry.append( atom );
+
+ atom.m_uds = UDS_USER;
+ atom.m_str = remoteEncoding()->decode(archiveEntry->user().local8Bit());
+ entry.append( atom );
+
+ atom.m_uds = UDS_GROUP;
+ atom.m_str = remoteEncoding()->decode(archiveEntry->group().local8Bit());
+ entry.append( atom );
+
+ atom.m_uds = UDS_LINK_DEST;
+ atom.m_str = remoteEncoding()->decode(archiveEntry->symlink().local8Bit());
+ entry.append( atom );
+}
+
+void ArchiveProtocol::listDir( const KURL & url )
+{
+ kdDebug( 7109 ) << "ArchiveProtocol::listDir " << url << endl;
+
+ QString path;
+ KIO::Error errorNum;
+ if ( !checkNewFile( url, path, errorNum ) )
+ {
+ if ( errorNum == KIO::ERR_CANNOT_OPEN_FOR_READING )
+ {
+ // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
+ // Therefore give a more specific error message
+ error( KIO::ERR_SLAVE_DEFINED,
+ i18n( "Could not open the file, probably due to an unsupported file format.\n%1")
+ .arg( url.prettyURL() ) );
+ return;
+ }
+ else if ( errorNum != ERR_IS_DIRECTORY )
+ {
+ // We have any other error
+ error( errorNum, url.prettyURL() );
+ return;
+ }
+ // It's a real dir -> redirect
+ KURL redir;
+ redir.setPath( url.path() );
+ kdDebug( 7109 ) << "Ok, redirection to " << redir.url() << endl;
+ redirection( redir );
+ finished();
+ // And let go of the tar file - for people who want to unmount a cdrom after that
+ delete m_archiveFile;
+ m_archiveFile = 0L;
+ return;
+ }
+
+ if ( path.isEmpty() )
+ {
+ KURL redir( url.protocol() + QString::fromLatin1( ":/") );
+ kdDebug( 7109 ) << "url.path()==" << url.path() << endl;
+ redir.setPath( url.path() + QString::fromLatin1("/") );
+ kdDebug( 7109 ) << "ArchiveProtocol::listDir: redirection " << redir.url() << endl;
+ redirection( redir );
+ finished();
+ return;
+ }
+
+ path = QString::fromLocal8Bit(remoteEncoding()->encode(path));
+
+ kdDebug( 7109 ) << "checkNewFile done" << endl;
+ const KArchiveDirectory* root = m_archiveFile->directory();
+ const KArchiveDirectory* dir;
+ if (!path.isEmpty() && path != "/")
+ {
+ kdDebug(7109) << QString("Looking for entry %1").arg(path) << endl;
+ const KArchiveEntry* e = root->entry( path );
+ if ( !e )
+ {
+ error( KIO::ERR_DOES_NOT_EXIST, url.prettyURL() );
+ return;
+ }
+ if ( ! e->isDirectory() )
+ {
+ error( KIO::ERR_IS_FILE, url.prettyURL() );
+ return;
+ }
+ dir = (KArchiveDirectory*)e;
+ } else {
+ dir = root;
+ }
+
+ QStringList l = dir->entries();
+ totalSize( l.count() );
+
+ UDSEntry entry;
+ QStringList::Iterator it = l.begin();
+ for( ; it != l.end(); ++it )
+ {
+ kdDebug(7109) << (*it) << endl;
+ const KArchiveEntry* archiveEntry = dir->entry( (*it) );
+
+ createUDSEntry( archiveEntry, entry );
+
+ listEntry( entry, false );
+ }
+
+ listEntry( entry, true ); // ready
+
+ finished();
+
+ kdDebug( 7109 ) << "ArchiveProtocol::listDir done" << endl;
+}
+
+void ArchiveProtocol::stat( const KURL & url )
+{
+ QString path;
+ UDSEntry entry;
+ KIO::Error errorNum;
+ if ( !checkNewFile( url, path, errorNum ) )
+ {
+ // We may be looking at a real directory - this happens
+ // when pressing up after being in the root of an archive
+ if ( errorNum == KIO::ERR_CANNOT_OPEN_FOR_READING )
+ {
+ // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
+ // Therefore give a more specific error message
+ error( KIO::ERR_SLAVE_DEFINED,
+ i18n( "Could not open the file, probably due to an unsupported file format.\n%1")
+ .arg( url.prettyURL() ) );
+ return;
+ }
+ else if ( errorNum != ERR_IS_DIRECTORY )
+ {
+ // We have any other error
+ error( errorNum, url.prettyURL() );
+ return;
+ }
+ // Real directory. Return just enough information for KRun to work
+ UDSAtom atom;
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = url.fileName();
+ entry.append( atom );
+ kdDebug( 7109 ) << "ArchiveProtocol::stat returning name=" << url.fileName() << endl;
+
+ KDE_struct_stat buff;
+ if ( KDE_stat( QFile::encodeName( url.path() ), &buff ) == -1 )
+ {
+ // Should not happen, as the file was already stated by checkNewFile
+ error( KIO::ERR_COULD_NOT_STAT, url.prettyURL() );
+ return;
+ }
+
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = buff.st_mode & S_IFMT;
+ entry.append( atom );
+
+ statEntry( entry );
+
+ finished();
+
+ // And let go of the tar file - for people who want to unmount a cdrom after that
+ delete m_archiveFile;
+ m_archiveFile = 0L;
+ return;
+ }
+
+ const KArchiveDirectory* root = m_archiveFile->directory();
+ const KArchiveEntry* archiveEntry;
+ if ( path.isEmpty() )
+ {
+ path = QString::fromLatin1( "/" );
+ archiveEntry = root;
+ } else {
+ path = QString::fromLocal8Bit(remoteEncoding()->encode(path));
+ archiveEntry = root->entry( path );
+ }
+ if ( !archiveEntry )
+ {
+ error( KIO::ERR_DOES_NOT_EXIST, url.prettyURL() );
+ return;
+ }
+
+ createUDSEntry( archiveEntry, entry );
+ statEntry( entry );
+
+ finished();
+}
+
+void ArchiveProtocol::get( const KURL & url )
+{
+ kdDebug( 7109 ) << "ArchiveProtocol::get " << url << endl;
+
+ QString path;
+ KIO::Error errorNum;
+ if ( !checkNewFile( url, path, errorNum ) )
+ {
+ if ( errorNum == KIO::ERR_CANNOT_OPEN_FOR_READING )
+ {
+ // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
+ // Therefore give a more specific error message
+ error( KIO::ERR_SLAVE_DEFINED,
+ i18n( "Could not open the file, probably due to an unsupported file format.\n%1")
+ .arg( url.prettyURL() ) );
+ return;
+ }
+ else
+ {
+ // We have any other error
+ error( errorNum, url.prettyURL() );
+ return;
+ }
+ }
+
+ path = QString::fromLocal8Bit(remoteEncoding()->encode(path));
+
+ const KArchiveDirectory* root = m_archiveFile->directory();
+ const KArchiveEntry* archiveEntry = root->entry( path );
+
+ if ( !archiveEntry )
+ {
+ error( KIO::ERR_DOES_NOT_EXIST, url.prettyURL() );
+ return;
+ }
+ if ( archiveEntry->isDirectory() )
+ {
+ error( KIO::ERR_IS_DIRECTORY, url.prettyURL() );
+ return;
+ }
+ const KArchiveFile* archiveFileEntry = static_cast<const KArchiveFile *>(archiveEntry);
+ if ( !archiveEntry->symlink().isEmpty() )
+ {
+ kdDebug(7109) << "Redirection to " << archiveEntry->symlink() << endl;
+ KURL realURL;
+ if (archiveEntry->symlink().startsWith("/")) { // absolute path
+ realURL.setPath(archiveEntry->symlink() ); // goes out of tar:/, back into file:
+ } else {
+ realURL = KURL( url, archiveEntry->symlink() );
+ }
+ kdDebug(7109) << "realURL= " << realURL << endl;
+ redirection( realURL );
+ finished();
+ return;
+ }
+
+ //kdDebug(7109) << "Preparing to get the archive data" << endl;
+
+ /*
+ * The easy way would be to get the data by calling archiveFileEntry->data()
+ * However this has drawbacks:
+ * - the complete file must be read into the memory
+ * - errors are skipped, resulting in an empty file
+ */
+
+ QIODevice* io = 0;
+ // Getting the device is hard, as archiveFileEntry->device() is not virtual!
+ if ( url.protocol() == "tar" )
+ {
+ io = archiveFileEntry->device();
+ }
+ else if ( url.protocol() == "ar" )
+ {
+ io = archiveFileEntry->device();
+ }
+ else if ( url.protocol() == "zip" )
+ {
+ io = ((KZipFileEntry*) archiveFileEntry)->device();
+ }
+ else
+ {
+ // Wrong protocol? Why was this not catched by checkNewFile?
+ kdWarning(7109) << "Protocol " << url.protocol() << " not supported by this IOSlave; " << k_funcinfo << endl;
+ error( KIO::ERR_UNSUPPORTED_PROTOCOL, url.protocol() );
+ return;
+ }
+
+ if (!io)
+ {
+ error( KIO::ERR_SLAVE_DEFINED,
+ i18n( "The archive file could not be opened, perhaps because the format is unsupported.\n%1" )
+ .arg( url.prettyURL() ) );
+ return;
+ }
+
+ if ( !io->open( IO_ReadOnly ) )
+ {
+ error( KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyURL() );
+ return;
+ }
+
+ totalSize( archiveFileEntry->size() );
+
+ // Size of a QIODevice read. It must be large enough so that the mime type check will not fail
+ const int maxSize = 0x100000; // 1MB
+
+ int bufferSize = kMin( maxSize, archiveFileEntry->size() );
+ QByteArray buffer ( bufferSize );
+ if ( buffer.isEmpty() && bufferSize > 0 )
+ {
+ // Something went wrong
+ error( KIO::ERR_OUT_OF_MEMORY, url.prettyURL() );
+ return;
+ }
+
+ bool firstRead = true;
+
+ // How much file do we still have to process?
+ int fileSize = archiveFileEntry->size();
+ KIO::filesize_t processed = 0;
+
+ while ( !io->atEnd() && fileSize > 0 )
+ {
+ if ( !firstRead )
+ {
+ bufferSize = kMin( maxSize, fileSize );
+ buffer.resize( bufferSize, QGArray::SpeedOptim );
+ }
+ const Q_LONG read = io->readBlock( buffer.data(), buffer.size() ); // Avoid to use bufferSize here, in case something went wrong.
+ if ( read != bufferSize )
+ {
+ kdWarning(7109) << "Read " << read << " bytes but expected " << bufferSize << endl;
+ error( KIO::ERR_COULD_NOT_READ, url.prettyURL() );
+ return;
+ }
+ if ( firstRead )
+ {
+ // We use the magic one the first data read
+ // (As magic detection is about fixed positions, we can be sure that it is enough data.)
+ KMimeMagicResult * result = KMimeMagic::self()->findBufferFileType( buffer, path );
+ kdDebug(7109) << "Emitting mimetype " << result->mimeType() << endl;
+ mimeType( result->mimeType() );
+ firstRead = false;
+ }
+ data( buffer );
+ processed += read;
+ processedSize( processed );
+ fileSize -= bufferSize;
+ }
+ io->close();
+ delete io;
+
+ data( QByteArray() );
+
+ finished();
+}
+
+/*
+ In case someone wonders how the old filter stuff looked like : :)
+void TARProtocol::slotData(void *_p, int _len)
+{
+ switch (m_cmd) {
+ case CMD_PUT:
+ assert(m_pFilter);
+ m_pFilter->send(_p, _len);
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void TARProtocol::slotDataEnd()
+{
+ switch (m_cmd) {
+ case CMD_PUT:
+ assert(m_pFilter && m_pJob);
+ m_pFilter->finish();
+ m_pJob->dataEnd();
+ m_cmd = CMD_NONE;
+ break;
+ default:
+ abort();
+ break;
+ }
+}
+
+void TARProtocol::jobData(void *_p, int _len)
+{
+ switch (m_cmd) {
+ case CMD_GET:
+ assert(m_pFilter);
+ m_pFilter->send(_p, _len);
+ break;
+ case CMD_COPY:
+ assert(m_pFilter);
+ m_pFilter->send(_p, _len);
+ break;
+ default:
+ abort();
+ }
+}
+
+void TARProtocol::jobDataEnd()
+{
+ switch (m_cmd) {
+ case CMD_GET:
+ assert(m_pFilter);
+ m_pFilter->finish();
+ dataEnd();
+ break;
+ case CMD_COPY:
+ assert(m_pFilter);
+ m_pFilter->finish();
+ m_pJob->dataEnd();
+ break;
+ default:
+ abort();
+ }
+}
+
+void TARProtocol::filterData(void *_p, int _len)
+{
+debug("void TARProtocol::filterData");
+ switch (m_cmd) {
+ case CMD_GET:
+ data(_p, _len);
+ break;
+ case CMD_PUT:
+ assert (m_pJob);
+ m_pJob->data(_p, _len);
+ break;
+ case CMD_COPY:
+ assert(m_pJob);
+ m_pJob->data(_p, _len);
+ break;
+ default:
+ abort();
+ }
+}
+*/
+
+// kate: space-indent on; indent-width 4; replace-tabs on;
diff --git a/kioslave/tar/tar.h b/kioslave/tar/tar.h
new file mode 100644
index 000000000..644a8b100
--- /dev/null
+++ b/kioslave/tar/tar.h
@@ -0,0 +1,55 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _TAR_H
+#define _TAR_H
+
+#include <sys/types.h>
+
+#include <kio/global.h>
+#include <kio/slavebase.h>
+
+class ArchiveProtocol : public KIO::SlaveBase
+{
+public:
+ ArchiveProtocol( const QCString &pool, const QCString &app );
+ virtual ~ArchiveProtocol();
+
+ virtual void listDir( const KURL & url );
+ virtual void stat( const KURL & url );
+ virtual void get( const KURL & url );
+
+protected:
+ void createUDSEntry( const KArchiveEntry * tarEntry, KIO::UDSEntry & entry );
+
+ /**
+ * \brief find, check and open the archive file
+ * \param url The URL of the archive
+ * \param path Path where the archive really is (returned value)
+ * \param errNum KIO error number (undefined if the function returns true)
+ * \return true if file was found, false if there was an error
+ */
+ bool checkNewFile( const KURL & url, QString & path, KIO::Error& errorNum );
+
+ KArchive * m_archiveFile;
+ QString m_archiveName;
+ time_t m_mtime;
+};
+
+#endif
diff --git a/kioslave/tar/tar.protocol b/kioslave/tar/tar.protocol
new file mode 100644
index 000000000..096ed249a
--- /dev/null
+++ b/kioslave/tar/tar.protocol
@@ -0,0 +1,12 @@
+[Protocol]
+exec=kio_tar
+protocol=tar
+mimetype=application/x-tar
+input=filesystem
+output=filesystem
+listing=Name,Type,Size,Date,Access,Owner,Group,Link
+reading=true
+source=true
+DocPath=kioslave/tar.html
+Icon=tar
+Class=:local
diff --git a/kioslave/tar/zip.protocol b/kioslave/tar/zip.protocol
new file mode 100644
index 000000000..a7dffe7ad
--- /dev/null
+++ b/kioslave/tar/zip.protocol
@@ -0,0 +1,12 @@
+[Protocol]
+exec=kio_tar
+protocol=zip
+mimetype=application/x-zip
+input=filesystem
+output=filesystem
+listing=Name,Type,Size,Date,Access,Owner,Group,Link
+reading=true
+source=true
+DocPath=kioslave/zip.html
+Icon=zip
+Class=:local