summaryrefslogtreecommitdiffstats
path: root/installerbuilder/libinstaller/3rdparty/p7zip_9.04/lib7z_facade.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'installerbuilder/libinstaller/3rdparty/p7zip_9.04/lib7z_facade.cpp')
-rw-r--r--installerbuilder/libinstaller/3rdparty/p7zip_9.04/lib7z_facade.cpp1253
1 files changed, 1253 insertions, 0 deletions
diff --git a/installerbuilder/libinstaller/3rdparty/p7zip_9.04/lib7z_facade.cpp b/installerbuilder/libinstaller/3rdparty/p7zip_9.04/lib7z_facade.cpp
new file mode 100644
index 000000000..f1dd2e439
--- /dev/null
+++ b/installerbuilder/libinstaller/3rdparty/p7zip_9.04/lib7z_facade.cpp
@@ -0,0 +1,1253 @@
+#include "lib7z_facade.h"
+
+#include "StdAfx.h"
+
+#include "Common/MyInitGuid.h"
+
+#include "Common/CommandLineParser.h"
+#include "Common/IntToString.h"
+#include "Common/MyException.h"
+#include "Common/StdOutStream.h"
+#include "Common/StringConvert.h"
+#include "Common/StringToInt.h"
+
+#include "Windows/Defs.h"
+#include "Windows/Error.h"
+#include "Windows/FileDir.h"
+#include "Windows/FileName.h"
+
+#include "7zip/ICoder.h"
+#include "7zip/IPassword.h"
+
+#include "7zip/UI/Common/ArchiveCommandLine.h"
+#include "7zip/UI/Common/ExitCode.h"
+#include "7zip/UI/Common/Extract.h"
+#include "7zip/UI/Common/Update.h"
+#include "7zip/UI/Common/ArchiveExtractCallback.h"
+
+#include "Windows/Defs.h"
+#include "Windows/Error.h"
+#include "Windows/FileDir.h"
+#include "Windows/FileName.h"
+
+#include "7zip/UI/Common/LoadCodecs.h"
+#include "7zip/UI/Common/PropIDUtils.h"
+#include "Windows/PropVariant.h"
+#include "Windows/PropVariantConversions.h"
+
+#include <common/errors.h>
+#include <common/fileutils.h>
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QIODevice>
+#include <QPointer>
+#include <QString>
+#include <QStringList>
+#include <QTemporaryFile>
+
+#ifdef Q_WS_WIN
+#include <time.h>
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000 /* trick for Unix */
+#define S_IFMT 00170000
+#define S_IFLNK 0120000
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+
+typedef BOOL (WINAPI *CREATEHARDLINK)(LPCSTR dst, LPCSTR str, LPSECURITY_ATTRIBUTES sa);
+
+bool CreateHardLinkWrapper( const QString& dest, const QString& file )
+{
+ static HMODULE module = 0;
+ static CREATEHARDLINK proc = 0;
+
+ if( module == 0 )
+ module = LoadLibrary( L"kernel32.dll" );
+ if( module == 0 )
+ return false;
+ if( proc == 0 )
+ proc = (CREATEHARDLINK) GetProcAddress( module, "CreateHardLinkA" );
+ if( proc == 0 )
+ return false;
+ QString target = file;
+ if( !QFileInfo( file ).isAbsolute() )
+ {
+ target = QFileInfo( dest ).dir().absoluteFilePath( file );
+ }
+ const QString link = QDir::toNativeSeparators( dest );
+ const QString existingFile = QDir::toNativeSeparators( target );
+ return proc( link.toLocal8Bit(), existingFile.toLocal8Bit(), 0 );
+}
+
+#else
+#include <sys/stat.h>
+#endif
+
+#include <iostream>
+#include <memory>
+
+#include <cassert>
+
+using namespace Lib7z;
+using namespace NWindows;
+
+namespace {
+ /**
+ * RAII class to create a directory (tryCreate()) and delete it on destruction unless released.
+ */
+ struct DirectoryGuard {
+ explicit DirectoryGuard( const QString& path )
+ : m_path( path ),
+ m_created( false ),
+ m_released( false )
+ {
+ static const QRegExp re( QLatin1String( "\\\\|/" ) );
+ static const QLatin1String sep( "/" );
+ m_path.replace( re, sep );
+ }
+ ~DirectoryGuard() {
+ if ( !m_created || m_released )
+ return;
+ QDir dir( m_path );
+ if ( !dir.rmdir( m_path ) )
+ qWarning() << "Could not delete directory " << m_path;
+ }
+
+ /**
+ * Tries to create the directorie structure.
+ * Returns a list of every directory created.
+ */
+ QStringList tryCreate() {
+ if( m_path.isEmpty() )
+ return QStringList();
+
+ const QFileInfo fi( m_path );
+ if ( fi.exists() && fi.isDir() )
+ return QStringList();
+ if ( fi.exists() && !fi.isDir() )
+ throw SevenZipException( QObject::tr("Path exists but is not a folder: %1").arg( m_path ) );
+
+ QStringList created;
+
+ QDir toCreate( m_path );
+ while( !toCreate.exists() )
+ {
+ QString p = toCreate.absolutePath();
+ created.push_front( p );
+ p = p.section( QLatin1Char( '/' ), 0, -2 );
+ toCreate = QDir( p );
+ }
+
+ QDir dir( m_path );
+ m_created = dir.mkpath( m_path );
+ if ( !m_created )
+ throw SevenZipException( QObject::tr("Could not create folder: %1").arg( m_path ) );
+
+ return created;
+ }
+
+ void release() {
+ m_released = true;
+ }
+
+ QString m_path;
+ bool m_created;
+ bool m_released;
+ };
+}
+
+static void throwIfNotOK( HRESULT result, const QString& msg ) {
+ if ( result != S_OK )
+ throw SevenZipException( msg );
+}
+
+static UString QString2UString( const QString& str ) {
+ return str.toStdWString().c_str();
+}
+
+static QString UString2QString( const UString& str ) {
+ return QString::fromStdWString( static_cast<const wchar_t*>( str ) );
+}
+
+static QString generateTempFileName() {
+ QTemporaryFile tmp;
+ if ( !tmp.open() )
+ throw SevenZipException( QObject::tr("Could not create temporary file") );
+ return QDir::toNativeSeparators( tmp.fileName() );
+}
+
+/*
+static QStringList UStringVector2QStringList( const UStringVector& vec ) {
+ QStringList res;
+ for( int i = 0; i < vec.Size(); ++i )
+ res += UString2QString( vec[i] );
+ return res;
+}
+*/
+
+static NCOM::CPropVariant readProperty( IInArchive* archive, int index, int propId ) {
+ NCOM::CPropVariant prop;
+ throwIfNotOK( archive->GetProperty(index, propId, &prop), QObject::tr("Could not retrieve property %1 for item %2").arg( QString::number( propId ), QString::number( index ) ) );
+ return prop;
+}
+static bool IsFileTimeZero( const FILETIME *lpFileTime )
+{
+ return (lpFileTime->dwLowDateTime == 0) && (lpFileTime->dwHighDateTime == 0);
+}
+
+static bool IsDST( const QDateTime& datetime = QDateTime() )
+{
+ const time_t seconds = static_cast< time_t >( datetime.isValid() ? datetime.toTime_t() : QDateTime::currentDateTime().toTime_t() );
+ const tm* const t = localtime( &seconds );
+ return t->tm_isdst;
+}
+
+static QDateTime getDateTimeProperty( IInArchive* archive, int index, int propId, const QDateTime& defaultValue ) {
+ const NCOM::CPropVariant prop = readProperty( archive, index, propId );
+ if (prop.vt != VT_FILETIME)
+ throw SevenZipException( QObject::tr("Property %1 for item %2 not of type VT_FILETIME but %3").arg( QString::number( propId ), QString::number( index ), QString::number( prop.vt ) ) );
+ if ( IsFileTimeZero( &prop.filetime ) )
+ return defaultValue;
+
+ FILETIME localFileTime;
+ if ( !FileTimeToLocalFileTime( &prop.filetime, &localFileTime ) )
+ throw SevenZipException( QObject::tr("Could not convert file time to local time") );
+
+ SYSTEMTIME st;
+ if ( !BOOLToBool(FileTimeToSystemTime(&localFileTime, &st)) )
+ throw SevenZipException( QObject::tr("Could not convert local file time to system time") );
+
+ const QDate date( st.wYear, st.wMonth, st.wDay );
+ const QTime time( st.wHour, st.wMinute, st.wSecond );
+ QDateTime result( date, time );
+
+ // fix daylight saving time
+ const bool dst = IsDST();
+ if( dst != IsDST( result ) )
+ result = result.addSecs( dst ? -3600 : 3600 );
+
+ return result;
+}
+
+static quint64 getUInt64Property( IInArchive* archive, int index, int propId, quint64 defaultValue=0 ) {
+ const NCOM::CPropVariant prop = readProperty( archive, index, propId );
+ if ( prop.vt == VT_EMPTY )
+ return defaultValue;
+ return static_cast<quint64>( ConvertPropVariantToUInt64( prop ) );
+}
+
+static quint32 getUInt32Property( IInArchive* archive, int index, int propId, quint32 defaultValue=0 ) {
+ const NCOM::CPropVariant prop = readProperty( archive, index, propId );
+ if ( prop.vt == VT_EMPTY )
+ return defaultValue;
+ return static_cast< quint32 >( prop.ulVal );
+}
+
+static QFile::Permissions getPermissions( IInArchive* archive, int index, bool* hasPermissions = 0 )
+{
+ quint32 attributes = getUInt32Property( archive, index, kpidAttrib, 0 );
+ QFile::Permissions permissions = 0;
+ if( attributes & FILE_ATTRIBUTE_UNIX_EXTENSION )
+ {
+ if( hasPermissions != 0 )
+ *hasPermissions = true;
+ // filter the unix permissions
+ attributes = ( attributes >> 16 ) & 0777;
+ permissions |= static_cast< QFile::Permissions >( ( attributes & 0700 ) << 2 ); // owner rights
+ permissions |= static_cast< QFile::Permissions >( ( attributes & 0070 ) << 1 ); // group
+ permissions |= static_cast< QFile::Permissions >( ( attributes & 0007 ) << 0 ); // and world rights
+ }
+ else if( hasPermissions != 0 )
+ *hasPermissions = false;
+ return permissions;
+}
+
+namespace Lib7z {
+
+ class QIODeviceSequentialOutStream : public ISequentialOutStream, public CMyUnknownImp {
+ public:
+ MY_UNKNOWN_IMP
+ explicit QIODeviceSequentialOutStream( QIODevice* device );
+ ~QIODeviceSequentialOutStream();
+
+ /* reimp */ STDMETHOD(Write)( const void* data, UInt32 size, UInt32* processedSize );
+
+ private:
+ QPointer<QIODevice> m_device;
+ const bool closeOnDestroy;
+ };
+
+
+ QIODeviceSequentialOutStream::QIODeviceSequentialOutStream( QIODevice* device )
+ : ISequentialOutStream(),
+ CMyUnknownImp(),
+ m_device( device ),
+ closeOnDestroy( !device->isOpen() )
+ {
+ assert( m_device );
+ if( closeOnDestroy )
+ m_device->open( QIODevice::WriteOnly );
+ }
+
+ QIODeviceSequentialOutStream::~QIODeviceSequentialOutStream()
+ {
+ if( closeOnDestroy )
+ {
+ m_device->close();
+ delete m_device;
+ }
+ }
+
+ HRESULT QIODeviceSequentialOutStream::Write( const void* data, UInt32 size, UInt32* processedSize ) {
+ if ( !m_device ) {
+ if ( processedSize )
+ *processedSize = 0;
+ return E_FAIL;
+ }
+ if( closeOnDestroy && !m_device->isOpen() ) {
+ const bool opened = m_device->open( QIODevice::WriteOnly );
+ if ( !opened ) {
+ if ( processedSize )
+ *processedSize = 0;
+ return E_FAIL;
+ }
+ }
+
+ const qint64 written = m_device->write( reinterpret_cast<const char*>( data ), size );
+ if ( processedSize )
+ *processedSize = written;
+ return written >= 0 ? S_OK : E_FAIL;
+ }
+
+ class QIODeviceInStream : public IInStream, public CMyUnknownImp {
+ public:
+ MY_UNKNOWN_IMP
+
+ explicit QIODeviceInStream( QIODevice* device ) : IInStream(), CMyUnknownImp(), m_device( device ) {
+ assert( m_device );
+ assert( !m_device->isSequential() );
+ }
+
+ /* reimp */ STDMETHOD(Read)( void* data, UInt32 size, UInt32* processedSize ) {
+ assert( m_device );
+ assert( m_device->isReadable() );
+ const qint64 actual = m_device->read( reinterpret_cast<char*>( data ), size );
+ Q_ASSERT( actual != 0 || m_device->atEnd() );
+ if ( processedSize )
+ *processedSize = actual;
+ return actual >= 0 ? S_OK : E_FAIL;
+ }
+
+ /* reimp */ STDMETHOD(Seek)( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) {
+ assert( m_device );
+ assert( !m_device->isSequential() );
+ assert( m_device->isReadable() );
+ if ( seekOrigin > STREAM_SEEK_END )
+ return STG_E_INVALIDFUNCTION;
+ UInt64 np = 0;
+ switch( seekOrigin ) {
+ case STREAM_SEEK_SET:
+ np = offset;
+ break;
+ case STREAM_SEEK_CUR:
+ np = m_device->pos() + offset;
+ break;
+ case STREAM_SEEK_END:
+ np = m_device->size() + offset;
+ break;
+ default:
+ return STG_E_INVALIDFUNCTION;
+ }
+
+ np = qBound( static_cast<UInt64>( 0 ), np, static_cast<UInt64>( m_device->size() - 1 ) );
+ const bool ok = m_device->seek( np );
+ if ( newPosition )
+ *newPosition = np;
+ return ok ? S_OK : E_FAIL;
+ }
+
+ private:
+ QPointer<QIODevice> m_device;
+ };
+}
+
+File::File() : permissions( 0 ), uncompressedSize( 0 ), compressedSize( 0 ), isDirectory( false ) {
+}
+
+QVector<File> File::subtreeInPreorder() const {
+ QVector<File> res;
+ res += *this;
+ Q_FOREACH( const File& child, children )
+ res += child.subtreeInPreorder();
+ return res;
+}
+
+bool File::operator<( const File& other ) const {
+ if ( path != other.path )
+ return path < other.path;
+ if ( mtime != other.mtime )
+ return mtime < other.mtime;
+ if ( uncompressedSize != other.uncompressedSize )
+ return uncompressedSize < other.uncompressedSize;
+ if ( compressedSize != other.compressedSize )
+ return compressedSize < other.compressedSize;
+ if ( isDirectory != other.isDirectory )
+ return !isDirectory;
+ if( permissions != other.permissions )
+ return permissions < other.permissions;
+ return false;
+}
+
+bool File::operator==( const File& other ) const {
+ return mtime == other.mtime
+ && path == other.path
+ && uncompressedSize == other.uncompressedSize
+ && compressedSize == other.compressedSize
+ && isDirectory == other.isDirectory
+ && children == other.children
+ && (permissions == other.permissions || permissions == static_cast< QFile::Permissions >( -1 ) || other.permissions == static_cast< QFile::Permissions >( -1 ) );
+}
+
+QByteArray Lib7z::formatKeyValuePairs( const QVariantList& l ) {
+ assert( l.size() % 2 == 0 );
+ QByteArray res;
+ for ( QVariantList::ConstIterator it = l.constBegin(); it != l.constEnd(); ++it ) {
+ if ( !res.isEmpty() )
+ res += ", ";
+ res += qPrintable(it->toString()) + QByteArray(" = ");
+ ++it;
+ res += qPrintable(it->toString());
+ }
+ return res;
+}
+
+class Job::Private {
+public:
+ Private() : error(Lib7z::NoError) {}
+ int error;
+ QString errorString;
+};
+
+Job::Job( QObject* parent ) : QObject( parent ), QRunnable(), d( new Private ) {
+}
+
+Job::~Job() {
+ delete d;
+}
+
+void Job::emitResult() {
+ emit finished( this );
+}
+
+void Job::setError( int code ) {
+ d->error = code;
+}
+
+void Job::setErrorString( const QString& str ) {
+ d->errorString = str;
+}
+
+void Job::emitProgress( qint64 completed, qint64 total ) {
+ emit progress( completed, total );
+}
+
+int Job::error() const {
+ return d->error;
+}
+
+bool Job::hasError() const {
+ return d->error != NoError;
+}
+
+void Job::run() {
+ doStart();
+}
+
+QString Job::errorString() const {
+ return d->errorString;
+}
+
+void Job::start() {
+ QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection );
+}
+
+class ListArchiveJob::Private {
+public:
+ QPointer<QIODevice> archive;
+ QVector<File> files;
+};
+
+ListArchiveJob::ListArchiveJob( QObject* parent ) : Job( parent ), d( new Private ) {
+}
+
+ListArchiveJob::~ListArchiveJob() {
+ delete d;
+}
+
+QIODevice* ListArchiveJob::archive() const {
+ return d->archive;
+}
+
+void ListArchiveJob::setArchive( QIODevice* device ) {
+ d->archive = device;
+}
+
+QVector<File> ListArchiveJob::index() const {
+ return d->files;
+}
+
+class OpenArchiveInfo
+{
+private:
+ OpenArchiveInfo( QIODevice* device )
+ : codecs( new CCodecs ),
+ stream( new QIODeviceInStream( device ) )
+ {
+ throwIfNotOK( codecs->Load(), QObject::tr( "Could not load codecs" ) );
+
+ if( !codecs->FindFormatForArchiveType( L"", formatIndices ) )
+ throw SevenZipException( QObject::tr( "Could not retrieve default format" ) );
+
+ throwIfNotOK( archiveLink.Open2( codecs.get(), formatIndices, false, stream, UString(), 0 ), QObject::tr( "Could not open archive" ) );
+ if( archiveLink.Arcs.Size() == 0 )
+ throw SevenZipException( QObject::tr( "No CArc found" ) );
+
+ m_cleaner = new OpenArchiveInfoCleaner();
+ m_cleaner->moveToThread( device->thread() );
+ QObject::connect(device, SIGNAL(destroyed(QObject*)), m_cleaner, SLOT(deviceDestroyed(QObject*)));
+ }
+
+public:
+ ~OpenArchiveInfo()
+ {
+ m_cleaner->deleteLater();
+ }
+
+ static QMap< QIODevice*, OpenArchiveInfo* > instances;
+
+ static OpenArchiveInfo* instance( QIODevice* device )
+ {
+ if( instances[ device ] == 0 )
+ instances[ device ] = new OpenArchiveInfo( device );
+ return instances[ device ];
+ }
+
+ std::auto_ptr< CCodecs > codecs;
+ CIntVector formatIndices;
+ CArchiveLink archiveLink;
+ CMyComPtr< IInStream > stream;
+private:
+ OpenArchiveInfoCleaner *m_cleaner;
+};
+
+QMap< QIODevice*, OpenArchiveInfo* > OpenArchiveInfo::instances;
+
+void OpenArchiveInfoCleaner::deviceDestroyed(QObject* dev)
+{
+ QIODevice* device = static_cast<QIODevice*>(dev);
+ Q_ASSERT(device);
+ Q_ASSERT(OpenArchiveInfo::instances.contains(device));
+ delete OpenArchiveInfo::instances.take(device);
+}
+
+QVector<File> Lib7z::listArchive( QIODevice* archive ) {
+ assert( archive );
+ try {
+ const OpenArchiveInfo* const openArchive = OpenArchiveInfo::instance( archive );
+
+ QVector<File> flat;
+
+ for ( int i = 0; i < openArchive->archiveLink.Arcs.Size(); ++i ) {
+ const CArc& arc = openArchive->archiveLink.Arcs[i];
+ IInArchive* const arch = arc.Archive;
+
+ UInt32 numItems = 0;
+ throwIfNotOK( arch->GetNumberOfItems(&numItems), QObject::tr("Could not retrieve number of items in archive") );
+
+ flat.reserve( flat.size() + numItems );
+ for ( uint item = 0; item < numItems; ++item ) {
+ UString s;
+ throwIfNotOK( arc.GetItemPath( item, s ), QObject::tr("Could not retrieve path of archive item %1").arg( item ) );
+ File f;
+ f.archiveIndex.setX( i );
+ f.archiveIndex.setY( item );
+ f.path = UString2QString( s ).replace( QLatin1Char( '\\' ), QLatin1Char( '/' ) );
+ IsArchiveItemFolder( arch, item, f.isDirectory );
+ f.permissions = getPermissions( arch, item );
+ f.mtime = getDateTimeProperty( arch, item, kpidMTime, QDateTime() );
+ f.uncompressedSize = getUInt64Property( arch, item, kpidSize, 0 );
+ f.compressedSize = getUInt64Property( arch, item, kpidPackSize, 0 );
+ flat.push_back( f );
+ qApp->processEvents();
+ }
+ }
+ return flat;
+ } catch ( const SevenZipException& e ) {
+ throw e;
+ }
+ catch(const char *err) {
+ throw SevenZipException( err );
+ }
+ catch ( ... ) {
+ throw SevenZipException( QObject::tr("Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO)) );
+ }
+ return QVector<File>(); // never reached
+}
+
+void ListArchiveJob::doStart() {
+ try {
+ if ( !d->archive )
+ throw SevenZipException( tr("Could not list archive: QIODevice already destroyed") );
+ d->files = listArchive( d->archive );
+ } catch ( const SevenZipException& e ) {
+ setError( Failed );
+ setErrorString( e.message() );
+ } catch ( ... ) {
+ setError( Failed );
+ setErrorString( QObject::tr("Unknown exception caught (%1)").arg( QObject::tr( "Failed" )) );
+ }
+ emitResult();
+}
+
+class Lib7z::ExtractCallbackImpl : public IArchiveExtractCallback, public CMyUnknownImp {
+public:
+ MY_UNKNOWN_IMP
+ explicit ExtractCallbackImpl( ExtractCallback* qq )
+ : q( qq ),
+ currentIndex( 0 ),
+ arc( 0 ),
+ total( 0 ),
+ completed( 0 ),
+ device( 0 )
+ {
+ }
+
+ void setTarget( QIODevice* dev )
+ {
+ device = dev;
+ }
+
+ void setTarget( const QString& targetDirectory )
+ {
+ targetDir = targetDirectory;
+ }
+
+ // this method will be called by CFolderOutStream::OpenFile to stream via
+ // CDecoder::CodeSpec extracted content to an output stream.
+ /* reimp */ STDMETHOD(GetStream)( UInt32 index, ISequentialOutStream** outStream, Int32 askExtractMode )
+ {
+ Q_UNUSED( askExtractMode )
+ *outStream = 0;
+ if( device != 0 )
+ {
+ CMyComPtr<ISequentialOutStream> stream = new QIODeviceSequentialOutStream( device );
+ *outStream = stream.Detach();
+ return S_OK;
+ }
+ else if( !targetDir.isEmpty() )
+ {
+ assert( arc );
+
+ currentIndex = index;
+
+ UString s;
+ throwIfNotOK( arc->GetItemPath( index, s ), QObject::tr( "Could not retrieve path of archive item %1" ).arg( index ) );
+ const QString path = UString2QString( s ).replace( QLatin1Char( '\\' ), QLatin1Char( '/' ) );
+
+ const QFileInfo fi( QString::fromLatin1( "%1/%2" ).arg( targetDir, path ) );
+ DirectoryGuard guard( fi.absolutePath() );
+ const QStringList directories = guard.tryCreate();
+
+ bool isDir = false;
+ IsArchiveItemFolder( arc->Archive, index, isDir );
+ if( isDir )
+ {
+ QDir( fi.absolutePath() ).mkdir( fi.fileName() );
+ }
+
+ // this makes sure that all directories created get removed as well
+ for( QStringList::const_iterator it = directories.begin(); it != directories.end(); ++it )
+ q->setCurrentFile( *it );
+
+ if( !isDir && !q->prepareForFile( fi.absoluteFilePath() ) )
+ return E_FAIL;
+
+ q->setCurrentFile( fi.absoluteFilePath() );
+
+ if( !isDir )
+ {
+ CMyComPtr< ISequentialOutStream > stream = new QIODeviceSequentialOutStream( new QFile( fi.absoluteFilePath() ) );
+ *outStream = stream;
+ stream.Detach();
+ }
+
+ guard.release();
+ return S_OK;
+ }
+ return E_FAIL;
+ }
+
+ /* reimp */ STDMETHOD(PrepareOperation)( Int32 askExtractMode ) {
+ Q_UNUSED( askExtractMode )
+ return S_OK;
+ }
+
+ /* reimp */ STDMETHOD(SetOperationResult)( Int32 resultEOperationResult ) {
+ Q_UNUSED( resultEOperationResult )
+
+ if( !targetDir.isEmpty() )
+ {
+ bool hasPerm;
+ const QFile::Permissions permissions = getPermissions( arc->Archive, currentIndex, &hasPerm );
+ if( hasPerm )
+ {
+ UString s;
+ throwIfNotOK( arc->GetItemPath( currentIndex, s ), QObject::tr( "Could not retrieve path of archive item %1" ).arg( currentIndex ) );
+ const QString path = UString2QString( s ).replace( QLatin1Char( '\\' ), QLatin1Char( '/' ) );
+ const QFileInfo fi( QString::fromLatin1( "%1/%2" ).arg( targetDir, path ) );
+ QFile::setPermissions( fi.absoluteFilePath(), permissions );
+
+ // do we have a symlink?
+ const quint32 attributes = getUInt32Property( arc->Archive, currentIndex, kpidAttrib, 0 );
+ struct stat stat_info;
+ stat_info.st_mode = attributes >> 16;
+ if( S_ISLNK( stat_info.st_mode ) )
+ {
+ QFile f( fi.absoluteFilePath() );
+ f.open( QIODevice::ReadOnly );
+ const QByteArray path = f.readAll();
+ f.close();
+ f.remove();
+#ifdef Q_WS_WIN
+ if( !CreateHardLinkWrapper( fi.absoluteFilePath(), QLatin1String( path ) ) )
+ throw SevenZipException( QObject::tr( "Could not create file system lik at %1" ).arg( fi.absoluteFilePath() ) );
+#else
+ if( !QFile::link( QString::fromLatin1( path ), fi.absoluteFilePath() ) )
+ throw SevenZipException( QObject::tr( "Could not create softlink at %1" ).arg( fi.absoluteFilePath() ) );
+#endif
+ }
+ }
+ }
+
+ return S_OK;
+ }
+
+ /* reimp */ STDMETHOD(SetTotal)( UInt64 t ) {
+ total = t;
+ return S_OK;
+ }
+
+ /* reimp */ STDMETHOD(SetCompleted)( const UInt64* c ) {
+ completed = *c;
+ if ( total > 0 ) {
+ return q->setCompleted( completed, total );
+ }
+ return S_OK;
+ }
+
+ void setArchive( const CArc* archive )
+ {
+ arc = archive;
+ }
+
+private:
+ ExtractCallback* const q;
+ UInt32 currentIndex;
+ const CArc* arc;
+ UInt64 total;
+ UInt64 completed;
+ QPointer<QIODevice> device;
+ QString targetDir;
+};
+
+
+class Lib7z::ExtractCallbackPrivate {
+public:
+ explicit ExtractCallbackPrivate( ExtractCallback* qq ) {
+ impl = new ExtractCallbackImpl( qq );
+ }
+
+ CMyComPtr<ExtractCallbackImpl> impl;
+};
+
+ExtractCallback::ExtractCallback() : d( new ExtractCallbackPrivate( this ) ) {
+}
+
+ExtractCallback::~ExtractCallback() {
+ delete d;
+}
+
+ExtractCallbackImpl* ExtractCallback::impl() {
+ return d->impl;
+}
+
+const ExtractCallbackImpl* ExtractCallback::impl() const {
+ return d->impl;
+}
+
+void ExtractCallback::setTarget( QIODevice* dev )
+{
+ d->impl->setTarget( dev );
+}
+
+void ExtractCallback::setTarget( const QString& dir )
+{
+ d->impl->setTarget( dir );
+}
+
+HRESULT ExtractCallback::setCompleted( quint64, quint64 )
+{
+ return S_OK;
+}
+
+void ExtractCallback::setCurrentFile( const QString& )
+{
+}
+
+bool ExtractCallback::prepareForFile( const QString& )
+{
+ return true;
+}
+
+class Lib7z::ExtractCallbackJobImpl : public ExtractCallback {
+ public:
+ explicit ExtractCallbackJobImpl( ExtractItemJob* j ) : ExtractCallback(), job( j ) {}
+ private:
+ /* reimp */ HRESULT setCompleted( quint64 c, quint64 t ) {
+ emit job->progress( c, t );
+ return S_OK;
+ }
+
+ ExtractItemJob* const job;
+};
+
+class Lib7z::UpdateCallbackImpl : public IUpdateCallbackUI2, public CMyUnknownImp
+{
+public:
+ MY_UNKNOWN_IMP
+ explicit UpdateCallbackImpl( UpdateCallback* qq )
+ : q( qq )
+ {
+ }
+ virtual ~UpdateCallbackImpl()
+ {
+ }
+ /**
+ * \reimp
+ */
+ HRESULT SetTotal( UInt64 )
+ {
+ return S_OK;
+ }
+ /**
+ * \reimp
+ */
+ HRESULT SetCompleted( const UInt64* )
+ {
+ return S_OK;
+ }
+ HRESULT SetRatioInfo( const UInt64*, const UInt64* )
+ {
+ return S_OK;
+ }
+ HRESULT CheckBreak()
+ {
+ return S_OK;
+ }
+ HRESULT Finilize()
+ {
+ return S_OK;
+ }
+ HRESULT SetNumFiles( UInt64 )
+ {
+ return S_OK;
+ }
+ HRESULT GetStream( const wchar_t*, bool )
+ {
+ return S_OK;
+ }
+ HRESULT OpenFileError( const wchar_t*, DWORD )
+ {
+ return S_OK;
+ }
+ HRESULT CryptoGetTextPassword2( Int32* passwordIsDefined, OLECHAR** password )
+ {
+ *password = 0;
+ *passwordIsDefined = false;
+ return S_OK;
+ }
+ HRESULT CryptoGetTextPassword(OLECHAR**)
+ {
+ return E_NOTIMPL;
+ }
+ HRESULT OpenResult(const wchar_t*, LONG )
+ {
+ return S_OK;
+ }
+ HRESULT StartScanning()
+ {
+ return S_OK;
+ }
+ HRESULT ScanProgress(UInt64, UInt64, const wchar_t*)
+ {
+ return S_OK;
+ }
+ HRESULT CanNotFindError(const wchar_t*, DWORD)
+ {
+ return S_OK;
+ }
+ HRESULT FinishScanning()
+ {
+ return S_OK;
+ }
+ HRESULT StartArchive(const wchar_t*, bool)
+ {
+ return S_OK;
+ }
+ HRESULT FinishArchive()
+ {
+ return S_OK;
+ }
+
+ /**
+ * \reimp
+ */
+ HRESULT SetOperationResult( Int32 )
+ {
+ // TODO!
+ return S_OK;
+ }
+ void setSource( const QString& dir )
+ {
+ sourceDir = dir;
+ }
+ void setTarget( QIODevice* archive )
+ {
+ target = archive;
+ }
+
+private:
+ UpdateCallback* const q;
+
+ QIODevice* target;
+ QString sourceDir;
+};
+
+class Lib7z::UpdateCallbackPrivate
+{
+public:
+ explicit UpdateCallbackPrivate( UpdateCallback* qq )
+ {
+ m_impl = new UpdateCallbackImpl( qq );
+ }
+
+ UpdateCallbackImpl* impl()
+ {
+ return m_impl;
+ }
+
+private:
+ CMyComPtr< UpdateCallbackImpl > m_impl;
+};
+
+UpdateCallback::UpdateCallback()
+ : d( new UpdateCallbackPrivate( this ) )
+{
+}
+
+UpdateCallback::~UpdateCallback()
+{
+ delete d;
+}
+
+UpdateCallbackImpl* UpdateCallback::impl()
+{
+ return d->impl();
+}
+
+
+void UpdateCallback::setSource( const QString& dir )
+{
+ d->impl()->setSource( dir );
+}
+
+void UpdateCallback::setTarget( QIODevice* target )
+{
+ d->impl()->setTarget( target );
+}
+
+class ExtractItemJob::Private {
+public:
+ Private( ExtractItemJob* qq )
+ : q( qq),
+ target( 0 ),
+ callback( new ExtractCallbackJobImpl( q ) )
+ {
+ }
+
+ ~Private() {
+ }
+
+ ExtractItemJob* q;
+ File item;
+ QPointer<QIODevice> archive;
+ QString targetDirectory;
+ QIODevice* target;
+ ExtractCallback* callback;
+};
+
+ExtractItemJob::ExtractItemJob( QObject* parent ) : Job( parent ), d( new Private( this ) ) {
+}
+
+ExtractItemJob::~ExtractItemJob() {
+ delete d;
+}
+
+File ExtractItemJob::item() const {
+ return d->item;
+}
+
+void ExtractItemJob::setItem( const File& item ) {
+ d->item = item;
+}
+
+QIODevice* ExtractItemJob::archive() const {
+ return d->archive;
+}
+
+void ExtractItemJob::setArchive( QIODevice* archive ) {
+ d->archive = archive;
+}
+
+QString ExtractItemJob::targetDirectory() const {
+ return d->targetDirectory;
+}
+
+void ExtractItemJob::setTargetDirectory( const QString& dir ) {
+ d->targetDirectory = dir;
+ d->target = 0;
+}
+
+void ExtractItemJob::setTarget( QIODevice* dev )
+{
+ d->target = dev;
+}
+
+void Lib7z::createArchive( QIODevice* archive, const QString& sourceDirectory, UpdateCallback* callback )
+{
+ assert( archive );
+
+ std::auto_ptr< UpdateCallback > dummyCallback( callback ? 0 : new UpdateCallback );
+ if ( !callback )
+ callback = dummyCallback.get();
+
+ try
+ {
+ std::auto_ptr< CCodecs > codecs( new CCodecs );
+ throwIfNotOK( codecs->Load(), QObject::tr( "Could not load codecs" ) );
+
+ CIntVector formatIndices;
+
+ if( !codecs.get()->FindFormatForArchiveType( L"", formatIndices ) )
+ throw SevenZipException( QObject::tr( "Could not retrieve default format" ) );
+
+ // yes this is crap, but there seems to be no streaming solution to this...
+
+ const QString tempFile = generateTempFileName();
+
+ NWildcard::CCensor censor;
+ const UString sourceDirectoryPath = QString2UString( QDir::toNativeSeparators( sourceDirectory ) );
+ if( UString2QString( sourceDirectoryPath ) != QDir::toNativeSeparators( sourceDirectory ) )
+ throw UString2QString( sourceDirectoryPath ).toLatin1().data();
+ censor.AddItem( true, sourceDirectoryPath, true );
+
+ CUpdateOptions options;
+ CArchivePath archivePath;
+ archivePath.ParseFromPath( QString2UString( tempFile ) );
+ CUpdateArchiveCommand command;
+ command.ArchivePath = archivePath;
+ command.ActionSet = NUpdateArchive::kAddActionSet;
+ options.Commands.Add( command );
+ options.ArchivePath = archivePath;
+ options.MethodMode.FormatIndex = codecs->FindFormatForArchiveType( L"7z" );
+
+ CUpdateErrorInfo errorInfo;
+
+ callback->setTarget( archive );
+ callback->setSource( sourceDirectory );
+ const HRESULT res = UpdateArchive( codecs.get(), censor, options, errorInfo, 0, callback->impl() );
+ if( res != S_OK || !QFile::exists( tempFile ) )
+ throw SevenZipException( QObject::tr( "Could not create archive %1" ).arg( tempFile ) );
+ {
+ //TODO remove temp file even if one the following throws
+ QFile file( tempFile );
+ QInstaller::openForRead( &file, tempFile );
+ QInstaller::blockingCopy( &file, archive, file.size() );
+ }
+ QFile file( tempFile );
+ if ( !file.remove() )
+ qWarning("%s: Could not remove temporary file %s: %s", Q_FUNC_INFO, qPrintable(tempFile), qPrintable(file.errorString()) );
+ }
+ catch(const char *err)
+ {
+ std::cout << err << std::endl;
+ throw SevenZipException( err );
+ }
+ catch (const QInstaller::Error &err )
+ {
+ throw SevenZipException( err.message() );
+ }
+ catch( ... )
+ {
+ throw SevenZipException( QObject::tr("Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO)) );
+ }
+}
+
+void Lib7z::extractArchive( QIODevice* archive, const File& item, QIODevice* target, ExtractCallback* callback ) {
+ assert( archive );
+ assert( target );
+
+ std::auto_ptr<ExtractCallback> dummyCallback( callback ? 0 : new ExtractCallback );
+ if ( !callback )
+ callback = dummyCallback.get();
+
+ try {
+ const OpenArchiveInfo* const openArchive = OpenArchiveInfo::instance( archive );
+
+ const int arcIdx = item.archiveIndex.x();
+ if ( arcIdx < 0 || arcIdx >= openArchive->archiveLink.Arcs.Size() )
+ throw SevenZipException( QObject::tr("CArc index %1 out of bounds [0, %2]").arg( openArchive->archiveLink.Arcs.Size() - 1 ) );
+ const CArc& arc = openArchive->archiveLink.Arcs[arcIdx];
+ IInArchive* const parchive = arc.Archive;
+
+ const UInt32 itemIdx = item.archiveIndex.y();
+ UInt32 numItems = 0;
+ throwIfNotOK( parchive->GetNumberOfItems(&numItems), QObject::tr("Could not retrieve number of items in archive") );
+ if ( itemIdx >= numItems )
+ throw SevenZipException( QObject::tr("Item index %1 out of bounds [0, %2]").arg( itemIdx ).arg( numItems - 1 ) );
+
+ UString s;
+ throwIfNotOK( arc.GetItemPath( itemIdx, s ), QObject::tr( "Could not retrieve path of archive item %1" ).arg( itemIdx ) );
+ assert( item.path == UString2QString( s ).replace( QLatin1Char( '\\' ), QLatin1Char( '/' ) ) );
+
+ callback->setTarget( target );
+ const LONG extractResult = parchive->Extract( &itemIdx, 1, /*testmode=*/1, callback->impl() );
+ //TODO: how to interpret result?
+ throwIfNotOK( extractResult, QObject::tr("Extracting %1 failed.").arg( item.path ) );
+ } catch(const char *err) {
+ throw SevenZipException( err );
+ } catch ( ... ) {
+ throw SevenZipException( QObject::tr("Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO)) );
+ }
+}
+
+void Lib7z::extractArchive( QIODevice* archive, const File& item, const QString& targetDirectory, ExtractCallback* callback ) {
+ assert( archive );
+
+ std::auto_ptr<ExtractCallback> dummyCallback( callback ? 0 : new ExtractCallback );
+ if ( !callback )
+ callback = dummyCallback.get();
+
+ QFileInfo fi( targetDirectory + QLatin1String( "/" ) + item.path );
+ DirectoryGuard outDir( fi.absolutePath() );
+ outDir.tryCreate();
+ QFile out( fi.absoluteFilePath() );
+ if ( !out.open( QIODevice::WriteOnly ) ) //TODO use tmp file
+ throw SevenZipException( QObject::tr("Could not create output file for writing: %1").arg( fi.absoluteFilePath() ) );
+ if( item.permissions )
+ out.setPermissions( item.permissions );
+ callback->setTarget( &out );
+ extractArchive( archive, item, &out, callback );
+ outDir.release();
+}
+
+void Lib7z::extractArchive( QIODevice* archive, const QString& targetDirectory, ExtractCallback* callback )
+{
+ assert( archive );
+
+ std::auto_ptr<ExtractCallback> dummyCallback( callback ? 0 : new ExtractCallback );
+ if ( !callback )
+ callback = dummyCallback.get();
+
+ callback->setTarget( targetDirectory );
+
+ const QFileInfo fi( targetDirectory );
+ DirectoryGuard outDir( fi.absolutePath() );
+ outDir.tryCreate();
+
+ const OpenArchiveInfo* const openArchive = OpenArchiveInfo::instance( archive );
+
+ for( int a = 0; a < openArchive->archiveLink.Arcs.Size(); ++a )
+ {
+ const CArc& arc = openArchive->archiveLink.Arcs[ a ];
+ IInArchive* const arch = arc.Archive;
+ callback->impl()->setArchive( &arc );
+ const LONG extractResult = arch->Extract( 0, static_cast< UInt32 >( -1 ), false, callback->impl() );
+ throwIfNotOK( extractResult, QObject::tr("Extraction failed.") ); //TODO is it possible to get a more detailed error?
+ }
+
+ outDir.release();
+}
+
+bool Lib7z::isSupportedArchive( const QString& archive )
+{
+ QFile file( archive );
+ if( !file.open( QIODevice::ReadOnly ) )
+ return false;
+
+ return isSupportedArchive( &file );
+}
+
+bool Lib7z::isSupportedArchive( QIODevice* archive ) {
+ assert( archive );
+ assert( !archive->isSequential() );
+ const qint64 initialPos = archive->pos();
+ try {
+ std::auto_ptr<CCodecs> codecs( new CCodecs );
+ throwIfNotOK( codecs->Load(), QObject::tr("Could not load codecs") );
+
+ CIntVector formatIndices;
+
+ if ( !codecs.get()->FindFormatForArchiveType(L"", formatIndices) )
+ throw SevenZipException( QObject::tr("Could not retrieve default format") );
+
+ CArchiveLink archiveLink;
+ const CMyComPtr<IInStream> stream = new QIODeviceInStream( archive ); //CMyComPtr is needed, otherwise it crashes in OpenStream()
+
+ const HRESULT result = archiveLink.Open2(codecs.get(), formatIndices, /*stdInMode*/false, stream, UString(), 0);
+
+ archive->seek( initialPos );
+ return result == S_OK;
+ } catch ( const SevenZipException& e ) {
+ archive->seek( initialPos );
+ throw e;
+ } catch(const char *err) {
+ archive->seek( initialPos );
+ throw SevenZipException( err );
+ } catch( ... ) {
+ archive->seek( initialPos );
+ throw SevenZipException( QObject::tr("Unknown exception caught (%1)").arg(QString::fromLatin1(Q_FUNC_INFO)) );
+ }
+ return false; // never reached
+}
+
+void ExtractItemJob::doStart() {
+ try {
+ if ( !d->archive )
+ throw SevenZipException( tr("Could not list archive: QIODevice not set or already destroyed") );
+ if ( d->target )
+ extractArchive( d->archive, d->item, d->target, d->callback );
+ else if( !d->item.path.isEmpty() )
+ extractArchive( d->archive, d->item, d->targetDirectory, d->callback );
+ else
+ extractArchive( d->archive, d->targetDirectory, d->callback );
+ } catch ( const SevenZipException& e ) {
+ setError( Failed );
+ setErrorString( e.message() );
+ } catch ( ... ) {
+ setError( Failed );
+ setErrorString( QObject::tr("Unknown exception caught (%1)").arg( QObject::tr( "Failed" )) );
+ }
+ emitResult();
+}