From 96cd834fbb3d14905758c730591e0372599aecfb Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 16 Nov 2010 11:23:11 +0100 Subject: [PATCH] Symbian: Warn when patching sis files Wharn when qmake patches sis files to make them compatible with self-signing. Task-number: QTCREATORBUG-2403 Reviewed-by: Pawel Polanski --- .../qt-s60/s60createpackageparser.cpp | 94 +++-- .../qt-s60/s60createpackageparser.h | 55 +-- .../qt-s60/s60createpackagestep.cpp | 413 ++++++++++++--------- .../qt-s60/s60createpackagestep.h | 36 +- 4 files changed, 361 insertions(+), 237 deletions(-) diff --git a/src/plugins/qt4projectmanager/qt-s60/s60createpackageparser.cpp b/src/plugins/qt4projectmanager/qt-s60/s60createpackageparser.cpp index b093042554..9daa10e3ca 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60createpackageparser.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60createpackageparser.cpp @@ -27,43 +27,91 @@ ** **************************************************************************/ - #include "s60createpackageparser.h" #include +#include -using namespace Qt4ProjectManager; -using namespace ProjectExplorer; -using namespace ProjectExplorer::Constants; +using namespace Qt4ProjectManager::Internal; -S60CreatePackageParser::S60CreatePackageParser() +S60CreatePackageParser::S60CreatePackageParser(const QString &packageName) : + m_packageName(packageName), + m_needPassphrase(false) { - m_signSis.setPattern("^(error):([A-Z\\d]+):(.+)$"); + setObjectName(QLatin1String("S60CreatePackageParser")); + m_signSis.setPattern("^(error:\\s?)+(.+)$"); m_signSis.setMinimal(true); } -void S60CreatePackageParser::stdOutput(const QString &line) +bool S60CreatePackageParser::parseLine(const QString &line) { - QString lne = line.trimmed(); - - if (m_signSis.indexIn(lne) > -1) { - QString errorDescription(m_signSis.cap(3)); - int index = errorDescription.indexOf(QLatin1String("error:")); - if (index >= 0) { - stdOutput(errorDescription.mid(index)); - errorDescription = errorDescription.left(index); - } - Task task(Task::Error, - errorDescription /* description */, - QString(), -1, - TASK_CATEGORY_BUILDSYSTEM); + if (line.startsWith("Patching: ")) { + m_patchingLines.append(line.mid(10).trimmed()); + return true; + } + if (!m_patchingLines.isEmpty()) { + emit packageWasPatched(m_packageName, m_patchingLines); + + QString lines = m_patchingLines.join("\n"); + m_patchingLines.clear(); + //: %1 package name, %2 will be replaced by a list of patching lines. + QString message = tr("The binary package '%1' was patched to be installable after being self-signed!\n%2\n" + "Use a developer certificate or any other signing option to prevent " + "this patching from happening."). + arg(m_packageName, lines); + ProjectExplorer::Task task(ProjectExplorer::Task::Warning, message, QString(), -1, + ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM); + + QTextLayout::FormatRange fr; + fr.start = message.indexOf(lines); + fr.length = lines.length(); + fr.format.setFontItalic(true); + task.formats.append(fr); + emit addTask(task); } - IOutputParser::stdOutput(line); + + if (m_signSis.indexIn(line) > -1) { + if (m_signSis.cap(2).contains(QLatin1String("bad password")) + || m_signSis.cap(2).contains(QLatin1String("bad decrypt"))) + m_needPassphrase = true; + else + emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error, m_signSis.cap(2), QString(), -1, + ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM)); + return true; + } + return false; +} + +void S60CreatePackageParser::stdOutput(const QString &line) +{ + if (!parseLine(line)) + IOutputParser::stdOutput(line); } void S60CreatePackageParser::stdError(const QString &line) { - stdOutput(line); - IOutputParser::stdError(line); + if (!parseLine(line)) + IOutputParser::stdError(line); +} + +bool S60CreatePackageParser::needPassphrase() const +{ + return m_needPassphrase; } + +/* STDOUT: +make[1]: Entering directory `C:/temp/test/untitled131' +createpackage.bat -g untitled131_template.pkg RELEASE-armv5 +Auto-patching capabilities for self signed package. + +Patching package file and relevant binaries... +Patching: Removed dependency to qt.sis (0x2001E61C) to avoid installation issues in case qt.sis is also patched. + + +NOTE: A patched package may not work as expected due to reduced capabilities and other modifications, + so it should not be used for any kind of Symbian signing or distribution! + Use a proper certificate to avoid the need to patch the package. + +Processing untitled131_release-armv5.pkg... +*/ diff --git a/src/plugins/qt4projectmanager/qt-s60/s60createpackageparser.h b/src/plugins/qt4projectmanager/qt-s60/s60createpackageparser.h index 0d13495140..6c050641fe 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60createpackageparser.h +++ b/src/plugins/qt4projectmanager/qt-s60/s60createpackageparser.h @@ -1,56 +1,41 @@ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** Commercial Usage -** -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. -** -** GNU Lesser General Public License Usage -** -** Alternatively, 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. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. -** -**************************************************************************/ - -#ifndef S60CREATEPACKAGEPARSER_H -#define S60CREATEPACKAGEPARSER_H +#ifndef SIGNSISPARSER_H +#define SIGNSISPARSER_H #include #include namespace Qt4ProjectManager { +namespace Internal { class S60CreatePackageParser : public ProjectExplorer::IOutputParser { Q_OBJECT public: - S60CreatePackageParser(); + S60CreatePackageParser(const QString &packageName); - virtual void stdOutput(const QString & line); - virtual void stdError(const QString & line); + virtual void stdOutput(const QString &line); + virtual void stdError(const QString &line); + + bool needPassphrase() const; + +signals: + void packageWasPatched(const QString &, const QStringList &pachingLines); private: + bool parseLine(const QString &line); + + const QString m_packageName; + QRegExp m_signSis; + QStringList m_patchingLines; + + bool m_needPassphrase; }; +} // namespace Internal } // namespace Qt4ProjectExplorer -#endif // S60CREATEPACKAGEPARSER_H +#endif // SIGNSISPARSER_H diff --git a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp index 17c6a3b2f7..8bfb76c7f4 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp +++ b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.cpp @@ -33,11 +33,15 @@ #include "qt4buildconfiguration.h" #include "qt4nodes.h" #include "qt4project.h" +#include "s60createpackageparser.h" #include "abldparser.h" #include "sbsv2parser.h" -#include "s60createpackageparser.h" #include "passphraseforkeydialog.h" +#include + +#include + #include #include #include @@ -61,6 +65,8 @@ namespace { const char * const CERTIFICATE_KEY("Qt4ProjectManager.S60CreatePackageStep.Certificate"); const char * const KEYFILE_KEY("Qt4ProjectManager.S60CreatePackageStep.Keyfile"); const char * const SMART_INSTALLER_KEY("Qt4ProjectManager.S60CreatorPackageStep.SmartInstaller"); + const char * const PATCH_WARNING_SHOWN_KEY("Qt4ProjectManager.S60CreatorPackageStep.PatchWarningShown"); + const char * const SUPPRESS_PATCH_WARNING_DIALOG_KEY("Qt4ProjectManager.S60CreatorPackageStep.SuppressPatchWarningDialog"); const char * const MAKE_PASSPHRASE_ARGUMENT("QT_SIS_PASSPHRASE="); const char * const MAKE_KEY_ARGUMENT("QT_SIS_KEY="); @@ -76,8 +82,10 @@ S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildStepList *bsl) m_timer(0), m_eventLoop(0), m_futureInterface(0), - m_errorType(ErrorNone), - m_settings(0) + m_passphrases(0), + m_parser(0), + m_suppressPatchWarningDialog(false), + m_patchWarningDialog(0) { ctor_package(); } @@ -93,8 +101,10 @@ S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildStepList *bsl, m_timer(0), m_eventLoop(0), m_futureInterface(0), - m_errorType(ErrorNone), - m_settings(0) + m_passphrases(0), + m_parser(0), + m_suppressPatchWarningDialog(false), + m_patchWarningDialog(0) { ctor_package(); } @@ -107,8 +117,10 @@ S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildStepList *bsl, m_timer(0), m_eventLoop(0), m_futureInterface(0), - m_errorType(ErrorNone), - m_settings(0) + m_passphrases(0), + m_parser(0), + m_suppressPatchWarningDialog(false), + m_patchWarningDialog(0) { ctor_package(); } @@ -119,22 +131,26 @@ void S60CreatePackageStep::ctor_package() setDefaultDisplayName(tr("Create SIS Package")); connect(this, SIGNAL(badPassphrase()), this, SLOT(definePassphrase()), Qt::QueuedConnection); + connect(this, SIGNAL(warnAboutPatching()), + this, SLOT(handleWarnAboutPatching()), Qt::QueuedConnection); - m_settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, + m_passphrases = new QSettings(QSettings::IniFormat, QSettings::UserScope, QLatin1String("Nokia"), QLatin1String("QtCreatorKeys"), this); } S60CreatePackageStep::~S60CreatePackageStep() { + delete m_patchWarningDialog; } QVariantMap S60CreatePackageStep::toMap() const { QVariantMap map(BuildStep::toMap()); - map.insert(QLatin1String(SIGNMODE_KEY), (int)m_signingMode); + map.insert(QLatin1String(SIGNMODE_KEY), static_cast(m_signingMode)); map.insert(QLatin1String(CERTIFICATE_KEY), m_customSignaturePath); map.insert(QLatin1String(KEYFILE_KEY), m_customKeyPath); map.insert(QLatin1String(SMART_INSTALLER_KEY), m_createSmartInstaller); + map.insert(QLatin1String(SUPPRESS_PATCH_WARNING_DIALOG_KEY), m_suppressPatchWarningDialog); return map; } @@ -144,6 +160,8 @@ bool S60CreatePackageStep::fromMap(const QVariantMap &map) m_customSignaturePath = map.value(QLatin1String(CERTIFICATE_KEY)).toString(); setCustomKeyPath(map.value(QLatin1String(KEYFILE_KEY)).toString()); m_createSmartInstaller = map.value(QLatin1String(SMART_INSTALLER_KEY), false).toBool(); + m_suppressPatchWarningDialog = map.value(QLatin1String(SUPPRESS_PATCH_WARNING_DIALOG_KEY), + false).toBool(); return BuildStep::fromMap(map); } @@ -175,75 +193,96 @@ bool S60CreatePackageStep::init() m_environment = qt4BuildConfiguration()->environment(); - m_args.clear(); - if (m_createSmartInstaller) { - if(signingMode() == NotSigned) - m_args << QLatin1String("unsigned_installer_sis"); - else - m_args << QLatin1String("installer_sis"); - } else if (signingMode() == NotSigned) - m_args << QLatin1String("unsigned_sis"); - else - m_args << QLatin1String("sis"); - - if (signingMode() == SignCustom) { - m_args << QLatin1String(MAKE_CERTIFICATE_ARGUMENT) + QDir::toNativeSeparators(customSignaturePath()) - << QLatin1String(MAKE_KEY_ARGUMENT) + QDir::toNativeSeparators(customKeyPath()); - - setPassphrase(loadPassphraseForKey(m_keyId)); - - if (!passphrase().isEmpty()) { - m_args << QLatin1String(MAKE_PASSPHRASE_ARGUMENT) + passphrase(); - } - } - - delete m_outputParserChain; - if (qt4BuildConfiguration()->qtVersion()->isBuildWithSymbianSbsV2()) - m_outputParserChain = new Qt4ProjectManager::SbsV2Parser; - else - m_outputParserChain = new Qt4ProjectManager::AbldParser; - m_outputParserChain->appendOutputParser(new ProjectExplorer::GnuMakeParser); - m_outputParserChain->appendOutputParser(new Qt4ProjectManager::S60CreatePackageParser); - - connect(m_outputParserChain, SIGNAL(addOutput(QString, ProjectExplorer::BuildStep::OutputFormat)), - this, SLOT(outputAdded(QString, ProjectExplorer::BuildStep::OutputFormat))); - connect(m_outputParserChain, SIGNAL(addTask(ProjectExplorer::Task)), - this, SLOT(taskAdded(ProjectExplorer::Task)), Qt::DirectConnection); + m_cancel = false; return true; } void S60CreatePackageStep::definePassphrase() { + Q_ASSERT(!m_cancel); PassphraseForKeyDialog *passwordDialog = new PassphraseForKeyDialog(QFileInfo(customKeyPath()).fileName()); if (passwordDialog->exec()) { - setPassphrase(passwordDialog->passphrase()); + QString newPassphrase = passwordDialog->passphrase(); + setPassphrase(newPassphrase); if (passwordDialog->savePassphrase()) - savePassphraseForKey(m_keyId, passphrase()); - } else - m_errorType = ErrorUndefined; + savePassphraseForKey(m_keyId, newPassphrase); + } else { + m_cancel = true; + } delete passwordDialog; - passwordDialog = 0; m_waitCondition.wakeAll(); } +void S60CreatePackageStep::packageWasPatched(const QString &package, const QStringList &changes) +{ + m_packageChanges.append(qMakePair(package, changes)); +} + +void S60CreatePackageStep::handleWarnAboutPatching() +{ + if (!m_suppressPatchWarningDialog && !m_packageChanges.isEmpty()) { + if (m_patchWarningDialog){ + m_patchWarningDialog->raise(); + return; + } + + m_patchWarningDialog = new Utils::CheckableMessageBox(0); + connect(m_patchWarningDialog, SIGNAL(finished(int)), this, SLOT(packageWarningDialogDone())); + + QString title; + QString text; + const QString &url = QString::fromLatin1("qthelp://com.nokia.qtcreator.%1%2%3/doc/creator-run-settings.html#capabilities-and-signing"). + arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR).arg(IDE_VERSION_RELEASE); + if (m_packageChanges.count() == 1) { + title = tr("A Package was modified"); + text = tr("

Qt modified your package %1.

" + "

These changes were not part of your build system but are required to " + "make sure the self-signed package can be installed successfully on a " + "device.

" + "

Check the Build Issues for more details on the modifications made.

" + "

Please see " + "the documentation for other signing options. These will prevent " + "this patching from happening.

").arg(m_packageChanges.at(0).first, url); + } else { + title = tr("Several Packages were modified"); + text = tr("

Qt modified some of your packages.

" + "

These changes were not part of your build system but are required to " + "make sure the self-signed packages can be installed successfully.

" + "

Check the Build Issues for more details on the modifications made.

" + "

Please see " + "the documentation for other signing options. These will prevent " + "this patching from happening.

").arg(url); + } + m_patchWarningDialog->setWindowTitle(title); + m_patchWarningDialog->setText(text); + m_patchWarningDialog->setCheckBoxText(tr("Ignore patching for this packaging step.")); + m_patchWarningDialog->setIconPixmap(QMessageBox::standardIcon(QMessageBox::Warning)); + m_patchWarningDialog->setChecked(m_suppressPatchWarningDialog); + m_patchWarningDialog->setStandardButtons(QDialogButtonBox::Ok); + m_patchWarningDialog->open(); + } +} + void S60CreatePackageStep::savePassphraseForKey(const QString &keyId, const QString &passphrase) { - m_settings->beginGroup("keys"); + m_passphrases->beginGroup("keys"); if (passphrase.isEmpty()) - m_settings->remove(keyId); + m_passphrases->remove(keyId); else - m_settings->setValue(keyId, obfuscatePassphrase(passphrase, keyId)); - m_settings->endGroup(); + m_passphrases->setValue(keyId, obfuscatePassphrase(passphrase, keyId)); + m_passphrases->endGroup(); } QString S60CreatePackageStep::loadPassphraseForKey(const QString &keyId) { - m_settings->beginGroup("keys"); - QString passphrase = elucidatePassphrase(m_settings->value(keyId, QByteArray()).toByteArray(), keyId); - m_settings->endGroup(); + if (keyId.isEmpty()) + return QString(); + m_passphrases->beginGroup("keys"); + QString passphrase = elucidatePassphrase(m_passphrases->value(keyId, QByteArray()).toByteArray(), keyId); + m_passphrases->endGroup(); return passphrase; } @@ -261,46 +300,38 @@ QByteArray S60CreatePackageStep::obfuscatePassphrase(const QString &passphrase, QString S60CreatePackageStep::elucidatePassphrase(QByteArray obfuscatedPassphrase, const QString &key) const { QByteArray byteArray = QByteArray::fromBase64(obfuscatedPassphrase); + if (byteArray.isEmpty()) + return QString(); + char *data = byteArray.data(); const QChar *keyData = key.data(); int keyDataSize = key.size(); for (int i = 0; i < byteArray.size(); ++i) data[i] = data[i]^keyData[i%keyDataSize].toAscii(); - return byteArray.data(); + return QString::fromUtf8(byteArray.data()); } void S60CreatePackageStep::run(QFutureInterface &fi) { - m_futureInterface = &fi; - if (m_workingDirectories.isEmpty()) { fi.reportResult(true); return; } - // Setup everything... - m_process = new QProcess(); - m_process->setEnvironment(m_environment.toStringList()); - - connect(m_process, SIGNAL(readyReadStandardOutput()), - this, SLOT(processReadyReadStdOutput()), - Qt::DirectConnection); - connect(m_process, SIGNAL(readyReadStandardError()), - this, SLOT(processReadyReadStdError()), - Qt::DirectConnection); - - connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), - this, SLOT(slotProcessFinished(int, QProcess::ExitStatus)), - Qt::DirectConnection); - m_timer = new QTimer(); connect(m_timer, SIGNAL(timeout()), this, SLOT(checkForCancel()), Qt::DirectConnection); m_timer->start(500); m_eventLoop = new QEventLoop; bool returnValue = false; - if (startProcess()) - returnValue = m_eventLoop->exec(); + if (!createOnePackage()) { + fi.reportResult(false); + return; + } + + Q_ASSERT(!m_futureInterface); + m_futureInterface = &fi; + returnValue = m_eventLoop->exec(); // Finished m_timer->stop(); @@ -311,81 +342,83 @@ void S60CreatePackageStep::run(QFutureInterface &fi) m_process = 0; delete m_eventLoop; m_eventLoop = 0; - fi.reportResult(returnValue); + m_futureInterface = 0; - return; + if (returnValue) + emit warnAboutPatching(); + fi.reportResult(returnValue); } -void S60CreatePackageStep::slotProcessFinished(int, QProcess::ExitStatus) +bool S60CreatePackageStep::createOnePackage() { - QString line = QString::fromLocal8Bit(m_process->readAllStandardError()); - if (!line.isEmpty()) - stdError(line); + // Setup everything... + Q_ASSERT(!m_process); + m_process = new QProcess(); + m_process->setEnvironment(m_environment.toStringList()); - line = QString::fromLocal8Bit(m_process->readAllStandardOutput()); - if (!line.isEmpty()) - stdOutput(line); + connect(m_process, SIGNAL(readyReadStandardOutput()), + this, SLOT(processReadyReadStdOutput()), + Qt::DirectConnection); + connect(m_process, SIGNAL(readyReadStandardError()), + this, SLOT(processReadyReadStdError()), + Qt::DirectConnection); - bool returnValue = false; - if (m_process->exitStatus() == QProcess::NormalExit && m_process->exitCode() == 0) { - emit addOutput(tr("The process \"%1\" exited normally.") - .arg(QDir::toNativeSeparators(m_makeCmd)), - BuildStep::MessageOutput); - returnValue = true; - } else if (m_process->exitStatus() == QProcess::NormalExit) { - emit addOutput(tr("The process \"%1\" exited with code %2.") - .arg(QDir::toNativeSeparators(m_makeCmd), QString::number(m_process->exitCode())), - BuildStep::ErrorMessageOutput); - } else { - emit addOutput(tr("The process \"%1\" crashed.").arg(QDir::toNativeSeparators(m_makeCmd)), BuildStep::ErrorMessageOutput); - } + connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(packageDone(int, QProcess::ExitStatus)), + Qt::DirectConnection); - switch (m_errorType) { - case ErrorUndefined: - m_eventLoop->exit(false); - return; - case ErrorBadPassphrase: { - emit badPassphrase(); - QMutexLocker locker(&m_mutex); - //waiting for the user to input new passphrase or to abort - m_waitCondition.wait(&m_mutex); - if( m_errorType == ErrorUndefined ) { - m_eventLoop->exit(false); - return; - } else { - QRegExp passphraseRegExp("^"+QLatin1String(MAKE_PASSPHRASE_ARGUMENT)+"(.+)$"); - int index = m_args.indexOf(passphraseRegExp); - if (index>=0) - m_args.removeAt(index); - if (!passphrase().isEmpty()) - m_args << QLatin1String(MAKE_PASSPHRASE_ARGUMENT) + passphrase(); - } - break; - } - default: - m_workingDirectories.removeFirst(); - break; - } + // Setup arguments: + m_args.clear(); + if (m_createSmartInstaller) { + if(signingMode() == NotSigned) + m_args << QLatin1String("unsigned_installer_sis"); + else + m_args << QLatin1String("installer_sis"); + } else if (signingMode() == NotSigned) + m_args << QLatin1String("unsigned_sis"); + else + m_args << QLatin1String("sis"); - if (m_workingDirectories.isEmpty() || !returnValue) { - m_eventLoop->exit(returnValue); - } else { - // Success, do next - if (!startProcess()) - m_eventLoop->exit(returnValue); + if (signingMode() == SignCustom + && !customSignaturePath().isEmpty() && QFileInfo(customSignaturePath()).exists() + && !customKeyPath().isEmpty() && QFileInfo(customKeyPath()).exists()) { + m_args << QLatin1String(MAKE_CERTIFICATE_ARGUMENT) + QDir::toNativeSeparators(customSignaturePath()) + << QLatin1String(MAKE_KEY_ARGUMENT) + QDir::toNativeSeparators(customKeyPath()); + + setPassphrase(loadPassphraseForKey(m_keyId)); + + if (!passphrase().isEmpty()) + m_args << QLatin1String(MAKE_PASSPHRASE_ARGUMENT) + passphrase(); } -} -bool S60CreatePackageStep::startProcess() -{ - m_errorType = ErrorNone; + // Setup working directory: QString workingDirectory = m_workingDirectories.first(); QDir wd(workingDirectory); if (!wd.exists()) wd.mkpath(wd.absolutePath()); - m_process->setWorkingDirectory(workingDirectory); + m_process->setWorkingDirectory(wd.absolutePath()); + + // Setup parsers: + Q_ASSERT(!m_outputParserChain); + if (!qt4BuildConfiguration()->qtVersion()->isBuildWithSymbianSbsV2()) { + m_outputParserChain = new Qt4ProjectManager::AbldParser; + m_outputParserChain->appendOutputParser(new ProjectExplorer::GnuMakeParser); + } else { + m_outputParserChain = new ProjectExplorer::GnuMakeParser(wd.absolutePath()); + } + Q_ASSERT(!m_parser); + m_parser = new S60CreatePackageParser(wd.absolutePath()); + m_outputParserChain->appendOutputParser(m_parser); + + connect(m_outputParserChain, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)), + this, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat))); + connect(m_outputParserChain, SIGNAL(addTask(ProjectExplorer::Task)), + this, SIGNAL(addTask(ProjectExplorer::Task)), Qt::DirectConnection); + + connect(m_parser, SIGNAL(packageWasPatched(QString,QStringList)), + this, SLOT(packageWasPatched(QString,QStringList)), Qt::DirectConnection); // Go for it! m_process->start(m_makeCmd, m_args); @@ -404,6 +437,69 @@ bool S60CreatePackageStep::startProcess() return true; } +void S60CreatePackageStep::packageWarningDialogDone() +{ + if (m_patchWarningDialog) + m_suppressPatchWarningDialog = m_patchWarningDialog->isChecked(); + if (m_suppressPatchWarningDialog) { + m_patchWarningDialog->deleteLater(); + m_patchWarningDialog = 0; + } +} + +void S60CreatePackageStep::packageDone(int exitCode, QProcess::ExitStatus status) +{ + QString line = QString::fromLocal8Bit(m_process->readAllStandardError()); + if (!line.isEmpty()) + stdError(line); + + line = QString::fromLocal8Bit(m_process->readAllStandardOutput()); + if (!line.isEmpty()) + stdOutput(line); + + if (status == QProcess::NormalExit && exitCode == 0) { + emit addOutput(tr("The process \"%1\" exited normally.") + .arg(QDir::toNativeSeparators(m_makeCmd)), + BuildStep::MessageOutput); + } else if (status == QProcess::NormalExit) { + emit addOutput(tr("The process \"%1\" exited with code %2.") + .arg(QDir::toNativeSeparators(m_makeCmd), QString::number(exitCode)), + BuildStep::ErrorMessageOutput); + } else { + emit addOutput(tr("The process \"%1\" crashed.").arg(QDir::toNativeSeparators(m_makeCmd)), BuildStep::ErrorMessageOutput); + } + + bool needPassphrase = m_parser->needPassphrase(); + + // Clean up: + delete m_outputParserChain; + m_outputParserChain = 0; + m_parser = 0; + delete m_process; + m_process = 0; + + // Process next directories: + if (needPassphrase) { + emit badPassphrase(); + QMutexLocker locker(&m_mutex); + m_waitCondition.wait(&m_mutex); + } else { + if (status != QProcess::NormalExit || exitCode != 0) { + m_eventLoop->exit(false); + return; + } + + m_workingDirectories.removeFirst(); + if (m_workingDirectories.isEmpty()) { + m_eventLoop->exit(true); + return; + } + } + + if (m_cancel || !createOnePackage()) + m_eventLoop->exit(false); +} + void S60CreatePackageStep::processReadyReadStdOutput() { m_process->setReadChannel(QProcess::StandardOutput); @@ -438,35 +534,21 @@ void S60CreatePackageStep::stdError(const QString &line) void S60CreatePackageStep::checkForCancel() { - if (m_futureInterface->isCanceled() && m_timer->isActive()) { + if (m_futureInterface->isCanceled() + && m_timer && m_timer->isActive()) { m_timer->stop(); - m_process->terminate(); - m_process->waitForFinished(5000); - m_process->kill(); - } -} - -void S60CreatePackageStep::taskAdded(const ProjectExplorer::Task &task) -{ - ProjectExplorer::Task editable(task); - QString filePath = QDir::cleanPath(task.file.trimmed()); - if (!filePath.isEmpty() && !QDir::isAbsolutePath(filePath)) { - // TODO which kind of tasks do we get from package building? - // No absoulte path - } - if (task.type == ProjectExplorer::Task::Error) { - if (task.description.contains(QLatin1String("bad password")) - || task.description.contains(QLatin1String("bad decrypt"))) - m_errorType = ErrorBadPassphrase; - else if (m_errorType == ErrorNone) - m_errorType = ErrorUndefined; + if (m_process) { + m_process->terminate(); + m_process->waitForFinished(5000); + m_process->kill(); + } + m_eventLoop->exit(false); } - emit addTask(editable); } QString S60CreatePackageStep::generateKeyId(const QString &keyPath) const { - if (keyPath.isEmpty()) + if (keyPath.isNull()) return QString(); QFile file(keyPath); @@ -478,11 +560,6 @@ QString S60CreatePackageStep::generateKeyId(const QString &keyPath) const QCryptographicHash::Md5).toHex(); } -void S60CreatePackageStep::outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format) -{ - emit addOutput(string, format); -} - bool S60CreatePackageStep::immutable() const { return false; @@ -531,6 +608,8 @@ QString S60CreatePackageStep::passphrase() const void S60CreatePackageStep::setPassphrase(const QString &passphrase) { + if (passphrase.isEmpty()) + return; m_passphrase = passphrase; } @@ -557,13 +636,13 @@ void S60CreatePackageStep::setCreatesSmartInstaller(bool value) void S60CreatePackageStep::resetPassphrases() { - m_settings->beginGroup("keys"); - QStringList keys = m_settings->allKeys(); + m_passphrases->beginGroup("keys"); + QStringList keys = m_passphrases->allKeys(); foreach (QString key, keys) { - m_settings->setValue(key, ""); + m_passphrases->setValue(key, ""); } - m_settings->remove(""); - m_settings->endGroup(); + m_passphrases->remove(""); + m_passphrases->endGroup(); } // #pragma mark -- S60SignBuildStepFactory diff --git a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.h b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.h index f0a5520327..136a9bb96e 100644 --- a/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.h +++ b/src/plugins/qt4projectmanager/qt-s60/s60createpackagestep.h @@ -42,9 +42,15 @@ QT_BEGIN_NAMESPACE class QSettings; QT_END_NAMESPACE +namespace Utils { +class CheckableMessageBox; +} // namespace Utils + namespace Qt4ProjectManager { namespace Internal { +class S60CreatePackageParser; + class S60CreatePackageStepFactory : public ProjectExplorer::IBuildStepFactory { Q_OBJECT @@ -71,6 +77,7 @@ class S60CreatePackageStep : public ProjectExplorer::BuildStep { Q_OBJECT friend class S60CreatePackageStepFactory; + public: enum SigningMode { SignSelf = 0, @@ -105,6 +112,7 @@ public: signals: void badPassphrase(); + void warnAboutPatching(); protected: S60CreatePackageStep(ProjectExplorer::BuildStepList *bsl, S60CreatePackageStep *bs); @@ -114,24 +122,22 @@ protected: Qt4BuildConfiguration *qt4BuildConfiguration() const; private slots: - void slotProcessFinished(int, QProcess::ExitStatus); + void packageWarningDialogDone(); + void packageDone(int, QProcess::ExitStatus); void processReadyReadStdOutput(); void processReadyReadStdError(); - void taskAdded(const ProjectExplorer::Task &task); - void outputAdded(const QString &string, ProjectExplorer::BuildStep::OutputFormat format); void checkForCancel(); void definePassphrase(); -private: - enum ErrorType { - ErrorNone = 0, - ErrorUndefined, - ErrorBadPassphrase - }; + void packageWasPatched(const QString &, const QStringList &); + void handleWarnAboutPatching(); +private: void stdOutput(const QString &line); void stdError(const QString &line); - bool startProcess(); + + void setupProcess(); + bool createOnePackage(); QString generateKeyId(const QString &keyPath) const; QString loadPassphraseForKey(const QString &keyId); @@ -159,12 +165,18 @@ private: QTimer *m_timer; QEventLoop *m_eventLoop; QFutureInterface *m_futureInterface; - ErrorType m_errorType; QWaitCondition m_waitCondition; QMutex m_mutex; - QSettings *m_settings; + bool m_cancel; + + QSettings *m_passphrases; + S60CreatePackageParser *m_parser; + QList > m_packageChanges; + + bool m_suppressPatchWarningDialog; + Utils::CheckableMessageBox *m_patchWarningDialog; }; class S60CreatePackageStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget -- 2.11.0