comparison ui/createinstallerdialog.cpp @ 571:6c4fff146999

Implement codesigning in the administrator tool
author Andre Heinecke <aheinecke@intevation.de>
date Fri, 23 May 2014 16:17:18 +0000
parents 421b69eeffe3
children 49168bcb02e2
comparison
equal deleted inserted replaced
570:c9d296f04995 571:6c4fff146999
17 #include <QFileDialog> 17 #include <QFileDialog>
18 #include <QSettings> 18 #include <QSettings>
19 #include <QStyle> 19 #include <QStyle>
20 #include <QApplication> 20 #include <QApplication>
21 #include <QMessageBox> 21 #include <QMessageBox>
22 #include <QTemporaryDir>
23
24 /* Static information used in codesigning */
25 #ifndef SIGN_HASH
26 #define SIGN_HASH "sha256"
27 #endif
28 #ifndef SIGN_URL
29 #define SIGN_URL "https://wald.intevation.org/projects/trustbridge/"
30 #endif
31 #ifndef SIGN_PUBLISHER
32 #define SIGN_PUBLISHER "TrustBridge Test with ümlaut"
33 #endif
34
22 35
23 CreateInstallerDialog::CreateInstallerDialog(QMainWindow *parent) : 36 CreateInstallerDialog::CreateInstallerDialog(QMainWindow *parent) :
24 QDialog(parent), 37 QDialog(parent),
25 mProgress(this) 38 mProgress(this),
39 mInstallerPath(),
40 mCurrentWorkingDir(NULL)
26 { 41 {
27 QSettings settings; 42 QSettings settings;
28 setWindowTitle(tr("Create binary installer")); 43 setWindowTitle(tr("Create binary installer"));
29 setupGUI(); 44 setupGUI();
30 resize(500, 250); 45 resize(500, 250);
114 topLayout->addLayout(bottomLayout); 129 topLayout->addLayout(bottomLayout);
115 130
116 setLayout(topLayout); 131 setLayout(topLayout);
117 132
118 mProgress.setWindowModality(Qt::WindowModal); 133 mProgress.setWindowModality(Qt::WindowModal);
119 mProgress.setLabelText(tr("Creating installer package..."));
120 mProgress.setCancelButton(0); 134 mProgress.setCancelButton(0);
121 mProgress.setRange(0,0); 135 mProgress.setRange(0,0);
122 mProgress.setMinimumDuration(0); 136 mProgress.setMinimumDuration(0);
123 137
124 return; 138 return;
160 QMessageBox::warning(this, tr("Error!"), msg); 174 QMessageBox::warning(this, tr("Error!"), msg);
161 } 175 }
162 176
163 void CreateInstallerDialog::processFinished(int exitCode, QProcess::ExitStatus exitStatus) 177 void CreateInstallerDialog::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
164 { 178 {
179 if (mCurrentWorkingDir) {
180 delete mCurrentWorkingDir;
181 mCurrentWorkingDir = NULL;
182 }
165 FinishedDialog *fin = new FinishedDialog(0, tr("Created installer in %1.") 183 FinishedDialog *fin = new FinishedDialog(0, tr("Created installer in %1.")
166 .arg(mSaveFile->text()), mNSISProc.readAll(), false); 184 .arg(mSaveFile->text()), mNSISProc.readAll(), false);
167 qDebug() << "Finished: " << mNSISProc.readAll(); 185 qDebug() << "Finished: " << mNSISProc.readAll();
186 mProgress.setLabelText(tr("Signing installer package..."));
187 if (!signFile(mInstallerPath)) {
188 showErrorMessage(tr("Failed to sign installer package."));
189 QFile::remove(mInstallerPath);
190 }
168 mProgress.cancel(); 191 mProgress.cancel();
169 fin->show(); 192 fin->show();
170 close(); 193 close();
171 } 194 }
172 195
173 void CreateInstallerDialog::processError(QProcess::ProcessError error) 196 void CreateInstallerDialog::processError(QProcess::ProcessError error)
174 { 197 {
198 if (mCurrentWorkingDir) {
199 delete mCurrentWorkingDir;
200 mCurrentWorkingDir = NULL;
201 }
175 qDebug() << "Error: " << mNSISProc.readAll(); 202 qDebug() << "Error: " << mNSISProc.readAll();
176 mProgress.cancel(); 203 mProgress.cancel();
177 } 204 }
178 205
179 void CreateInstallerDialog::createInstaller() 206 void CreateInstallerDialog::createInstaller()
180 { 207 {
208 mProgress.setLabelText(tr("Creating installer package..."));
181 QDir binDir(mBinaryFolder->text()); 209 QDir binDir(mBinaryFolder->text());
182 QDir outDir(mSaveFile->text()); 210 QDir outDir(mSaveFile->text());
183 if (mBinaryFolder->text().isEmpty() || !binDir.exists()) { 211 if (mBinaryFolder->text().isEmpty() || !binDir.exists()) {
184 showErrorMessage(tr("Please select an existing input folder.")); 212 showErrorMessage(tr("Please select an existing input folder."));
185 return; 213 return;
199 showErrorMessage(tr("Folder %1 does not appear to contain a meta.ini") 227 showErrorMessage(tr("Folder %1 does not appear to contain a meta.ini")
200 .arg(binDir.path())); 228 .arg(binDir.path()));
201 return; 229 return;
202 } 230 }
203 231
232 QTemporaryDir *signedFilesDir = codesignBinaries(binDir.path() + "/windows");
233
234 if (!signedFilesDir) {
235 /* Error messages should have been shown by the codesign function */
236 return;
237 }
238
239 mProgress.setLabelText(tr("Creating NSIS package..."));
240
204 /* Copy windows directory contents to tmpdir */ 241 /* Copy windows directory contents to tmpdir */
205 QStringList arguments; 242 QStringList arguments;
206 mNSISProc.setProgram("makensis"); 243 mNSISProc.setProgram("makensis");
207 mNSISProc.setProcessChannelMode(QProcess::MergedChannels); 244 mNSISProc.setProcessChannelMode(QProcess::MergedChannels);
208 mNSISProc.setWorkingDirectory(outDir.path()); 245 mNSISProc.setWorkingDirectory(outDir.path());
209 #ifdef Q_OS_WIN 246 #ifdef Q_OS_WIN
210 arguments << QString::fromLatin1("/Dfiles_dir=") + binDir.path().replace("/", "\\") + "\\windows"; 247 arguments << QString::fromLatin1("/Dfiles_dir=") + signedFilesDir->path().replace("/", "\\");
211 arguments << "/Dpath_sep=\\"; 248 arguments << "/Dpath_sep=\\";
212 foreach (const QString &key, keys) { 249 foreach (const QString &key, keys) {
213 QString value = options.value(key, QString()).toString(); 250 QString value = options.value(key, QString()).toString();
214 if (key == "setupname") { 251 if (key == "setupname") {
215 value = value.arg(outDir.path().replace("/", "\\") + "\\"); 252 value = value.arg(outDir.path().replace("/", "\\") + "\\");
253 mInstallerPath = value;
216 } 254 }
217 arguments << QString::fromLatin1("/D%1=%2").arg(key, value); 255 arguments << QString::fromLatin1("/D%1=%2").arg(key, value);
218 } 256 }
219 #else 257 #else
220 arguments << QString::fromLatin1("-Dfiles_dir=") + binDir.path() + "/windows"; 258 arguments << QString::fromLatin1("-Dfiles_dir=") + signedFilesDir->path();
221 arguments << "-Dpath_sep=/"; 259 arguments << "-Dpath_sep=/";
222 foreach (const QString &key, keys) { 260 foreach (const QString &key, keys) {
223 QString value = options.value(key, QString()).toString(); 261 QString value = options.value(key, QString()).toString();
224 if (key == "setupname") { 262 if (key == "setupname") {
225 value = value.arg(outDir.path() + "/"); 263 value = value.arg(outDir.path() + "/");
264 mInstallerPath = value;
226 } 265 }
227 arguments << QString::fromLatin1("-D%1=%2").arg(key, value); 266 arguments << QString::fromLatin1("-D%1=%2").arg(key, value);
228 } 267 }
229 #endif 268 #endif
230 269
238 if (!mNSISProc.waitForStarted() || 277 if (!mNSISProc.waitForStarted() ||
239 mNSISProc.state() == QProcess::NotRunning) { 278 mNSISProc.state() == QProcess::NotRunning) {
240 showErrorMessage(tr("Failed to start makensis.\n" 279 showErrorMessage(tr("Failed to start makensis.\n"
241 "Please ensure that makensis is installed and in your PATH variable.")); 280 "Please ensure that makensis is installed and in your PATH variable."));
242 } 281 }
282 }
283
284 bool CreateInstallerDialog::signFile(QString filePath) {
285 QProcess signProc;
286 signProc.setProcessChannelMode(QProcess::MergedChannels);
287 signProc.setProgram("osslsigncode");
288 QStringList arguments;
289
290 QSettings mySettings;
291
292 QString publisher = mySettings.value("sign_publisher", SIGN_PUBLISHER).toString();
293 QString url = mySettings.value("sign_url", SIGN_URL).toString();
294 QString hash = mySettings.value("sign_hash", SIGN_HASH).toString();
295
296 arguments << "sign" << "-certs" << mCertFile->text() << "-key"
297 << mCertFile->text() << "-n" << publisher << "-i" <<
298 url << "-h" << hash << "-in" << filePath << "-out" << filePath + ".signed";
299
300 qDebug() << "Starting osslsigncode with arguments: " << arguments;
301 signProc.setArguments(arguments);
302 signProc.start();
303
304 if (!signProc.waitForFinished(30000)) {
305 qDebug() << "Signing takes longer then 30 seconds. Aborting.";
306 return false;
307 }
308
309 if (signProc.exitStatus() != QProcess::NormalExit ||
310 signProc.exitCode() != 0) {
311 qDebug() << "Error process returned: " << signProc.exitCode();
312 qDebug() << "Output: " << signProc.readAllStandardOutput();
313 return false;
314 }
315
316 if (!QFile::remove(filePath)) {
317 qDebug() << "Failed to remove file.";
318 return false;
319 }
320 if (!QFile::copy(filePath + ".signed", filePath)) {
321 qDebug() << "Failed to copy signed file in place.";
322 return false;
323 }
324 if (!QFile::remove(filePath + ".signed")) {
325 qDebug() << "Failed to remove signed.";
326 return false;
327 }
328 return true;
329 }
330
331
332 QTemporaryDir *CreateInstallerDialog::codesignBinaries(const QDir& sourceDir) {
333 QTemporaryDir* target = new QTemporaryDir();
334 /* Copy all files from the source dir to a temporary location */
335 mProgress.setLabelText(tr("Signing binaries..."));
336
337 mProgress.show();
338 foreach (const QFileInfo& entry, sourceDir.entryInfoList()) {
339 QString targetPath = target->path() + QString::fromLatin1("/") + entry.fileName();
340 if (entry.fileName() == "." || entry.fileName() == "..") {
341 continue;
342 }
343 if (!QFile::copy(entry.absoluteFilePath(), targetPath)) {
344 qDebug() << "Failed to copy: " << entry.absoluteFilePath() << " to: " << targetPath;
345 showErrorMessage(tr("Failed to copy binaries to temporary location."));
346 mProgress.cancel();
347 return NULL;
348 }
349 if (entry.suffix() == "exe") {
350 if (!signFile(targetPath)) {
351 showErrorMessage(tr("Failed to sign binaries with osslsigncode.\n"
352 "Please check that %1 is a valid code signing certificate and that"
353 "osslsigncode can be found in the PATH.").arg(mCertFile->text()));
354 mProgress.cancel();
355 return NULL;
356 }
357 }
358 }
359 mProgress.cancel();
360 return target;
243 } 361 }
244 362
245 FinishedDialog::FinishedDialog(QDialog *parent, 363 FinishedDialog::FinishedDialog(QDialog *parent,
246 QString msg, QString details, bool isErr): 364 QString msg, QString details, bool isErr):
247 QDialog(parent) 365 QDialog(parent)

http://wald.intevation.org/projects/trustbridge/