/* ark -- archiver for the KDE project Copyright (C) 1997-1999: Rob Palmbos palm9744@kettering.edu 1999: Francois-Xavier Duranceau duranceau@kde.org 1999-2000: Corel Corporation (author: Emily Ezust, emilye@corel.com) 2001: Corel Corporation (author: Michael Jarrett, michaelj@corel.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // TQt includes #include #include // KDE includes #include #include #include #include #include #include // ark includes #include "zip.h" #include "arkwidget.h" #include "settings.h" #include "filelistview.h" ZipArch::ZipArch( ArkWidget *_gui, const TQString & _fileName ) : Arch( _gui, _fileName ) { m_archiver_program = "zip"; m_unarchiver_program = "unzip"; verifyCompressUtilityIsAvailable( m_archiver_program ); verifyUncompressUtilityIsAvailable( m_unarchiver_program ); m_headerString = "----"; m_numCols = 7; } void ZipArch::setHeaders() { ColumnList list; list.append( FILENAME_COLUMN ); list.append( SIZE_COLUMN ); list.append( METHOD_COLUMN ); list.append( PACKED_COLUMN ); list.append( RATIO_COLUMN ); list.append( TIMESTAMP_COLUMN ); list.append( CRC_COLUMN ); emit headers( list ); } void ZipArch::open() { setHeaders(); m_buffer = ""; m_header_removed = false; m_finished = false; TDEProcess *kp = m_currentProcess = new TDEProcess; *kp << m_unarchiver_program << "-v" << m_filename; connect( kp, TQ_SIGNAL( receivedStdout(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedTOC(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( receivedStderr(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( processExited(TDEProcess*) ), TQ_SLOT( slotOpenExited(TDEProcess*) ) ); if ( !kp->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ) ) { KMessageBox::error( 0, i18n( "Could not start a subprocess." ) ); emit sigOpen( this, false, TQString(), 0 ); } } void ZipArch::create() { emit sigCreate( this, true, m_filename, Arch::Extract | Arch::Delete | Arch::Add | Arch::View ); } void ZipArch::createPassword() { if( m_password.isEmpty() && ArkSettings::askCreatePassword() ) KPasswordDialog::getNewPassword( m_password, i18n("Warning!\nUsing KGpg for encryption is more secure.\nCancel this dialog or enter password for %1 archiver:").arg(m_archiver_program) ); } void ZipArch::addDir( const TQString & _dirName ) { if ( !_dirName.isEmpty() ) { bool bOldRecVal = ArkSettings::rarRecurseSubdirs(); // must be true for add directory - otherwise why would user try? ArkSettings::setRarRecurseSubdirs( true ); TQStringList list; list.append( _dirName ); addFile( list ); ArkSettings::setRarRecurseSubdirs( bOldRecVal ); // reset to old val } } void ZipArch::addFile( const TQStringList &urls ) { TDEProcess *kp = m_currentProcess = new TDEProcess; kp->clearArguments(); *kp << m_archiver_program; if ( !m_password.isEmpty() ) *kp << "-P" << m_password.local8Bit(); if ( ArkSettings::rarRecurseSubdirs() ) *kp << "-r"; if ( ArkSettings::rarStoreSymlinks() ) *kp << "-y"; if ( ArkSettings::forceMSDOS() ) *kp << "-k"; if ( ArkSettings::convertLF2CRLF() ) *kp << "-l"; if ( ArkSettings::replaceOnlyWithNewer() ) *kp << "-u"; *kp << m_filename; TQStringList::ConstIterator iter; KURL url( urls.first() ); TQDir::setCurrent( url.directory() ); for ( iter = urls.begin(); iter != urls.end(); ++iter ) { KURL fileURL( *iter ); *kp << fileURL.fileName(); } connect( kp, TQ_SIGNAL( receivedStdout(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( receivedStderr(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( processExited(TDEProcess*) ), TQ_SLOT( slotAddExited(TDEProcess*) ) ); if ( !kp->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ) ) { KMessageBox::error( 0, i18n( "Could not start a subprocess." ) ); emit sigAdd( false ); } } void ZipArch::unarchFileInternal() { // if fileList is empty, all files are extracted. // if destDir is empty, abort with error. if ( m_destDir.isEmpty() || m_destDir.isNull() ) { kdError( 1601 ) << "There was no extract directory given." << endl; return; } TDEProcess *kp = m_currentProcess = new TDEProcess; kp->clearArguments(); *kp << m_unarchiver_program; if ( !m_password.isEmpty() ) *kp << "-P" << m_password.local8Bit(); if ( ArkSettings::extractJunkPaths() && !m_viewFriendly ) *kp << "-j" ; if ( ArkSettings::rarToLower() ) *kp << "-L"; if ( ArkSettings::extractOverwrite() ) *kp << "-o"; else *kp << "-n"; *kp << m_filename; // if the list is empty, no filenames go on the command line, // and we then extract everything in the archive. if ( m_fileList ) { TQStringList::Iterator it; for ( it = m_fileList->begin(); it != m_fileList->end(); ++it ) { *kp << (*it); } } *kp << "-d" << m_destDir; connect( kp, TQ_SIGNAL( receivedStdout(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( receivedStderr(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( processExited(TDEProcess*) ), TQ_SLOT( slotExtractExited(TDEProcess*) ) ); if ( !kp->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ) ) { KMessageBox::error( 0, i18n( "Could not start a subprocess." ) ); emit sigExtract( false ); } } bool ZipArch::passwordRequired() { return m_lastShellOutput.findRev("password:") >= 0 || m_lastShellOutput.findRev("unable to get password\n")!=-1 || m_lastShellOutput.endsWith("password inflating\n") || m_lastShellOutput.findRev("password incorrect--reenter:")!=-1 || m_lastShellOutput.endsWith("incorrect password\n"); } void ZipArch::remove( TQStringList *list ) { if ( !list ) return; TDEProcess *kp = m_currentProcess = new TDEProcess; kp->clearArguments(); *kp << m_archiver_program << "-d" << m_filename; TQStringList::Iterator it; for ( it = list->begin(); it != list->end(); ++it ) { TQString str = *it; *kp << str; } connect( kp, TQ_SIGNAL( receivedStdout(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( receivedStderr(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( processExited(TDEProcess*) ), TQ_SLOT( slotDeleteExited(TDEProcess*) ) ); if ( !kp->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ) ) { KMessageBox::error( 0, i18n( "Could not start a subprocess." ) ); emit sigDelete( false ); } } void ZipArch::test() { clearShellOutput(); TDEProcess *kp = m_currentProcess = new TDEProcess; kp->clearArguments(); *kp << m_unarchiver_program << "-t"; if ( !m_password.isEmpty() ) *kp << "-P" << m_password.local8Bit(); *kp << m_filename; connect( kp, TQ_SIGNAL( receivedStdout(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( receivedStderr(TDEProcess*, char*, int) ), TQ_SLOT( slotReceivedOutput(TDEProcess*, char*, int) ) ); connect( kp, TQ_SIGNAL( processExited(TDEProcess*) ), TQ_SLOT( slotTestExited(TDEProcess*) ) ); if ( !kp->start( TDEProcess::NotifyOnExit, TDEProcess::AllOutput ) ) { KMessageBox::error( 0, i18n( "Could not start a subprocess." ) ); emit sigTest( false ); } } bool ZipArch::processLine( const TQCString &line ) { TQTextCodec *codec = TQTextCodec::codecForLocale(); TQString tqunicode_line = codec->toUnicode( line ); // Header structure: // Length Method Size Cmpr Date Time CRC-32 Name // -------- ------ ------- ---- ---------- ----- -------- ---- TQRegExp lineRx { "^" "\\s*" "(" "[0-9]+" ")" // 1 Length "\\s+" "(" "\\S+" ")" // 2 Method "\\s+" "(" "[0-9]+" ")" // 3 Size "\\s+" "(" "[0-9.]+%" ")" // 4 Compression rate "\\s+" "(" "[0-9\\-]+" ")" // 5 Date "\\s+" "(" "[0-9:]+" ")" // 6 Time "\\s+" "(" "[a-fA-F0-9]+" ")" // 7 CRC-32 " " "(" "[^\\n]+" ")" // 8 Name "\\n?$" }; if( lineRx.search(tqunicode_line) == -1 ) { kdDebug(1601) << "processLine failed to match unzip line: " << line << endl; return false; } // unzip-6 can be configured at build time to return date in either of three // formats: // - MM-DD-YYYY (the default on *nix systems) // - DD-MM-YYYY (not used by default) // - YYYY-MM-DD (used in several linux distribution e.g. debian) // Unfortunately there is no easy way to query unzip which format it does // use, so we will have to guestimate here. Also since the DMY is not widely // used and in general case indistinguishable from MDY we will ignore it and // concentrate on distinguishing between MDY and YMD. Luckily unzip-6 unlike // unzip-5 uses 4 digits for years, so it will be relatively painless. TQString date = lineRx.cap(5); TQString time = lineRx.cap(6); TQRegExp mdyDateRx{"^([01][0-9])-([0-3][0-9])-([0-9]{4,})$"}; if(mdyDateRx.search(date) != -1) { date = mdyDateRx.cap(3) + "-" + mdyDateRx.cap(1) + "-" + mdyDateRx.cap(2); } TQString timestamp = date + " " + time; TQStringList l; l << lineRx.cap(8); // FILENAME_COLUMN l << lineRx.cap(1); // SIZE_COLUMN l << lineRx.cap(2); // METHOD_COLUMN l << lineRx.cap(3); // PACKED_COLUMN l << lineRx.cap(4); // RATIO_COLUMN l << timestamp; // TIMESTAMP_COLUMN l << lineRx.cap(7); // CRC_COLUMN m_gui->fileList()->addItem(l); return true; } #include "zip.moc"