diff --git a/isoimagewriter/CMakeLists.txt b/isoimagewriter/CMakeLists.txt index 9e609fd..bc2de82 100644 --- a/isoimagewriter/CMakeLists.txt +++ b/isoimagewriter/CMakeLists.txt @@ -1,71 +1,72 @@ set(GPGME_REQUIRED_VERSION "1.8.0") find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) set(isoimagewriter_common_SRCS platform_lin.cpp common.cpp verifyiso.cpp verifyneoniso.cpp verifykubuntuiso.cpp - verifynetrunneriso.cpp) + verifynetrunneriso.cpp + verifyarchiso.cpp) ecm_qt_declare_logging_category(isoimagewriter_common_SRCS HEADER isoimagewriter_debug.h IDENTIFIER ISOIMAGEWRITER_LOG CATEGORY_NAME org.kde.isoimagewriter DEFAULT_SEVERITY Debug) set(isoimagewriter_SRCS ${isoimagewriter_common_SRCS} usbdevicemonitor.h common.cpp mainapplication.cpp maindialog.cpp platform_lin.cpp externalprogressbar_lin.cpp imagewriter.cpp physicaldevice.cpp usbdevicemonitor_lin.cpp main.cpp verifyisoworker.cpp ) ki18n_wrap_ui(isoimagewriter_SRCS maindialog.ui) add_executable(isoimagewriter ${isoimagewriter_SRCS}) add_executable(testy testy.cpp ${isoimagewriter_common_SRCS}) target_link_libraries(testy Qt5::Widgets KF5::Auth) if (ROSA_BRANDING) target_compile_definitions(isoimagewriter PRIVATE -DROSA_BRANDING="1") endif (ROSA_BRANDING) target_compile_definitions(isoimagewriter PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}") target_link_libraries(isoimagewriter Qt5::Widgets KF5::I18n KF5::CoreAddons KF5::Auth QGpgme dl KF5::WidgetsAddons KF5::IconThemes ) target_link_libraries(testy Qt5::Widgets KF5::I18n KF5::CoreAddons dl QGpgme Gpgmepp ) install(TARGETS isoimagewriter ${INSTALL_TARGETS_DEFAULT_ARGS}) install(PROGRAMS org.kde.isoimagewriter.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(FILES org.kde.isoimagewriter.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) add_executable(isoimagewriter_helper imagewriter_helper.cpp imagewriter.cpp physicaldevice.cpp) target_link_libraries(isoimagewriter_helper Qt5::Widgets KF5::Auth KF5::I18n) install(TARGETS isoimagewriter_helper DESTINATION ${KAUTH_HELPER_INSTALL_DIR}) kauth_install_helper_files(isoimagewriter_helper org.kde.isoimagewriter root) kauth_install_actions(org.kde.isoimagewriter isoimagewriter.actions) diff --git a/isoimagewriter/maindialog.cpp b/isoimagewriter/maindialog.cpp index ceb0b2e..99ae6e7 100644 --- a/isoimagewriter/maindialog.cpp +++ b/isoimagewriter/maindialog.cpp @@ -1,594 +1,611 @@ /* * 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 #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" +#include "verifyarchiso.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)))); m_busyWidget = new KPixmapSequenceOverlayPainter(this); m_busyWidget->setSequence(KIconLoader::global()->loadPixmapSequence("process-working", KIconLoader::SizeSmallMedium)); m_busyWidget->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_busyWidget->setWidget(ui->busySpinner); ui->busySpinner->setFixedSize(24, 24); ui->busySpinner->show(); QApplication::setOverrideCursor(Qt::WaitCursor); m_busyWidget->start(); IsoResult isoResult = verifyISO(); ui->busySpinner->hide(); m_busyWidget->stop(); QApplication::setOverrideCursor(Qt::ArrowCursor); delete m_busyWidget; 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; } } + VerifyArchISO verifyArch(m_ImageFile); + if (verifyArch.canVerify()) { + if (verifyArch.isValid()) { + ui->verificationResultLabel->setText(i18n("Verified as valid Arch ISO")); + result.resultType = Fine; + result.error = i18n("Verified as valid Arch ISO"); + return result; + } else { + QString error(i18n("Invalid Arch image")); + ui->verificationResultLabel->show(); + ui->verificationResultLabel->setText(verifyArch.m_error); + result.resultType = Invalid; + result.error = verifyArch.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->busySpinner->hide(); 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/verifyneoniso.cpp b/isoimagewriter/verifyarchiso.cpp similarity index 85% copy from isoimagewriter/verifyneoniso.cpp copy to isoimagewriter/verifyarchiso.cpp index f77c3d5..e9e9ce2 100644 --- a/isoimagewriter/verifyneoniso.cpp +++ b/isoimagewriter/verifyarchiso.cpp @@ -1,74 +1,75 @@ /* * * 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 "verifyneoniso.h" +#include "verifyarchiso.h" #include "verifyisoworker.h" -VerifyNeonISO::VerifyNeonISO(QString filename) : VerifyISO(filename) +VerifyArchISO::VerifyArchISO(QString filename) : VerifyISO(filename) { - m_humanReadableDistroName = "KDE neon"; + m_humanReadableDistroName = "Arch"; } -bool VerifyNeonISO::canVerify() { - if (!verifyFileMatches("neon-")) { +bool VerifyArchISO::canVerify() { + qDebug() << "canVerify arch"; + if (!verifyFileMatches("archlinux-")) { return false; } - if (!importSigningKey("neon-signing-key.gpg")) { + if (!importSigningKey("arch-signing-key.gpg")) { return false; } return true; } -bool VerifyNeonISO::isValid() { +bool VerifyArchISO::isValid() { if (!verifyFileExists()) { return false; } if (!verifySignatureFileExists(m_filename + ".sig")) { return false; } QFile signatureFile(m_filename + ".sig"); if (!signatureFile.open(QIODevice::ReadOnly)) { qDebug() << "error",signatureFile.errorString(); } - VerifyISOWorker* verifyISOWorker = new VerifyISOWorker(m_filename, true); + VerifyISOWorker* verifyISOWorker = new VerifyISOWorker(m_filename, Arch); connect(verifyISOWorker, &QThread::finished, verifyISOWorker, &QObject::deleteLater); verifyISOWorker->start(); while (verifyISOWorker->isResultReady() == false) { QCoreApplication::processEvents(); } if (verifyISOWorker->getResult() == false) { m_error = verifyISOWorker->getErrorMessage(); return false; } return true; } diff --git a/isoimagewriter/verifyarchiso.h b/isoimagewriter/verifyarchiso.h new file mode 100644 index 0000000..57bda04 --- /dev/null +++ b/isoimagewriter/verifyarchiso.h @@ -0,0 +1,44 @@ +/* + * + * 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 VERIFYARCHISO_H +#define VERIFYARCHISO_H + +#include "verifyiso.h" + +/** + * @todo write docs + */ +class VerifyArchISO : public VerifyISO +{ + Q_OBJECT + +public: + /** + * @todo write docs + */ + /** + * Default constructor + */ + VerifyArchISO(QString filename); + bool canVerify(); + bool isValid(); +}; + +#endif // VERIFYNEONISO_H diff --git a/isoimagewriter/verifykubuntuiso.cpp b/isoimagewriter/verifykubuntuiso.cpp index bc7cb48..716e453 100644 --- a/isoimagewriter/verifykubuntuiso.cpp +++ b/isoimagewriter/verifykubuntuiso.cpp @@ -1,81 +1,81 @@ /* * * 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 #include "verifykubuntuiso.h" #include "verifyisoworker.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; } - VerifyISOWorker* verifyISOWorker = new VerifyISOWorker(m_filename, false); + VerifyISOWorker* verifyISOWorker = new VerifyISOWorker(m_filename, Kubuntu); connect(verifyISOWorker, &QThread::finished, verifyISOWorker, &QObject::deleteLater); verifyISOWorker->start(); while (verifyISOWorker->isResultReady() == false) { QCoreApplication::processEvents(); } if (verifyISOWorker->getResult() == false) { m_error = verifyISOWorker->getErrorMessage(); return false; } return true; } diff --git a/isoimagewriter/verifyneoniso.cpp b/isoimagewriter/verifyneoniso.cpp index f77c3d5..e7d8746 100644 --- a/isoimagewriter/verifyneoniso.cpp +++ b/isoimagewriter/verifyneoniso.cpp @@ -1,74 +1,74 @@ /* * * 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 "verifyneoniso.h" #include "verifyisoworker.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; } if (!verifySignatureFileExists(m_filename + ".sig")) { return false; } QFile signatureFile(m_filename + ".sig"); if (!signatureFile.open(QIODevice::ReadOnly)) { qDebug() << "error",signatureFile.errorString(); } - VerifyISOWorker* verifyISOWorker = new VerifyISOWorker(m_filename, true); + VerifyISOWorker* verifyISOWorker = new VerifyISOWorker(m_filename, Neon); connect(verifyISOWorker, &QThread::finished, verifyISOWorker, &QObject::deleteLater); verifyISOWorker->start(); while (verifyISOWorker->isResultReady() == false) { QCoreApplication::processEvents(); } if (verifyISOWorker->getResult() == false) { m_error = verifyISOWorker->getErrorMessage(); return false; } return true; } diff --git a/signing-keys/CMakeLists.txt b/signing-keys/CMakeLists.txt index 14ed899..e8db815 100644 --- a/signing-keys/CMakeLists.txt +++ b/signing-keys/CMakeLists.txt @@ -1,4 +1,5 @@ install(FILES neon-signing-key.gpg DESTINATION ${DATA_INSTALL_DIR}/isoimagewriter/) -install(FILES neon-signing-key.gpg DESTINATION ${DATA_INSTALL_DIR}/testy/) - +install(FILES arch-signing-key.gpg DESTINATION ${DATA_INSTALL_DIR}/isoimagewriter/) install(FILES ubuntu-signing-key.gpg DESTINATION ${DATA_INSTALL_DIR}/isoimagewriter/) + +install(FILES neon-signing-key.gpg DESTINATION ${DATA_INSTALL_DIR}/testy/) diff --git a/signing-keys/arch-signing-key.gpg b/signing-keys/arch-signing-key.gpg new file mode 100644 index 0000000..f2b26c1 --- /dev/null +++ b/signing-keys/arch-signing-key.gpg @@ -0,0 +1,234 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2 + +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 +hOkeFSV5aoOrZmXkx4kCHAQQAQIABgUCTsBOCAAKCRAoT8NMjksaJdcuEACMy1E9 +b+jrfZtksgzlzHOkJNv3tGrdY6ZUnhvXXMwupwJS7n697jj+DsWw5PrBcJj5tXme +6sFqILk05oRwavLtagkPhjFGPQVEu4E8ZjlXyDNYnmfhg6y6wCvrDylLiMqSUe6d +sqx+RT7GE61KU5wOHtDpBWfLsJPpu9t1RNNErF+pNviuJDJaFogC0b8BQRIl2pTo +0PCSWeUKJv3rncMRi7cKEJ/oNJQ+RaaTDyC0yBs/YT6fTWNal71YeFzoscAVusJ2 +M0P5P+3h4MdBz/zd4k5HZxcRfouPgT55H8j+qAhUMMd+FSqjtGiT0HnJdE1PDFiD +oN0aGBvu5VuJHmN+eokBNYnJvDf9U1SYLz04u6jSx9TCqFdxQdKNHscBlVr15cHz +qafXQM9+oYpe33VY5Hc353H4k2ytJdId87+EgNPG4NFsGF+oeridg6xAcm/B+uin +anLa7wSa6niDw4R7RkIDLPx2NjJRhftlV5bPuZ43GBlhs+Q8U2rNXPmplaQ/+z26 +U7k/12vBBdwb+pJ9fd3icC03RV0KKf5/mfjDmJyGtqKH+ZA4ZAZ++TFDHJGRThc7 +qtHDH2egePAU26CvGSSe0Eg1lEqiM3pY5mgCw9Z4worCFWAs6rltAZj0IVfbZqu4 +XY+J8D5izq62toPn9lcKhd9EBQWnb7SEtY8/LokCHAQQAQIABgUCTsBOCAAKCRAo +T8NMjksaJdcuEACMy1E9b+jrfZtksgzlzHOkJNv3tGrdY6ZUnhvXXMwupwJS7n69 +7jj+DsWw5PrBcJj5tXme6sFqILk05oRwavLtagkPhjFGPQVEu4E8ZjlXyDNYnmfh +g6y6wCvrDylLiMqSUe6dsqx+RT7GE61KU5wOHtDpBWfLsJPpu9t1RNNErF+pNviu +JDJaFogC0b8BQRIl2pTo0PCSWeUKJv3rncMRi7cKEJ/oNJQ+RaaTDyC0yBv///// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////4kCHAQQ +AQIABgUCTty4cwAKCRC6Hftk//l552kIEAC1sC3xwov/UBSx14EOfkGJ6aCaMb7I +AtIqLJoI5gdTDFyvq8DKgGbYK93GowSflzau12a4hZYhVeKFx4hXUd8447r7yCYW +J+ddO/WCoWVdrUKmbfTTvq757NnzMMdolzKLtfoO28jlKYpyJDV7U4t4tx8V/uJy +oy9x5ONkHX/sERS0pd/g6SMfaqweUXi/F2ITL5QqHYzVj1DYty3LCxJeO/puKgUg +ZkG9Jr8taKG2jS10/PtHE+VB0AKkJAdSyFnAvB3pNIieKE48KZlP9+jR+9X4n+sq +Q1lNvKW7kcR71462LkouAvvtddEoUHUxDHtsLfTKUSJKd9GiH1lAqurgplEeQzvE +CTNwj7cJaRM1yF6I6eAC3yjmMecYNVDhsSCxKwhODuBGM0DO6SwahbzIN63RLu76 +50a4pcx2VVj3nl2yRvrs2G4YnMnYeFFCxdh8+i/7zBCf4q8XRC1f4fH8pCShOcBY +oMWS9VYJljCACEhmfjeDaa9fbtVJk9xsjOKviJizrm1h1N1alRrOWpWUFmb4u9rG +BClAalGO/8HyLxT3VyeAQjyWasUgri8uZqSxHmhqZ4RUgYGR8wcMD8r5d2XPT9ta +yQ9KfDWzKfrIQrXln2nTJ5gXyxpB1IT+qxmZO4NnI7hWrE53abr/PpUvTYqTbern +o64PxvjVhQBB+YkCHAQQAQIABgUCU0UnugAKCRCHLmcU6vXsRHSTD/0fR8mHPnnQ +o+Psl2Iyr4CX1HyS+9ReheyXKN/MJ4pME1NGL4vYqAbNZI0ihpnKwb6MHwU0snq8 +C8U/Iv2GMa9H5C01z847zoiN4xmnxXCZmUaBgBiKrER4M6sdHqQGHX4LfnRGZ4P5 +vICKOOTJYI9JtaHefHZSrfq0FsOwOB9I/G2ubCMiOFhSIzzADmUoHD6t7eY62d9L +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 +=QNs8 +-----END PGP PUBLIC KEY BLOCK-----