diff --git a/CMakeLists.txt b/CMakeLists.txt index 82b8ea3..38473d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,44 +1,40 @@ project(imagewriter) set(PROJECT_VERSION "0.1") cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(QT_MIN_VERSION "5.4.0") set(KF5_REQUIRED_VERSION "5.56.0") find_package(ECM 1.1.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMPackageConfigHelpers) include(ECMOptionalAddSubdirectory) include(ECMQtDeclareLoggingCategory) option(ROSA_BRANDING "Build with ROSA branding" OFF) -option(KAUTH "Build with KAuth support") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core Gui Widgets) find_package(KF5 ${KF5_REQUIRED_VERSION} REQUIRED COMPONENTS I18n CoreAddons WidgetsAddons IconThemes ) -if(KAUTH) - find_package(KF5Auth REQUIRED) -endif() - if(CMAKE_SYSTEM_NAME STREQUAL Linux) - find_package(UDev REQUIRED) + find_package(UDev REQUIRED) + find_package(KF5Auth REQUIRED) endif() KDE_ENABLE_EXCEPTIONS() add_subdirectory(isoimagewriter) add_subdirectory(images) add_subdirectory(signing-keys) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/isoimagewriter/CMakeLists.txt b/isoimagewriter/CMakeLists.txt index 86844e6..45728ab 100644 --- a/isoimagewriter/CMakeLists.txt +++ b/isoimagewriter/CMakeLists.txt @@ -1,103 +1,96 @@ set(GPGME_REQUIRED_VERSION "1.8.0") find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) #find_package(KF5 REQUIRED COMPONENTS QGpgme) set(isoimagewriter_SRCS usbdevicemonitor.h common.cpp mainapplication.cpp maindialog.cpp mainwindow.cpp imagewriter.cpp physicaldevice.cpp main.cpp verifyisoworker.cpp verifyiso.cpp verifyneoniso.cpp verifykubuntuiso.cpp verifynetrunneriso.cpp verifyarchiso.cpp ) ecm_qt_declare_logging_category(isoimagewriter_SRCS HEADER isoimagewriter_debug.h IDENTIFIER ISOIMAGEWRITER_LOG CATEGORY_NAME org.kde.isoimagewriter DEFAULT_SEVERITY Debug) if(CMAKE_SYSTEM_NAME STREQUAL Windows) message("Compiling isoimagewriter for Windaes") set(isoimagewriter_SRCS ${isoimagewriter_SRCS} platform_win.cpp usbdevicemonitor_win.cpp externalprogressbar_win.cpp ) elseif(CMAKE_SYSTEM_NAME STREQUAL Linux) message("Compiling isoimagewriter for Linux") set(isoimagewriter_SRCS ${isoimagewriter_SRCS} platform_lin.cpp usbdevicemonitor_lin.cpp externalprogressbar_lin.cpp ) else() message("Unsupported Platform " . ${CMAKE_SYSTEM_NAME}) endif() 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}") -if(KAUTH) - add_compile_definitions(KAUTH) -endif() - target_link_libraries(isoimagewriter Qt5::Widgets KF5::I18n KF5::CoreAddons QGpgme KF5::WidgetsAddons KF5::IconThemes ) if(CMAKE_SYSTEM_NAME STREQUAL Linux) - target_link_libraries(isoimagewriter udev) -endif() -if(KAUTH) - target_link_libraries(isoimagewriter KF5::AuthCore) + target_link_libraries(isoimagewriter udev KF5::AuthCore) endif() #target_link_libraries(testy # Qt5::Widgets # KF5::I18n # KF5::CoreAddons # dl # QGpgme # Gpgmepp #) install(TARGETS isoimagewriter ${INSTALL_TARGETS_DEFAULT_ARGS}) if(CMAKE_SYSTEM_NAME STREQUAL Linux) install(PROGRAMS org.kde.isoimagewriter.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install(FILES org.kde.isoimagewriter.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) endif() # KAuth -if(KAUTH) +if(CMAKE_SYSTEM_NAME STREQUAL Linux) add_executable(isoimagewriter_helper common.cpp imagewriter_helper.cpp imagewriter.cpp physicaldevice.cpp) target_link_libraries(isoimagewriter_helper Qt5::Widgets KF5::AuthCore KF5::I18n KF5::WidgetsAddons) 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) endif() diff --git a/isoimagewriter/imagewriter.cpp b/isoimagewriter/imagewriter.cpp index 1cc0287..f8b4e1d 100644 --- a/isoimagewriter/imagewriter.cpp +++ b/isoimagewriter/imagewriter.cpp @@ -1,263 +1,263 @@ /* * Copyright 2016 ROSA * 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 ImageWriter #include "imagewriter.h" #include -#if defined(KAUTH) +#if defined(Q_OS_LINUX) #include #endif #include #include "common.h" #include "physicaldevice.h" ImageWriter::ImageWriter(const QString& ImageFile, UsbDevice* Device, QObject *parent) : QObject(parent), m_Device(Device), m_ImageFile(ImageFile), m_CancelWriting(false) { } // The main method that writes the image void ImageWriter::writeImage() { qDebug() << "XX writeImage()"; const qint64 TRANSFER_BLOCK_SIZE = 1024 * 1024; void* buffer = NULL; bool isError = false; bool cancelRequested = false; bool zeroing = (m_ImageFile == ""); // Using try-catch for processing errors // Invalid values are used for indication non-initialized objects; // after the try-catch block all the initialized objects are freed try { #if defined(Q_OS_WIN32) // Using VirtualAlloc so that the buffer was properly aligned (required for // direct access to devices and for unbuffered reading/writing) buffer = VirtualAlloc(NULL, TRANSFER_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (buffer == NULL) throw formatErrorMessageFromCode(i18n("Failed to allocate memory for buffer:")); #elif defined(Q_OS_LINUX) || defined(Q_OS_MAC) buffer = malloc(TRANSFER_BLOCK_SIZE); if (buffer == NULL) throw i18n("Failed to allocate memory for buffer."); #endif QFile imageFile; if (zeroing) { // Prepare zero-filled buffer memset(buffer, 0, TRANSFER_BLOCK_SIZE); } else { // Open the source image file for reading imageFile.setFileName(m_ImageFile); if (!imageFile.open(QIODevice::ReadOnly)) throw i18n("Failed to open the image file: %1", imageFile.errorString()); } // Unmount volumes that belong to the selected target device // TODO: Check first if they are used and show warning // (problem: have to show request in the GUI thread and return reply back here) QStringList errMessages; #if defined(Q_OS_WIN32) for (int i = 0; i < m_Device->m_Volumes.size(); ++i) { DWORD bret; HANDLE volume = CreateFile( reinterpret_cast(("\\\\.\\" + m_Device->m_Volumes[i]).utf16()), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (volume == INVALID_HANDLE_VALUE) { errMessages << formatErrorMessageFromCode(i18n("Failed to open the drive %1", m_Device->m_Volumes[i])); continue; } // Trying to lock the volume but ignore if we failed (such call seems to be required for // dismounting the volume on WinXP) DeviceIoControl(volume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bret, NULL); if (!DeviceIoControl(volume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bret, NULL)) errMessages << formatErrorMessageFromCode(i18n("Failed to unmount the drive %1", m_Device->m_Volumes[i])); CloseHandle(volume); volume = INVALID_HANDLE_VALUE; } #elif defined(Q_OS_MAC) struct statfs* mntEntries = NULL; int mntEntriesNum = getmntinfo(&mntEntries, MNT_WAIT); for (int i = 0; i < mntEntriesNum; ++i) { for (int j = 0; j < m_Device->m_Volumes.size(); ++j) { // Check that the mount point is either our target device itself or a partition on it if ((mntEntries[i].f_mntfromname == m_Device->m_Volumes[j]) || QString(mntEntries[i].f_mntfromname).startsWith(m_Device->m_Volumes[j] + 's')) { // Mount point is the selected device or one of its partitions - try to unmount it if (unmount(mntEntries[i].f_mntonname, MNT_FORCE) != 0) errMessages << i18n("Failed to unmount the volume %1\n%2", m_Device->m_Volumes[i], strerror(errno)); } } } #endif if (errMessages.size() > 0) throw errMessages.join("\n\n"); // Open the target USB device for writing and lock it PhysicalDevice deviceFile(m_Device->m_PhysicalDevice); if (!deviceFile.open()) throw i18n("Failed to open the target device:\n%1", deviceFile.errorString()); qint64 readBytes; qint64 writtenBytes; // Start reading/writing cycle for (;;) { qDebug() << "For Loop3"; -#if defined(KAUTH) +#if defined(Q_OS_LINUX) if (KAuth::HelperSupport::isStopped()) { qDebug() << "isStopped"; } else { qDebug() << "not isStopped"; } #endif if (zeroing) { readBytes = TRANSFER_BLOCK_SIZE; } else { if ((readBytes = imageFile.read(static_cast(buffer), TRANSFER_BLOCK_SIZE)) <= 0) break; } // Align the number of bytes to the sector size readBytes = alignNumber(readBytes, (qint64)m_Device->m_SectorSize); writtenBytes = deviceFile.write(static_cast(buffer), readBytes); if (writtenBytes < 0) throw i18n("Failed to write to the device:\n%1", deviceFile.errorString()); if (writtenBytes != readBytes) throw i18n("The last block was not fully written (%1 of %2 bytes)!\nAborting.", writtenBytes, readBytes); #if defined(Q_OS_LINUX) || defined(Q_OS_MAC) // In Linux/MacOS the USB device is opened with buffering. Using forced sync to validate progress bar. // For unknown reason, deviceFile.flush() does not work as intended here. fsync(deviceFile.handle()); #endif // Inform the GUI thread that next block was written // TODO: Make sure that when TRANSFER_BLOCK_SIZE is not a multiple of DEFAULT_UNIT // this still works or at least fails compilation emit blockWritten(TRANSFER_BLOCK_SIZE / DEFAULT_UNIT); -#if defined(KAUTH) +#if defined(Q_OS_LINUX) KAuth::HelperSupport::progressStep(TRANSFER_BLOCK_SIZE / DEFAULT_UNIT); // FIXME why doesn't this work? QVariantMap progressArgs; progressArgs[QStringLiteral("progress")] = TRANSFER_BLOCK_SIZE / DEFAULT_UNIT; KAuth::HelperSupport::progressStep(progressArgs); // used because above doesn't work #endif // Check for the cancel request (using temporary variable to avoid multiple unlock calls in the code) m_Mutex.lock(); -#if defined(KAUTH) +#if defined(Q_OS_LINUX) cancelRequested = KAuth::HelperSupport::isStopped(); #else cancelRequested = m_CancelWriting; #endif m_Mutex.unlock(); if (cancelRequested) { qDebug() << "cancelRequested"; // The cancel request was issued emit cancelled(); break; } if (zeroing) { // In zeroing mode only write 1 block - 1 MB is enough to clear both MBR and GPT break; } } if (!zeroing) { if (readBytes < 0) throw i18n("Failed to read the image file:\n%1", imageFile.errorString()); imageFile.close(); } deviceFile.close(); } catch (QString msg) { // Something went wrong :-( -#if defined(KAUTH) +#if defined(Q_OS_LINUX) QVariantMap args; args[QStringLiteral("error")] = msg; KAuth::HelperSupport::progressStep(args); #endif emit error(msg); isError = true; } if (buffer != NULL) #if defined(Q_OS_WIN32) VirtualFree(buffer, TRANSFER_BLOCK_SIZE, MEM_DECOMMIT | MEM_RELEASE); #elif defined(Q_OS_LINUX) || defined(Q_OS_MAC) free(buffer); #endif // If no errors occurred and user did not stop the operation, it means everything went fine if (!isError && !cancelRequested) { QString message = i18n("The operation completed successfully.") + "

" + (zeroing ? i18n("Now you need to format your device.") : i18n("To be able to store data on this device again, please, use the button \"Wipe USB Disk\".")); -#if defined(KAUTH) +#if defined(Q_OS_LINUX) QVariantMap args; args[QStringLiteral("success")] = message; KAuth::HelperSupport::progressStep(args); #endif emit success(message); } // In any case the operation is finished emit finished(); } // Implements reaction to the cancel request from user void ImageWriter::cancelWriting() { m_Mutex.lock(); m_CancelWriting = true; m_Mutex.unlock(); } diff --git a/isoimagewriter/maindialog.cpp b/isoimagewriter/maindialog.cpp index 3e4fc87..745ded5 100644 --- a/isoimagewriter/maindialog.cpp +++ b/isoimagewriter/maindialog.cpp @@ -1,684 +1,684 @@ /* * 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.isEmpty()) { 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.isEmpty()) { // If something was really 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.isEmpty()) { 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.isEmpty()) 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; } -#if defined(KAUTH) +#if defined(Q_OS_LINUX) // Starts writing data to the device void MainDialog::writeToDeviceKAuth(bool zeroing) { qCDebug(ISOIMAGEWRITER_LOG) << "writeToDeviceKAuth()"; if ((ui->deviceList->count() == 0) || (!zeroing && (m_ImageFile.isEmpty()))) 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*,ulong)), this, SLOT(progressStep(KJob*,ulong)), Qt::DirectConnection); connect(m_job, SIGNAL(newData(QVariantMap)), this, SLOT(progressStep(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(); } #else void MainDialog::writeToDevice(bool zeroing) { 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, tr("The image is larger than your selected device!") + "\n" + tr("Image size:") + " " + QString::number(m_ImageSize / DEFAULT_UNIT) + " " + tr("MB") + " (" + currentLocale.toString(m_ImageSize) + " " + tr("b") + ")\n" + tr("Disk size:") + " " + QString::number(selectedDevice->m_Size / DEFAULT_UNIT) + " " + tr("MB") + " (" + currentLocale.toString(selectedDevice->m_Size) + " " + tr("b") + ")", QMessageBox::Ok ); return; } if (QMessageBox::warning( this, ApplicationTitle, "" + tr("Warning!") + " " + tr("All existing data on the selected device will be lost!") + "
" + tr("Are you sure you wish to proceed?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) return; showWritingProgress(alignNumberDiv((zeroing ? DEFAULT_UNIT : m_ImageSize), DEFAULT_UNIT)); ImageWriter* writer = new ImageWriter(zeroing ? "" : m_ImageFile, 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, &MainDialog::updateProgressBar); // Show the message about successful completion on success connect(writer, &ImageWriter::success, this, &MainDialog::showSuccessMessage); // Show error message if error is sent by the worker connect(writer, &ImageWriter::error, this, &MainDialog::showErrorMessage); // Silently return back to normal dialog form if the operation was cancelled connect(writer, &ImageWriter::cancelled, this, &MainDialog::hideWritingProgress); // Now start the writer thread writer->moveToThread(writerThread); writerThread->start(); } #endif // Starts writing the image void MainDialog::writeImageToDevice() { -#if defined(KAUTH) +#if defined(Q_OS_LINUX) writeToDeviceKAuth(false); #else writeToDevice(false); #endif } // Clears the selected USB device void MainDialog::clearDevice() { -#if defined(KAUTH) +#if defined(Q_OS_LINUX) writeToDeviceKAuth(true); #else writeToDevice(true); #endif } // 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/maindialog.h b/isoimagewriter/maindialog.h index 6f0fae5..10dff44 100644 --- a/isoimagewriter/maindialog.h +++ b/isoimagewriter/maindialog.h @@ -1,135 +1,135 @@ /* * Copyright 2016 ROSA * 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 MAINDIALOG_H #define MAINDIALOG_H //////////////////////////////////////////////////////////////////////////////// // MainDialog is the main application window -#if defined(KAUTH) +#if defined(Q_OS_LINUX) #include #endif #include #include #include "common.h" #include "externalprogressbar.h" namespace Ui { class MainDialog; } enum VerificationResult { Fine, DinnaeKen, Invalid }; struct IsoResult { VerificationResult resultType; QString error; }; class MainDialog : public QDialog { Q_OBJECT public: explicit MainDialog(QWidget *parent = 0); ~MainDialog(); private: Ui::MainDialog *ui; QPushButton *m_writeButton, *m_clearButton, *m_cancelButton; -#if defined(KAUTH) +#if defined(Q_OS_LINUX) KAuth::ExecuteJob *m_job; #endif IsoResult verifyISO(); protected: // Image file currently selected by the user QString m_ImageFile; // Size of the image file (cached here to avoid excessive file system requests) quint64 m_ImageSize; // Remember the last opened directory to suggest it automatically on next Open QString m_LastOpenedDir; // Whether image is being written at the moment or not bool m_IsWriting; // Flag indicating that flash disks enumerating is pending bool m_EnumFlashDevicesWaiting; // Abstraction layer for projecting the progress bar into operating system (if supported) ExternalProgressBar m_ExtProgressBar; // Retrieves information about the selected file and displays it in the dialog void preprocessImageFile(const QString& newImageFile); // Starts writing data to the device -#if defined(KAUTH) +#if defined(Q_OS_LINUX) void writeToDeviceKAuth(bool zeroing); #else void writeToDevice(bool zeroing); #endif // Frees the GUI-specific allocated resources void cleanup(); // Reimplemented event handlers for drag&drop support void dragEnterEvent(QDragEnterEvent* event) override; void dropEvent(QDropEvent* event) override; // Reimplemented event handlers for protecting dialog closing during operation void closeEvent(QCloseEvent* event) override; void keyPressEvent(QKeyEvent* event) override; // Reloads the list of USB flash disks void enumFlashDevices(); KPixmapSequenceOverlayPainter *m_busyWidget; public slots: // Suggests to select image file using the Open File dialog void openImageFile(); // Schedules reloading the list of USB flash disks to run when possible void scheduleEnumFlashDevices(); // Starts writing the image void writeImageToDevice(); // Clears the selected USB device void clearDevice(); // Updates GUI to the "writing" mode (progress bar shown, controls disabled) // Also sets the progress bar limits void showWritingProgress(int maxValue); // Updates GUI to the "idle" mode (progress bar hidden, controls enabled) void hideWritingProgress(); // Increments the progress bar counter by the specified number void updateProgressBar(int increment); // Displays the message about successful completion and returns to the "idle" mode void showSuccessMessage(QString msg); // Displays the specified error message and returns to the "idle" mode void showErrorMessage(QString msg); -#if defined(KAUTH) +#if defined(Q_OS_LINUX) //cancel button clicked 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 // MAINDIALOG_H