/*
ark - - archiver for the KDE project
Copyright ( C )
2003 : Helio Chissini de Castro < helio @ conectiva . com >
2000 : Corel Corporation ( author : Emily Ezust , emilye @ 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 .
*/
// Std includes
# include <sys/errno.h>
# include <unistd.h>
# include <iostream>
# include <string>
// QT includes
# include <tqstring.h>
# include <tqregexp.h>
# include <tqfile.h>
# include <tqdir.h>
# include <tqtextcodec.h>
// KDE includes
# include <kdebug.h>
# include <tdeglobal.h>
# include <tdelocale.h>
# include <tdemessagebox.h>
# include <kmimetype.h>
# include <kpassdlg.h>
# include <kprocess.h>
# include <kstandarddirs.h>
// ark includes
# include <config.h>
# include "arkwidget.h"
# include "arch.h"
# include "settings.h"
# include "rar.h"
# include "arkutils.h"
# include "filelistview.h"
// MMmmppbb ; M-major, m-minor, p-patch b-(100-beta)
# define VERSION_MAJOR 1000000
# define VERSION_MINOR 10000
# define VERSION_PATCH 100
# define VERSION_5 (5*VERSION_MAJOR - VERSION_PATCH + 1 ) // consider betas
RarArch : : RarArch ( ArkWidget * _gui , const TQString & _fileName )
: Arch ( _gui , _fileName ) , m_isFirstLine ( false ) , m_version ( 0 )
{
// Check if rar is available
bool have_rar = ! TDEGlobal : : dirs ( ) - > findExe ( " rar " ) . isNull ( ) ;
bool have_unrar = ! TDEGlobal : : dirs ( ) - > findExe ( " unrar " ) . isNull ( ) ;
bool have_unrar_free = ! TDEGlobal : : dirs ( ) - > findExe ( " unrar-free " ) . isNull ( ) ;
if ( have_rar )
{
// If it is, then use it as archiver and unarchiver
m_archiver_program = m_unarchiver_program = " rar " ;
verifyCompressUtilityIsAvailable ( m_archiver_program ) ;
verifyUncompressUtilityIsAvailable ( m_unarchiver_program ) ;
}
else if ( have_unrar )
{
// If rar is not available, try to use unrar to open the archive read-only
m_unarchiver_program = " unrar " ;
verifyUncompressUtilityIsAvailable ( m_unarchiver_program ) ;
setReadOnly ( true ) ;
}
else
{
// If rar is not available, try to use unrar to open the archive read-only
m_unarchiver_program = " unrar-free " ;
verifyUncompressUtilityIsAvailable ( m_unarchiver_program ) ;
setReadOnly ( true ) ;
}
}
bool RarArch : : processLine ( const TQCString & line )
{
TQString uline = TQTextCodec : : codecForLocale ( ) - > toUnicode ( line ) ;
// Look for rar/unrar version first
if ( ! m_version )
{
TQRegExp versionRegExp ( TQString : : fromLatin1 ( " (?:UN) ? RAR \ \ s + ( \ \ d + ) \ \ . ( \ \ d + ) ( \ \ s + beta \ \ s + ( \ \ d + ) ) ? \ \ s . * Copyright . * " )) ;
if ( versionRegExp . exactMatch ( uline ) )
{
// Rar displays verion in form of "M.mp (beta b)?"
m_version = versionRegExp . cap ( 1 ) . toShort ( ) * VERSION_MAJOR ;
m_version + = versionRegExp . cap ( 2 ) . toShort ( ) / 10 * VERSION_MINOR ;
m_version + = versionRegExp . cap ( 2 ) . toShort ( ) % 10 * VERSION_PATCH ;
if ( ! versionRegExp . cap ( 4 ) . isEmpty ( ) ) { // beta versions should go befor release ones
m_version - = VERSION_PATCH ;
m_version + = versionRegExp . cap ( 4 ) . toShort ( ) ;
}
if ( m_version < VERSION_5 ) {
m_headerString = " ------------------------------------------------------------------------------- " ;
m_isFirstLine = true ;
} else {
m_headerString = " ----------- --------- -------- ----- ---------- ----- -------- ---- " ;
}
setHeaders ( ) ; //< Note: header order for version 5 is different, but keep the old one for consistency
return true ;
}
return false ;
}
TQStringList entry ;
TQStringList parsedData = TQStringList : : split ( QChar ( ' ' ) , uline ) ;
if ( m_version < VERSION_5 ) {
if ( m_isFirstLine )
{
m_entryFilename = uline . remove ( 0 , 1 ) ;
m_isFirstLine = false ;
return true ;
}
if ( parsedData . size ( ) < 9 ) {
kdError ( 1601 ) < < " Failed to parse rar<5 output string: \" " < < uline < < " \" " < < endl ;
}
entry < < m_entryFilename ; // filename
entry < < parsedData [ 0 ] ; // size
entry < < parsedData [ 1 ] ; // packed
entry < < parsedData [ 2 ] ; // ratio
TQStringList date = TQStringList : : split ( ' - ' , parsedData [ 3 ] ) ;
entry < < ArkUtils : : fixYear ( date [ 2 ] . latin1 ( ) ) + ' - ' + date [ 1 ] + ' - ' + date [ 0 ] + ' ' + parsedData [ 4 ] ; // date
entry < < parsedData [ 5 ] ; // attributes
entry < < parsedData [ 6 ] ; // crc
entry < < parsedData [ 7 ] ; // method
entry < < parsedData [ 8 ] ; // Version
m_isFirstLine = true ;
}
else
{
// Note: don't use parsedData for names due to they may contain trailing spaces
TQRegExp nameRegExp ( TQString : : fromLatin1 ( " \\ s*( \\ S+ \\ s+) { 6 } \ \ S + ( . * ) " ));
if ( parsedData . size ( ) > = 8 & & nameRegExp . exactMatch ( uline ) ) {
m_entryFilename = nameRegExp . capturedTexts ( ) [ 2 ] ;
if ( m_version < 5 * VERSION_MAJOR + 3 * VERSION_MINOR ) { // workaround bug with extra spaces in rar<5.3.0
m_entryFilename = m_entryFilename . stripWhiteSpace ( ) ;
}
entry < < m_entryFilename ; // filename
entry < < parsedData [ 1 ] ; // size
entry < < parsedData [ 2 ] ; // packed
entry < < parsedData [ 3 ] ; // ratio
entry < < parsedData [ 4 ] + " " + parsedData [ 5 ] ; // date and time
entry < < parsedData [ 0 ] ; // attributes
entry < < parsedData [ 6 ] ; // crc
} else {
kdError ( 1601 ) < < " Failed to parse rar-5+ output string: \" " < < uline < < " \" " < < endl ;
return false ;
}
}
// send to GUI
// Use addOrUpdateItem() rather than addItem() due to recent RAR version
// place directories in archive after their content.
FileLVI * item = m_gui - > fileList ( ) - > addOrUpdateItem ( entry ) ;
// But archives packaged with older versions of rar may have directories
// entries first, so make sure they will get an appropriate icon
if ( item & & entry [ 5 ] . find ( ' d ' , 0 , false ) ! = - 1 ) {
// check attr's for d (case insensitive to handle windows archives)
item - > setPixmap ( 0 , KMimeType : : mimeType ( " inode/directory " ) - > pixmap ( TDEIcon : : Small ) ) ;
}
return true ;
}
void RarArch : : open ( )
{
m_buffer = " " ;
m_header_removed = false ;
m_finished = false ;
TDEProcess * kp = m_currentProcess = new TDEProcess ;
* kp < < m_unarchiver_program < < " v " < < " -c- " ;
if ( ! m_password . isEmpty ( ) )
* kp < < " -p " + m_password ;
else
* kp < < " -p- " ;
* kp < < m_filename ;
connect ( kp , TQT_SIGNAL ( receivedStdout ( TDEProcess * , char * , int ) ) ,
TQT_SLOT ( slotReceivedTOC ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , TQT_SIGNAL ( receivedStderr ( TDEProcess * , char * , int ) ) ,
TQT_SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , TQT_SIGNAL ( processExited ( TDEProcess * ) ) ,
TQT_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 RarArch : : setHeaders ( )
{
ColumnList list ;
list . append ( FILENAME_COLUMN ) ;
list . append ( SIZE_COLUMN ) ;
list . append ( PACKED_COLUMN ) ;
list . append ( RATIO_COLUMN ) ;
list . append ( TIMESTAMP_COLUMN ) ;
list . append ( PERMISSION_COLUMN ) ;
list . append ( CRC_COLUMN ) ;
if ( m_version < VERSION_5 )
{
list . append ( METHOD_COLUMN ) ;
list . append ( VERSION_COLUMN ) ;
}
emit headers ( list ) ;
}
void RarArch : : create ( )
{
emit sigCreate ( this , true , m_filename ,
Arch : : Extract | Arch : : Delete | Arch : : Add | Arch : : View ) ;
}
void RarArch : : createPassword ( )
{
if ( m_password . isEmpty ( ) & & ArkSettings : : askCreatePassword ( ) )
KPasswordDialog : : getNewPassword ( m_password , i18n ( " Warning! \n Using KGpg for encryption is more secure. \n Cancel this dialog or enter password for %1 archiver: " ) . arg ( m_archiver_program ) ) ;
}
void RarArch : : addDir ( const TQString & _dirName )
{
if ( ! _dirName . isEmpty ( ) )
{
TQStringList list ;
list . append ( _dirName ) ;
addFile ( list ) ;
}
}
void RarArch : : addFile ( const TQStringList & urls )
{
TDEProcess * kp = m_currentProcess = new TDEProcess ;
kp - > clearArguments ( ) ;
* kp < < m_archiver_program ;
if ( ArkSettings : : replaceOnlyWithNewer ( ) )
* kp < < " u " ;
else
* kp < < " a " ;
if ( ArkSettings : : rarStoreSymlinks ( ) )
* kp < < " -ol " ;
if ( ArkSettings : : rarRecurseSubdirs ( ) )
* kp < < " -r " ;
if ( ! m_password . isEmpty ( ) )
* kp < < " -p " + m_password ;
* kp < < m_filename ;
KURL dir ( urls . first ( ) ) ;
TQDir : : setCurrent ( dir . directory ( ) ) ;
TQStringList : : ConstIterator iter ;
for ( iter = urls . begin ( ) ; iter ! = urls . end ( ) ; + + iter )
{
KURL url ( * iter ) ;
* kp < < url . fileName ( ) ;
}
connect ( kp , TQT_SIGNAL ( receivedStdout ( TDEProcess * , char * , int ) ) ,
TQT_SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , TQT_SIGNAL ( receivedStderr ( TDEProcess * , char * , int ) ) ,
TQT_SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , TQT_SIGNAL ( processExited ( TDEProcess * ) ) ,
TQT_SLOT ( slotAddExited ( TDEProcess * ) ) ) ;
if ( ! kp - > start ( TDEProcess : : NotifyOnExit , TDEProcess : : AllOutput ) )
{
KMessageBox : : error ( 0 , i18n ( " Could not start a subprocess. " ) ) ;
emit sigAdd ( false ) ;
}
}
void RarArch : : unarchFileInternal ( )
{
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 ( ) ;
// extract (and maybe overwrite)
* kp < < m_unarchiver_program < < " x " ;
if ( ! m_password . isEmpty ( ) )
* kp < < " -p " + m_password ;
else
* kp < < " -p- " ;
if ( ! ArkSettings : : extractOverwrite ( ) )
{
* kp < < " -o+ " ;
}
else
{
* kp < < " -o- " ;
}
* kp < < m_filename ;
// if the file 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 < < m_destDir ;
connect ( kp , TQT_SIGNAL ( receivedStdout ( TDEProcess * , char * , int ) ) ,
TQT_SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , TQT_SIGNAL ( receivedStderr ( TDEProcess * , char * , int ) ) ,
TQT_SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , TQT_SIGNAL ( processExited ( TDEProcess * ) ) ,
TQT_SLOT ( slotExtractExited ( TDEProcess * ) ) ) ;
if ( ! kp - > start ( TDEProcess : : NotifyOnExit , TDEProcess : : AllOutput ) )
{
KMessageBox : : error ( 0 , i18n ( " Could not start a subprocess. " ) ) ;
emit sigExtract ( false ) ;
}
}
bool RarArch : : passwordRequired ( )
{
return m_lastShellOutput . find ( " Enter password " ) > = 0 | | m_lastShellOutput . find ( " encrypted " ) > = 0 ;
}
void RarArch : : 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 , TQT_SIGNAL ( receivedStdout ( TDEProcess * , char * , int ) ) ,
TQT_SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , TQT_SIGNAL ( receivedStderr ( TDEProcess * , char * , int ) ) ,
TQT_SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , TQT_SIGNAL ( processExited ( TDEProcess * ) ) ,
TQT_SLOT ( slotDeleteExited ( TDEProcess * ) ) ) ;
if ( ! kp - > start ( TDEProcess : : NotifyOnExit , TDEProcess : : AllOutput ) )
{
KMessageBox : : error ( 0 , i18n ( " Could not start a subprocess. " ) ) ;
emit sigDelete ( false ) ;
}
}
void RarArch : : test ( )
{
clearShellOutput ( ) ;
TDEProcess * kp = m_currentProcess = new TDEProcess ;
kp - > clearArguments ( ) ;
* kp < < m_unarchiver_program < < " t " ;
if ( ! m_password . isEmpty ( ) )
* kp < < " -p " + m_password ;
* kp < < m_filename ;
connect ( kp , SIGNAL ( receivedStdout ( TDEProcess * , char * , int ) ) ,
SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , SIGNAL ( receivedStderr ( TDEProcess * , char * , int ) ) ,
SLOT ( slotReceivedOutput ( TDEProcess * , char * , int ) ) ) ;
connect ( kp , SIGNAL ( processExited ( TDEProcess * ) ) ,
SLOT ( slotTestExited ( TDEProcess * ) ) ) ;
if ( ! kp - > start ( TDEProcess : : NotifyOnExit , TDEProcess : : AllOutput ) )
{
KMessageBox : : error ( 0 , i18n ( " Could not start a subprocess. " ) ) ;
emit sigTest ( false ) ;
}
}
# include "rar.moc"