1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
31 **************************************************************************/
33 #include "s60createpackagestep.h"
35 #include "qt4projectmanagerconstants.h"
36 #include "qt4buildconfiguration.h"
38 #include "qt4project.h"
39 #include "s60createpackageparser.h"
40 #include "abldparser.h"
41 #include "sbsv2parser.h"
42 #include "passphraseforkeydialog.h"
43 #include "s60certificateinfo.h"
44 #include "s60certificatedetailsdialog.h"
45 #include "symbianqtversion.h"
47 #include <app/app_version.h>
49 #include <utils/checkablemessagebox.h>
50 #include <utils/fileutils.h>
52 #include <projectexplorer/buildconfiguration.h>
53 #include <projectexplorer/buildsteplist.h>
54 #include <projectexplorer/projectexplorerconstants.h>
55 #include <projectexplorer/target.h>
56 #include <projectexplorer/project.h>
57 #include <projectexplorer/gnumakeparser.h>
58 #include <projectexplorer/task.h>
60 #include <QtCore/QDir>
61 #include <QtCore/QTimer>
62 #include <QtCore/QCryptographicHash>
64 #include <QtCore/QSettings>
65 #include <QtGui/QMessageBox>
67 using namespace Qt4ProjectManager;
68 using namespace Qt4ProjectManager::Internal;
71 const char * const SIGN_BS_ID = "Qt4ProjectManager.S60SignBuildStep";
72 const char * const SIGNMODE_KEY("Qt4ProjectManager.S60CreatePackageStep.SignMode");
73 const char * const CERTIFICATE_KEY("Qt4ProjectManager.S60CreatePackageStep.Certificate");
74 const char * const KEYFILE_KEY("Qt4ProjectManager.S60CreatePackageStep.Keyfile");
75 const char * const SMART_INSTALLER_KEY("Qt4ProjectManager.S60CreatorPackageStep.SmartInstaller");
76 const char * const PATCH_WARNING_SHOWN_KEY("Qt4ProjectManager.S60CreatorPackageStep.PatchWarningShown");
77 const char * const SUPPRESS_PATCH_WARNING_DIALOG_KEY("Qt4ProjectManager.S60CreatorPackageStep.SuppressPatchWarningDialog");
79 const char * const MAKE_PASSPHRASE_ARGUMENT("QT_SIS_PASSPHRASE=");
80 const char * const MAKE_KEY_ARGUMENT("QT_SIS_KEY=");
81 const char * const MAKE_CERTIFICATE_ARGUMENT("QT_SIS_CERTIFICATE=");
84 S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildStepList *bsl) :
85 BuildStep(bsl, QLatin1String(SIGN_BS_ID)),
86 m_signingMode(SignSelf),
87 m_createSmartInstaller(false),
88 m_outputParserChain(0),
95 m_suppressPatchWarningDialog(false),
96 m_patchWarningDialog(0)
101 S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildStepList *bsl, S60CreatePackageStep *bs) :
103 m_signingMode(bs->m_signingMode),
104 m_customSignaturePath(bs->m_customSignaturePath),
105 m_customKeyPath(bs->m_customKeyPath),
106 m_passphrase(bs->m_passphrase),
107 m_createSmartInstaller(bs->m_createSmartInstaller),
108 m_outputParserChain(0),
111 m_futureInterface(0),
114 m_suppressPatchWarningDialog(false),
115 m_patchWarningDialog(0)
120 S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildStepList *bsl, const QString &id) :
122 m_signingMode(SignSelf),
123 m_createSmartInstaller(false),
124 m_outputParserChain(0),
127 m_futureInterface(0),
130 m_suppressPatchWarningDialog(false),
131 m_patchWarningDialog(0)
136 void S60CreatePackageStep::ctor_package()
138 //: default create SIS package build step display name
139 setDefaultDisplayName(tr("Create SIS Package"));
140 connect(this, SIGNAL(badPassphrase()),
141 this, SLOT(definePassphrase()), Qt::QueuedConnection);
142 connect(this, SIGNAL(warnAboutPatching()),
143 this, SLOT(handleWarnAboutPatching()), Qt::QueuedConnection);
145 m_passphrases = new QSettings(QSettings::IniFormat, QSettings::UserScope,
146 QLatin1String("Nokia"), QLatin1String("QtCreatorKeys"), this);
149 S60CreatePackageStep::~S60CreatePackageStep()
151 delete m_patchWarningDialog;
154 QVariantMap S60CreatePackageStep::toMap() const
156 QVariantMap map(BuildStep::toMap());
157 map.insert(QLatin1String(SIGNMODE_KEY), static_cast<int>(m_signingMode));
158 map.insert(QLatin1String(CERTIFICATE_KEY), m_customSignaturePath);
159 map.insert(QLatin1String(KEYFILE_KEY), m_customKeyPath);
160 map.insert(QLatin1String(SMART_INSTALLER_KEY), m_createSmartInstaller);
161 map.insert(QLatin1String(SUPPRESS_PATCH_WARNING_DIALOG_KEY), m_suppressPatchWarningDialog);
165 bool S60CreatePackageStep::fromMap(const QVariantMap &map)
167 m_signingMode = static_cast<SigningMode>(map.value(QLatin1String(SIGNMODE_KEY), static_cast<int>(SignSelf)).toInt());
168 m_customSignaturePath = map.value(QLatin1String(CERTIFICATE_KEY)).toString();
169 setCustomKeyPath(map.value(QLatin1String(KEYFILE_KEY)).toString());
170 m_createSmartInstaller = map.value(QLatin1String(SMART_INSTALLER_KEY), false).toBool();
171 m_suppressPatchWarningDialog = map.value(QLatin1String(SUPPRESS_PATCH_WARNING_DIALOG_KEY),
173 return BuildStep::fromMap(map);
176 Qt4BuildConfiguration *S60CreatePackageStep::qt4BuildConfiguration() const
178 return static_cast<Qt4BuildConfiguration *>(buildConfiguration());
181 bool S60CreatePackageStep::init()
183 Qt4Project *pro = qobject_cast<Qt4Project *>(buildConfiguration()->target()->project());
185 QList<Qt4ProFileNode *> nodes = pro->allProFiles();
187 SymbianQtVersion *sqv = dynamic_cast<SymbianQtVersion *>(qt4BuildConfiguration()->qtVersion());
190 m_isBuildWithSymbianSbsV2 = sqv->isBuildWithSymbianSbsV2();
192 m_workingDirectories.clear();
193 QStringList projectCapabilities;
194 foreach (Qt4ProFileNode *node, nodes) {
195 projectCapabilities += node->symbianCapabilities();
196 m_workingDirectories << node->buildDir();
198 projectCapabilities.removeDuplicates();
200 m_makeCmd = qt4BuildConfiguration()->makeCommand();
201 if (!QFileInfo(m_makeCmd).isAbsolute()) {
202 // Try to detect command in environment
203 const QString tmp = buildConfiguration()->environment().searchInPath(m_makeCmd);
205 emit addOutput(tr("Could not find make command '%1' in the build environment").arg(m_makeCmd), BuildStep::ErrorOutput);
211 if (signingMode() == SignCustom && !validateCustomSigningResources(projectCapabilities))
214 m_environment = qt4BuildConfiguration()->environment();
221 void S60CreatePackageStep::definePassphrase()
224 PassphraseForKeyDialog *passwordDialog
225 = new PassphraseForKeyDialog(QFileInfo(customKeyPath()).fileName());
226 if (passwordDialog->exec()) {
227 QString newPassphrase = passwordDialog->passphrase();
228 setPassphrase(newPassphrase);
229 if (passwordDialog->savePassphrase())
230 savePassphraseForKey(m_keyId, newPassphrase);
234 delete passwordDialog;
236 m_waitCondition.wakeAll();
239 void S60CreatePackageStep::packageWasPatched(const QString &package, const QStringList &changes)
241 m_packageChanges.append(qMakePair(package, changes));
244 void S60CreatePackageStep::handleWarnAboutPatching()
246 if (!m_suppressPatchWarningDialog && !m_packageChanges.isEmpty()) {
247 if (m_patchWarningDialog){
248 m_patchWarningDialog->raise();
252 m_patchWarningDialog = new Utils::CheckableMessageBox(0);
253 connect(m_patchWarningDialog, SIGNAL(finished(int)), this, SLOT(packageWarningDialogDone()));
257 const QString url = QString::fromLatin1("qthelp://com.nokia.qtcreator.%1%2%3/doc/creator-run-settings.html#capabilities-and-signing").
258 arg(IDE_VERSION_MAJOR).arg(IDE_VERSION_MINOR).arg(IDE_VERSION_RELEASE);
259 if (m_packageChanges.count() == 1) {
260 title = tr("Package Modified");
261 changedText = tr("<p>Qt modified your package <b>%1</b>.</p>").arg(m_packageChanges.at(0).first);
263 title = tr("Packages Modified");
264 changedText = tr("<p>Qt modified some of your packages.</p>");
267 tr("%1<p><em>These changes were not part of your build system</em> but are required to "
268 "make sure the <em>self-signed</em> package can be installed successfully on a device.</p>"
269 "<p>Check the Build Issues pane for more details on the modifications made.</p>"
270 "<p>Please see the <a href=\"%2\">documentation</a> for other signing options which "
271 "remove the need for this patching.</p>").arg(changedText, url);
272 m_patchWarningDialog->setWindowTitle(title);
273 m_patchWarningDialog->setText(text);
274 m_patchWarningDialog->setCheckBoxText(tr("Ignore patching for this packaging step."));
275 m_patchWarningDialog->setIconPixmap(QMessageBox::standardIcon(QMessageBox::Warning));
276 m_patchWarningDialog->setChecked(m_suppressPatchWarningDialog);
277 m_patchWarningDialog->setStandardButtons(QDialogButtonBox::Ok);
278 m_patchWarningDialog->open();
282 void S60CreatePackageStep::savePassphraseForKey(const QString &keyId, const QString &passphrase)
284 m_passphrases->beginGroup("keys");
285 if (passphrase.isEmpty())
286 m_passphrases->remove(keyId);
288 m_passphrases->setValue(keyId, obfuscatePassphrase(passphrase, keyId));
289 m_passphrases->endGroup();
292 QString S60CreatePackageStep::loadPassphraseForKey(const QString &keyId)
296 m_passphrases->beginGroup("keys");
297 QString passphrase = elucidatePassphrase(m_passphrases->value(keyId, QByteArray()).toByteArray(), keyId);
298 m_passphrases->endGroup();
302 QByteArray S60CreatePackageStep::obfuscatePassphrase(const QString &passphrase, const QString &key) const
304 QByteArray byteArray = passphrase.toUtf8();
305 char *data = byteArray.data();
306 const QChar *keyData = key.data();
307 int keyDataSize = key.size();
308 for (int i = 0; i <byteArray.size(); ++i)
309 data[i] = data[i]^keyData[i%keyDataSize].toAscii();
310 return byteArray.toBase64();
313 QString S60CreatePackageStep::elucidatePassphrase(QByteArray obfuscatedPassphrase, const QString &key) const
315 QByteArray byteArray = QByteArray::fromBase64(obfuscatedPassphrase);
316 if (byteArray.isEmpty())
319 char *data = byteArray.data();
320 const QChar *keyData = key.data();
321 int keyDataSize = key.size();
322 for (int i = 0; i < byteArray.size(); ++i)
323 data[i] = data[i]^keyData[i%keyDataSize].toAscii();
324 return QString::fromUtf8(byteArray.data());
327 void S60CreatePackageStep::run(QFutureInterface<bool> &fi)
329 if (m_workingDirectories.isEmpty()) {
330 fi.reportResult(true);
334 m_timer = new QTimer();
335 connect(m_timer, SIGNAL(timeout()), this, SLOT(checkForCancel()), Qt::DirectConnection);
337 m_eventLoop = new QEventLoop;
339 bool returnValue = false;
340 if (!createOnePackage()) {
341 fi.reportResult(false);
345 Q_ASSERT(!m_futureInterface);
346 m_futureInterface = &fi;
347 returnValue = m_eventLoop->exec();
359 m_futureInterface = 0;
362 emit warnAboutPatching();
363 fi.reportResult(returnValue);
366 bool S60CreatePackageStep::createOnePackage()
370 if (m_createSmartInstaller) {
371 if (signingMode() == NotSigned)
372 m_args << QLatin1String("unsigned_installer_sis");
374 m_args << QLatin1String("installer_sis");
375 } else if (signingMode() == NotSigned)
376 m_args << QLatin1String("unsigned_sis");
378 m_args << QLatin1String("sis");
380 if (signingMode() == SignCustom) {
381 m_args << QLatin1String(MAKE_CERTIFICATE_ARGUMENT) + QDir::toNativeSeparators(customSignaturePath())
382 << QLatin1String(MAKE_KEY_ARGUMENT) + QDir::toNativeSeparators(customKeyPath());
384 setPassphrase(loadPassphraseForKey(m_keyId));
386 if (!passphrase().isEmpty())
387 m_args << QLatin1String(MAKE_PASSPHRASE_ARGUMENT) + passphrase();
390 // Setup working directory:
391 QString workingDirectory = m_workingDirectories.first();
392 QDir wd(workingDirectory);
394 wd.mkpath(wd.absolutePath());
398 Q_ASSERT(!m_process);
399 m_process = new QProcess();
400 m_process->setEnvironment(m_environment.toStringList());
402 connect(m_process, SIGNAL(readyReadStandardOutput()),
403 this, SLOT(processReadyReadStdOutput()),
404 Qt::DirectConnection);
405 connect(m_process, SIGNAL(readyReadStandardError()),
406 this, SLOT(processReadyReadStdError()),
407 Qt::DirectConnection);
409 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
410 this, SLOT(packageDone(int, QProcess::ExitStatus)),
411 Qt::DirectConnection);
413 m_process->setWorkingDirectory(wd.absolutePath());
416 Q_ASSERT(!m_outputParserChain);
417 if (!m_isBuildWithSymbianSbsV2) {
418 m_outputParserChain = new Qt4ProjectManager::AbldParser;
419 m_outputParserChain->appendOutputParser(new ProjectExplorer::GnuMakeParser);
421 m_outputParserChain = new ProjectExplorer::GnuMakeParser();
424 m_parser = new S60CreatePackageParser(wd.absolutePath());
425 m_outputParserChain->appendOutputParser(m_parser);
426 m_outputParserChain->setWorkingDirectory(wd.absolutePath());
428 connect(m_outputParserChain, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)),
429 this, SIGNAL(addOutput(QString,ProjectExplorer::BuildStep::OutputFormat)));
430 connect(m_outputParserChain, SIGNAL(addTask(ProjectExplorer::Task)),
431 this, SIGNAL(addTask(ProjectExplorer::Task)), Qt::DirectConnection);
433 connect(m_parser, SIGNAL(packageWasPatched(QString,QStringList)),
434 this, SLOT(packageWasPatched(QString,QStringList)), Qt::DirectConnection);
437 m_process->start(m_makeCmd, m_args);
438 if (!m_process->waitForStarted()) {
439 emit addOutput(tr("Could not start process \"%1\" in %2")
440 .arg(QDir::toNativeSeparators(m_makeCmd),
442 BuildStep::ErrorMessageOutput);
445 emit addOutput(tr("Starting: \"%1\" %2 in %3\n")
446 .arg(QDir::toNativeSeparators(m_makeCmd),
449 BuildStep::MessageOutput);
453 bool S60CreatePackageStep::validateCustomSigningResources(const QStringList &capabilitiesInPro)
455 Q_ASSERT(signingMode() == SignCustom);
458 if (customSignaturePath().isEmpty())
459 errorString = tr("No certificate file specified. Please specify one in the project settings.");
460 else if (!QFileInfo(customSignaturePath()).exists())
461 errorString = tr("Certificate file \"%1\" does not exist. "
462 "Please specify an existing certificate file in the project settings.").arg(customSignaturePath());
464 if (customKeyPath().isEmpty())
465 errorString = tr("No key file specified. Please specify one in the project settings.");
466 else if (!QFileInfo(customKeyPath()).exists())
467 errorString = tr("Key file \"%1\" does not exist. "
468 "Please specify an existing key file in the project settings.").arg(customKeyPath());
470 if (!errorString.isEmpty()) {
471 reportPackageStepIssue(errorString, true);
474 QScopedPointer<S60CertificateInfo> certInfoPtr(new S60CertificateInfo(customSignaturePath()));
475 S60CertificateInfo::CertificateState certState = certInfoPtr.data()->validateCertificate();
477 case S60CertificateInfo::CertificateError:
478 reportPackageStepIssue(certInfoPtr.data()->errorString(), true);
480 case S60CertificateInfo::CertificateWarning:
481 reportPackageStepIssue(certInfoPtr.data()->errorString(), false);
487 QStringList unsupportedCaps;
488 if (certInfoPtr.data()->compareCapabilities(capabilitiesInPro, unsupportedCaps)) {
489 if (!unsupportedCaps.isEmpty()) {
490 QString message = tr("The package created will not install on a "
491 "device as some of the defined capabilities "
492 "are not supported by the certificate: %1")
493 .arg(unsupportedCaps.join(" "));
494 reportPackageStepIssue(message, true);
499 reportPackageStepIssue(certInfoPtr.data()->errorString(), false);
503 void S60CreatePackageStep::reportPackageStepIssue(const QString &message, bool isError )
505 emit addOutput(message, isError?
506 BuildStep::ErrorMessageOutput:
507 BuildStep::MessageOutput);
508 emit addTask(ProjectExplorer::Task(isError?
509 ProjectExplorer::Task::Error:
510 ProjectExplorer::Task::Warning,
513 ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
516 void S60CreatePackageStep::packageWarningDialogDone()
518 if (m_patchWarningDialog)
519 m_suppressPatchWarningDialog = m_patchWarningDialog->isChecked();
520 if (m_suppressPatchWarningDialog) {
521 m_patchWarningDialog->deleteLater();
522 m_patchWarningDialog = 0;
526 void S60CreatePackageStep::packageDone(int exitCode, QProcess::ExitStatus status)
528 QString line = QString::fromLocal8Bit(m_process->readAllStandardError());
532 line = QString::fromLocal8Bit(m_process->readAllStandardOutput());
536 if (status == QProcess::NormalExit && exitCode == 0) {
537 emit addOutput(tr("The process \"%1\" exited normally.")
538 .arg(QDir::toNativeSeparators(m_makeCmd)),
539 BuildStep::MessageOutput);
540 } else if (status == QProcess::NormalExit) {
541 emit addOutput(tr("The process \"%1\" exited with code %2.")
542 .arg(QDir::toNativeSeparators(m_makeCmd), QString::number(exitCode)),
543 BuildStep::ErrorMessageOutput);
545 emit addOutput(tr("The process \"%1\" crashed.").arg(QDir::toNativeSeparators(m_makeCmd)), BuildStep::ErrorMessageOutput);
548 bool needPassphrase = m_parser->needPassphrase();
551 delete m_outputParserChain;
552 m_outputParserChain = 0;
557 // Process next directories:
558 if (needPassphrase) {
559 emit badPassphrase();
560 QMutexLocker locker(&m_mutex);
561 m_waitCondition.wait(&m_mutex);
563 if (status != QProcess::NormalExit || exitCode != 0) {
564 m_eventLoop->exit(false);
568 m_workingDirectories.removeFirst();
569 if (m_workingDirectories.isEmpty()) {
570 m_eventLoop->exit(true);
575 if (m_cancel || !createOnePackage())
576 m_eventLoop->exit(false);
579 void S60CreatePackageStep::processReadyReadStdOutput()
581 m_process->setReadChannel(QProcess::StandardOutput);
582 while (m_process->canReadLine()) {
583 QString line = QString::fromLocal8Bit(m_process->readLine());
588 void S60CreatePackageStep::stdOutput(const QString &line)
590 if (m_outputParserChain)
591 m_outputParserChain->stdOutput(line);
592 emit addOutput(line, BuildStep::NormalOutput, BuildStep::DontAppendNewline);
595 void S60CreatePackageStep::processReadyReadStdError()
597 m_process->setReadChannel(QProcess::StandardError);
598 while (m_process->canReadLine()) {
599 QString line = QString::fromLocal8Bit(m_process->readLine());
604 void S60CreatePackageStep::stdError(const QString &line)
606 if (m_outputParserChain)
607 m_outputParserChain->stdError(line);
608 emit addOutput(line, BuildStep::ErrorOutput, BuildStep::DontAppendNewline);
611 void S60CreatePackageStep::checkForCancel()
613 if (m_futureInterface->isCanceled()
614 && m_timer && m_timer->isActive()) {
617 m_process->terminate();
618 m_process->waitForFinished(5000); //while waiting, the process can be killed
623 m_eventLoop->exit(false);
627 QString S60CreatePackageStep::generateKeyId(const QString &keyPath) const
629 if (keyPath.isEmpty())
632 Utils::FileReader reader;
633 if (!reader.fetch(keyPath, QIODevice::Text)) {
634 emit addOutput(reader.errorString(), BuildStep::ErrorOutput);
638 //key file is quite small in size
639 return QCryptographicHash::hash(reader.data(),
640 QCryptographicHash::Md5).toHex();
643 bool S60CreatePackageStep::immutable() const
648 ProjectExplorer::BuildStepConfigWidget *S60CreatePackageStep::createConfigWidget()
650 return new S60CreatePackageStepConfigWidget(this);
653 S60CreatePackageStep::SigningMode S60CreatePackageStep::signingMode() const
655 return m_signingMode;
658 void S60CreatePackageStep::setSigningMode(SigningMode mode)
660 m_signingMode = mode;
663 QString S60CreatePackageStep::customSignaturePath() const
665 return m_customSignaturePath;
668 void S60CreatePackageStep::setCustomSignaturePath(const QString &path)
670 m_customSignaturePath = path;
673 QString S60CreatePackageStep::customKeyPath() const
675 return m_customKeyPath;
678 void S60CreatePackageStep::setCustomKeyPath(const QString &path)
680 m_customKeyPath = path;
681 m_keyId = generateKeyId(m_customKeyPath);
684 QString S60CreatePackageStep::passphrase() const
689 void S60CreatePackageStep::setPassphrase(const QString &passphrase)
691 if (passphrase.isEmpty())
693 m_passphrase = passphrase;
696 QString S60CreatePackageStep::keyId() const
701 void S60CreatePackageStep::setKeyId(const QString &keyId)
706 bool S60CreatePackageStep::createsSmartInstaller() const
708 return m_createSmartInstaller;
711 void S60CreatePackageStep::setCreatesSmartInstaller(bool value)
713 m_createSmartInstaller = value;
714 static_cast<Qt4BuildConfiguration *>(buildConfiguration())->emitS60CreatesSmartInstallerChanged();
717 void S60CreatePackageStep::resetPassphrases()
719 m_passphrases->beginGroup("keys");
720 QStringList keys = m_passphrases->allKeys();
721 foreach (const QString &key, keys) {
722 m_passphrases->setValue(key, QString());
724 m_passphrases->remove(QString());
725 m_passphrases->endGroup();
728 // #pragma mark -- S60SignBuildStepFactory
730 S60CreatePackageStepFactory::S60CreatePackageStepFactory(QObject *parent) :
731 ProjectExplorer::IBuildStepFactory(parent)
735 S60CreatePackageStepFactory::~S60CreatePackageStepFactory()
739 bool S60CreatePackageStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const QString &id) const
741 if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY))
743 if (parent->target()->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
745 return (id == QLatin1String(SIGN_BS_ID));
748 ProjectExplorer::BuildStep *S60CreatePackageStepFactory::create(ProjectExplorer::BuildStepList *parent, const QString &id)
750 if (!canCreate(parent, id))
752 return new S60CreatePackageStep(parent);
755 bool S60CreatePackageStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source) const
757 return canCreate(parent, source->id());
760 ProjectExplorer::BuildStep *S60CreatePackageStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source)
762 if (!canClone(parent, source))
764 return new S60CreatePackageStep(parent, static_cast<S60CreatePackageStep *>(source));
767 bool S60CreatePackageStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const
769 QString id(ProjectExplorer::idFromMap(map));
770 return canCreate(parent, id);
773 ProjectExplorer::BuildStep *S60CreatePackageStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map)
775 if (!canRestore(parent, map))
777 S60CreatePackageStep *bs(new S60CreatePackageStep(parent));
778 if (bs->fromMap(map))
784 QStringList S60CreatePackageStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const
786 if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY))
787 return QStringList();
788 if (parent->target()->id() == QLatin1String(Constants::S60_DEVICE_TARGET_ID))
789 return QStringList() << QLatin1String(SIGN_BS_ID);
790 return QStringList();
793 QString S60CreatePackageStepFactory::displayNameForId(const QString &id) const
795 if (id == QLatin1String(SIGN_BS_ID))
796 return tr("Create SIS Package");
800 // #pragma mark -- S60SignBuildStepConfigWidget
802 S60CreatePackageStepConfigWidget::S60CreatePackageStepConfigWidget(S60CreatePackageStep *signStep)
803 : BuildStepConfigWidget(), m_signStep(signStep)
806 m_ui.signaturePath->setExpectedKind(Utils::PathChooser::File);
807 m_ui.signaturePath->setPromptDialogFilter(QLatin1String("*.cer *.crt *.der *.pem"));
808 m_ui.keyFilePath->setExpectedKind(Utils::PathChooser::File);
811 bool enableCertDetails = m_signStep->signingMode() == S60CreatePackageStep::SignCustom
812 && m_ui.signaturePath->isValid();
813 m_ui.certificateDetails->setEnabled(enableCertDetails);
815 connect(m_ui.certificateDetails, SIGNAL(clicked()),
816 this, SLOT(displayCertificateDetails()));
817 connect(m_ui.customCertificateButton, SIGNAL(clicked()),
818 this, SLOT(updateFromUi()));
819 connect(m_ui.selfSignedButton, SIGNAL(clicked()),
820 this, SLOT(updateFromUi()));
821 connect(m_ui.notSignedButton, SIGNAL(clicked()),
822 this, SLOT(updateFromUi()));
823 connect(m_ui.signaturePath, SIGNAL(changed(QString)),
824 this, SLOT(signatureChanged(QString)));
825 connect(m_ui.keyFilePath, SIGNAL(changed(QString)),
826 this, SLOT(updateFromUi()));
827 connect(m_ui.smartInstaller, SIGNAL(clicked()),
828 this, SLOT(updateFromUi()));
829 connect(m_ui.resetPassphrasesButton, SIGNAL(clicked()),
830 this, SLOT(resetPassphrases()));
833 void S60CreatePackageStepConfigWidget::signatureChanged(QString certFile)
835 m_ui.certificateDetails->setEnabled(m_ui.signaturePath->isValid());
837 if (!certFile.isEmpty() && m_ui.keyFilePath->path().isEmpty()) {
838 /* If a cert file is selected and there is not key file inserted,
839 then we check if there is a .key or .pem file in the folder with
840 the same base name as the cert file. This file is probably a key
841 file for this cert and the key field is then populated automatically
843 QFileInfo certFileInfo(certFile);
844 QDir directory = QDir(certFileInfo.absolutePath());
845 QString keyFile(certFileInfo.baseName() + QLatin1String(".key"));
846 QString pemFile(certFileInfo.baseName() + QLatin1String(".pem"));
849 keys << keyFile << pemFile;
850 files = directory.entryList(QStringList(keys),
851 QDir::Files | QDir::NoSymLinks);
854 m_ui.keyFilePath->setInitialBrowsePathBackup(certFileInfo.path());
856 m_ui.keyFilePath->setPath(directory.filePath(files[0]));
861 void S60CreatePackageStepConfigWidget::updateUi()
863 switch(m_signStep->signingMode()) {
864 case S60CreatePackageStep::SignCustom:
865 m_ui.selfSignedButton->setChecked(false);
866 m_ui.customCertificateButton->setChecked(true);
867 m_ui.notSignedButton->setChecked(false);
868 m_ui.certificateDetails->setEnabled(m_ui.signaturePath->isValid());
870 case S60CreatePackageStep::NotSigned:
871 m_ui.selfSignedButton->setChecked(false);
872 m_ui.customCertificateButton->setChecked(false);
873 m_ui.notSignedButton->setChecked(true);
874 m_ui.certificateDetails->setEnabled(false);
877 m_ui.selfSignedButton->setChecked(true);
878 m_ui.customCertificateButton->setChecked(false);
879 m_ui.notSignedButton->setChecked(false);
880 m_ui.certificateDetails->setEnabled(false);
883 bool customSigned = m_signStep->signingMode() == S60CreatePackageStep::SignCustom;
884 m_ui.signaturePath->setEnabled(customSigned);
885 m_ui.keyFilePath->setEnabled(customSigned);
886 m_ui.signaturePath->setPath(m_signStep->customSignaturePath());
887 m_ui.keyFilePath->setPath(m_signStep->customKeyPath());
888 m_ui.smartInstaller->setChecked(m_signStep->createsSmartInstaller());
889 emit updateSummary();
892 void S60CreatePackageStepConfigWidget::updateFromUi()
894 S60CreatePackageStep::SigningMode signingMode(S60CreatePackageStep::SignSelf);
895 if (m_ui.selfSignedButton->isChecked())
896 signingMode = S60CreatePackageStep::SignSelf;
897 else if (m_ui.customCertificateButton->isChecked())
898 signingMode = S60CreatePackageStep::SignCustom;
899 else if (m_ui.notSignedButton->isChecked())
900 signingMode = S60CreatePackageStep::NotSigned;
902 m_signStep->setSigningMode(signingMode);
903 m_signStep->setCustomSignaturePath(m_ui.signaturePath->path());
904 m_signStep->setCustomKeyPath(m_ui.keyFilePath->path());
905 m_signStep->setCreatesSmartInstaller(m_ui.smartInstaller->isChecked());
909 void S60CreatePackageStepConfigWidget::displayCertificateDetails()
911 S60CertificateInfo *certificateInformation = new S60CertificateInfo(m_ui.signaturePath->path());
912 certificateInformation->devicesSupported().sort();
914 S60CertificateDetailsDialog dialog;
915 dialog.setText(certificateInformation->toHtml(false));
917 delete certificateInformation;
920 void S60CreatePackageStepConfigWidget::resetPassphrases()
922 QMessageBox msgBox(QMessageBox::Question, tr("Reset Passphrases"),
923 tr("Do you want to reset all passphrases saved for keys used?"),
924 QMessageBox::Reset|QMessageBox::Cancel, this);
925 if (msgBox.exec() == QMessageBox::Reset)
926 m_signStep->resetPassphrases();
929 QString S60CreatePackageStepConfigWidget::summaryText() const
932 switch(m_signStep->signingMode()) {
933 case S60CreatePackageStep::SignCustom:
934 if (!m_signStep->customSignaturePath().isEmpty()
935 && !m_signStep->customKeyPath().isEmpty())
936 text = tr("signed with the certificate \"%1\" using the key \"%2\"")
937 .arg(QFileInfo(m_signStep->customSignaturePath()).fileName(),
938 QFileInfo(m_signStep->customKeyPath()).fileName());
940 text = tr("signed with a certificate and a key that need to be specified");
942 case S60CreatePackageStep::NotSigned:
943 text = tr("not signed");
946 text = tr("self-signed");
949 if (m_signStep->createsSmartInstaller())
950 return tr("<b>Create SIS Package:</b> %1, using Smart Installer").arg(text);
951 return tr("<b>Create SIS Package:</b> %1").arg(text);
954 QString S60CreatePackageStepConfigWidget::displayName() const
956 return m_signStep->displayName();