1 /* This file is part of the KDE libraries
2 Copyright (C) 2000 David Faure <faure@kde.org>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include <sys/types.h>
30 #include <kcomponentdata.h>
34 #include <kmimetype.h>
37 #include <kio/global.h>
39 #include "kio_archive.h"
44 extern "C" { int KDE_EXPORT
kdemain(int argc
, char **argv
); }
46 int kdemain( int argc
, char **argv
)
48 KComponentData
componentData( "kio_archive" );
50 kDebug(7109) << "Starting" << getpid();
54 fprintf(stderr
, "Usage: kio_archive protocol domain-socket1 domain-socket2\n");
58 ArchiveProtocol
slave(argv
[2], argv
[3]);
61 kDebug(7109) << "Done";
65 ArchiveProtocol::ArchiveProtocol( const QByteArray
&pool
, const QByteArray
&app
) : SlaveBase( "tar", pool
, app
)
67 kDebug( 7109 ) << "ArchiveProtocol::ArchiveProtocol";
71 ArchiveProtocol::~ArchiveProtocol()
76 bool ArchiveProtocol::checkNewFile( const KUrl
& url
, QString
& path
, KIO::Error
& errorNum
)
78 QString fullPath
= url
.path();
79 kDebug(7109) << "ArchiveProtocol::checkNewFile" << fullPath
;
82 // Are we already looking at that file ?
83 if ( m_archiveFile
&& m_archiveName
== fullPath
.left(m_archiveName
.length()) )
86 KDE_struct_stat statbuf
;
87 if ( KDE_stat( QFile::encodeName( m_archiveName
), &statbuf
) == 0 )
89 if ( m_mtime
== statbuf
.st_mtime
)
91 path
= fullPath
.mid( m_archiveName
.length() );
92 kDebug(7109) << "ArchiveProtocol::checkNewFile returning" << path
;
97 kDebug(7109) << "Need to open a new file";
99 // Close previous file
102 m_archiveFile
->close();
103 delete m_archiveFile
;
107 // Find where the tar file is in the full path
112 int len
= fullPath
.length();
113 if ( len
!= 0 && fullPath
[ len
- 1 ] != '/' )
116 kDebug(7109) << "the full path is" << fullPath
;
117 KDE_struct_stat statbuf
;
118 statbuf
.st_mode
= 0; // be sure to clear the directory bit
119 while ( (pos
=fullPath
.indexOf( '/', pos
+1 )) != -1 )
121 QString tryPath
= fullPath
.left( pos
);
122 kDebug(7109) << fullPath
<< "trying" << tryPath
;
123 if ( KDE_stat( QFile::encodeName(tryPath
), &statbuf
) == -1 )
125 // We are not in the file system anymore, either we have already enough data or we will never get any useful data anymore
128 if ( !S_ISDIR(statbuf
.st_mode
) )
130 archiveFile
= tryPath
;
131 m_mtime
= statbuf
.st_mtime
;
132 #ifdef Q_WS_WIN // st_uid and st_gid provides no information
136 KUser
user(statbuf
.st_uid
);
137 m_user
= user
.loginName();
138 KUserGroup
group(statbuf
.st_gid
);
139 m_group
= group
.name();
141 path
= fullPath
.mid( pos
+ 1 );
142 kDebug(7109).nospace() << "fullPath=" << fullPath
<< " path=" << path
;
146 if ( path
[ len
- 1 ] == '/' )
147 path
.truncate( len
- 1 );
150 path
= QString::fromLatin1("/");
151 kDebug(7109).nospace() << "Found. archiveFile=" << archiveFile
<< " path=" << path
;
155 if ( archiveFile
.isEmpty() )
157 kDebug(7109) << "ArchiveProtocol::checkNewFile: not found";
158 if ( S_ISDIR(statbuf
.st_mode
) ) // Was the last stat about a directory?
160 // Too bad, it is a directory, not an archive.
161 kDebug(7109) << "Path is a directory, not an archive.";
162 errorNum
= KIO::ERR_IS_DIRECTORY
;
165 errorNum
= KIO::ERR_DOES_NOT_EXIST
;
170 if ( url
.protocol() == "tar" ) {
171 kDebug(7109) << "Opening KTar on" << archiveFile
;
172 m_archiveFile
= new KTar( archiveFile
);
173 } else if ( url
.protocol() == "ar" ) {
174 kDebug(7109) << "Opening KAr on " << archiveFile
;
175 m_archiveFile
= new KAr( archiveFile
);
176 } else if ( url
.protocol() == "zip" ) {
177 kDebug(7109) << "Opening KZip on " << archiveFile
;
178 m_archiveFile
= new KZip( archiveFile
);
180 kWarning(7109) << "Protocol" << url
.protocol() << "not supported by this IOSlave" ;
181 errorNum
= KIO::ERR_UNSUPPORTED_PROTOCOL
;
185 if ( !m_archiveFile
->open( QIODevice::ReadOnly
) )
187 kDebug(7109) << "Opening" << archiveFile
<< "failed.";
188 delete m_archiveFile
;
190 errorNum
= KIO::ERR_CANNOT_OPEN_FOR_READING
;
194 m_archiveName
= archiveFile
;
199 void ArchiveProtocol::createRootUDSEntry( KIO::UDSEntry
& entry
)
202 entry
.insert( KIO::UDSEntry::UDS_NAME
, "." );
203 entry
.insert( KIO::UDSEntry::UDS_FILE_TYPE
, S_IFDIR
);
204 entry
.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME
, m_mtime
);
205 //entry.insert( KIO::UDSEntry::UDS_ACCESS, 07777 ); // fake 'x' permissions, this is a pseudo-directory
206 entry
.insert( KIO::UDSEntry::UDS_USER
, m_user
);
207 entry
.insert( KIO::UDSEntry::UDS_GROUP
, m_group
);
210 void ArchiveProtocol::createUDSEntry( const KArchiveEntry
* archiveEntry
, UDSEntry
& entry
)
213 entry
.insert( KIO::UDSEntry::UDS_NAME
, archiveEntry
->name() );
214 entry
.insert( KIO::UDSEntry::UDS_FILE_TYPE
, archiveEntry
->permissions() & S_IFMT
); // keep file type only
215 entry
.insert( KIO::UDSEntry::UDS_SIZE
, archiveEntry
->isFile() ? ((KArchiveFile
*)archiveEntry
)->size() : 0L );
216 entry
.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME
, archiveEntry
->date());
217 entry
.insert( KIO::UDSEntry::UDS_ACCESS
, archiveEntry
->permissions() & 07777 ); // keep permissions only
218 entry
.insert( KIO::UDSEntry::UDS_USER
, archiveEntry
->user());
219 entry
.insert( KIO::UDSEntry::UDS_GROUP
, archiveEntry
->group());
220 entry
.insert( KIO::UDSEntry::UDS_LINK_DEST
, archiveEntry
->symLinkTarget());
223 void ArchiveProtocol::listDir( const KUrl
& url
)
225 kDebug( 7109 ) << "ArchiveProtocol::listDir" << url
.url();
229 if ( !checkNewFile( url
, path
, errorNum
) )
231 if ( errorNum
== KIO::ERR_CANNOT_OPEN_FOR_READING
)
233 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
234 // Therefore give a more specific error message
235 error( KIO::ERR_SLAVE_DEFINED
,
236 i18n( "Could not open the file, probably due to an unsupported file format.\n%1",
240 else if ( errorNum
!= ERR_IS_DIRECTORY
)
242 // We have any other error
243 error( errorNum
, url
.prettyUrl() );
246 // It's a real dir -> redirect
248 redir
.setPath( url
.path() );
249 kDebug( 7109 ) << "Ok, redirection to" << redir
.url();
250 redirection( redir
);
252 // And let go of the tar file - for people who want to unmount a cdrom after that
253 delete m_archiveFile
;
258 if ( path
.isEmpty() )
260 KUrl
redir( url
.protocol() + QString::fromLatin1( ":/") );
261 kDebug( 7109 ) << "url.path()=" << url
.path();
262 redir
.setPath( url
.path() + QString::fromLatin1("/") );
263 kDebug( 7109 ) << "ArchiveProtocol::listDir: redirection" << redir
.url();
264 redirection( redir
);
269 kDebug( 7109 ) << "checkNewFile done";
270 const KArchiveDirectory
* root
= m_archiveFile
->directory();
271 const KArchiveDirectory
* dir
;
272 if (!path
.isEmpty() && path
!= "/")
274 kDebug(7109) << "Looking for entry" << path
;
275 const KArchiveEntry
* e
= root
->entry( path
);
278 error( KIO::ERR_DOES_NOT_EXIST
, url
.prettyUrl() );
281 if ( ! e
->isDirectory() )
283 error( KIO::ERR_IS_FILE
, url
.prettyUrl() );
286 dir
= (KArchiveDirectory
*)e
;
291 const QStringList l
= dir
->entries();
292 totalSize( l
.count() );
295 if (!l
.contains(".")) {
296 createRootUDSEntry(entry
);
297 listEntry(entry
, false);
300 QStringList::const_iterator it
= l
.begin();
301 for( ; it
!= l
.end(); ++it
)
303 kDebug(7109) << (*it
);
304 const KArchiveEntry
* archiveEntry
= dir
->entry( (*it
) );
306 createUDSEntry( archiveEntry
, entry
);
308 listEntry( entry
, false );
311 listEntry( entry
, true ); // ready
315 kDebug( 7109 ) << "ArchiveProtocol::listDir done";
318 void ArchiveProtocol::stat( const KUrl
& url
)
323 if ( !checkNewFile( url
, path
, errorNum
) )
325 // We may be looking at a real directory - this happens
326 // when pressing up after being in the root of an archive
327 if ( errorNum
== KIO::ERR_CANNOT_OPEN_FOR_READING
)
329 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
330 // Therefore give a more specific error message
331 error( KIO::ERR_SLAVE_DEFINED
,
332 i18n( "Could not open the file, probably due to an unsupported file format.\n%1",
336 else if ( errorNum
!= ERR_IS_DIRECTORY
)
338 // We have any other error
339 error( errorNum
, url
.prettyUrl() );
342 // Real directory. Return just enough information for KRun to work
343 entry
.insert( KIO::UDSEntry::UDS_NAME
, url
.fileName());
344 kDebug( 7109 ).nospace() << "ArchiveProtocol::stat returning name=" << url
.fileName();
346 KDE_struct_stat buff
;
347 if ( KDE_stat( QFile::encodeName( url
.path() ), &buff
) == -1 )
349 // Should not happen, as the file was already stated by checkNewFile
350 error( KIO::ERR_COULD_NOT_STAT
, url
.prettyUrl() );
354 entry
.insert( KIO::UDSEntry::UDS_FILE_TYPE
, buff
.st_mode
& S_IFMT
);
360 // And let go of the tar file - for people who want to unmount a cdrom after that
361 delete m_archiveFile
;
366 const KArchiveDirectory
* root
= m_archiveFile
->directory();
367 const KArchiveEntry
* archiveEntry
;
368 if ( path
.isEmpty() )
370 path
= QString::fromLatin1( "/" );
373 archiveEntry
= root
->entry( path
);
377 error( KIO::ERR_DOES_NOT_EXIST
, url
.prettyUrl() );
381 createUDSEntry( archiveEntry
, entry
);
387 void ArchiveProtocol::get( const KUrl
& url
)
389 kDebug( 7109 ) << "ArchiveProtocol::get" << url
.url();
393 if ( !checkNewFile( url
, path
, errorNum
) )
395 if ( errorNum
== KIO::ERR_CANNOT_OPEN_FOR_READING
)
397 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
398 // Therefore give a more specific error message
399 error( KIO::ERR_SLAVE_DEFINED
,
400 i18n( "Could not open the file, probably due to an unsupported file format.\n%1",
406 // We have any other error
407 error( errorNum
, url
.prettyUrl() );
412 const KArchiveDirectory
* root
= m_archiveFile
->directory();
413 const KArchiveEntry
* archiveEntry
= root
->entry( path
);
417 error( KIO::ERR_DOES_NOT_EXIST
, url
.prettyUrl() );
420 if ( archiveEntry
->isDirectory() )
422 error( KIO::ERR_IS_DIRECTORY
, url
.prettyUrl() );
425 const KArchiveFile
* archiveFileEntry
= static_cast<const KArchiveFile
*>(archiveEntry
);
426 if ( !archiveEntry
->symLinkTarget().isEmpty() )
428 kDebug(7109) << "Redirection to" << archiveEntry
->symLinkTarget();
429 KUrl
realURL( url
, archiveEntry
->symLinkTarget() );
430 kDebug(7109).nospace() << "realURL=" << realURL
.url();
431 redirection( realURL
);
436 //kDebug(7109) << "Preparing to get the archive data";
439 * The easy way would be to get the data by calling archiveFileEntry->data()
440 * However this has drawbacks:
441 * - the complete file must be read into the memory
442 * - errors are skipped, resulting in an empty file
445 QIODevice
* io
= archiveFileEntry
->createDevice();
449 error( KIO::ERR_SLAVE_DEFINED
,
450 i18n( "The archive file could not be opened, perhaps because the format is unsupported.\n%1" ,
455 if ( !io
->open( QIODevice::ReadOnly
) )
457 error( KIO::ERR_CANNOT_OPEN_FOR_READING
, url
.prettyUrl() );
462 totalSize( archiveFileEntry
->size() );
464 // Size of a QIODevice read. It must be large enough so that the mime type check will not fail
465 const qint64 maxSize
= 0x100000; // 1MB
467 qint64 bufferSize
= qMin( maxSize
, archiveFileEntry
->size() );
469 buffer
.resize( bufferSize
);
470 if ( buffer
.isEmpty() && bufferSize
> 0 )
472 // Something went wrong
473 error( KIO::ERR_OUT_OF_MEMORY
, url
.prettyUrl() );
478 bool firstRead
= true;
480 // How much file do we still have to process?
481 qint64 fileSize
= archiveFileEntry
->size();
482 KIO::filesize_t processed
= 0;
484 while ( !io
->atEnd() && fileSize
> 0 )
488 bufferSize
= qMin( maxSize
, fileSize
);
489 buffer
.resize( bufferSize
);
491 const qint64 read
= io
->read( buffer
.data(), buffer
.size() ); // Avoid to use bufferSize here, in case something went wrong.
492 if ( read
!= bufferSize
)
494 kWarning(7109) << "Read" << read
<< "bytes but expected" << bufferSize
;
495 error( KIO::ERR_COULD_NOT_READ
, url
.prettyUrl() );
501 // We use the magic one the first data read
502 // (As magic detection is about fixed positions, we can be sure that it is enough data.)
503 KMimeType::Ptr mime
= KMimeType::findByNameAndContent( path
, buffer
);
504 kDebug(7109) << "Emitting mimetype" << mime
->name();
505 mimeType( mime
->name() );
510 processedSize( processed
);
511 fileSize
-= bufferSize
;
516 data( QByteArray() );
522 In case someone wonders how the old filter stuff looked like : :)
523 void TARProtocol::slotData(void *_p, int _len)
528 m_pFilter->send(_p, _len);
536 void TARProtocol::slotDataEnd()
540 assert(m_pFilter && m_pJob);
551 void TARProtocol::jobData(void *_p, int _len)
556 m_pFilter->send(_p, _len);
560 m_pFilter->send(_p, _len);
567 void TARProtocol::jobDataEnd()
585 void TARProtocol::filterData(void *_p, int _len)
587 debug("void TARProtocol::filterData");
594 m_pJob->data(_p, _len);
598 m_pJob->data(_p, _len);
606 // kate: space-indent on; indent-width 4; replace-tabs on;