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 QStringList files = dir.entryList();
00210 for ( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
00211 {
00212 if ( *it != dot && *it != dotdot )
00213 {
00214 QString fileName = path + "/" + *it;
00215
00216 QString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
00217 QFileInfo fileInfo( fileName );
00218
00219 if ( fileInfo.isFile() || fileInfo.isSymLink() )
00220 addLocalFile( fileName, dest );
00221 else if ( fileInfo.isDir() )
00222 addLocalDirectory( fileName, dest );
00223
00224 }
00225 }
00226 return true;
00227 }
00228
00229 bool KArchive::writeFile( const QString& name, const QString& user, const QString& group, uint size, const char* data )
00230 {
00231 mode_t perm = 0100644;
00232 time_t the_time = time(0);
00233 return writeFile(name,user,group,size,perm,the_time,the_time,the_time,data);
00234 }
00235
00236 bool KArchive::prepareWriting( const QString& name, const QString& user,
00237 const QString& group, uint size, mode_t perm,
00238 time_t atime, time_t mtime, time_t ctime ) {
00239 PrepareWritingParams params;
00240 params.name = &name;
00241 params.user = &user;
00242 params.group = &group;
00243 params.size = size;
00244 params.perm = perm;
00245 params.atime = atime;
00246 params.mtime = mtime;
00247 params.ctime = ctime;
00248 virtual_hook(VIRTUAL_PREPARE_WRITING,¶ms);
00249 return params.retval;
00250 }
00251
00252 bool KArchive::prepareWriting_impl(const QString &name, const QString &user,
00253 const QString &group, uint size, mode_t ,
00254 time_t , time_t , time_t ) {
00255 kdWarning(7040) << "New prepareWriting API not implemented in this class." << endl
00256 << "Falling back to old API (metadata information will be lost)" << endl;
00257 return prepareWriting(name,user,group,size);
00258 }
00259
00260 bool KArchive::writeFile( const QString& name, const QString& user,
00261 const QString& group, uint size, mode_t perm,
00262 time_t atime, time_t mtime, time_t ctime,
00263 const char* data ) {
00264 WriteFileParams params;
00265 params.name = &name;
00266 params.user = &user;
00267 params.group = &group;
00268 params.size = size;
00269 params.perm = perm;
00270 params.atime = atime;
00271 params.mtime = mtime;
00272 params.ctime = ctime;
00273 params.data = data;
00274 virtual_hook(VIRTUAL_WRITE_FILE,¶ms);
00275 return params.retval;
00276 }
00277
00278 bool KArchive::writeFile_impl( const QString& name, const QString& user,
00279 const QString& group, uint size, mode_t perm,
00280 time_t atime, time_t mtime, time_t ctime,
00281 const char* data ) {
00282
00283 if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) )
00284 {
00285 kdWarning() << "KArchive::writeFile prepareWriting failed" << endl;
00286 return false;
00287 }
00288
00289
00290
00291 if ( data && size && !writeData( data, size ) )
00292 {
00293 kdWarning() << "KArchive::writeFile writeData failed" << endl;
00294 return false;
00295 }
00296
00297 if ( !doneWriting( size ) )
00298 {
00299 kdWarning() << "KArchive::writeFile doneWriting failed" << endl;
00300 return false;
00301 }
00302 return true;
00303 }
00304
00305 bool KArchive::writeDir(const QString& name, const QString& user,
00306 const QString& group, mode_t perm,
00307 time_t atime, time_t mtime, time_t ctime) {
00308 WriteDirParams params;
00309 params.name = &name;
00310 params.user = &user;
00311 params.group = &group;
00312 params.perm = perm;
00313 params.atime = atime;
00314 params.mtime = mtime;
00315 params.ctime = ctime;
00316 virtual_hook(VIRTUAL_WRITE_DIR,¶ms);
00317 return params.retval;
00318 }
00319
00320 bool KArchive::writeDir_impl(const QString &name, const QString &user,
00321 const QString &group, mode_t ,
00322 time_t , time_t , time_t ) {
00323 kdWarning(7040) << "New writeDir API not implemented in this class." << endl
00324 << "Falling back to old API (metadata information will be lost)" << endl;
00325 return writeDir(name,user,group);
00326 }
00327
00328 bool KArchive::writeSymLink(const QString &name, const QString &target,
00329 const QString &user, const QString &group,
00330 mode_t perm, time_t atime, time_t mtime, time_t ctime) {
00331 WriteSymlinkParams params;
00332 params.name = &name;
00333 params.target = ⌖
00334 params.user = &user;
00335 params.group = &group;
00336 params.perm = perm;
00337 params.atime = atime;
00338 params.mtime = mtime;
00339 params.ctime = ctime;
00340 virtual_hook(VIRTUAL_WRITE_SYMLINK,¶ms);
00341 return params.retval;
00342 }
00343
00344 bool KArchive::writeSymLink_impl(const QString &,const QString &,
00345 const QString &, const QString &,
00346 mode_t , time_t , time_t ,
00347 time_t ) {
00348 kdWarning(7040) << "writeSymLink not implemented in this class." << endl
00349 << "No fallback available." << endl;
00350
00351 return false;
00352 }
00353
00354 bool KArchive::writeData( const char* data, uint size )
00355 {
00356 WriteDataParams params;
00357 params.data = data;
00358 params.size = size;
00359 virtual_hook( VIRTUAL_WRITE_DATA, ¶ms );
00360 return params.retval;
00361 }
00362
00363 bool KArchive::writeData_impl( const char* data, uint size )
00364 {
00365 Q_ASSERT( device() );
00366 return device()->writeBlock( data, size ) == (Q_LONG)size;
00367 }
00368
00369 KArchiveDirectory * KArchive::rootDir()
00370 {
00371 if ( !d->rootDir )
00372 {
00373
00374 struct passwd* pw = getpwuid( getuid() );
00375 struct group* grp = getgrgid( getgid() );
00376 QString username = pw ? QFile::decodeName(pw->pw_name) : QString::number( getuid() );
00377 QString groupname = grp ? QFile::decodeName(grp->gr_name) : QString::number( getgid() );
00378
00379 d->rootDir = new KArchiveDirectory( this, QString::fromLatin1("/"), (int)(0777 + S_IFDIR), 0, username, groupname, QString::null );
00380 }
00381 return d->rootDir;
00382 }
00383
00384 KArchiveDirectory * KArchive::findOrCreate( const QString & path )
00385 {
00386
00387 if ( path.isEmpty() || path == "/" || path == "." )
00388 {
00389
00390 return rootDir();
00391 }
00392
00393
00394
00395
00396
00397
00398
00399 KArchiveEntry* ent = rootDir()->entry( path );
00400 if ( ent )
00401 {
00402 if ( ent->isDirectory() )
00403
00404 return (KArchiveDirectory *) ent;
00405 else
00406 kdWarning() << "Found " << path << " but it's not a directory" << endl;
00407 }
00408
00409
00410 int pos = path.findRev( '/' );
00411 KArchiveDirectory * parent;
00412 QString dirname;
00413 if ( pos == -1 )
00414 {
00415 parent = rootDir();
00416 dirname = path;
00417 }
00418 else
00419 {
00420 QString left = path.left( pos );
00421 dirname = path.mid( pos + 1 );
00422 parent = findOrCreate( left );
00423 }
00424
00425
00426
00427 KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(),
00428 d->rootDir->date(), d->rootDir->user(),
00429 d->rootDir->group(), QString::null );
00430 parent->addEntry( e );
00431 return e;
00432 }
00433
00434 void KArchive::setDevice( QIODevice * dev )
00435 {
00436 m_dev = dev;
00437 }
00438
00439 void KArchive::setRootDir( KArchiveDirectory *rootDir )
00440 {
00441 Q_ASSERT( !d->rootDir );
00442 d->rootDir = rootDir;
00443 }
00444
00445 QString KArchive::decodeEntryName( const QCString & name )
00446 {
00447 QTextCodec *codec = NULL;
00448 char *src = name.data();
00449 int len = name.length();
00450
00451 if (strncmp(setlocale(LC_CTYPE,""), "ja_JP", 5) != 0)
00452 return QFile::decodeName(name);
00453
00454 for (; len>0 && *src != '\0'; len--) {
00455
00456 int c1, c2, c3;
00457 c1 = *src & 0xff;
00458 src++;
00459 c2 = *(src++) & 0xff;
00460
00461 if (c2 == '\0') {
00462 if (c1 > 0xa0 && c1 < 0xe0)
00463
00464 codec = QTextCodec::codecForName( "Shift-JIS" );
00465 break;
00466 }
00467 else if (c1 == 0x1b) {
00468 if (c2 == '$' || c2 == '&')
00469
00470 codec = QTextCodec::codecForName( "Shift-JIS" );
00471 break;
00472 }
00473 else if (c1 < 0x80) { continue; }
00474 else if (((c1 > 0x80 && c1 < 0xa0) || (c1 >= 0xe0 && c1 < 0xfd))
00475 && ((c2 >= 0x40 && c2 < 0x7f) || (c2 >= 0x80 && c2 < 0xfd))) {
00476
00477 codec = QTextCodec::codecForName( "Shift-JIS" );
00478 break;
00479 }
00480 else if (c1 == 0x8f && c2 > 0xa0 && c2 < 0xff) {
00481 c3 = *(src++) & 0xff;
00482 if (c3 > 0x0a && c3 < 0xff)
00483
00484 codec = QTextCodec::codecForName( "eucJP" );
00485 break;
00486 }
00487 else if (c1 > 0xa0 && c1 < 0xff) {
00488 if (c2 > 0xa0 && c2 < 0xff)
00489
00490 codec = QTextCodec::codecForName( "eucJP" );
00491 else if (c1 < 0xe0)
00492
00493 codec = QTextCodec::codecForName( "Shift-JIS" );
00494 break;
00495 }
00496 }
00497
00498 if (codec == NULL)
00499 return QFile::decodeName(name);
00500 else
00501 return codec->toUnicode(name);
00502 }
00503
00507 KArchiveEntry::KArchiveEntry( KArchive* t, const QString& name, int access, int date,
00508 const QString& user, const QString& group, const
00509 QString& symlink)
00510 {
00511 m_name = name;
00512 m_access = access;
00513 m_date = date;
00514 m_user = user;
00515 m_group = group;
00516 m_symlink = symlink;
00517 m_archive = t;
00518
00519 }
00520
00521 QDateTime KArchiveEntry::datetime() const
00522 {
00523 QDateTime d;
00524 d.setTime_t( m_date );
00525 return d;
00526 }
00527
00531
00532 KArchiveFile::KArchiveFile( KArchive* t, const QString& name, int access, int date,
00533 const QString& user, const QString& group,
00534 const QString & symlink,
00535 int pos, int size )
00536 : KArchiveEntry( t, name, access, date, user, group, symlink )
00537 {
00538 m_pos = pos;
00539 m_size = size;
00540 }
00541
00542 int KArchiveFile::position() const
00543 {
00544 return m_pos;
00545 }
00546
00547 int KArchiveFile::size() const
00548 {
00549 return m_size;
00550 }
00551
00552 QByteArray KArchiveFile::data() const
00553 {
00554 archive()->device()->at( m_pos );
00555
00556
00557 QByteArray arr( m_size );
00558 if ( m_size )
00559 {
00560 assert( arr.data() );
00561 int n = archive()->device()->readBlock( arr.data(), m_size );
00562 if ( n != m_size )
00563 arr.resize( n );
00564 }
00565 return arr;
00566 }
00567
00568
00569 QIODevice *KArchiveFile::device() const
00570 {
00571 return new KLimitedIODevice( archive()->device(), m_pos, m_size );
00572 }
00573
00574 void KArchiveFile::copyTo(const QString& dest) const
00575 {
00576 QFile f( dest + "/" + name() );
00577 f.open( IO_ReadWrite | IO_Truncate );
00578 f.writeBlock( data() );
00579 f.close();
00580 }
00581
00585
00586
00587 KArchiveDirectory::KArchiveDirectory( KArchive* t, const QString& name, int access,
00588 int date,
00589 const QString& user, const QString& group,
00590 const QString &symlink)
00591 : KArchiveEntry( t, name, access, date, user, group, symlink )
00592 {
00593 m_entries.setAutoDelete( true );
00594 }
00595
00596 QStringList KArchiveDirectory::entries() const
00597 {
00598 QStringList l;
00599
00600 QDictIterator<KArchiveEntry> it( m_entries );
00601 for( ; it.current(); ++it )
00602 l.append( it.currentKey() );
00603
00604 return l;
00605 }
00606
00607 KArchiveEntry* KArchiveDirectory::entry( QString name )
00608
00609
00610 {
00611 int pos = name.find( '/' );
00612 if ( pos == 0 )
00613 {
00614 if (name.length()>1)
00615 {
00616 name = name.mid( 1 );
00617 pos = name.find( '/' );
00618 }
00619 else
00620 return this;
00621 }
00622
00623 if ( pos != -1 && pos == (int)name.length()-1 )
00624 {
00625 name = name.left( pos );
00626 pos = name.find( '/' );
00627 }
00628 if ( pos != -1 )
00629 {
00630 QString left = name.left( pos );
00631 QString right = name.mid( pos + 1 );
00632
00633
00634
00635 KArchiveEntry* e = m_entries[ left ];
00636 if ( !e || !e->isDirectory() )
00637 return 0;
00638 return ((KArchiveDirectory*)e)->entry( right );
00639 }
00640
00641 return m_entries[ name ];
00642 }
00643
00644 const KArchiveEntry* KArchiveDirectory::entry( QString name ) const
00645 {
00646 return ((KArchiveDirectory*)this)->entry( name );
00647 }
00648
00649 void KArchiveDirectory::addEntry( KArchiveEntry* entry )
00650 {
00651 Q_ASSERT( !entry->name().isEmpty() );
00652 if( m_entries[ entry->name() ] ) {
00653 kdWarning() << "KArchiveDirectory::addEntry: directory " << name()
00654 << " has entry " << entry->name() << " already" << endl;
00655 }
00656 m_entries.insert( entry->name(), entry );
00657 }
00658
00659 void KArchiveDirectory::copyTo(const QString& dest, bool recursiveCopy ) const
00660 {
00661 QDir root;
00662
00663 PosSortedPtrList fileList;
00664 QMap<int, QString> fileToDir;
00665
00666 QStringList::Iterator it;
00667
00668
00669 KArchiveDirectory* curDir;
00670 QString curDirName;
00671
00672 QStringList dirEntries;
00673 KArchiveEntry* curEntry;
00674 KArchiveFile* curFile;
00675
00676
00677 QPtrStack<KArchiveDirectory> dirStack;
00678 QValueStack<QString> dirNameStack;
00679
00680 dirStack.push( this );
00681 dirNameStack.push( dest );
00682 do {
00683 curDir = dirStack.pop();
00684 curDirName = dirNameStack.pop();
00685 root.mkdir(curDirName);
00686
00687 dirEntries = curDir->entries();
00688 for ( it = dirEntries.begin(); it != dirEntries.end(); ++it ) {
00689 curEntry = curDir->entry(*it);
00690 if (!curEntry->symlink().isEmpty()) {
00691 const QString linkName = curDirName+'/'+curEntry->name();
00692 kdDebug() << "symlink(" << curEntry->symlink() << ',' << linkName << ')';
00693 #ifdef Q_OS_UNIX
00694 if (!::symlink(curEntry->symlink().local8Bit(), linkName.local8Bit())) {
00695 kdDebug() << "symlink(" << curEntry->symlink() << ',' << linkName << ") failed:" << strerror(errno);
00696 }
00697 #endif
00698 } else {
00699 if ( curEntry->isFile() ) {
00700 curFile = dynamic_cast<KArchiveFile*>( curEntry );
00701 if (curFile) {
00702 fileList.append( curFile );
00703 fileToDir.insert( curFile->position(), curDirName );
00704 }
00705 }
00706
00707 if ( curEntry->isDirectory() )
00708 if ( recursiveCopy ) {
00709 KArchiveDirectory *ad = dynamic_cast<KArchiveDirectory*>( curEntry );
00710 if (ad) {
00711 dirStack.push( ad );
00712 dirNameStack.push( curDirName + "/" + curEntry->name() );
00713 }
00714 }
00715 }
00716 }
00717 } while (!dirStack.isEmpty());
00718
00719 fileList.sort();
00720
00721 KArchiveFile* f;
00722 for ( f = fileList.first(); f; f = fileList.next() ) {
00723 int pos = f->position();
00724 f->copyTo( fileToDir[pos] );
00725 }
00726 }
00727
00728 void KArchive::virtual_hook( int id, void* data )
00729 {
00730 switch (id) {
00731 case VIRTUAL_WRITE_DATA: {
00732 WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
00733 params->retval = writeData_impl( params->data, params->size );
00734 break;
00735 }
00736 case VIRTUAL_WRITE_SYMLINK: {
00737 WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
00738 params->retval = writeSymLink_impl(*params->name,*params->target,
00739 *params->user,*params->group,params->perm,
00740 params->atime,params->mtime,params->ctime);
00741 break;
00742 }
00743 case VIRTUAL_WRITE_DIR: {
00744 WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
00745 params->retval = writeDir_impl(*params->name,*params->user,
00746 *params->group,params->perm,
00747 params->atime,params->mtime,params->ctime);
00748 break;
00749 }
00750 case VIRTUAL_WRITE_FILE: {
00751 WriteFileParams *params = reinterpret_cast<WriteFileParams *>(data);
00752 params->retval = writeFile_impl(*params->name,*params->user,
00753 *params->group,params->size,params->perm,
00754 params->atime,params->mtime,params->ctime,
00755 params->data);
00756 break;
00757 }
00758 case VIRTUAL_PREPARE_WRITING: {
00759 PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
00760 params->retval = prepareWriting_impl(*params->name,*params->user,
00761 *params->group,params->size,params->perm,
00762 params->atime,params->mtime,params->ctime);
00763 break;
00764 }
00765 default:
00766 ;
00767 }
00768 }
00769
00770 void KArchiveEntry::virtual_hook( int, void* )
00771 { }
00772
00773 void KArchiveFile::virtual_hook( int id, void* data )
00774 { KArchiveEntry::virtual_hook( id, data ); }
00775
00776 void KArchiveDirectory::virtual_hook( int id, void* data )
00777 { KArchiveEntry::virtual_hook( id, data ); }