Mercurial > trustbridge
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) |