00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <time.h>
00025 #include <unistd.h>
00026 #include <errno.h>
00027 #include <grp.h>
00028 #include <pwd.h>
00029 #include <assert.h>
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 #include <locale.h>
00033
00034 #include <qptrlist.h>
00035 #include <qptrstack.h>
00036 #include <qvaluestack.h>
00037 #include <qmap.h>
00038 #include <qcstring.h>
00039 #include <qdir.h>
00040 #include <qfile.h>
00041 #include <qtextcodec.h>
00042
00043 #include <kdebug.h>
00044 #include <kfilterdev.h>
00045 #include <kfilterbase.h>
00046 #include <kde_file.h>
00047
00048 #include "karchive.h"
00049 #include "klimitediodevice.h"
00050
00051 template class QDict<KArchiveEntry>;
00052
00053
00054 class KArchive::KArchivePrivate
00055 {
00056 public:
00057 KArchiveDirectory* rootDir;
00058 bool closeSucceeded;
00059 };
00060
00061 class PosSortedPtrList : public QPtrList<KArchiveFile> {
00062 protected:
00063 int compareItems( QPtrCollection::Item i1,
00064 QPtrCollection::Item i2 )
00065 {
00066 int pos1 = static_cast<KArchiveFile*>( i1 )->position();
00067 int pos2 = static_cast<KArchiveFile*>( i2 )->position();
00068 return ( pos1 - pos2 );
00069 }
00070 };
00071
00072
00076
00077 KArchive::KArchive( QIODevice * dev )
00078 {
00079 d = new KArchivePrivate;
00080 d->rootDir = 0;
00081 m_dev = dev;
00082 m_open = false;
00083 }
00084
00085 KArchive::~KArchive()
00086 {
00087 if ( m_open )
00088 close();
00089 delete d->rootDir;
00090 delete d;
00091 }
00092
00093 bool KArchive::open( int mode )
00094 {
00095 if ( m_dev && !m_dev->open( mode ) )
00096 return false;
00097
00098 if ( m_open )
00099 close();
00100
00101 m_mode = mode;
00102 m_open = true;
00103
00104 Q_ASSERT( d->rootDir == 0L );
00105 d->rootDir = 0L;
00106
00107 return openArchive( mode );
00108 }
00109
00110 void KArchive::close()
00111 {
00112 if ( !m_open )
00113 return;
00114
00115
00116 d->closeSucceeded = closeArchive();
00117
00118 if ( m_dev )
00119 m_dev->close();
00120
00121 delete d->rootDir;
00122 d->rootDir = 0;
00123 m_open = false;
00124 }
00125
00126 bool KArchive::closeSucceeded() const
00127 {
00128 return d->closeSucceeded;
00129 }
00130
00131 const KArchiveDirectory* KArchive::directory() const
00132 {
00133
00134 return const_cast<KArchive *>(this)->rootDir();
00135 }
00136
00137
00138 bool KArchive::addLocalFile( const QString& fileName, const QString& destName )
00139 {
00140 QFileInfo fileInfo( fileName );
00141 if ( !fileInfo.isFile() && !fileInfo.isSymLink() )
00142 {
00143 kdWarning() << "KArchive::addLocalFile " << fileName << " doesn't exist or is not a regular file." << endl;
00144 return false;
00145 }
00146
00147 KDE_struct_stat fi;
00148 if (KDE_lstat(QFile::encodeName(fileName),&fi) == -1) {
00149 kdWarning() << "KArchive::addLocalFile stating " << fileName
00150 << " failed: " << strerror(errno) << endl;
00151 return false;
00152 }
00153
00154 if (fileInfo.isSymLink()) {
00155 return writeSymLink(destName, fileInfo.readLink(), fileInfo.owner(),
00156 fileInfo.group(), fi.st_mode, fi.st_atime, fi.st_mtime,
00157 fi.st_ctime);
00158 }
00159
00160 uint size = fileInfo.size();
00161
00162
00163
00164
00165 QFile file( fileName );
00166 if ( !file.open( IO_ReadOnly ) )
00167 {
00168 kdWarning() << "KArchive::addLocalFile couldn't open file " << fileName << endl;
00169 return false;
00170 }
00171
00172 if ( !prepareWriting( destName, fileInfo.owner(), fileInfo.group(), size,
00173 fi.st_mode, fi.st_atime, fi.st_mtime, fi.st_ctime ) )
00174 {
00175 kdWarning() << "KArchive::addLocalFile prepareWriting " << destName << " failed" << endl;
00176 return false;
00177 }
00178
00179
00180 QByteArray array(8*1024);
00181 int n;
00182 uint total = 0;
00183 while ( ( n = file.readBlock( array.data(), array.size() ) ) > 0 )
00184 {
00185 if ( !writeData( array.data(), n ) )
00186 {
00187 kdWarning() << "KArchive::addLocalFile writeData failed" << endl;
00188 return false;
00189 }
00190 total += n;
00191 }
00192 Q_ASSERT( total == size );
00193
00194 if ( !doneWriting( size ) )
00195 {
00196 kdWarning() << "KArchive::addLocalFile doneWriting failed" << endl;
00197 return false;
00198 }
00199 return true;
00200 }
00201
00202 bool KArchive::addLocalDirectory( const QString& path, const QString& destName )
00203 {
00204 QString dot = ".";
00205 QString dotdot = "..";
00206 QDir dir( path );
00207 if ( !dir.exists() )
00208 return false;
00209 dir.setFilter(dir.filter() | QDir::Hidden);
00210 QStringList files = dir.entryList();
00211 for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00212 {
00213 if ( *it != dot && *it != dotdot )
00214 {
00215 QString fileName = path + "/" + *it;
00216
00217 QString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
00218 QFileInfo fileInfo( fileName );
00219
00220 if ( fileInfo.isFile() || fileInfo.isSymLink() )
00221 addLocalFile( fileName, dest );
00222 else if ( fileInfo.isDir() )
00223 addLocalDirectory( fileName, dest );
00224
00225 }
00226 }
00227 return true;
00228 }
00229
00230 bool KArchive::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00231 {
00232 mode_t perm = 0100644;
00233 time_t the_time = time(0);
00234 return writeFile(name,user,group,size,perm,the_time,the_time,the_time,data);
00235 }
00236
00237 bool KArchive::prepareWriting( const QString& name, const QString& user,
00238 const QString& group, uint size, mode_t perm,
00239 time_t atime, time_t mtime, time_t ctime ) {
00240 PrepareWritingParams params;
00241 params.name = &name;
00242 params.user = &user;
00243 params.group = &group;
00244 params.size = size;
00245 params.perm = perm;
00246 params.atime = atime;
00247 params.mtime = mtime;
00248 params.ctime = ctime;
00249 virtual_hook(VIRTUAL_PREPARE_WRITING,¶ms);
00250 return params.retval;
00251 }
00252
00253 bool KArchive::prepareWriting_impl(const QString &name, const QString &user,
00254 const QString &group, uint size, mode_t ,
00255 time_t , time_t , time_t ) {
00256 kdWarning(7040) << "New prepareWriting API not implemented in this class." << endl
00257 << "Falling back to old API (metadata information will be lost)" << endl;
00258 return prepareWriting(name,user,group,size);
00259 }
00260
00261 bool KArchive::writeFile( const QString& name, const QString& user,
00262 const QString& group, uint size, mode_t perm,
00263 time_t atime, time_t mtime, time_t ctime,
00264 const char* data ) {
00265 WriteFileParams params;
00266 params.name = &name;
00267 params.user = &user;
00268 params.group = &group;
00269 params.size = size;
00270 params.perm = perm;
00271 params.atime = atime;
00272 params.mtime = mtime;
00273 params.ctime = ctime;
00274 params.data = data;
00275 virtual_hook(VIRTUAL_WRITE_FILE,¶ms);
00276 return params.retval;
00277 }
00278
00279 bool KArchive::writeFile_impl( const QString& name, const QString& user,
00280 const QString& group, uint size, mode_t perm,
00281 time_t atime, time_t mtime, time_t ctime,
00282 const char* data ) {
00283
00284 if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) )
00285 {
00286 kdWarning() << "KArchive::writeFile prepareWriting failed" << endl;
00287 return false;
00288 }
00289
00290
00291
00292 if ( data && size && !writeData( data, size ) )
00293 {
00294 kdWarning() << "KArchive::writeFile writeData failed" << endl;
00295 return false;
00296 }
00297
00298 if ( !doneWriting( size ) )
00299 {
00300 kdWarning() << "KArchive::writeFile doneWriting failed" << endl;
00301 return false;
00302 }
00303 return true;
00304 }
00305
00306 bool KArchive::writeDir(const QString& name, const QString& user,
00307 const QString& group, mode_t perm,
00308 time_t atime, time_t mtime, time_t ctime) {
00309 WriteDirParams params;
00310 params.name = &name;
00311 params.user = &user;
00312 params.group = &group;
00313 params.perm = perm;
00314 params.atime = atime;
00315 params.mtime = mtime;
00316 params.ctime = ctime;
00317 virtual_hook(VIRTUAL_WRITE_DIR,¶ms);
00318 return params.retval;
00319 }
00320
00321 bool KArchive::writeDir_impl(const QString &name, const QString &user,
00322 const QString &group, mode_t ,
00323 time_t , time_t , time_t ) {
00324 kdWarning(7040) << "New writeDir API not implemented in this class." << endl
00325 << "Falling back to old API (metadata information will be lost)" << endl;
00326 return writeDir(name,user,group);
00327 }
00328
00329 bool KArchive::writeSymLink(const QString &name, const QString &target,
00330 const QString &user, const QString &group,
00331 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
00332 WriteSymlinkParams params;
00333 params.name = &name;
00334 params.target = ⌖
00335 params.user = &user;
00336 params.group = &group;
00337 params.perm = perm;
00338 params.atime = atime;
00339 params.mtime = mtime;
00340 params.ctime = ctime;
00341 virtual_hook(VIRTUAL_WRITE_SYMLINK,¶ms);
00342 return params.retval;
00343 }
00344
00345 bool KArchive::writeSymLink_impl(const QString &,const QString &,
00346 const QString &, const QString &,
00347 mode_t , time_t , time_t ,
00348 time_t ) {
00349 kdWarning(7040) << "writeSymLink not implemented in this class." << endl
00350 << "No fallback available." << endl;
00351
00352 return false;
00353 }
00354
00355 bool KArchive::writeData( const char* data, uint size )
00356 {
00357 WriteDataParams params;
00358 params.data = data;
00359 params.size = size;
00360 virtual_hook( VIRTUAL_WRITE_DATA, ¶ms );
00361 return params.retval;
00362 }
00363
00364 bool KArchive::writeData_impl( const char* data, uint size )
00365 {
00366 Q_ASSERT( device() );
00367 return device()->writeBlock( data, size ) == (Q_LONG)size;
00368 }
00369
00370 KArchiveDirectory * KArchive::rootDir()
00371 {
00372 if ( !d->rootDir )
00373 {
00374
00375 struct passwd* pw = getpwuid( getuid() );
00376 struct group* grp = getgrgid( getgid() );
00377 QString username = pw ? QFile::decodeName(pw->pw_name) : QString::number( getuid() );
00378 QString groupname = grp ? QFile::decodeName(grp->gr_name) : QString::number( getgid() );
00379
00380 d->rootDir = new KArchiveDirectory( this, QString::fromLatin1("/"), (int)(0777 + S_IFDIR), 0, username, groupname, QString::null );
00381 }
00382 return d->rootDir;
00383 }
00384
00385 KArchiveDirectory * KArchive::findOrCreate( const QString & path )
00386 {
00387
00388 if ( path.isEmpty() || path == "/" || path == "." )
00389 {
00390
00391 return rootDir();
00392 }
00393
00394
00395
00396
00397
00398
00399
00400 KArchiveEntry* ent = rootDir()->entry( path );
00401 if ( ent )
00402 {
00403 if ( ent->isDirectory() )
00404
00405 return (KArchiveDirectory *) ent;
00406 else
00407 kdWarning() << "Found " << path << " but it's not a directory" << endl;
00408 }
00409
00410
00411 int pos = path.findRev( '/' );
00412 KArchiveDirectory * parent;
00413 QString dirname;
00414 if ( pos == -1 )
00415 {
00416 parent = rootDir();
00417 dirname = path;
00418 }
00419 else
00420 {
00421 QString left = path.left( pos );
00422 dirname = path.mid( pos + 1 );
00423 parent = findOrCreate( left );
00424 }
00425
00426
00427
00428 KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(),
00429 d->rootDir->date(), d->rootDir->user(),
00430 d->rootDir->group(), QString::null );
00431 parent->addEntry( e );
00432 return e;
00433 }
00434
00435 void KArchive::setDevice( QIODevice * dev )
00436 {
00437 m_dev = dev;
00438 }
00439
00440 void KArchive::setRootDir( KArchiveDirectory *rootDir )
00441 {
00442 Q_ASSERT( !d->rootDir );
00443 d->rootDir = rootDir;
00444 }
00445
00446 QString KArchive::decodeEntryName( const QCString & name )
00447 {
00448 QTextCodec *codec = NULL;
00449 char *src = name.data();
00450 int len = name.length();
00451
00452 if (strncmp(setlocale(LC_CTYPE,""), "ja_JP", 5) != 0)
00453 return QFile::decodeName(name);
00454
00455 for (; len>0 && *src != '\0'; len--) {
00456
00457 int c1, c2, c3;
00458 c1 = *src & 0xff;
00459 src++;
00460 c2 = *(src++) & 0xff;
00461
00462 if (c2 == '\0') {
00463 if (c1 > 0xa0 && c1 < 0xe0)
00464
00465 codec = QTextCodec::codecForName( "Shift-JIS" );
00466 break;
00467 }
00468 else if (c1 == 0x1b) {
00469 if (c2 == '$' || c2 == '&')
00470
00471 codec = QTextCodec::codecForName( "Shift-JIS" );
00472 break;
00473 }
00474 else if (c1 < 0x80) { continue; }
00475 else if (((c1 > 0x80 && c1 < 0xa0) || (c1 >= 0xe0 && c1 < 0xfd))
00476 && ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0x80 && c2 < 0xfd))) {
00477
00478 codec = QTextCodec::codecForName( "Shift-JIS" );
00479 break;
00480 }
00481 else if (c1 == 0x8f && c2 > 0xa0 && c2 < 0xff) {
00482 c3 = *(src++) & 0xff;
00483 if (c3 > 0x0a && c3 < 0xff)
00484
00485 codec = QTextCodec::codecForName( "eucJP" );
00486 break;
00487 }
00488 else if (c1 > 0xa0 && c1 < 0xff) {
00489 if (c2 > 0xa0 && c2 < 0xff)
00490
00491 codec = QTextCodec::codecForName( "eucJP" );
00492 else if (c1 < 0xe0)
00493
00494 codec = QTextCodec::codecForName( "Shift-JIS" );
00495 break;
00496 }
00497 }
00498
00499 if (codec == NULL)
00500 return QFile::decodeName(name);
00501 else
00502 return codec->toUnicode(name);
00503 }
00504
00508 KArchiveEntry::KArchiveEntry( KArchive* t, const QString& name, int access, int date,
00509 const QString& user, const QString& group, const
00510 QString& symlink)
00511 {
00512 m_name = name;
00513 m_access = access;
00514 m_date = date;
00515 m_user = user;
00516 m_group = group;
00517 m_symlink = symlink;
00518 m_archive = t;
00519
00520 }
00521
00522 QDateTime KArchiveEntry::datetime() const
00523 {
00524 QDateTime d;
00525 d.setTime_t( m_date );
00526 return d;
00527 }
00528
00532
00533 KArchiveFile::KArchiveFile( KArchive* t, const QString& name, int access, int date,
00534 const QString& user, const QString& group,
00535 const QString & symlink,
00536 int pos, int size )
00537 : KArchiveEntry( t, name, access, date, user, group, symlink )
00538 {
00539 m_pos = pos;
00540 m_size = size;
00541 }
00542
00543 int KArchiveFile::position() const
00544 {
00545 return m_pos;
00546 }
00547
00548 int KArchiveFile::size() const
00549 {
00550 return m_size;
00551 }
00552
00553 QByteArray KArchiveFile::data() const
00554 {
00555 archive()->device()->at( m_pos );
00556
00557
00558 QByteArray arr( m_size );
00559 if ( m_size )
00560 {
00561 assert( arr.data() );
00562 int n = archive()->device()->readBlock( arr.data(), m_size );
00563 if ( n != m_size )
00564 arr.resize( n );
00565 }
00566 return arr;
00567 }
00568
00569
00570 QIODevice *KArchiveFile::device() const
00571 {
00572 return new KLimitedIODevice( archive()->device(), m_pos, m_size );
00573 }
00574
00575 void KArchiveFile::copyTo(const QString& dest) const
00576 {
00577 QFile f( dest + "/" + name() );
00578 f.open( IO_ReadWrite | IO_Truncate );
00579 f.writeBlock( data() );
00580 f.close();
00581 }
00582
00586
00587
00588 KArchiveDirectory::KArchiveDirectory( KArchive* t, const QString& name, int access,
00589 int date,
00590 const QString& user, const QString& group,
00591 const QString &symlink)
00592 : KArchiveEntry( t, name, access, date, user, group, symlink )
00593 {
00594 m_entries.setAutoDelete( true );
00595 }
00596
00597 QStringList KArchiveDirectory::entries() const
00598 {
00599 QStringList l;
00600
00601 QDictIterator<KArchiveEntry> it( m_entries );
00602 for( ; it.current(); ++it )
00603 l.append( it.currentKey() );
00604
00605 return l;
00606 }
00607
00608 KArchiveEntry* KArchiveDirectory::entry( QString name )
00609
00610
00611 {
00612 int pos = name.find( '/' );
00613 if ( pos == 0 )
00614 {
00615 if (name.length()>1)
00616 {
00617 name = name.mid( 1 );
00618 pos = name.find( '/' );
00619 }
00620 else
00621 return this;
00622 }
00623
00624 if ( pos != -1 && pos == (int)name.length()-1 )
00625 {
00626 name = name.left( pos );
00627 pos = name.find( '/' );
00628 }
00629 if ( pos != -1 )
00630 {
00631 QString left = name.left( pos );
00632 QString right = name.mid( pos + 1 );
00633
00634
00635
00636 KArchiveEntry* e = m_entries[ left ];
00637 if ( !e || !e->isDirectory() )
00638 return 0;
00639 return ((KArchiveDirectory*)e)->entry( right );
00640 }
00641
00642 return m_entries[ name ];
00643 }
00644
00645 const KArchiveEntry* KArchiveDirectory::entry( QString name ) const
00646 {
00647 return ((KArchiveDirectory*)this)->entry( name );
00648 }
00649
00650 void KArchiveDirectory::addEntry( KArchiveEntry* entry )
00651 {
00652 Q_ASSERT( !entry->name().isEmpty() );
00653 if( m_entries[ entry->name() ] ) {
00654 kdWarning() << "KArchiveDirectory::addEntry: directory " << name()
00655 << " has entry " << entry->name() << " already" << endl;
00656 }
00657 m_entries.insert( entry->name(), entry );
00658 }
00659
00660 void KArchiveDirectory::copyTo(const QString& dest, bool recursiveCopy ) const
00661 {
00662 QDir root;
00663
00664 PosSortedPtrList fileList;
00665 QMap<int, QString> fileToDir;
00666
00667 QStringList::Iterator it;
00668
00669
00670 KArchiveDirectory* curDir;
00671 QString curDirName;
00672
00673 QStringList dirEntries;
00674 KArchiveEntry* curEntry;
00675 KArchiveFile* curFile;
00676
00677
00678 QPtrStack<KArchiveDirectory> dirStack;
00679 QValueStack<QString> dirNameStack;
00680
00681 dirStack.push( this );
00682 dirNameStack.push( dest );
00683 do {
00684 curDir = dirStack.pop();
00685 curDirName = dirNameStack.pop();
00686 root.mkdir(curDirName);
00687
00688 dirEntries = curDir->entries();
00689 for ( it = dirEntries.begin(); it != dirEntries.end(); ++it ) {
00690 curEntry = curDir->entry(*it);
00691 if (!curEntry->symlink().isEmpty()) {
00692 const QString linkName = curDirName+'/'+curEntry->name();
00693 kdDebug() << "symlink(" << curEntry->symlink() << ',' << linkName << ')';
00694 #ifdef Q_OS_UNIX
00695 if (!::symlink(curEntry->symlink().local8Bit(), linkName.local8Bit())) {
00696 kdDebug() << "symlink(" << curEntry->symlink() << ',' << linkName << ") failed:" << strerror(errno);
00697 }
00698 #endif
00699 } else {
00700 if ( curEntry->isFile() ) {
00701 curFile = dynamic_cast<KArchiveFile*>( curEntry );
00702 if (curFile) {
00703 fileList.append( curFile );
00704 fileToDir.insert( curFile->position(), curDirName );
00705 }
00706 }
00707
00708 if ( curEntry->isDirectory() )
00709 if ( recursiveCopy ) {
00710 KArchiveDirectory *ad = dynamic_cast<KArchiveDirectory*>( curEntry );
00711 if (ad) {
00712 dirStack.push( ad );
00713 dirNameStack.push( curDirName + "/" + curEntry->name() );
00714 }
00715 }
00716 }
00717 }
00718 } while (!dirStack.isEmpty());
00719
00720 fileList.sort();
00721
00722 KArchiveFile* f;
00723 for ( f = fileList.first(); f; f = fileList.next() ) {
00724 int pos = f->position();
00725 f->copyTo( fileToDir[pos] );
00726 }
00727 }
00728
00729 void KArchive::virtual_hook( int id, void* data )
00730 {
00731 switch (id) {
00732 case VIRTUAL_WRITE_DATA: {
00733 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
00734 params->retval = writeData_impl( params->data, params->size );
00735 break;
00736 }
00737 case VIRTUAL_WRITE_SYMLINK: {
00738 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
00739 params->retval = writeSymLink_impl(*params->name,*params->target,
00740 *params->user,*params->group,params->perm,
00741 params->atime,params->mtime,params->ctime);
00742 break;
00743 }
00744 case VIRTUAL_WRITE_DIR: {
00745 WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
00746 params->retval = writeDir_impl(*params->name,*params->user,
00747 *params->group,params->perm,
00748 params->atime,params->mtime,params->ctime);
00749 break;
00750 }
00751 case VIRTUAL_WRITE_FILE: {
00752 WriteFileParams *params = reinterpret_cast<WriteFileParams *>(data);
00753 params->retval = writeFile_impl(*params->name,*params->user,
00754 *params->group,params->size,params->perm,
00755 params->atime,params->mtime,params->ctime,
00756 params->data);
00757 break;
00758 }
00759 case VIRTUAL_PREPARE_WRITING: {
00760 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
00761 params->retval = prepareWriting_impl(*params->name,*params->user,
00762 *params->group,params->size,params->perm,
00763 params->atime,params->mtime,params->ctime);
00764 break;
00765 }
00766 default:
00767 ;
00768 }
00769 }
00770
00771 void KArchiveEntry::virtual_hook( int, void* )
00772 { }
00773
00774 void KArchiveFile::virtual_hook( int id, void* data )
00775 { KArchiveEntry::virtual_hook( id, data ); }
00776
00777 void KArchiveDirectory::virtual_hook( int id, void* data )
00778 { KArchiveEntry::virtual_hook( id, data ); }