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 "kio_archive.h"
22 #include <sys/types.h>
32 #include <kcomponentdata.h>
36 #include <kmimetype.h>
39 #include <kio/global.h>
45 extern "C" { int KDE_EXPORT
kdemain(int argc
, char **argv
); }
47 int kdemain( int argc
, char **argv
)
49 KComponentData
componentData( "kio_archive" );
51 kDebug(7109) << "Starting" << getpid();
55 fprintf(stderr
, "Usage: kio_archive protocol domain-socket1 domain-socket2\n");
59 ArchiveProtocol
slave(argv
[2], argv
[3]);
62 kDebug(7109) << "Done";
66 ArchiveProtocol::ArchiveProtocol( const QByteArray
&pool
, const QByteArray
&app
) : SlaveBase( "tar", pool
, app
)
68 kDebug( 7109 ) << "ArchiveProtocol::ArchiveProtocol";
72 ArchiveProtocol::~ArchiveProtocol()
77 bool ArchiveProtocol::checkNewFile( const KUrl
& url
, QString
& path
, KIO::Error
& errorNum
)
79 QString fullPath
= url
.path();
80 kDebug(7109) << "ArchiveProtocol::checkNewFile" << fullPath
;
83 // Are we already looking at that file ?
84 if ( m_archiveFile
&& m_archiveName
== fullPath
.left(m_archiveName
.length()) )
87 KDE_struct_stat statbuf
;
88 if ( KDE_stat( QFile::encodeName( m_archiveName
), &statbuf
) == 0 )
90 if ( m_mtime
== statbuf
.st_mtime
)
92 path
= fullPath
.mid( m_archiveName
.length() );
93 kDebug(7109) << "ArchiveProtocol::checkNewFile returning" << path
;
98 kDebug(7109) << "Need to open a new file";
100 // Close previous file
103 m_archiveFile
->close();
104 delete m_archiveFile
;
108 // Find where the tar file is in the full path
113 int len
= fullPath
.length();
114 if ( len
!= 0 && fullPath
[ len
- 1 ] != '/' )
117 kDebug(7109) << "the full path is" << fullPath
;
118 KDE_struct_stat statbuf
;
119 statbuf
.st_mode
= 0; // be sure to clear the directory bit
120 while ( (pos
=fullPath
.indexOf( '/', pos
+1 )) != -1 )
122 QString tryPath
= fullPath
.left( pos
);
123 kDebug(7109) << fullPath
<< "trying" << tryPath
;
124 if ( KDE_stat( QFile::encodeName(tryPath
), &statbuf
) == -1 )
126 // We are not in the file system anymore, either we have already enough data or we will never get any useful data anymore
129 if ( !S_ISDIR(statbuf
.st_mode
) )
131 archiveFile
= tryPath
;
132 m_mtime
= statbuf
.st_mtime
;
133 #ifdef Q_WS_WIN // st_uid and st_gid provides no information
137 KUser
user(statbuf
.st_uid
);
138 m_user
= user
.loginName();
139 KUserGroup
group(statbuf
.st_gid
);
140 m_group
= group
.name();
142 path
= fullPath
.mid( pos
+ 1 );
143 kDebug(7109).nospace() << "fullPath=" << fullPath
<< " path=" << path
;
147 if ( path
[ len
- 1 ] == '/' )
148 path
.truncate( len
- 1 );
151 path
= QString::fromLatin1("/");
152 kDebug(7109).nospace() << "Found. archiveFile=" << archiveFile
<< " path=" << path
;
156 if ( archiveFile
.isEmpty() )
158 kDebug(7109) << "ArchiveProtocol::checkNewFile: not found";
159 if ( S_ISDIR(statbuf
.st_mode
) ) // Was the last stat about a directory?
161 // Too bad, it is a directory, not an archive.
162 kDebug(7109) << "Path is a directory, not an archive.";
163 errorNum
= KIO::ERR_IS_DIRECTORY
;
166 errorNum
= KIO::ERR_DOES_NOT_EXIST
;
171 if ( url
.protocol() == "tar" ) {
172 kDebug(7109) << "Opening KTar on" << archiveFile
;
173 m_archiveFile
= new KTar( archiveFile
);
174 } else if ( url
.protocol() == "ar" ) {
175 kDebug(7109) << "Opening KAr on " << archiveFile
;
176 m_archiveFile
= new KAr( archiveFile
);
177 } else if ( url
.protocol() == "zip" ) {
178 kDebug(7109) << "Opening KZip on " << archiveFile
;
179 m_archiveFile
= new KZip( archiveFile
);
181 kWarning(7109) << "Protocol" << url
.protocol() << "not supported by this IOSlave" ;
182 errorNum
= KIO::ERR_UNSUPPORTED_PROTOCOL
;
186 if ( !m_archiveFile
->open( QIODevice::ReadOnly
) )
188 kDebug(7109) << "Opening" << archiveFile
<< "failed.";
189 delete m_archiveFile
;
191 errorNum
= KIO::ERR_CANNOT_OPEN_FOR_READING
;
195 m_archiveName
= archiveFile
;
200 void ArchiveProtocol::createRootUDSEntry( KIO::UDSEntry
& entry
)
203 entry
.insert( KIO::UDSEntry::UDS_NAME
, "." );
204 entry
.insert( KIO::UDSEntry::UDS_FILE_TYPE
, S_IFDIR
);
205 entry
.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME
, m_mtime
);
206 //entry.insert( KIO::UDSEntry::UDS_ACCESS, 07777 ); // fake 'x' permissions, this is a pseudo-directory
207 entry
.insert( KIO::UDSEntry::UDS_USER
, m_user
);
208 entry
.insert( KIO::UDSEntry::UDS_GROUP
, m_group
);
211 void ArchiveProtocol::createUDSEntry( const KArchiveEntry
* archiveEntry
, UDSEntry
& entry
)
214 entry
.insert( KIO::UDSEntry::UDS_NAME
, archiveEntry
->name() );
215 entry
.insert( KIO::UDSEntry::UDS_FILE_TYPE
, archiveEntry
->permissions() & S_IFMT
); // keep file type only
216 entry
.insert( KIO::UDSEntry::UDS_SIZE
, archiveEntry
->isFile() ? ((KArchiveFile
*)archiveEntry
)->size() : 0L );
217 entry
.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME
, archiveEntry
->date());
218 entry
.insert( KIO::UDSEntry::UDS_ACCESS
, archiveEntry
->permissions() & 07777 ); // keep permissions only
219 entry
.insert( KIO::UDSEntry::UDS_USER
, archiveEntry
->user());
220 entry
.insert( KIO::UDSEntry::UDS_GROUP
, archiveEntry
->group());
221 entry
.insert( KIO::UDSEntry::UDS_LINK_DEST
, archiveEntry
->symLinkTarget());
224 void ArchiveProtocol::listDir( const KUrl
& url
)
226 kDebug( 7109 ) << "ArchiveProtocol::listDir" << url
.url();
230 if ( !checkNewFile( url
, path
, errorNum
) )
232 if ( errorNum
== KIO::ERR_CANNOT_OPEN_FOR_READING
)
234 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
235 // Therefore give a more specific error message
236 error( KIO::ERR_SLAVE_DEFINED
,
237 i18n( "Could not open the file, probably due to an unsupported file format.\n%1",
241 else if ( errorNum
!= ERR_IS_DIRECTORY
)
243 // We have any other error
244 error( errorNum
, url
.prettyUrl() );
247 // It's a real dir -> redirect
249 redir
.setPath( url
.path() );
250 kDebug( 7109 ) << "Ok, redirection to" << redir
.url();
251 redirection( redir
);
253 // And let go of the tar file - for people who want to unmount a cdrom after that
254 delete m_archiveFile
;
259 if ( path
.isEmpty() )
261 KUrl
redir( url
.protocol() + QString::fromLatin1( ":/") );
262 kDebug( 7109 ) << "url.path()=" << url
.path();
263 redir
.setPath( url
.path() + QString::fromLatin1("/") );
264 kDebug( 7109 ) << "ArchiveProtocol::listDir: redirection" << redir
.url();
265 redirection( redir
);
270 kDebug( 7109 ) << "checkNewFile done";
271 const KArchiveDirectory
* root
= m_archiveFile
->directory();
272 const KArchiveDirectory
* dir
;
273 if (!path
.isEmpty() && path
!= "/")
275 kDebug(7109) << "Looking for entry" << path
;
276 const KArchiveEntry
* e
= root
->entry( path
);
279 error( KIO::ERR_DOES_NOT_EXIST
, url
.prettyUrl() );
282 if ( ! e
->isDirectory() )
284 error( KIO::ERR_IS_FILE
, url
.prettyUrl() );
287 dir
= (KArchiveDirectory
*)e
;
292 const QStringList l
= dir
->entries();
293 totalSize( l
.count() );
296 if (!l
.contains(".")) {
297 createRootUDSEntry(entry
);
298 listEntry(entry
, false);
301 QStringList::const_iterator it
= l
.begin();
302 for( ; it
!= l
.end(); ++it
)
304 kDebug(7109) << (*it
);
305 const KArchiveEntry
* archiveEntry
= dir
->entry( (*it
) );
307 createUDSEntry( archiveEntry
, entry
);
309 listEntry( entry
, false );
312 listEntry( entry
, true ); // ready
316 kDebug( 7109 ) << "ArchiveProtocol::listDir done";
319 void ArchiveProtocol::stat( const KUrl
& url
)
324 if ( !checkNewFile( url
, path
, errorNum
) )
326 // We may be looking at a real directory - this happens
327 // when pressing up after being in the root of an archive
328 if ( errorNum
== KIO::ERR_CANNOT_OPEN_FOR_READING
)
330 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
331 // Therefore give a more specific error message
332 error( KIO::ERR_SLAVE_DEFINED
,
333 i18n( "Could not open the file, probably due to an unsupported file format.\n%1",
337 else if ( errorNum
!= ERR_IS_DIRECTORY
)
339 // We have any other error
340 error( errorNum
, url
.prettyUrl() );
343 // Real directory. Return just enough information for KRun to work
344 entry
.insert( KIO::UDSEntry::UDS_NAME
, url
.fileName());
345 kDebug( 7109 ).nospace() << "ArchiveProtocol::stat returning name=" << url
.fileName();
347 KDE_struct_stat buff
;
348 if ( KDE_stat( QFile::encodeName( url
.path() ), &buff
) == -1 )
350 // Should not happen, as the file was already stated by checkNewFile
351 error( KIO::ERR_COULD_NOT_STAT
, url
.prettyUrl() );
355 entry
.insert( KIO::UDSEntry::UDS_FILE_TYPE
, buff
.st_mode
& S_IFMT
);
361 // And let go of the tar file - for people who want to unmount a cdrom after that
362 delete m_archiveFile
;
367 const KArchiveDirectory
* root
= m_archiveFile
->directory();
368 const KArchiveEntry
* archiveEntry
;
369 if ( path
.isEmpty() )
371 path
= QString::fromLatin1( "/" );
374 archiveEntry
= root
->entry( path
);
378 error( KIO::ERR_DOES_NOT_EXIST
, url
.prettyUrl() );
382 createUDSEntry( archiveEntry
, entry
);
388 void ArchiveProtocol::get( const KUrl
& url
)
390 kDebug( 7109 ) << "ArchiveProtocol::get" << url
.url();
394 if ( !checkNewFile( url
, path
, errorNum
) )
396 if ( errorNum
== KIO::ERR_CANNOT_OPEN_FOR_READING
)
398 // If we cannot open, it might be a problem with the archive header (e.g. unsupported format)
399 // Therefore give a more specific error message
400 error( KIO::ERR_SLAVE_DEFINED
,
401 i18n( "Could not open the file, probably due to an unsupported file format.\n%1",
407 // We have any other error
408 error( errorNum
, url
.prettyUrl() );
413 const KArchiveDirectory
* root
= m_archiveFile
->directory();
414 const KArchiveEntry
* archiveEntry
= root
->entry( path
);
418 error( KIO::ERR_DOES_NOT_EXIST
, url
.prettyUrl() );
421 if ( archiveEntry
->isDirectory() )
423 error( KIO::ERR_IS_DIRECTORY
, url
.prettyUrl() );
426 const KArchiveFile
* archiveFileEntry
= static_cast<const KArchiveFile
*>(archiveEntry
);
427 if ( !archiveEntry
->symLinkTarget().isEmpty() )
429 kDebug(7109) << "Redirection to" << archiveEntry
->symLinkTarget();
430 KUrl
realURL( url
, archiveEntry
->symLinkTarget() );
431 kDebug(7109).nospace() << "realURL=" << realURL
.url();
432 redirection( realURL
);
437 //kDebug(7109) << "Preparing to get the archive data";
440 * The easy way would be to get the data by calling archiveFileEntry->data()
441 * However this has drawbacks:
442 * - the complete file must be read into the memory
443 * - errors are skipped, resulting in an empty file
446 QIODevice
* io
= archiveFileEntry
->createDevice();
450 error( KIO::ERR_SLAVE_DEFINED
,
451 i18n( "The archive file could not be opened, perhaps because the format is unsupported.\n%1" ,
456 if ( !io
->open( QIODevice::ReadOnly
) )
458 error( KIO::ERR_CANNOT_OPEN_FOR_READING
, url
.prettyUrl() );
463 totalSize( archiveFileEntry
->size() );
465 // Size of a QIODevice read. It must be large enough so that the mime type check will not fail
466 const qint64 maxSize
= 0x100000; // 1MB
468 qint64 bufferSize
= qMin( maxSize
, archiveFileEntry
->size() );
470 buffer
.resize( bufferSize
);
471 if ( buffer
.isEmpty() && bufferSize
> 0 )
473 // Something went wrong
474 error( KIO::ERR_OUT_OF_MEMORY
, url
.prettyUrl() );
479 bool firstRead
= true;
481 // How much file do we still have to process?
482 qint64 fileSize
= archiveFileEntry
->size();
483 KIO::filesize_t processed
= 0;
485 while ( !io
->atEnd() && fileSize
> 0 )
489 bufferSize
= qMin( maxSize
, fileSize
);
490 buffer
.resize( bufferSize
);
492 const qint64 read
= io
->read( buffer
.data(), buffer
.size() ); // Avoid to use bufferSize here, in case something went wrong.
493 if ( read
!= bufferSize
)
495 kWarning(7109) << "Read" << read
<< "bytes but expected" << bufferSize
;
496 error( KIO::ERR_COULD_NOT_READ
, url
.prettyUrl() );
502 // We use the magic one the first data read
503 // (As magic detection is about fixed positions, we can be sure that it is enough data.)
504 KMimeType::Ptr mime
= KMimeType::findByNameAndContent( path
, buffer
);
505 kDebug(7109) << "Emitting mimetype" << mime
->name();
506 mimeType( mime
->name() );
511 processedSize( processed
);
512 fileSize
-= bufferSize
;
517 data( QByteArray() );
523 In case someone wonders how the old filter stuff looked like : :)
524 void TARProtocol::slotData(void *_p, int _len)
529 m_pFilter->send(_p, _len);
537 void TARProtocol::slotDataEnd()
541 assert(m_pFilter && m_pJob);
552 void TARProtocol::jobData(void *_p, int _len)
557 m_pFilter->send(_p, _len);
561 m_pFilter->send(_p, _len);
568 void TARProtocol::jobDataEnd()
586 void TARProtocol::filterData(void *_p, int _len)
588 debug("void TARProtocol::filterData");
595 m_pJob->data(_p, _len);
599 m_pJob->data(_p, _len);
607 // kate: space-indent on; indent-width 4; replace-tabs on;