mirror of
https://github.com/QuasarApp/installer-framework.git
synced 2025-05-20 08:49:34 +00:00
345 lines
12 KiB
C++
345 lines
12 KiB
C++
/**************************************************************************
|
|
**
|
|
** This file is part of Qt SDK**
|
|
**
|
|
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).*
|
|
**
|
|
** Contact: Nokia Corporation qt-info@nokia.com**
|
|
**
|
|
** No Commercial Usage
|
|
**
|
|
** This file contains pre-release code and may not be distributed.
|
|
** You may use this file in accordance with the terms and conditions
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
** this package.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
**
|
|
** This file may be used under the terms of the GNU Lesser General Public
|
|
** License version 2.1 as published by the Free Software Foundation and
|
|
** appearing in the file LICENSE.LGPL included in the packaging of this file.
|
|
** Please review the following information to ensure the GNU Lesser General
|
|
** Public License version 2.1 requirements will be met:
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception version
|
|
** 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** If you are unsure which license is appropriate for your use, please contact
|
|
** (qt-info@nokia.com).
|
|
**
|
|
**************************************************************************/
|
|
//#include "kdupdaterupdateoperations.h"
|
|
#include <KDUpdater/kdupdaterapplication.h>
|
|
#include <KDUpdater/kdupdaterpackagesinfo.h>
|
|
|
|
#include "createshortcutoperation.h"
|
|
#include "common/errors.h"
|
|
#include "common/fileutils.h"
|
|
#include "common/utils.h"
|
|
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QTemporaryFile>
|
|
|
|
#include <algorithm>
|
|
#include <cerrno>
|
|
|
|
#ifdef Q_WS_WIN
|
|
#include <windows.h>
|
|
#include <shlobj.h>
|
|
#endif
|
|
|
|
using namespace QInstaller;
|
|
|
|
static bool createLink( QString fileName, QString linkName, QString workingDir, QString arguments = QString() )
|
|
{
|
|
#ifdef Q_WS_WIN
|
|
bool ret = false;
|
|
fileName = QDir::toNativeSeparators(fileName);
|
|
linkName = QDir::toNativeSeparators(linkName);
|
|
if ( workingDir.isEmpty() )
|
|
workingDir = QFileInfo(fileName).absolutePath();
|
|
workingDir = QDir::toNativeSeparators(workingDir);
|
|
|
|
//### assume that they add .lnk
|
|
|
|
IShellLink *psl;
|
|
bool neededCoInit = false;
|
|
|
|
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
|
|
|
|
if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
|
|
neededCoInit = true;
|
|
CoInitialize(NULL);
|
|
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
|
|
}
|
|
|
|
if (SUCCEEDED(hres)) {
|
|
hres = psl->SetPath((wchar_t *)fileName.utf16());
|
|
if( SUCCEEDED( hres ) && !arguments.isNull() )
|
|
hres = psl->SetArguments((wchar_t*)arguments.utf16() );
|
|
if (SUCCEEDED(hres)) {
|
|
hres = psl->SetWorkingDirectory((wchar_t *)workingDir.utf16());
|
|
if (SUCCEEDED(hres)) {
|
|
IPersistFile *ppf;
|
|
hres = psl->QueryInterface(IID_IPersistFile, (void **)&ppf);
|
|
if (SUCCEEDED(hres)) {
|
|
hres = ppf->Save((wchar_t*)linkName.utf16(), TRUE);
|
|
if (SUCCEEDED(hres))
|
|
ret = true;
|
|
ppf->Release();
|
|
}
|
|
}
|
|
}
|
|
psl->Release();
|
|
}
|
|
|
|
if (neededCoInit)
|
|
CoUninitialize();
|
|
|
|
return ret;
|
|
#else
|
|
Q_UNUSED( workingDir )
|
|
Q_UNUSED( arguments )
|
|
return QFile::link( fileName, linkName );
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
TRANSLATOR QInstaller::CreateShortcutOperation
|
|
*/
|
|
|
|
CreateShortcutOperation::CreateShortcutOperation()
|
|
{
|
|
setName( QLatin1String( "CreateShortcut" ) );
|
|
}
|
|
|
|
CreateShortcutOperation::~CreateShortcutOperation()
|
|
{
|
|
deleteFileNowOrLater( value( QLatin1String( "backupOfExistingShortcut" ) ).toString() );
|
|
}
|
|
|
|
static bool isWorkingDirOption( const QString& s ) {
|
|
return s.startsWith( QLatin1String("workingDirectory=") );
|
|
}
|
|
|
|
static QString getWorkingDir( QStringList& args ) {
|
|
QString workingDir;
|
|
// if the args contain an option in the form "workingDirectory=...", find it and consume it
|
|
QStringList::iterator wdiropt = std::find_if( args.begin(), args.end(), isWorkingDirOption );
|
|
if ( wdiropt != args.end() ) {
|
|
workingDir = wdiropt->mid( QString::fromLatin1("workingDirectory=").size() );
|
|
args.erase( wdiropt );
|
|
}
|
|
return workingDir;
|
|
}
|
|
|
|
void CreateShortcutOperation::backup()
|
|
{
|
|
QStringList args = this->arguments();
|
|
getWorkingDir( args ); //consume workingDirectory= option
|
|
|
|
const QString path = QDir::fromNativeSeparators( QFileInfo( args.at( 1 ) ).absolutePath() );
|
|
|
|
//verbose() << "dir to create shortcut in " << path << std::endl;
|
|
|
|
QDir createdDir = QDir::root();
|
|
|
|
// find out, which part of the path is the first one we actually need to create
|
|
int end = 0;
|
|
QStringList directoriesToCreate;
|
|
while( true )
|
|
{
|
|
QString p = path.section( QLatin1String("/"), 0, ++end );
|
|
createdDir = QDir( p );
|
|
if( !createdDir.exists() )
|
|
{
|
|
directoriesToCreate.push_back( QDir::toNativeSeparators( createdDir.absolutePath() ) );
|
|
verbose() << " backup created dir_pre " << QDir::toNativeSeparators( createdDir.absolutePath() ) << std::endl;
|
|
if ( p == path)
|
|
break;
|
|
|
|
}
|
|
else if( p == path )
|
|
{
|
|
// everything did already exist -> nothing to do for us (nothing to revert then, either)
|
|
createdDir = QDir::root();
|
|
break;
|
|
}
|
|
}
|
|
verbose() << " backup created dir " << createdDir.absolutePath() << std::endl;
|
|
|
|
setValue( QLatin1String( "createddirs" ), directoriesToCreate );
|
|
|
|
//link creation context
|
|
const QString linkLocation = arguments()[ 1 ];
|
|
if( !QFile::exists( linkLocation ) )
|
|
return;
|
|
|
|
try {
|
|
setValue( QLatin1String( "backupOfExistingShortcut" ), generateTemporaryFileName( linkLocation ) );
|
|
} catch ( const QInstaller::Error& e ) {
|
|
setErrorString( e.message() );
|
|
return;
|
|
}
|
|
|
|
QFile f( linkLocation );
|
|
if( !f.copy( value( QLatin1String( "backupOfExistingShortcut" ) ).toString() ) )
|
|
setErrorString( QObject::tr( "Could not backup file %1: %2" ).arg( linkLocation, f.errorString() ) );
|
|
}
|
|
|
|
bool CreateShortcutOperation::performOperation()
|
|
{
|
|
QStringList args = this->arguments();
|
|
const QString workingDir = getWorkingDir( args );
|
|
|
|
if( args.count() != 2 && args.count() != 3 )
|
|
{
|
|
setError( InvalidArguments );
|
|
setErrorString( QObject::tr("Invalid arguments: %1 arguments given, 2 or 3 expected (optional: \"workingDirectory=...\").").arg( args.count() ) );
|
|
return false;
|
|
}
|
|
|
|
const QString& linkTarget = args.at( 0 );
|
|
const QString& linkLocation = args.at( 1 );
|
|
const QString targetArguments = args.count() == 3 ? args[ 2 ] : QString();
|
|
|
|
const QString dirName = QFileInfo( linkLocation ).absolutePath();
|
|
|
|
//verbose() << "dir to create shortcut in " << dirName << std::endl;
|
|
|
|
errno = 0;
|
|
|
|
const bool dirAlreadyExists = QDir( dirName ).exists();
|
|
const bool created = dirAlreadyExists || QDir::root().mkpath(dirName);
|
|
|
|
if ( !created ) {
|
|
setError( UserDefinedError );
|
|
setErrorString( tr("Could not create folder %1: %2.").arg( QDir::toNativeSeparators( dirName ), QLatin1String(strerror(errno)) ) );
|
|
return false;
|
|
}
|
|
|
|
TempDirDeleter deleter( dirName );
|
|
|
|
if( dirAlreadyExists )
|
|
deleter.releaseAll();
|
|
|
|
#if 0 // disabled for now, isDir() also returns true if the link exists and points to a folder, then removing it fails
|
|
// link creation
|
|
if ( QFileInfo( linkLocation ).isDir() ) {
|
|
errno = 0;
|
|
if ( !QDir().rmdir( linkLocation ) ) {
|
|
setError( UserDefinedError );
|
|
setErrorString( QObject::tr( "Could not create link: failed to remove folder %1: %2" ).arg( QDir::toNativeSeparators( linkLocation ), QLatin1String(strerror(errno)) ) );
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
QString err;
|
|
|
|
if( QFile::exists( linkLocation ) && !deleteFileNowOrLater( linkLocation, &err ) )
|
|
{
|
|
setError( UserDefinedError );
|
|
setErrorString( QObject::tr( "Failed to overwrite %1: %2" ).arg( QDir::toNativeSeparators( linkLocation ), err ) );
|
|
return false;
|
|
}
|
|
|
|
const bool linked = createLink( linkTarget, linkLocation, workingDir, targetArguments );
|
|
if ( !linked ) {
|
|
setError( UserDefinedError );
|
|
setErrorString( tr("Could not create link %1: %2").arg( QDir::toNativeSeparators( linkLocation ), qt_error_string() ) );
|
|
return false;
|
|
}
|
|
deleter.releaseAll();
|
|
return true;
|
|
}
|
|
|
|
bool CreateShortcutOperation::undoOperation()
|
|
{
|
|
const QString linkLocation = arguments()[ 1 ];
|
|
const QStringList args = this->arguments();
|
|
verbose() << " undo Shortcutoperation with arguments ";
|
|
Q_FOREACH( const QString& val, args )
|
|
verbose() << val << " ";
|
|
verbose() << std::endl;
|
|
|
|
// first remove the link
|
|
if( !deleteFileNowOrLater( linkLocation ) )
|
|
{
|
|
setErrorString( QObject::tr( "Could not delete file %1" ).arg( linkLocation ) );
|
|
return false;
|
|
}
|
|
verbose() << " link has been deleted " << std::endl;
|
|
|
|
if( hasValue( QLatin1String( "backupOfExistingShortcut" ) ) ){
|
|
|
|
const QString backupOfExistingShortcut = value( QLatin1String( "backupOfExistingShortcut" ) ).toString();
|
|
const bool success = QFile::copy( backupOfExistingShortcut, linkLocation ) && deleteFileNowOrLater( backupOfExistingShortcut );
|
|
if( !success ){
|
|
setErrorString( QObject::tr( "Could not restore backup file into %1" ).arg( linkLocation ) );
|
|
return success;
|
|
}
|
|
}
|
|
verbose() << " got behin backup " << std::endl;
|
|
|
|
const QStringList createdDirsPaths = value( QLatin1String( "createddirs" ) ).toStringList();
|
|
if ( createdDirsPaths.isEmpty() ) //no dir to delete (QDir( createdDirPath ) would return the current working directory -> never do that
|
|
return true;
|
|
|
|
const bool forceremoval = QVariant( value( QLatin1String( "forceremoval" ) ) ).toBool();
|
|
QListIterator<QString> it( createdDirsPaths );
|
|
for ( it.toBack(); it.hasPrevious(); )
|
|
{
|
|
const QString createdDirPath = it.previous();
|
|
const QDir createdDir = QDir( createdDirPath );
|
|
verbose() << createdDir.absolutePath() << std::endl;
|
|
if( createdDir == QDir::root() )
|
|
return true;
|
|
QString errorString;
|
|
if( forceremoval )
|
|
{
|
|
verbose() << " forced removal of path " << createdDir.path() << std::endl;
|
|
try{
|
|
removeDirectory( createdDir.path(), false );
|
|
}catch(const QInstaller::Error e){
|
|
setError( UserDefinedError, e.message() );
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// even remove some hidden, OS-created files in there
|
|
#if defined Q_WS_MAC
|
|
QFile::remove( createdDir.path() + QLatin1String( "/.DS_Store" ) );
|
|
#elif defined Q_WS_WIN
|
|
QFile::remove( createdDir.path() + QLatin1String( "/Thumbs.db" ) );
|
|
#endif
|
|
|
|
errno = 0;
|
|
verbose() << " removal of path " << createdDir.path() << std::endl;
|
|
const bool result = QDir::root().rmdir( createdDir.path() );
|
|
if ( !result ) {
|
|
if ( errorString.isEmpty() )
|
|
setError( UserDefinedError, tr("Cannot remove directory %1: %2").arg( createdDir.path(), errorString ) );
|
|
else
|
|
setError( UserDefinedError, tr("Cannot remove directory %1: %2").arg( createdDir.path(), QLatin1String(strerror(errno)) ) );
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CreateShortcutOperation::testOperation()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
CreateShortcutOperation* CreateShortcutOperation::clone() const
|
|
{
|
|
return new CreateShortcutOperation();
|
|
}
|