00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <stdlib.h>
00021 #include <time.h>
00022
00023
00024
00025 #include <assert.h>
00026
00027 #include <qcstring.h>
00028 #include <qdir.h>
00029 #include <qfile.h>
00030 #include <kdebug.h>
00031 #include <kmimetype.h>
00032
00033 #include <kfilterdev.h>
00034 #include <kfilterbase.h>
00035
00036 #include "ktar.h"
00037
00041
00042 class KTar::KTarPrivate
00043 {
00044 public:
00045 KTarPrivate() {}
00046 QStringList dirList;
00047 };
00048
00049 KTar::KTar( const QString& filename, const QString & _mimetype )
00050 : KArchive( 0L )
00051 {
00052 m_filename = filename;
00053 d = new KTarPrivate;
00054 QString mimetype( _mimetype );
00055 bool forced = true;
00056 if ( mimetype.isEmpty() )
00057 {
00058 if ( QFile::exists( filename ) )
00059 mimetype = KMimeType::findByFileContent( filename )->name();
00060 else
00061 mimetype = KMimeType::findByPath( filename, 0, true )->name();
00062 kdDebug(7041) << "KTar::KTar mimetype=" << mimetype << endl;
00063
00064
00065 if ( mimetype == "application/x-tgz" || mimetype == "application/x-targz" ||
00066 mimetype == "application/x-webarchive" )
00067
00068 mimetype = "application/x-gzip";
00069 else if ( mimetype == "application/x-tbz" )
00070 mimetype = "application/x-bzip2";
00071 else
00072 {
00073
00074 QFile file( filename );
00075 if ( file.open( IO_ReadOnly ) )
00076 {
00077 unsigned char firstByte = file.getch();
00078 unsigned char secondByte = file.getch();
00079 unsigned char thirdByte = file.getch();
00080 if ( firstByte == 0037 && secondByte == 0213 )
00081 mimetype = "application/x-gzip";
00082 else if ( firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h' )
00083 mimetype = "application/x-bzip2";
00084 else if ( firstByte == 'P' && secondByte == 'K' && thirdByte == 3 )
00085 {
00086 unsigned char fourthByte = file.getch();
00087 if ( fourthByte == 4 )
00088 mimetype = "application/x-zip";
00089 }
00090 }
00091 }
00092 forced = false;
00093 }
00094
00095 prepareDevice( filename, mimetype, forced );
00096 }
00097
00098 void KTar::prepareDevice( const QString & filename,
00099 const QString & mimetype, bool forced )
00100 {
00101 if( "application/x-tar" == mimetype )
00102 setDevice( new QFile( filename ) );
00103 else
00104 {
00105 if( "application/x-gzip" == mimetype
00106 || "application/x-bzip2" == mimetype)
00107 forced = true;
00108
00109 QIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced );
00110 if( dev )
00111 setDevice( dev );
00112 }
00113 }
00114
00115 KTar::KTar( QIODevice * dev )
00116 : KArchive( dev )
00117 {
00118 d = new KTarPrivate;
00119 }
00120
00121 KTar::~KTar()
00122 {
00123
00124 if( isOpened() )
00125 close();
00126 if ( !m_filename.isEmpty() )
00127 delete device();
00128 delete d;
00129 }
00130
00131 void KTar::setOrigFileName( const QCString & fileName )
00132 {
00133 if ( !isOpened() || !(mode() & IO_WriteOnly) )
00134 {
00135 kdWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
00136 return;
00137 }
00138 static_cast<KFilterDev *>(device())->setOrigFileName( fileName );
00139 }
00140
00141
00142 bool KTar::openArchive( int mode )
00143 {
00144 if ( !(mode & IO_ReadOnly) )
00145 return true;
00146
00147
00148
00149
00150
00151
00152 d->dirList.clear();
00153 QIODevice* dev = device();
00154
00155
00156 char buffer[ 0x200 ];
00157 bool ende = false;
00158 do
00159 {
00160
00161 int n = dev->readBlock( buffer, 0x200 );
00162 if ( n == 0x200 && buffer[0] != 0 )
00163 {
00164
00165 if (strncmp(buffer + 257, "ustar", 5))
00166 {
00167
00168 QCString s;
00169
00170 int check = 0;
00171 for( uint j = 0; j < 0x200; ++j )
00172 check += buffer[j];
00173
00174
00175 for( uint j = 0; j < 8 ; j++ )
00176 check -= buffer[148 + j];
00177 check += 8 * ' ';
00178
00179 s.sprintf("%o", check );
00180
00181
00182
00183 if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() ) )
00184 {
00185 kdWarning(7041) << "KTar: invalid TAR file. Header is: " << QCString( buffer+257, 5 ) << endl;
00186 return false;
00187 }
00188 }
00189
00190 QString name( QString::fromLocal8Bit(buffer) );
00191
00192
00193 if ( name == "././@LongLink" )
00194 {
00195
00196
00197 n = dev->readBlock( buffer, 0x200 );
00198 if ( n == 0x200 && buffer[0] != 0 )
00199 {
00200 name = QString::fromLocal8Bit(buffer);
00201
00202 n = dev->readBlock( buffer, 0x200 );
00203 if (!( n == 0x200 && buffer[0] != 0 ))
00204 break;
00205 }
00206 else
00207 break;
00208 }
00209
00210 bool isdir = false;
00211 QString nm;
00212
00213 if ( name.right(1) == "/" )
00214 {
00215 isdir = true;
00216 name = name.left( name.length() - 1 );
00217 }
00218
00219 int pos = name.findRev( '/' );
00220 if ( pos == -1 )
00221 nm = name;
00222 else
00223 nm = name.mid( pos + 1 );
00224
00225
00226 buffer[ 0x6b ] = 0;
00227 char *dummy;
00228 const char* p = buffer + 0x64;
00229 while( *p == ' ' ) ++p;
00230 int access = (int)strtol( p, &dummy, 8 );
00231
00232
00233 QString user( buffer + 0x109 );
00234 QString group( buffer + 0x129 );
00235
00236 QString symlink(buffer + 0x9d );
00237
00238
00239 buffer[ 0x93 ] = 0;
00240 p = buffer + 0x88;
00241 while( *p == ' ' ) ++p;
00242 int time = (int)strtol( p, &dummy, 8 );
00243
00244
00245 char typeflag = buffer[ 0x9c ];
00246
00247
00248 if ( typeflag == '1' )
00249 isdir = true;
00250
00251 bool isDumpDir = false;
00252 if ( typeflag == 'D' )
00253 {
00254 isdir = false;
00255 isDumpDir = true;
00256 }
00257
00258
00259
00260 if (isdir)
00261 access |= S_IFDIR;
00262
00263 KArchiveEntry* e;
00264 if ( isdir )
00265 {
00266
00267 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
00268 }
00269 else
00270 {
00271
00272 buffer[ 0x88 ] = 0;
00273 char *dummy;
00274 const char* p = buffer + 0x7c;
00275 while( *p == ' ' ) ++p;
00276 int size = (int)strtol( p, &dummy, 8 );
00277
00278
00279 if ( isDumpDir )
00280 {
00281 e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
00282 }
00283 else
00284 {
00285
00286
00287 if ( typeflag == '1' )
00288 {
00289 size = nm.length();
00290 kdDebug(7041) << "HARD LINK, setting size to " << size << endl;
00291 }
00292
00293
00294
00295 e = new KArchiveFile( this, nm, access, time, user, group, symlink,
00296 dev->at(), size );
00297 }
00298
00299
00300 int rest = size % 0x200;
00301 int skip = size + (rest ? 0x200 - rest : 0);
00302
00303 if (! dev->at( dev->at() + skip ) )
00304 kdWarning(7041) << "KArchive::open skipping " << skip << " failed" << endl;
00305 }
00306
00307 if ( pos == -1 )
00308 {
00309 if ( nm == "." )
00310 {
00311 Q_ASSERT( isdir );
00312 if ( isdir )
00313 setRootDir( static_cast<KArchiveDirectory *>( e ) );
00314 }
00315 else
00316 rootDir()->addEntry( e );
00317 }
00318 else
00319 {
00320
00321 QString path = QDir::cleanDirPath( name.left( pos ) );
00322
00323 KArchiveDirectory * d = findOrCreate( path );
00324 d->addEntry( e );
00325 }
00326 }
00327 else
00328 {
00329
00330 ende = true;
00331 }
00332 } while( !ende );
00333 return true;
00334 }
00335
00336 bool KTar::closeArchive()
00337 {
00338 d->dirList.clear();
00339 return true;
00340 }
00341
00342 bool KTar::writeDir( const QString& name, const QString& user, const QString& group )
00343 {
00344 if ( !isOpened() )
00345 {
00346 kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
00347 return false;
00348 }
00349
00350 if ( !(mode() & IO_WriteOnly) )
00351 {
00352 kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
00353 return false;
00354 }
00355
00356
00357 QString dirName ( QDir::cleanDirPath( name ) );
00358
00359
00360 if ( dirName.right(1) != "/" )
00361 dirName += "/";
00362
00363 if ( d->dirList.contains( dirName ) )
00364 return true;
00365
00366 char buffer[ 0x201 ];
00367 memset( buffer, 0, 0x200 );
00368
00369
00370 if ( dirName.length() > 99 )
00371 {
00372 strcpy( buffer, "././@LongLink" );
00373 fillBuffer( buffer, " 0", dirName.length()+1, 'L', user.local8Bit(), group.local8Bit() );
00374 device()->writeBlock( buffer, 0x200 );
00375 strncpy( buffer, QFile::encodeName(dirName), 0x200 );
00376 buffer[0x200] = 0;
00377
00378 device()->writeBlock( buffer, 0x200 );
00379
00380 }
00381 else
00382 {
00383
00384 strncpy( buffer, QFile::encodeName(dirName), 0x200 );
00385 buffer[0x200] = 0;
00386 }
00387
00388 fillBuffer( buffer, " 40755", 0, 0x35, user.local8Bit(), group.local8Bit());
00389
00390
00391 device()->writeBlock( buffer, 0x200 );
00392
00393 d->dirList.append( dirName );
00394 return true;
00395 }
00396
00397 bool KTar::prepareWriting( const QString& name, const QString& user, const QString& group, uint size )
00398 {
00399 if ( !isOpened() )
00400 {
00401 kdWarning(7041) << "KArchive::writeFile: You must open the tar file before writing to it\n";
00402 return false;
00403 }
00404
00405 if ( !(mode() & IO_WriteOnly) )
00406 {
00407 kdWarning(7041) << "KArchive::writeFile: You must open the tar file for writing\n";
00408 return false;
00409 }
00410
00411
00412 QString fileName ( QDir::cleanDirPath( name ) );
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433 char buffer[ 0x201 ];
00434 memset( buffer, 0, 0x200 );
00435
00436
00437 if ( fileName.length() > 99 )
00438 {
00439 strcpy( buffer, "././@LongLink" );
00440 fillBuffer( buffer, " 0", fileName.length()+1, 'L', user.local8Bit(), group.local8Bit() );
00441 device()->writeBlock( buffer, 0x200 );
00442
00443 strncpy( buffer, QFile::encodeName(fileName), 0x200 );
00444 buffer[0x200] = 0;
00445
00446 device()->writeBlock( buffer, 0x200 );
00447
00448 }
00449 else
00450 {
00451
00452 strncpy( buffer, QFile::encodeName(fileName), 0x200 );
00453 buffer[0x200] = 0;
00454 }
00455
00456 fillBuffer( buffer, "100644", size, 0x30, user.local8Bit(), group.local8Bit() );
00457
00458
00459 return device()->writeBlock( buffer, 0x200 ) == 0x200;
00460 }
00461
00462 bool KTar::doneWriting( uint size )
00463 {
00464
00465 int rest = size % 0x200;
00466 if ( rest )
00467 {
00468 char buffer[ 0x201 ];
00469 for( uint i = 0; i < 0x200; ++i )
00470 buffer[i] = 0;
00471 Q_LONG nwritten = device()->writeBlock( buffer, 0x200 - rest );
00472 return nwritten == 0x200 - rest;
00473 }
00474 return true;
00475 }
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500 void KTar::fillBuffer( char * buffer,
00501 const char * mode, int size, char typeflag, const char * uname, const char * gname )
00502 {
00503
00504 assert( strlen(mode) == 6 );
00505 strcpy( buffer+0x64, mode );
00506 buffer[ 0x6a ] = ' ';
00507 buffer[ 0x6b ] = '\0';
00508
00509
00510 strcpy( buffer + 0x6c, " 765 ");
00511
00512 strcpy( buffer + 0x74, " 144 ");
00513
00514
00515 QCString s;
00516 s.sprintf("%o", size);
00517 s = s.rightJustify( 11, ' ' );
00518 strcpy( buffer + 0x7c, s.data() );
00519 buffer[ 0x87 ] = ' ';
00520
00521
00522 s.sprintf("%lo", static_cast<unsigned long>(time( 0 )) );
00523 s = s.rightJustify( 11, ' ' );
00524 strcpy( buffer + 0x88, s.data() );
00525 buffer[ 0x93 ] = ' ';
00526
00527
00528 buffer[ 0x94 ] = 0x20;
00529 buffer[ 0x95 ] = 0x20;
00530 buffer[ 0x96 ] = 0x20;
00531 buffer[ 0x97 ] = 0x20;
00532 buffer[ 0x98 ] = 0x20;
00533 buffer[ 0x99 ] = 0x20;
00534
00535
00536
00537
00538
00539
00540 buffer[ 0x9a ] = '\0';
00541 buffer[ 0x9b ] = ' ';
00542
00543
00544 buffer[ 0x9c ] = typeflag;
00545
00546
00547 strcpy( buffer + 0x101, "ustar");
00548 strcpy( buffer + 0x107, "00" );
00549
00550
00551 strcpy( buffer + 0x109, uname );
00552
00553 strcpy( buffer + 0x129, gname );
00554
00555
00556 int check = 32;
00557 for( uint j = 0; j < 0x200; ++j )
00558 check += buffer[j];
00559 s.sprintf("%o", check );
00560 s = s.rightJustify( 7, ' ' );
00561 strcpy( buffer + 0x94, s.data() );
00562 }
00563
00564 void KTar::virtual_hook( int id, void* data )
00565 { KArchive::virtual_hook( id, data ); }
00566