diff --git a/isoimagewriter/isoverifier.cpp b/isoimagewriter/isoverifier.cpp index 69b4427..878c203 100644 --- a/isoimagewriter/isoverifier.cpp +++ b/isoimagewriter/isoverifier.cpp @@ -1,196 +1,244 @@ #include "isoverifier.h" #include #include #include #include #include #include #include #include #include #include #include #include IsoVerifier::IsoVerifier(const QString &filePath) : m_filePath(filePath), m_error(), m_isIsoValid(false), m_verificationMean(VerificationMean::None) {} void IsoVerifier::verifyIso() { QFileInfo fileInfo(m_filePath); QString fileName = fileInfo.fileName(); QString keyFingerprint; if (fileName.startsWith("neon-") && importSigningKey("neon-signing-key.gpg", keyFingerprint)) { m_verificationMean = VerificationMean::DotSigFile; } else if (fileName.startsWith("archlinux-") && importSigningKey("arch-signing-key.gpg", keyFingerprint)) { m_verificationMean = VerificationMean::DotSigFile; } else if (fileName.startsWith("kubuntu-") && importSigningKey("ubuntu-signing-key.gpg", keyFingerprint)) { m_verificationMean = VerificationMean::Sha256SumsFile; } else if (fileName.startsWith("ubuntu-") && importSigningKey("ubuntu-signing-key.gpg", keyFingerprint)) { m_verificationMean = VerificationMean::Sha256SumsFile; + } else if (fileName.startsWith("netrunner-")) { + m_verificationMean = VerificationMean::Sha256SumInput; } else { m_error = QString(i18n("Could not verify as a known distro image.")); } switch (m_verificationMean) { case VerificationMean::DotSigFile: verifyWithDotSigFile(keyFingerprint); break; case VerificationMean::Sha256SumsFile: verifyWithSha256SumsFile(keyFingerprint); break; + case VerificationMean::Sha256SumInput: + emit inputRequested(i18n("SHA256 Checksum"), + i18n("Paste the SHA256 checksum for this ISO:")); + break; + default: + emit finished(m_isIsoValid, m_error); + break; + } +} + +void IsoVerifier::verifyWithInputText(bool ok, const QString &text) +{ + switch (m_verificationMean) { + case VerificationMean::Sha256SumInput: + verifyWithSha256Sum(ok, text); + break; default: emit finished(m_isIsoValid, m_error); break; } } bool IsoVerifier::importSigningKey(const QString &fileName, QString &keyFingerprint) { QString signingKeyFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName); 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); if (!(importResult.numConsidered() == 1 && (importResult.numImported() == 1 || importResult.numUnchanged() == 1))) { qDebug() << "Could not import gpg signature"; return false; } keyFingerprint = QString(importResult.import(0).fingerprint()); return true; } void IsoVerifier::verifyWithDotSigFile(const QString &keyFingerprint) { QString sigFilePath = m_filePath + ".sig"; QFileInfo fileInfo(sigFilePath); QString sigFileName = fileInfo.fileName(); if (!QFile::exists(sigFilePath)) { m_error = i18n("Could not find %1, please download PGP signature file " "to same directory.", sigFileName); return; } QFile signatureFile(sigFilePath); if (!signatureFile.open(QIODevice::ReadOnly)) { m_error = i18n("Could not open signature file"); return; } QByteArray signatureData = signatureFile.readAll(); QFile isoFile(m_filePath); if (!isoFile.open(QIODevice::ReadOnly)) { m_error = i18n("Could not open ISO image"); return; } QByteArray isoData = isoFile.readAll(); QGpgME::VerifyDetachedJob *job = QGpgME::openpgp()->verifyDetachedJob(); GpgME::VerificationResult result = job->exec(signatureData, isoData); GpgME::Signature signature = result.signature(0); if (signature.summary() == GpgME::Signature::None && signature.fingerprint() == keyFingerprint) { m_isIsoValid = true; } else if (signature.summary() & GpgME::Signature::Valid) { m_isIsoValid = true; } else if (signature.summary() & GpgME::Signature::KeyRevoked) { m_error = i18n("Key is revoked."); } else { m_error = i18n("Uses wrong signature."); } emit finished(m_isIsoValid, m_error); } void IsoVerifier::verifyWithSha256SumsFile(const QString &keyFingerprint) { QFileInfo fileInfo(m_filePath); QFile checksumsFile(fileInfo.absolutePath() + "/SHA256SUMS"); if (!checksumsFile.open(QIODevice::ReadOnly | QIODevice::Text)) { m_error = i18n("Could not open SHA256SUMS file, please download to same directory"); return; } // Extract checksum from the SHA256SUMS file QString checksum; QRegExp rx("([abcdef\\d]+).." + fileInfo.fileName()); QByteArray checksumsData = checksumsFile.readAll(); int pos = rx.indexIn(QString(checksumsData)); if (pos > -1) { checksum = rx.cap(1); } else { m_error = i18n("Could not find checksum in SHA256SUMS file"); return; } // Calculate SHA256 checksum of the ISO image QCryptographicHash hash(QCryptographicHash::Sha256); QFile isoFile(m_filePath); if (!isoFile.open(QIODevice::ReadOnly)) { m_error = i18n("Could not read ISO image"); return; } if (!hash.addData(&isoFile)) { m_error = i18n("Could not perform checksum"); return; } QByteArray hashResult = hash.result(); if (checksum != hashResult.toHex()) { m_error = i18n("Checksum of .iso file does not match value in SHA256SUMS file"); return; } // Check GPG signature QString isoFileName = fileInfo.fileName(); QFile signatureFile(fileInfo.absolutePath() + "/SHA256SUMS.gpg"); if (!signatureFile.open(QIODevice::ReadOnly)) { m_error = i18n("Could not find SHA256SUMS.gpg, please download PGP signature file to same directory."); return; } QByteArray signatureData = signatureFile.readAll(); QGpgME::VerifyDetachedJob *job = QGpgME::openpgp()->verifyDetachedJob(); GpgME::VerificationResult result = job->exec(signatureData, checksumsData); GpgME::Signature signature = result.signature(0); if (signature.summary() == GpgME::Signature::None && signature.fingerprint() == keyFingerprint) { m_isIsoValid = true; } else if (signature.summary() & GpgME::Signature::Valid) { m_isIsoValid = true; } else if (signature.summary() & GpgME::Signature::KeyRevoked) { m_error = i18n("Key is revoked."); } else { m_error = i18n("Uses wrong signature."); } emit finished(m_isIsoValid, m_error); } + +void IsoVerifier::verifyWithSha256Sum(bool ok, const QString &checksum) +{ + if (ok && !checksum.isEmpty()) { + QCryptographicHash hash(QCryptographicHash::Sha256); + QFile iso(m_filePath); + if (!iso.open(QIODevice::ReadOnly)) { + m_error = i18n("Could not read ISO image"); + goto finish; + } + if (!hash.addData(&iso)) { + m_error = i18n("Could not perform checksum"); + goto finish; + } + QByteArray hashResult = hash.result(); + + if (checksum == hashResult.toHex()) { + m_isIsoValid = true; + goto finish; + } else { + m_error = i18n("Checksum did not match"); + goto finish; + } + } + + m_error = i18n("Requires an SHA256 checksum"); + +finish: + emit finished(m_isIsoValid, m_error); +} diff --git a/isoimagewriter/isoverifier.h b/isoimagewriter/isoverifier.h index b6482e4..7986a6b 100644 --- a/isoimagewriter/isoverifier.h +++ b/isoimagewriter/isoverifier.h @@ -1,30 +1,38 @@ #ifndef ISOVERIFIER_H #define ISOVERIFIER_H #include class IsoVerifier : public QObject { Q_OBJECT public: IsoVerifier(const QString &filePath); public slots: void verifyIso(); + void verifyWithInputText(bool ok, const QString &text); signals: void finished(const bool &isIsoValid, const QString &error); + void inputRequested(const QString &title, const QString &body); private: QString m_filePath; QString m_error; bool m_isIsoValid; - enum VerificationMean { None, DotSigFile, Sha256SumsFile } m_verificationMean; + enum VerificationMean { + None, + DotSigFile, + Sha256SumsFile, + Sha256SumInput + } m_verificationMean; bool importSigningKey(const QString &fileName, QString &keyFingerPrint); void verifyWithDotSigFile(const QString &keyFingerPrint); void verifyWithSha256SumsFile(const QString &keyFingerPrint); + void verifyWithSha256Sum(bool ok, const QString &checksum); }; #endif // ISOVERIFIER_H diff --git a/isoimagewriter/mainwindow.cpp b/isoimagewriter/mainwindow.cpp index 8fd382f..0035c8a 100644 --- a/isoimagewriter/mainwindow.cpp +++ b/isoimagewriter/mainwindow.cpp @@ -1,607 +1,619 @@ #include "mainwindow.h" #include "mainapplication.h" #include "common.h" #include "imagewriter.h" #include "isoverifier.h" #include "isoimagewriter_debug.h" #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), m_lastOpenedDir(""), m_isWriting(false), m_enumFlashDevicesWaiting(false), m_externalProgressBar(this) { setupUi(); setAcceptDrops(true); // Set initial directory m_lastOpenedDir = mApp->getInitialDir(); // Get path to ISO image from command line args (if supplied) QString isoImagePath = mApp->getInitialImage(); if (!isoImagePath.isEmpty()) { if (isoImagePath.left(7) == "file://") isoImagePath = QUrl(isoImagePath).toLocalFile(); if (!isoImagePath.isEmpty()) { isoImagePath = QDir(isoImagePath).absolutePath(); // Update the default open dir m_lastOpenedDir = isoImagePath.left(isoImagePath.lastIndexOf('/')); preprocessIsoImage(isoImagePath); } } // Load the list of USB flash devices QTimer::singleShot(0, this, &MainWindow::enumFlashDevices); } void MainWindow::scheduleEnumFlashDevices() { if (m_isWriting) m_enumFlashDevicesWaiting = true; else enumFlashDevices(); } +void MainWindow::showInputDialog(const QString &title, const QString &body) +{ + bool ok; + QString text = QInputDialog::getText(this, title, body, QLineEdit::Normal, "", &ok); + + emit inputTextReady(ok, text); +} + void MainWindow::setupUi() { // Logo QLabel *logoLabel = new QLabel; logoLabel->setPixmap(QIcon::fromTheme("drive-removable-media").pixmap(QSize(50, 50))); logoLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QLabel *titleLabel = new QLabel; titleLabel->setTextFormat(Qt::RichText); titleLabel->setText("

KDE ISO Image Writer

" "A quick and simple way to create a bootable USB drive."); QHBoxLayout *headerHBoxLayout = new QHBoxLayout; headerHBoxLayout->addWidget(logoLabel); headerHBoxLayout->addWidget(titleLabel); m_centralStackedWidget = new QStackedWidget; m_centralStackedWidget->addWidget(createFormWidget()); m_centralStackedWidget->addWidget(createConfirmWidget()); m_centralStackedWidget->addWidget(createProgressWidget()); m_centralStackedWidget->addWidget(createSuccessWidget()); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addLayout(headerHBoxLayout); mainVBoxLayout->addSpacing(15); mainVBoxLayout->addWidget(m_centralStackedWidget); QWidget *centralWidget = new QWidget; centralWidget->setLayout(mainVBoxLayout); setCentralWidget(centralWidget); } QWidget* MainWindow::createFormWidget() { // Form m_isoImageLineEdit = new QLineEdit; m_isoImageLineEdit->setReadOnly(true); m_isoImageLineEdit->setPlaceholderText(i18n("Path to ISO image...")); QAction *openIsoImageAction = m_isoImageLineEdit->addAction( QIcon::fromTheme("folder-open"), QLineEdit::TrailingPosition); connect(openIsoImageAction, &QAction::triggered, this, &MainWindow::openIsoImage); m_usbDriveComboBox = new QComboBox; m_createButton = new QPushButton(i18n("Create")); m_createButton->setEnabled(false); connect(m_createButton, &QPushButton::clicked, this, &MainWindow::showConfirmMessage); m_busyLabel = new QLabel; m_busyWidget = new QWidget; m_busySpinner = new KPixmapSequenceOverlayPainter(this); m_busySpinner->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_busySpinner->setWidget(m_busyWidget); m_busyWidget->setFixedSize(24, 24); QHBoxLayout *footerBoxLayout = new QHBoxLayout; footerBoxLayout->addWidget(m_busyWidget); footerBoxLayout->addWidget(m_busyLabel); footerBoxLayout->addWidget(m_createButton, 0, Qt::AlignRight); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addWidget(new QLabel(i18n("Write this ISO image:"))); mainVBoxLayout->addWidget(m_isoImageLineEdit); mainVBoxLayout->addSpacing(5); mainVBoxLayout->addWidget(new QLabel(i18n("To this USB drive:"))); mainVBoxLayout->addWidget(m_usbDriveComboBox); mainVBoxLayout->addSpacing(15); mainVBoxLayout->addStretch(); mainVBoxLayout->addLayout(footerBoxLayout); QWidget *formWidget = new QWidget; formWidget->setLayout(mainVBoxLayout); return formWidget; } QWidget* MainWindow::createConfirmWidget() { QLabel *iconLabel = new QLabel; iconLabel->setPixmap(QIcon::fromTheme("dialog-warning").pixmap(QSize(64, 64))); iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); QLabel *messageLabel = new QLabel(i18n("Everything on the USB drive will " "be overwritten." "\n\nDo you want to continue?")); QHBoxLayout *messageHBoxLayout = new QHBoxLayout; messageHBoxLayout->addWidget(iconLabel, 0, Qt::AlignTop); messageHBoxLayout->addWidget(messageLabel, 0, Qt::AlignTop); QPushButton *abortButton = new QPushButton(i18n("Abort")); connect(abortButton, &QPushButton::clicked, this, &MainWindow::hideWritingProgress); QPushButton *continueButton = new QPushButton(i18n("Continue")); connect(continueButton, &QPushButton::clicked, this, &MainWindow::writeIsoImage); QHBoxLayout *buttonsHBoxLayout = new QHBoxLayout; buttonsHBoxLayout->addWidget(abortButton, 0, Qt::AlignLeft); buttonsHBoxLayout->addWidget(continueButton, 0, Qt::AlignRight); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addLayout(messageHBoxLayout); mainVBoxLayout->addLayout(buttonsHBoxLayout); QWidget *confirmWidget = new QWidget; confirmWidget->setLayout(mainVBoxLayout); return confirmWidget; } QWidget* MainWindow::createProgressWidget() { QLabel *messageLabel = new QLabel(i18n("Your USB drive is being created.\n\n" "This may take some time depending " "on the size of the ISO image file " "and the transfer speed.")); messageLabel->setWordWrap(true); m_progressBar = new QProgressBar; m_cancelButton = new QPushButton(i18n("Cancel")); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addWidget(messageLabel, 0, Qt::AlignTop | Qt::AlignHCenter); mainVBoxLayout->addWidget(m_progressBar); mainVBoxLayout->addSpacing(15); mainVBoxLayout->addWidget(m_cancelButton, 0, Qt::AlignLeft); QWidget *progressWidget = new QWidget; progressWidget->setLayout(mainVBoxLayout); return progressWidget; } QWidget* MainWindow::createSuccessWidget() { QLabel *messageLabel = new QLabel(i18n("Your live USB flash drive is now " "complete and ready to use!")); messageLabel->setWordWrap(true); QLabel *successIconLabel = new QLabel(); successIconLabel->setPixmap(QIcon::fromTheme("emblem-success").pixmap(QSize(64, 64))); QVBoxLayout *mainVBoxLayout = new QVBoxLayout; mainVBoxLayout->addWidget(messageLabel, 0, Qt::AlignCenter); mainVBoxLayout->addWidget(successIconLabel, 0, Qt::AlignHCenter); mainVBoxLayout->addSpacing(15); QWidget *successWidget = new QWidget; successWidget->setLayout(mainVBoxLayout); return successWidget; } void MainWindow::preprocessIsoImage(const QString& isoImagePath) { QFile file(isoImagePath); if (!file.open(QIODevice::ReadOnly)) { QMessageBox::critical(this, "Error", i18n("Failed to open the image file:") + "\n" + QDir::toNativeSeparators(isoImagePath) + "\n" + file.errorString()); return; } m_isoImageSize = file.size(); m_isoImagePath = isoImagePath; m_isoImageLineEdit->setText(QDir::toNativeSeparators(m_isoImagePath) + " (" + KFormat().formatByteSize(m_isoImageSize) + ")"); file.close(); // Verify ISO image m_busyLabel->setText(i18n("Verifying ISO image")); m_busyWidget->show(); m_busySpinner->setSequence(KIconLoader::global()->loadPixmapSequence("process-working", KIconLoader::SizeSmallMedium)); m_busySpinner->start(); IsoVerifier *isoVerifier = new IsoVerifier(m_isoImagePath); QThread *verifierThread = new QThread(this); connect(verifierThread, &QThread::started, isoVerifier, &IsoVerifier::verifyIso); connect(verifierThread, &QThread::finished, verifierThread, &QThread::deleteLater); connect(isoVerifier, &IsoVerifier::finished, verifierThread, &QThread::quit); connect(isoVerifier, &IsoVerifier::finished, isoVerifier, &IsoVerifier::deleteLater); connect(isoVerifier, &IsoVerifier::finished, this, &MainWindow::showIsoVerificationResult); + connect(isoVerifier, &IsoVerifier::inputRequested, this, &MainWindow::showInputDialog); + + connect(this, &MainWindow::inputTextReady, isoVerifier, &IsoVerifier::verifyWithInputText); isoVerifier->moveToThread(verifierThread); verifierThread->start(); // Enable the Write button (if there are USB flash disks present) m_createButton->setEnabled(m_usbDriveComboBox->count() > 0); } void MainWindow::cleanUp() { // Delete all the allocated UsbDevice objects attached to the combobox for (int i = 0; i < m_usbDriveComboBox->count(); ++i) { delete m_usbDriveComboBox->itemData(i).value(); } } void MainWindow::enumFlashDevices() { m_enumFlashDevicesWaiting = false; // Remember the currently selected device QString selectedDevice = ""; int idx = m_usbDriveComboBox->currentIndex(); if (idx >= 0) { UsbDevice* dev = m_usbDriveComboBox->itemData(idx).value(); selectedDevice = dev->m_PhysicalDevice; } // Remove the existing entries cleanUp(); m_usbDriveComboBox->clear(); // Disable the combobox m_usbDriveComboBox->setEnabled(false); // Add the USB flash devices to the combobox platformEnumFlashDevices(addFlashDeviceCallback, m_usbDriveComboBox); // Restore the previously selected device (if present) if (!selectedDevice.isEmpty()) for (int i = 0; i < m_usbDriveComboBox->count(); ++i) { UsbDevice* dev = m_usbDriveComboBox->itemData(i).value(); if (dev->m_PhysicalDevice == selectedDevice) { m_usbDriveComboBox->setCurrentIndex(i); break; } } // Re-enable the combobox m_usbDriveComboBox->setEnabled(true); // Update the Write button enabled/disabled state m_createButton->setEnabled(m_usbDriveComboBox->count() > 0 && m_isoImagePath != ""); // Update the Clear button enabled/disabled state // m_clearButton->setEnabled(m_usbDriveComboBox->count() > 0); } void MainWindow::writeToDevice(bool zeroing) { UsbDevice* selectedDevice = m_usbDriveComboBox->itemData( m_usbDriveComboBox->currentIndex()).value(); // Use KAuth to get required previleges in supported platforms #if defined(Q_OS_LINUX) connect(m_cancelButton, &QPushButton::clicked, this, &MainWindow::cancelWriting); KAuth::Action action("org.kde.isoimagewriter.write"); action.setHelperId("org.kde.isoimagewriter"); QVariantMap args; args[QStringLiteral("zeroing")] = QVariant(zeroing); args[QStringLiteral("imagefile")] = m_isoImagePath; args[QStringLiteral("usbdevice_visiblename")] = selectedDevice->m_VisibleName; args[QStringLiteral("usbdevice_volumes")] = selectedDevice->m_Volumes[0]; args[QStringLiteral("usbdevice_size")] = QString("%1").arg(selectedDevice->m_Size); args[QStringLiteral("usbdevice_sectorsize")] = selectedDevice->m_SectorSize; args[QStringLiteral("usbdevice_physicaldevice")] = selectedDevice->m_PhysicalDevice; action.setArguments(args); action.setTimeout(3600000); // an hour m_job = action.execute(); connect(m_job, SIGNAL(percent(KJob*,ulong)), this, SLOT(progressStep(KJob*,ulong)), Qt::DirectConnection); connect(m_job, SIGNAL(newData(QVariantMap)), this, SLOT(progressStep(QVariantMap))); connect(m_job, &KAuth::ExecuteJob::statusChanged, this, &MainWindow::statusChanged); connect(m_job, &KAuth::ExecuteJob::result, this, &MainWindow::finished); m_job->start(); #else ImageWriter* writer = new ImageWriter(zeroing ? "" : m_isoImagePath, selectedDevice); QThread *writerThread = new QThread(this); // Connect start and end signals connect(writerThread, &QThread::started, writer, &ImageWriter::writeImage); // When writer finishes its job, quit the thread connect(writer, &ImageWriter::finished, writerThread, &QThread::quit); // Guarantee deleting the objects after completion connect(writer, &ImageWriter::finished, writer, &ImageWriter::deleteLater); connect(writerThread, &QThread::finished, writerThread, &QThread::deleteLater); // If the Cancel button is pressed, inform the writer to stop the operation // Using DirectConnection because the thread does not read its own event queue until completion connect(m_cancelButton, &QPushButton::clicked, writer, &ImageWriter::cancelWriting, Qt::DirectConnection); // Each time a block is written, update the progress bar connect(writer, &ImageWriter::blockWritten, this, &MainWindow::updateProgressBar); // Show the message about successful completion on success connect(writer, &ImageWriter::success, this, &MainWindow::showSuccessMessage); // Show error message if error is sent by the worker connect(writer, &ImageWriter::error, this, &MainWindow::showErrorMessage); // Silently return back to normal dialog form if the operation was cancelled connect(writer, &ImageWriter::cancelled, this, &MainWindow::hideWritingProgress); // Now start the writer thread writer->moveToThread(writerThread); writerThread->start(); #endif showWritingProgress(alignNumberDiv((zeroing ? DEFAULT_UNIT : m_isoImageSize), DEFAULT_UNIT)); } void MainWindow::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(); } void MainWindow::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.isEmpty()) { // If something was really received update the information preprocessIsoImage(newImageFile); } } void MainWindow::closeEvent(QCloseEvent* event) { if (m_isWriting) { const int answer = QMessageBox::question( this, i18n("Cancel?"), i18n("Writing is in progress, abort it?")); if (answer == QMessageBox::No) event->ignore(); } } void MainWindow::addFlashDeviceCallback(void* cbParam, UsbDevice* device) { auto usbDriveComboBox = (QComboBox*)cbParam; usbDriveComboBox->addItem(device->formatDisplayName(), QVariant::fromValue(device)); } void MainWindow::openIsoImage() { const QString filter = i18n("Disk Images (%1)", QString("*.iso *.bin *.img")) + ";;" + i18n("All Files (%1)", QString("*")); QString isoImagePath = QFileDialog::getOpenFileName(this, "", m_lastOpenedDir, filter, nullptr, QFileDialog::ReadOnly); if (!isoImagePath.isEmpty()) { m_lastOpenedDir = isoImagePath.left(isoImagePath.lastIndexOf('/')); preprocessIsoImage(isoImagePath); } } void MainWindow::writeIsoImage() { if (m_usbDriveComboBox->count() == 0 || m_isoImagePath == "") return; UsbDevice* selectedDevice = m_usbDriveComboBox->itemData( m_usbDriveComboBox->currentIndex()).value(); if (m_isoImageSize > selectedDevice->m_Size) { QMessageBox::critical( this, i18n("Error"), i18n("The image is larger than your selected device!\n\n" "Image size: %1 (%2 b)\n" "Disk size: %3 (%4 b)", KFormat().formatByteSize(m_isoImageSize), m_isoImageSize, KFormat().formatByteSize(selectedDevice->m_Size), selectedDevice->m_Size), QMessageBox::Ok); return; } writeToDevice(false); } void MainWindow::updateProgressBar(int increment) { int newValue = m_progressBar->value() + increment; m_progressBar->setValue(newValue); m_externalProgressBar.SetProgressValue(newValue); } void MainWindow::showWritingProgress(int maxValue) { m_isWriting = true; // Do not accept dropped files while writing setAcceptDrops(false); // Display and customize the progress bar part m_progressBar->setMinimum(0); m_progressBar->setMaximum(maxValue); m_progressBar->setValue(0); // Expose the progress bar state to the OS m_externalProgressBar.InitProgressBar(maxValue); m_centralStackedWidget->setCurrentIndex(2); } void MainWindow::hideWritingProgress() { m_isWriting = false; // Enable drag & drop setAcceptDrops(true); // Send a signal that progressbar is no longer present m_externalProgressBar.DestroyProgressBar(); m_centralStackedWidget->setCurrentIndex(0); // If device list changed during writing update it now if (m_enumFlashDevicesWaiting) enumFlashDevices(); } void MainWindow::showErrorMessage(const QString &message) { m_externalProgressBar.ProgressSetError(); QMessageBox::critical(this, i18n("Error"), message); hideWritingProgress(); } void MainWindow::showSuccessMessage() { m_isWriting = false; // Do not accept dropped files setAcceptDrops(false); m_centralStackedWidget->setCurrentIndex(3); } void MainWindow::showConfirmMessage() { // Do not accept dropped files setAcceptDrops(false); m_centralStackedWidget->setCurrentIndex(1); } void MainWindow::showIsoVerificationResult(const bool &isIsoValid, const QString &error) { if (isIsoValid) { m_busyLabel->setText(i18n("The ISO image is valid")); m_busySpinner->setSequence(KIconLoader::global()->loadPixmapSequence("checkmark", KIconLoader::SizeSmallMedium)); } else { m_busyLabel->setText(i18n("Could not verify ISO image")); m_busySpinner->setSequence(KIconLoader::global()->loadPixmapSequence("error", KIconLoader::SizeSmallMedium)); QMessageBox::warning(this, i18n("ISO Verification failed"), error); } } #if defined(Q_OS_LINUX) void MainWindow::cancelWriting() { qCDebug(ISOIMAGEWRITER_LOG) << "cancelWriting()"; m_job->kill(); qCDebug(ISOIMAGEWRITER_LOG) << "cancelWriting() done"; } void MainWindow::progressStep(KJob* job, unsigned long step) { Q_UNUSED(job) qCDebug(ISOIMAGEWRITER_LOG) << "progressStep %() " << step; updateProgressBar(step); } void MainWindow::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(); } } void MainWindow::statusChanged(KAuth::Action::AuthStatus status) { qCDebug(ISOIMAGEWRITER_LOG) << "statusChanged: " << status; } void MainWindow::finished(KJob* job) { qCDebug(ISOIMAGEWRITER_LOG) << "finished() " << job->error(); KAuth::ExecuteJob *job2 = (KAuth::ExecuteJob *)job; qCDebug(ISOIMAGEWRITER_LOG) << "finished() " << job2->data(); if (job2->data()[QStringLiteral("success")].isValid()) showSuccessMessage(); } #endif diff --git a/isoimagewriter/mainwindow.h b/isoimagewriter/mainwindow.h index f5cdf4c..87ec64a 100644 --- a/isoimagewriter/mainwindow.h +++ b/isoimagewriter/mainwindow.h @@ -1,88 +1,92 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include "usbdevice.h" #include "externalprogressbar.h" #include #include #include #include #include #include #include #include #if defined(Q_OS_LINUX) #include #endif class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); public slots: void scheduleEnumFlashDevices(); + void showInputDialog(const QString &title, const QString &body); + +signals: + void inputTextReady(bool ok, const QString &text); private: QLabel *m_busyLabel; QWidget *m_busyWidget; QLineEdit *m_isoImageLineEdit; QComboBox *m_usbDriveComboBox; QPushButton *m_createButton; QPushButton *m_cancelButton; QProgressBar *m_progressBar; QStackedWidget *m_centralStackedWidget; KPixmapSequenceOverlayPainter *m_busySpinner; QString m_isoImagePath; quint64 m_isoImageSize; QString m_lastOpenedDir; bool m_isWriting; bool m_enumFlashDevicesWaiting; ExternalProgressBar m_externalProgressBar; #if defined(Q_OS_LINUX) KAuth::ExecuteJob *m_job; #endif void setupUi(); QWidget* createFormWidget(); QWidget* createConfirmWidget(); QWidget* createProgressWidget(); QWidget* createSuccessWidget(); void preprocessIsoImage(const QString& isoImagePath); void cleanUp(); void enumFlashDevices(); void writeToDevice(bool zeroing); void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; void closeEvent(QCloseEvent* event) override; static void addFlashDeviceCallback(void* cbParam, UsbDevice* device); private slots: void openIsoImage(); void writeIsoImage(); void updateProgressBar(int increment); void showWritingProgress(int maxValue); void hideWritingProgress(); void showErrorMessage(const QString &message); void showSuccessMessage(); void showConfirmMessage(); void showIsoVerificationResult(const bool &isIsoValid, const QString &error); #if defined(Q_OS_LINUX) void cancelWriting(); void progressStep(KJob* job, unsigned long step); void progressStep(const QVariantMap &); void statusChanged(KAuth::Action::AuthStatus status); void finished(KJob* job); #endif }; #endif // MAINWINDOW_H