diff --git a/isoimagewriter/maindialog.cpp b/isoimagewriter/maindialog.cpp index ce30714..ddf138f 100644 --- a/isoimagewriter/maindialog.cpp +++ b/isoimagewriter/maindialog.cpp @@ -1,571 +1,572 @@ /* * Copyright 2016 ROSA * Copyright 2017 Jonathan Riddell * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // Implementation of MainDialog #include #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "mainapplication.h" #include "maindialog.h" #include "ui_maindialog.h" #include "imagewriter.h" #include "usbdevice.h" #include "isoimagewriter_debug.h" #include "verifyneoniso.h" #include "verifynetrunneriso.h" #include "verifykubuntuiso.h" MainDialog::MainDialog(QWidget *parent) : QDialog(parent), ui(new Ui::MainDialog), m_ImageFile(""), m_ImageSize(0), m_LastOpenedDir(""), m_IsWriting(false), m_EnumFlashDevicesWaiting(false), m_ExtProgressBar(this) { #if defined(Q_OS_MAC) // Quick hack for OS X to avoid hiding our main dialog when inactive // The Qt::WA_MacAlwaysShowToolWindow attribute does not work (see QTBUG-29816) void disableHideOnDeactivate(WId wid); disableHideOnDeactivate(winId()); #endif ui->setupUi(this); #if defined(ROSA_BRANDING) // Compile with -DROSA_BRANDING=On to use the ROSA name setWindowTitle("ROSA Image Writer"); ui->logo->setPixmap(QPixmap(QStandardPaths::locate(QStandardPaths::AppDataLocation, "logo-rosa.png"))); #else ui->logo->setPixmap(QIcon::fromTheme("drive-removable-media").pixmap(QSize(50, 50))); #endif ui->introLabel->setText(i18n("Select an ISO image file to write to a USB disk")); ui->imageSelectButton->setIcon(QIcon::fromTheme("folder-open")); ui->deviceRefreshButton->setIcon(QIcon::fromTheme("view-refresh")); ui->verificationResultLabel->hide(); m_writeButton = ui->buttonBox->button(QDialogButtonBox::Yes); m_clearButton = ui->buttonBox->button(QDialogButtonBox::Reset); m_cancelButton = ui->buttonBox->button(QDialogButtonBox::Cancel); m_clearButton->setText(i18n("Clear USB Disk")); m_writeButton->setText(i18n("Write")); connect(m_writeButton, &QPushButton::clicked, this, &MainDialog::writeImageToDevice); m_clearButton->setText(i18n("Wipe USB Disk")); connect(m_clearButton, &QPushButton::clicked, this, &MainDialog::clearDevice); m_cancelButton->hide(); // Remove the Context Help button and add the Minimize button to the titlebar setWindowFlags((windowFlags() | Qt::CustomizeWindowHint | Qt::WindowMinimizeButtonHint) & ~Qt::WindowContextHelpButtonHint); // Start in the "idle" mode hideWritingProgress(); // Change default open dir m_LastOpenedDir = mApp->getInitialDir(); // Get path to ISO from command line (if supplied) QString newImageFile = mApp->getInitialImage(); if (!newImageFile.isEmpty()) { if (newImageFile.left(7) == "file://") newImageFile = QUrl(newImageFile).toLocalFile(); if (newImageFile != "") { newImageFile = QDir(newImageFile).absolutePath(); // Update the default open dir m_LastOpenedDir = newImageFile.left(newImageFile.lastIndexOf('/')); preprocessImageFile(newImageFile); } } // Load the list of USB flash disks enumFlashDevices(); // TODO: Increase the dialog display speed by showing it with the empty list and enumerating devices // in the background (dialog disabled, print "please wait") // TODO: Use dialog disabling also for manual refreshing the list } MainDialog::~MainDialog() { cleanup(); delete ui; } // Retrieves information about the selected file and displays it in the dialog void MainDialog::preprocessImageFile(const QString& newImageFile) { QFile f(newImageFile); if (!f.open(QIODevice::ReadOnly)) { QMessageBox::critical( this, ApplicationTitle, i18n("Failed to open the image file:") + "\n" + QDir::toNativeSeparators(newImageFile) + "\n" + f.errorString() ); return; } m_ImageSize = f.size(); f.close(); m_ImageFile = newImageFile; ui->imageEdit->setText(QDir::toNativeSeparators(m_ImageFile) + " " + i18n("(%1 MiB)", QString::number(alignNumberDiv(m_ImageSize, DEFAULT_UNIT)))); IsoResult isoResult = verifyISO(); if (isoResult.resultType == Invalid) { QMessageBox::critical(this, i18n("Invalid ISO"), i18n("ISO is invalid:

%1", isoResult.error)); return; } else if (isoResult.resultType == DinnaeKen) { QMessageBox::StandardButton warningResult = QMessageBox::warning(this, i18n("Could not Verify ISO"), i18n("%1

Do you want to continue", isoResult.error), QMessageBox::Yes|QMessageBox::No); if (warningResult == QMessageBox::No) { return; } } // Enable the Write button (if there are USB flash disks present) m_writeButton->setEnabled(ui->deviceList->count() > 0); } // Frees the GUI-specific allocated resources void MainDialog::cleanup() { // Delete all the formerly allocated UsbDevice objects attached to the combobox entries for (int i = 0; i < ui->deviceList->count(); ++i) { delete ui->deviceList->itemData(i).value(); } } // The reimplemented dragEnterEvent to inform which incoming drag&drop events are acceptable void MainDialog::dragEnterEvent(QDragEnterEvent* event) { // Accept only files with ANSI or Unicode paths (Windows) and URIs (Linux) if (event->mimeData()->hasFormat("application/x-qt-windows-mime;value=\"FileName\"") || event->mimeData()->hasFormat("application/x-qt-windows-mime;value=\"FileNameW\"") || event->mimeData()->hasFormat("text/uri-list")) event->accept(); } // The reimplemented dropEvent to process the dropped file void MainDialog::dropEvent(QDropEvent* event) { QString newImageFile = ""; QByteArray droppedFileName; // First, try to use the Unicode file name droppedFileName = event->mimeData()->data("application/x-qt-windows-mime;value=\"FileNameW\""); if (!droppedFileName.isEmpty()) { newImageFile = QString::fromWCharArray(reinterpret_cast(droppedFileName.constData())); } else { // If failed, use the ANSI name with the local codepage droppedFileName = event->mimeData()->data("application/x-qt-windows-mime;value=\"FileName\""); if (!droppedFileName.isEmpty()) { newImageFile = QString::fromLocal8Bit(droppedFileName.constData()); } else { // And, finally, try the URI droppedFileName = event->mimeData()->data("text/uri-list"); if (!droppedFileName.isEmpty()) { // If several files are dropped they are separated by newlines, // take the first file int newLineIndexLF = droppedFileName.indexOf('\n'); int newLineIndex = droppedFileName.indexOf("\r\n"); // Make sure both CRLF and LF are accepted if ((newLineIndexLF != -1) && (newLineIndexLF < newLineIndex)) newLineIndex = newLineIndexLF; if (newLineIndex != -1) droppedFileName = droppedFileName.left(newLineIndex); // Decode the file path from percent-encoding QUrl url = QUrl::fromEncoded(droppedFileName); if (url.isLocalFile()) newImageFile = url.toLocalFile(); } } } if (newImageFile != "") { // If something was realy received update the information preprocessImageFile(newImageFile); } } // The reimplemented keyPressEvent to display confirmation if user closes the dialog during operation void MainDialog::closeEvent(QCloseEvent* event) { if (m_IsWriting) { if (QMessageBox::question(this, ApplicationTitle, i18n("Writing is in progress, abort it?")) == QMessageBox::No) event->ignore(); } } // The reimplemented keyPressEvent to display confirmation if Esc is pressed during operation // (it normally closes the dialog but does not issue closeEvent for unknown reason) void MainDialog::keyPressEvent(QKeyEvent* event) { if ((event->key() == Qt::Key_Escape) && m_IsWriting) { if (QMessageBox::question(this, ApplicationTitle, i18n("Writing is in progress, abort it?")) == QMessageBox::No) return; } QDialog::keyPressEvent(event); } // Suggests to select image file using the Open File dialog void MainDialog::openImageFile() { QString newImageFile = QFileDialog::getOpenFileName(this, "", m_LastOpenedDir, i18n("Disk Images (%1)", QString("*.iso *.bin *.img")) + ";;" + i18n("All Files (%1)", QString("(*)")), NULL, QFileDialog::ReadOnly); if (newImageFile != "") { m_LastOpenedDir = newImageFile.left(newImageFile.lastIndexOf('/')); preprocessImageFile(newImageFile); } } // Schedules reloading the list of USB flash disks to run when possible void MainDialog::scheduleEnumFlashDevices() { if (m_IsWriting) m_EnumFlashDevicesWaiting = true; else enumFlashDevices(); } void addFlashDeviceCallback(void* cbParam, UsbDevice* device) { Ui::MainDialog* ui = (Ui::MainDialog*)cbParam; ui->deviceList->addItem(device->formatDisplayName(), QVariant::fromValue(device)); } // Reloads the list of USB flash disks void MainDialog::enumFlashDevices() { // Remember the currently selected device QString selectedDevice = ""; int idx = ui->deviceList->currentIndex(); if (idx >= 0) { UsbDevice* dev = ui->deviceList->itemData(idx).value(); selectedDevice = dev->m_PhysicalDevice; } // Remove the existing entries cleanup(); ui->deviceList->clear(); // Disable the combobox // TODO: Disable the whole dialog ui->deviceList->setEnabled(false); platformEnumFlashDevices(addFlashDeviceCallback, ui); // Restore the previously selected device (if present) if (selectedDevice != "") for (int i = 0; i < ui->deviceList->count(); ++i) { UsbDevice* dev = ui->deviceList->itemData(i).value(); if (dev->m_PhysicalDevice == selectedDevice) { ui->deviceList->setCurrentIndex(i); break; } } // Reenable the combobox ui->deviceList->setEnabled(true); // Update the Write button enabled/disabled state m_writeButton->setEnabled((ui->deviceList->count() > 0) && (m_ImageFile != "")); // Update the Clear button enabled/disabled state m_clearButton->setEnabled(ui->deviceList->count() > 0); } // TODO currently separate classes for each distro, should be made // much more generic to avoid overhead and repetition IsoResult MainDialog::verifyISO() { ui->verificationResultLabel->show(); ui->verificationResultLabel->setText(i18n("Running ISO verification, please wait...")); QCoreApplication::instance()->processEvents(); IsoResult result; VerifyNeonISO verifyNeon(m_ImageFile); if (verifyNeon.canVerify()) { if (verifyNeon.isValid()) { ui->verificationResultLabel->setText(i18n("Verified as valid KDE neon ISO")); result.resultType = Fine; result.error = i18n("Verified as valid KDE neon ISO"); return result; } else { QString error(i18n("Invalid KDE neon image")); ui->verificationResultLabel->show(); ui->verificationResultLabel->setText(verifyNeon.m_error); result.resultType = Invalid; result.error = verifyNeon.m_error; return result; } } VerifyNetrunnerISO verifyNetrunner(m_ImageFile); if (verifyNetrunner.canVerify()) { if (verifyNetrunner.isValid()) { ui->verificationResultLabel->setText(i18n("Verified as valid Netrunner ISO")); result.resultType = Fine; result.error = i18n("Verified as valid Netrunner ISO"); return result; } else { QString error(i18n("Invalid Netrunner image")); ui->verificationResultLabel->setText(verifyNetrunner.m_error); result.resultType = Invalid; result.error = verifyNetrunner.m_error; return result; } } VerifyKubuntuISO verifyKubuntu(m_ImageFile); if (verifyKubuntu.canVerify()) { if (verifyKubuntu.isValid()) { ui->verificationResultLabel->setText(i18n("Verified as valid Kubuntu ISO")); result.resultType = Fine; result.error = i18n("Verified as valid Kubuntu ISO"); return result; } else { QString error(i18n("Invalid Kubuntu image")); ui->verificationResultLabel->show(); ui->verificationResultLabel->setText(verifyKubuntu.m_error); result.resultType = Invalid; result.error = verifyKubuntu.m_error; return result; } } QString error(i18n("Could not verify as a known distro image.")); qDebug() << "verify error: " << error; ui->verificationResultLabel->setText(error); result.resultType = DinnaeKen; result.error = QString(i18n("Could not verify as a known distro image.")); return result; } // Starts writing data to the device void MainDialog::writeToDeviceKAuth(bool zeroing) { qCDebug(ISOIMAGEWRITER_LOG) << "writeToDeviceKAuth()"; if ((ui->deviceList->count() == 0) || (!zeroing && (m_ImageFile == ""))) return; UsbDevice* selectedDevice = ui->deviceList->itemData(ui->deviceList->currentIndex()).value(); if (!zeroing && (m_ImageSize > selectedDevice->m_Size)) { QLocale currentLocale; QMessageBox::critical( this, ApplicationTitle, i18n("The image is larger than your selected device!") + "\n" + i18n("Image size: %1MiB (%2b)", QString::number(m_ImageSize / DEFAULT_UNIT), currentLocale.toString(m_ImageSize)) + "\n" + i18n("Disk size: %1MiB (%2b)", QString::number(selectedDevice->m_Size / DEFAULT_UNIT), currentLocale.toString(selectedDevice->m_Size)), QMessageBox::Ok ); return; } QMessageBox wipeWarningBox; wipeWarningBox.setText(i18n("All existing data on the selected device will be lost.")); wipeWarningBox.setInformativeText(i18n("Are you sure you wish to proceed?")); wipeWarningBox.setIcon(QMessageBox::Warning); wipeWarningBox.addButton(QMessageBox::Ok); wipeWarningBox.addButton(QMessageBox::Cancel); wipeWarningBox.button(QMessageBox::Ok)->setText(i18n("Clear Disk and Write Image")); wipeWarningBox.exec(); if (wipeWarningBox.result() != QMessageBox::Ok) { return; } connect(m_cancelButton, &QPushButton::clicked, this, &MainDialog::cancelWriting); KAuth::Action action(QLatin1String("org.kde.isoimagewriter.writefile")); action.setHelperId("org.kde.isoimagewriter"); QVariantMap helperargs; //helperargs[QStringLiteral("filename")] = "bar"; helperargs[QStringLiteral("zeroing")] = QVariant(zeroing); helperargs[QStringLiteral("imagefile")] = m_ImageFile; helperargs[QStringLiteral("usbdevice_visiblename")] = selectedDevice->m_VisibleName; helperargs[QStringLiteral("usbdevice_volumes")] = selectedDevice->m_Volumes[0]; qCDebug(ISOIMAGEWRITER_LOG) << "volumes" << selectedDevice->m_Volumes[0]; qCDebug(ISOIMAGEWRITER_LOG) << "size" << selectedDevice->m_Size; qCDebug(ISOIMAGEWRITER_LOG) << "m_SectorSize" << selectedDevice->m_SectorSize; helperargs[QStringLiteral("usbdevice_size")] = QString("%1").arg(selectedDevice->m_Size); helperargs[QStringLiteral("usbdevice_sectorsize")] = selectedDevice->m_SectorSize; helperargs[QStringLiteral("usbdevice_physicaldevice")] = selectedDevice->m_PhysicalDevice; action.setArguments(helperargs); action.setTimeout(3600000); // an hour m_job = action.execute(); connect(m_job, SIGNAL(percent(KJob*, unsigned long)), this, SLOT(progressStep(KJob*, unsigned long)), Qt::DirectConnection); connect(m_job, SIGNAL(newData(const QVariantMap &)), this, SLOT(progressStep(const QVariantMap &))); connect(m_job, SIGNAL(statusChanged(KAuth::Action::AuthStatus)), this, SLOT(statusChanged(KAuth::Action::AuthStatus))); connect(m_job, SIGNAL(result(KJob*)), this, SLOT(finished(KJob*))); qCDebug(ISOIMAGEWRITER_LOG) << "runWriteImage start()"; m_job->start(); qCDebug(ISOIMAGEWRITER_LOG) << "action.isValid()? " << action.isValid(); showWritingProgress(alignNumberDiv((zeroing ? DEFAULT_UNIT : m_ImageSize), DEFAULT_UNIT)); } void MainDialog::cancelWriting() { qCDebug(ISOIMAGEWRITER_LOG) << "cancelWriting()"; m_job->kill(); qCDebug(ISOIMAGEWRITER_LOG) << "cancelWriting() done"; } void MainDialog::progressStep(KJob* job, unsigned long step) { + Q_UNUSED(job) qCDebug(ISOIMAGEWRITER_LOG) << "progressStep %() " << step; updateProgressBar(step); } void MainDialog::progressStep(const QVariantMap & data) { qCDebug(ISOIMAGEWRITER_LOG) << "progressStep(QVariantMap) ";// << step; if (data[QStringLiteral("progress")].isValid()) { int step = data[QStringLiteral("progress")].toInt(); updateProgressBar(step); } else if (data[QStringLiteral("error")].isValid()) { showErrorMessage(data[QStringLiteral("error")].toString()); } else if (data[QStringLiteral("success")].isValid()) { showSuccessMessage(data[QStringLiteral("success")].toString()); } } void MainDialog::statusChanged(KAuth::Action::AuthStatus status) { qCDebug(ISOIMAGEWRITER_LOG) << "statusChanged: " << status; } void MainDialog::finished(KJob* job) { qCDebug(ISOIMAGEWRITER_LOG) << "finished() " << job->error(); KAuth::ExecuteJob *job2 = (KAuth::ExecuteJob *)job; qCDebug(ISOIMAGEWRITER_LOG) << "finished() " << job2->data(); hideWritingProgress(); } // Starts writing the image void MainDialog::writeImageToDevice() { writeToDeviceKAuth(false); } // Clears the selected USB device void MainDialog::clearDevice() { writeToDeviceKAuth(true); } // Updates GUI to the "writing" mode (progress bar shown, controls disabled) // Also sets the progress bar limits void MainDialog::showWritingProgress(int maxValue) { m_IsWriting = true; // Do not accept dropped files while writing setAcceptDrops(false); // Disable the main interface ui->imageLabel->setEnabled(false); ui->imageEdit->setEnabled(false); ui->imageSelectButton->setEnabled(false); ui->deviceLabel->setEnabled(false); ui->deviceList->setEnabled(false); ui->deviceRefreshButton->setEnabled(false); // Display and customize the progress bar part ui->progressBar->setMinimum(0); ui->progressBar->setMaximum(maxValue); ui->progressBar->setValue(0); ui->progressBar->setVisible(true); ui->progressBarSpacer->changeSize(0, 10, QSizePolicy::Fixed, QSizePolicy::Fixed); m_writeButton->setVisible(false); m_clearButton->setVisible(false); m_cancelButton->setVisible(true); // Expose the progress bar state to the OS m_ExtProgressBar.InitProgressBar(maxValue); } // Updates GUI to the "idle" mode (progress bar hidden, controls enabled) void MainDialog::hideWritingProgress() { m_IsWriting = false; // Reenable drag&drop setAcceptDrops(true); // Enable the main interface ui->imageLabel->setEnabled(true); ui->imageEdit->setEnabled(true); ui->imageSelectButton->setEnabled(true); ui->deviceLabel->setEnabled(true); ui->deviceList->setEnabled(true); ui->deviceRefreshButton->setEnabled(true); // Hide the progress bar and verification label ui->verificationResultLabel->hide(); ui->progressBar->setVisible(false); ui->progressBarSpacer->changeSize(10, 10, QSizePolicy::Expanding, QSizePolicy::Fixed); m_writeButton->setVisible(true); m_clearButton->setVisible(true); m_cancelButton->setVisible(false); // Send a signal that progressbar is no longer present m_ExtProgressBar.DestroyProgressBar(); // If device list changed during writing update it now if (m_EnumFlashDevicesWaiting) enumFlashDevices(); } // Increments the progress bar counter by the specified number void MainDialog::updateProgressBar(int increment) { int newValue = ui->progressBar->value() + increment; ui->progressBar->setValue(newValue); m_ExtProgressBar.SetProgressValue(newValue); } // Displays the message about successful completion and returns to the "idle" mode void MainDialog::showSuccessMessage(QString msg) { QMessageBox::information( this, ApplicationTitle, msg ); hideWritingProgress(); } // Displays the specified error message and returns to the "idle" mode void MainDialog::showErrorMessage(QString msg) { m_ExtProgressBar.ProgressSetError(); QMessageBox::critical( this, ApplicationTitle, msg ); hideWritingProgress(); } diff --git a/isoimagewriter/verifyiso.cpp b/isoimagewriter/verifyiso.cpp index 00a4757..61d68bb 100644 --- a/isoimagewriter/verifyiso.cpp +++ b/isoimagewriter/verifyiso.cpp @@ -1,97 +1,109 @@ /* * * Copyright (C) 2017 Jonathan Riddell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include "verifyiso.h" #include #include #include #include #include VerifyISO::VerifyISO(QString filename): m_filename(filename) { } bool VerifyISO::verifyFileExists() { if (!QFile::exists(getFilename())) { m_error = i18n("ISO File %1 does not exist", getFilename()); return false; } return true; } +bool VerifyISO::verifySignatureFileExists(QString filename) { + qDebug() << "verifySignatureFileExists" << filename; + QFileInfo fileInfo(filename); + QString sigFileName = fileInfo.fileName(); + qDebug() << "verifySignatureFileExists sigFileName" << sigFileName; + if (!QFile::exists(filename)) { + m_error = i18n("Could not find %1, please download PGP signature file to same directory.", sigFileName); + return false; + } + return true; +} + bool VerifyISO::verifyFileMatches(QString startsWith) { QFileInfo fileInfo(getFilename()); QString fileName = fileInfo.fileName(); if (!fileName.startsWith(startsWith)) { m_error = i18n("Filename does not match %1 ISO files", m_humanReadableDistroName); return false; } return true; } bool VerifyISO::importSigningKey(QString keyFilename) { QString signingKeyFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, keyFilename); if (signingKeyFile.isEmpty()) { qDebug() << "error can't find signing key" << signingKeyFile; return false; } QFile signingKey(signingKeyFile); if (!signingKey.open(QIODevice::ReadOnly)) { qDebug() << "error" << signingKey.errorString(); return false; } QByteArray signingKeyData = signingKey.readAll(); QGpgME::ImportJob *importJob = QGpgME::openpgp()->importJob(); GpgME::ImportResult importResult = importJob->exec(signingKeyData); qDebug() << "numConsidered " << importResult.numConsidered(); qDebug() << "numImported " << importResult.numImported(); qDebug() << "numUnchanged " << importResult.numUnchanged(); if (!(importResult.numConsidered() == 1 && (importResult.numImported() == 1 || importResult.numUnchanged() == 1))) { qDebug() << "Could not import gpg signature"; return false; } return true; } QString VerifyISO::getFilename() const { return m_filename; } QString VerifyISO::getError() const { return m_error; } void VerifyISO::setFilename(const QString& filename) { if (m_filename == filename) { return; } m_filename = filename; } diff --git a/isoimagewriter/verifyiso.h b/isoimagewriter/verifyiso.h index 62a0371..dd12a9d 100644 --- a/isoimagewriter/verifyiso.h +++ b/isoimagewriter/verifyiso.h @@ -1,68 +1,69 @@ /* * * Copyright (C) 2017 Jonathan Riddell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef VERIFYISO_H #define VERIFYISO_H #include /** * @todo write docs */ class VerifyISO : public QObject { Q_OBJECT public: /** * Default constructor */ VerifyISO(QString filename); /** * @return the m_fileName */ QString getFilename() const; /** * @return the m_error string */ QString getError() const; bool verifyFileExists(); bool verifyFileMatches(QString startsWith); bool importSigningKey(QString keyFilename); + bool verifySignatureFileExists(QString filename); virtual bool canVerify() = 0; virtual bool isValid() = 0; QString m_error; public Q_SLOTS: /** * Sets the m_fileName. * * @param m_filename the new m_fileName */ void setFilename(const QString& filename); protected: QString m_filename; QString m_humanReadableDistroName; }; #endif // VERIFYISO_H diff --git a/isoimagewriter/verifykubuntuiso.cpp b/isoimagewriter/verifykubuntuiso.cpp index b01c818..5943755 100644 --- a/isoimagewriter/verifykubuntuiso.cpp +++ b/isoimagewriter/verifykubuntuiso.cpp @@ -1,131 +1,138 @@ /* * * Copyright (C) 2017 Jonathan Riddell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "verifykubuntuiso.h" VerifyKubuntuISO::VerifyKubuntuISO(QString filename) : VerifyISO(filename) { m_humanReadableDistroName = "Kubuntu"; } bool VerifyKubuntuISO::canVerify() { if (!verifyFileMatches("kubuntu-")) { return false; } if (!importSigningKey("ubuntu-signing-key.gpg")) { return false; } return true; } bool VerifyKubuntuISO::isValid() { if (!verifyFileExists()) { return false; } qDebug() << "m_filename " << m_filename; QFileInfo fi(m_filename); QString fileNameChecksums = fi.absolutePath() + "/SHA256SUMS"; QString fileNameChecksumsSig = fi.absolutePath() + "/SHA256SUMS.gpg"; + if (!verifySignatureFileExists(fileNameChecksums)) { + return false; + } + if (!verifySignatureFileExists(fileNameChecksumsSig)) { + return false; + } + qDebug() << "fileNameChecksums " << fileNameChecksums; QFile fileChecksums(fileNameChecksums); if (!fileChecksums.open(QIODevice::ReadOnly | QIODevice::Text)) { m_error = i18n("Could not open SHA256SUMS file, please download to same directory"); return false; } QCryptographicHash hash(QCryptographicHash::Sha256); QFile iso(m_filename); if (!iso.open(QIODevice::ReadOnly)) { m_error = i18n("Could not read file"); } // slow, threadify me if (!hash.addData(&iso)) { m_error = i18n("Could not perform checksum"); return false; } QByteArray hashResult = hash.result(); QString checksum; qDebug() << "regex " << "([\\dabcdef]+) \*"+fi.fileName(); //QRegExp rx("([\\dabcdef]+) \*"+fi.fileName()); QRegExp rx("([abcdef\\d]+).."+fi.fileName()); QByteArray checksumData = fileChecksums.readAll(); qDebug() << "checksumData " << checksumData; int pos = rx.indexIn(QString(checksumData)); if (pos > -1) { checksum = rx.cap(1); } else { m_error = i18n("Could not find checksum in SHA256SUMS file"); return false; } qDebug() << "checksum: " << checksum; if (checksum != hashResult.toHex()) { m_error = i18n("Checksum of .iso file does not match value in SHA256SUMS file"); return false; } // check gpg signature QStringList splits = m_filename.split('/'); QString fileName = splits[splits.size()-1]; if (!QFile::exists(fileNameChecksumsSig)) { qDebug() << "does not exist SHA256SUMS.gpg" << "SHA256SUMS.gpg"; m_error = i18n("Could not find SHA256SUMS.gpg, please download PGP signature file to same directory."); return false; } QFile signatureFile(fileNameChecksumsSig); if (!signatureFile.open(QIODevice::ReadOnly)) { qDebug() << "error",signatureFile.errorString(); } QByteArray signatureData = signatureFile.readAll(); QByteArray fileChecksumsData = fileChecksums.readAll(); QGpgME::VerifyDetachedJob *job = QGpgME::openpgp()->verifyDetachedJob(); GpgME::VerificationResult result = job->exec(signatureData, fileChecksumsData); qDebug() << "numSignatures " << result.numSignatures(); qDebug() << "filename " << result.fileName(); GpgME::Signature signature = result.signature(0); qDebug() << "fingerprint " << signature.fingerprint(); if (strcmp(signature.fingerprint(), "46181433FBB75451") == 0) { qDebug() << "Uses right signature!" << signature.fingerprint(); } else { qDebug() << "Uses wrong signature!!" << signature.fingerprint(); m_error = i18n("Uses wrong signature."); return false; } if (signature.summary() & GpgME::Signature::KeyRevoked) { qDebug() << "Key is revoked" << signature.summary(); m_error = i18n("Key is revoked."); return false; } return true; } diff --git a/isoimagewriter/verifyneoniso.cpp b/isoimagewriter/verifyneoniso.cpp index ed26617..cda487c 100644 --- a/isoimagewriter/verifyneoniso.cpp +++ b/isoimagewriter/verifyneoniso.cpp @@ -1,89 +1,85 @@ /* * * Copyright (C) 2017 Jonathan Riddell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #include #include #include #include "verifyneoniso.h" VerifyNeonISO::VerifyNeonISO(QString filename) : VerifyISO(filename) { m_humanReadableDistroName = "KDE neon"; } bool VerifyNeonISO::canVerify() { if (!verifyFileMatches("neon-")) { return false; } if (!importSigningKey("neon-signing-key.gpg")) { return false; } return true; } bool VerifyNeonISO::isValid() { if (!verifyFileExists()) { return false; } - QStringList splits = m_filename.split('/'); - QString fileName = splits[splits.size()-1]; - if (!QFile::exists(m_filename+".sig")) { - qDebug() << "does not exist .sig" << fileName+".sig"; - m_error = i18n("Could not find %1, please download PGP signature file to same directory.", fileName+".sig"); + if (!verifySignatureFileExists(m_filename+".sig")) { return false; } QFile signatureFile(m_filename + ".sig"); if (!signatureFile.open(QIODevice::ReadOnly)) { qDebug() << "error",signatureFile.errorString(); } QByteArray signatureData = signatureFile.readAll(); QFile isoFile(m_filename); if (!isoFile.open(QIODevice::ReadOnly)) { qDebug() << "error",isoFile.errorString(); } QByteArray isoData = signatureFile.readAll(); QGpgME::VerifyDetachedJob *job = QGpgME::openpgp()->verifyDetachedJob(); GpgME::VerificationResult result = job->exec(signatureData, isoData); qDebug() << "numSignatures " << result.numSignatures(); qDebug() << "filename " << result.fileName(); GpgME::Signature signature = result.signature(0); qDebug() << "fingerprint " << signature.fingerprint(); if (strcmp(signature.fingerprint(), "DEACEA00075E1D76") == 0) { qDebug() << "Uses right signature!"; } else { qDebug() << "Uses wrong signature!!"; m_error = i18n("Uses wrong signature."); return false; } if (signature.summary() & GpgME::Signature::KeyRevoked) { qDebug() << "Key is revoked" << signature.summary(); m_error = i18n("Key is revoked."); return false; } return true; }