diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,8 @@ pkg_check_modules(FLATPAK flatpak>=0.6.12) +find_package(LIBFWUPD 1.0.7 REQUIRED) + if(NOT CMAKE_VERSION VERSION_LESS "3.10.0") # CMake 3.9+ warns about automoc on files without Q_OBJECT, and doesn't know about other macros. # 3.10+ lets us provide more macro names that require automoc. @@ -77,5 +79,10 @@ URL "http://www.freedesktop.org" PURPOSE "Required to build the Flatpak backend" TYPE OPTIONAL) +set_package_properties(LIBFWUPD PROPERTIES + DESCRIPTION "Library that exposes fwupd" + URL "http://www.fwupd.org" + PURPOSE "Required to build the Fwupd backend" + TYPE OPTIONAL) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/cmake/FindGIO.cmake b/cmake/FindGIO.cmake new file mode 100644 --- /dev/null +++ b/cmake/FindGIO.cmake @@ -0,0 +1,198 @@ +# FindGIO.cmake +# +# +# CMake support for GIO. +# +# License: +# +# Copyright (c) 2016 Evan Nemerson +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +find_package(PkgConfig) + +set(GIO_DEPS + GObject) + +if(PKG_CONFIG_FOUND) + pkg_search_module(GIO_PKG gio-2.0) +endif() + +find_library(GIO_LIBRARY gio-2.0 HINTS ${GIO_PKG_LIBRARY_DIRS}) +set(GIO "gio-2.0") + +if(GIO_LIBRARY AND NOT GIO_FOUND) + add_library(${GIO} SHARED IMPORTED) + set_property(TARGET ${GIO} PROPERTY IMPORTED_LOCATION "${GIO_LIBRARY}") + set_property(TARGET ${GIO} PROPERTY INTERFACE_COMPILE_OPTIONS "${GIO_PKG_CFLAGS_OTHER}") + + find_path(GIO_INCLUDE_DIR "gio/gio.h" + HINTS ${GIO_PKG_INCLUDE_DIRS}) + + find_package(GLib) + find_package(GObject) + set(GIO_VERSION "${GLib_VERSION}") + + list(APPEND GIO_DEPS_FOUND_VARS "GObject_FOUND") + list(APPEND GIO_INCLUDE_DIRS ${GObject_INCLUDE_DIRS}) + + set_property (TARGET "${GIO}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "gobject-2.0") + set_property(TARGET ${GIO} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${GIO_INCLUDE_DIR}") +endif() + +find_program(GLib_COMPILE_SCHEMAS glib-compile-schemas) +if(GLib_COMPILE_SCHEMAS AND NOT GLib_FOUND) + add_executable(glib-compile-schemas IMPORTED) + set_property(TARGET glib-compile-schemas PROPERTY IMPORTED_LOCATION "${GLib_COMPILE_SCHEMAS}") +endif() + +# glib_install_schemas( +# [DESTINATION directory] +# schemas…) +# +# Validate and install the listed schemas. +function(glib_install_schemas) + set (options) + set (oneValueArgs DESTINATION) + set (multiValueArgs) + cmake_parse_arguments(GSCHEMA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + unset (options) + unset (oneValueArgs) + unset (multiValueArgs) + + foreach(schema ${GSCHEMA_UNPARSED_ARGUMENTS}) + get_filename_component(schema_name "${schema}" NAME) + string(REGEX REPLACE "^(.+)\.gschema.xml$" "\\1" schema_name "${schema_name}") + set(schema_output "${CMAKE_CURRENT_BINARY_DIR}/${schema_name}.gschema.xml.valid") + + add_custom_command( + OUTPUT "${schema_output}" + COMMAND glib-compile-schemas + --strict + --dry-run + --schema-file="${schema}" + COMMAND "${CMAKE_COMMAND}" ARGS -E touch "${schema_output}" + DEPENDS "${schema}" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Validating ${schema}") + + add_custom_target("gsettings-schema-${schema_name}" ALL + DEPENDS "${schema_output}") + + if(CMAKE_INSTALL_FULL_DATADIR) + set(SCHEMADIR "${CMAKE_INSTALL_FULL_DATADIR}/glib-2.0/schemas") + else() + set(SCHEMADIR "${CMAKE_INSTALL_PREFIX}/share/glib-2.0/schemas") + endif() + install(FILES "${schema}" + DESTINATION "${SCHEMADIR}") + install(CODE "execute_process(COMMAND \"${GLib_COMPILE_SCHEMAS}\" \"${SCHEMADIR}\")") + endforeach() +endfunction() + +find_program(GLib_COMPILE_RESOURCES glib-compile-resources) +if(GLib_COMPILE_RESOURCES AND NOT GLib_FOUND) + add_executable(glib-compile-resources IMPORTED) + set_property(TARGET glib-compile-resources PROPERTY IMPORTED_LOCATION "${GLib_COMPILE_RESOURCES}") +endif() + +function(glib_compile_resources SPEC_FILE) + set (options INTERNAL) + set (oneValueArgs TARGET SOURCE_DIR HEADER SOURCE C_NAME) + set (multiValueArgs) + cmake_parse_arguments(GLib_COMPILE_RESOURCES "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + unset (options) + unset (oneValueArgs) + unset (multiValueArgs) + + if(NOT GLib_COMPILE_RESOURCES_SOURCE_DIR) + set(GLib_COMPILE_RESOURCES_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + set(FLAGS) + + if(GLib_COMPILE_RESOURCES_INTERNAL) + list(APPEND FLAGS "--internal") + endif() + + if(GLib_COMPILE_RESOURCES_C_NAME) + list(APPEND FLAGS "--c-name" "${GLib_COMPILE_RESOURCES_C_NAME}") + endif() + + get_filename_component(SPEC_FILE "${SPEC_FILE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + + execute_process( + COMMAND glib-compile-resources + --generate-dependencies + --sourcedir "${GLib_COMPILE_RESOURCES_SOURCE_DIR}" + "${SPEC_FILE}" + OUTPUT_VARIABLE deps + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(REPLACE "\n" ";" deps ${deps}) + + if(GLib_COMPILE_RESOURCES_HEADER) + get_filename_component(GLib_COMPILE_RESOURCES_HEADER "${GLib_COMPILE_RESOURCES_HEADER}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") + + add_custom_command( + OUTPUT "${GLib_COMPILE_RESOURCES_HEADER}" + COMMAND glib-compile-resources + --sourcedir "${GLib_COMPILE_RESOURCES_SOURCE_DIR}" + --generate-header + --target "${GLib_COMPILE_RESOURCES_HEADER}" + ${FLAGS} + "${SPEC_FILE}" + DEPENDS "${SPEC_FILE}" ${deps} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + if(GLib_COMPILE_RESOURCES_SOURCE) + get_filename_component(GLib_COMPILE_RESOURCES_SOURCE "${GLib_COMPILE_RESOURCES_SOURCE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") + + add_custom_command( + OUTPUT "${GLib_COMPILE_RESOURCES_SOURCE}" + COMMAND glib-compile-resources + --sourcedir "${GLib_COMPILE_RESOURCES_SOURCE_DIR}" + --generate-source + --target "${GLib_COMPILE_RESOURCES_SOURCE}" + ${FLAGS} + "${SPEC_FILE}" + DEPENDS "${SPEC_FILE}" ${deps} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + endif() +endfunction() + +find_program(GDBUS_CODEGEN gdbus-codegen) +if(GDBUS_CODEGEN AND NOT GLib_FOUND) + add_executable(gdbus-codegen IMPORTED) + set_property(TARGET gdbus-codegen PROPERTY IMPORTED_LOCATION "${GDBUS_CODEGEN}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GIO + REQUIRED_VARS + GIO_LIBRARY + GIO_INCLUDE_DIRS + ${GIO_DEPS_FOUND_VARS} + VERSION_VAR + GIO_VERSION) + +unset(GIO_DEPS_FOUND_VARS) diff --git a/cmake/FindGLib.cmake b/cmake/FindGLib.cmake new file mode 100644 --- /dev/null +++ b/cmake/FindGLib.cmake @@ -0,0 +1,83 @@ +# FindGLib.cmake +# +# +# CMake support for GLib/GObject/GIO. +# +# License: +# +# Copyright (c) 2016 Evan Nemerson +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +find_package(PkgConfig) + +if(PKG_CONFIG_FOUND) + pkg_search_module(GLib_PKG glib-2.0) +endif() + +find_library(GLib_LIBRARY glib-2.0 HINTS ${GLib_PKG_LIBRARY_DIRS}) +set(GLib glib-2.0) + +if(GLib_LIBRARY AND NOT GLib_FOUND) + add_library(${GLib} SHARED IMPORTED) + set_property(TARGET ${GLib} PROPERTY IMPORTED_LOCATION "${GLib_LIBRARY}") + set_property(TARGET ${GLib} PROPERTY INTERFACE_COMPILE_OPTIONS "${GLib_PKG_CFLAGS_OTHER}") + + find_path(GLib_INCLUDE_DIRS "glib.h" + HINTS ${GLib_PKG_INCLUDE_DIRS} + PATH_SUFFIXES "glib-2.0") + + get_filename_component(GLib_LIBDIR "${GLib}" DIRECTORY) + find_path(GLib_CONFIG_INCLUDE_DIR "glibconfig.h" + HINTS + ${GLib_LIBDIR} + ${GLib_PKG_INCLUDE_DIRS} + PATHS + "${CMAKE_LIBRARY_PATH}" + PATH_SUFFIXES + "glib-2.0/include" + "glib-2.0") + unset(GLib_LIBDIR) + + if(GLib_CONFIG_INCLUDE_DIR) + file(STRINGS "${GLib_CONFIG_INCLUDE_DIR}/glibconfig.h" GLib_MAJOR_VERSION REGEX "^#define GLIB_MAJOR_VERSION +([0-9]+)") + string(REGEX REPLACE "^#define GLIB_MAJOR_VERSION ([0-9]+)$" "\\1" GLib_MAJOR_VERSION "${GLib_MAJOR_VERSION}") + file(STRINGS "${GLib_CONFIG_INCLUDE_DIR}/glibconfig.h" GLib_MINOR_VERSION REGEX "^#define GLIB_MINOR_VERSION +([0-9]+)") + string(REGEX REPLACE "^#define GLIB_MINOR_VERSION ([0-9]+)$" "\\1" GLib_MINOR_VERSION "${GLib_MINOR_VERSION}") + file(STRINGS "${GLib_CONFIG_INCLUDE_DIR}/glibconfig.h" GLib_MICRO_VERSION REGEX "^#define GLIB_MICRO_VERSION +([0-9]+)") + string(REGEX REPLACE "^#define GLIB_MICRO_VERSION ([0-9]+)$" "\\1" GLib_MICRO_VERSION "${GLib_MICRO_VERSION}") + set(GLib_VERSION "${GLib_MAJOR_VERSION}.${GLib_MINOR_VERSION}.${GLib_MICRO_VERSION}") + unset(GLib_MAJOR_VERSION) + unset(GLib_MINOR_VERSION) + unset(GLib_MICRO_VERSION) + + list(APPEND GLib_INCLUDE_DIRS ${GLib_CONFIG_INCLUDE_DIR}) + set_property(TARGET ${GLib} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${GLib_INCLUDE_DIRS}") + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLib + REQUIRED_VARS + GLib_LIBRARY + GLib_INCLUDE_DIRS + VERSION_VAR + GLib_VERSION) diff --git a/cmake/FindGObject.cmake b/cmake/FindGObject.cmake new file mode 100644 --- /dev/null +++ b/cmake/FindGObject.cmake @@ -0,0 +1,81 @@ +# FindGObject.cmake +# +# +# CMake support for GObject. +# +# License: +# +# Copyright (c) 2016 Evan Nemerson +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. + +find_package(PkgConfig) + +set(GObject_DEPS + GLib) + +if(PKG_CONFIG_FOUND) + pkg_search_module(GObject_PKG gobject-2.0) +endif() + +find_library(GObject_LIBRARY gobject-2.0 HINTS ${GObject_PKG_LIBRARY_DIRS}) +set(GObject gobject-2.0) + +if(GObject_LIBRARY AND NOT GObject_FOUND) + add_library(${GObject} SHARED IMPORTED) + set_property(TARGET ${GObject} PROPERTY IMPORTED_LOCATION "${GObject_LIBRARY}") + set_property(TARGET ${GObject} PROPERTY INTERFACE_COMPILE_OPTIONS "${GObject_PKG_CFLAGS_OTHER}") + + find_path(GObject_INCLUDE_DIR "gobject/gobject.h" + HINTS ${GObject_PKG_INCLUDE_DIRS}) + + find_package(GLib) + set(GObject_VERSION "${GLib_VERSION}") + + list(APPEND GObject_DEPS_FOUND_VARS "GLib_FOUND") + list(APPEND GObject_INCLUDE_DIRS ${GLib_INCLUDE_DIRS}) + set_property(TARGET ${GObject} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${GObject_INCLUDE_DIR}") + + set_property (TARGET "${GObject}" APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${GLib}") +endif() + +find_program(GLib_GENMARSHAL glib-genmarshal) +if(GLib_GENMARSHAL AND NOT GLib_FOUND) + add_executable(glib-genmarshal IMPORTED) + set_property(TARGET glib-genmarshal PROPERTY IMPORTED_LOCATION "${GLib_GENMARSHAL}") +endif() + +find_program(GLib_MKENUMS glib-mkenums) +if(GLib_MKENUMS AND NOT GLib_FOUND) + add_executable(glib-mkenums IMPORTED) + set_property(TARGET glib-mkenums PROPERTY IMPORTED_LOCATION "${GLib_MKENUMS}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GObject + REQUIRED_VARS + GObject_LIBRARY + GObject_INCLUDE_DIRS + ${GObject_DEPS_FOUND_VARS} + VERSION_VAR + GObject_VERSION) + +unset(GObject_DEPS_FOUND_VARS) diff --git a/cmake/FindLIBFWUPD.cmake b/cmake/FindLIBFWUPD.cmake new file mode 100644 --- /dev/null +++ b/cmake/FindLIBFWUPD.cmake @@ -0,0 +1,58 @@ +# - Try to find the Fwupd library +# Once done this will define +# +# LIBFWUPD_FOUND - system has the fwupd library +# LIBFWUPD_INCLUDE_DIR - the Fwupd include directory +# LIBFWUPD_LIBRARY - Link this to use the fwupd +# +# Copyright © 2018, Abhijeet Sharma +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +if(LIBFWUPD_INCLUDE_DIRS AND LIBFWUPD_LIBRARIES) + set(LIBFWUPD_FOUND TRUE) + +else (LIBFWUPD_INCLUDE_DIRS AND LIBFWUPD_LIBRARIES) + + find_library (LIBFWUPD_LIBRARIES + NAMES fwupd libfwupd + ) + + find_path (LIBFWUPD_INCLUDE_DIRS + NAMES fwupd.h + PATH_SUFFIXES fwupd-1 + HINTS fwupd-1/libfwupd + ) + set(LIBFWUPD_FOUND TRUE) + +endif (LIBFWUPD_INCLUDE_DIRS AND LIBFWUPD_LIBRARIES) + +if (LIBFWUPD_FOUND) + add_library(LIBFWUPD SHARED IMPORTED) + set_target_properties(LIBFWUPD PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${LIBFWUPD_INCLUDE_DIRS} + IMPORTED_LOCATION ${LIBFWUPD_LIBRARIES} + ) +endif (LIBFWUPD_FOUND) diff --git a/discover/qml/SourcesPage.qml b/discover/qml/SourcesPage.qml --- a/discover/qml/SourcesPage.qml +++ b/discover/qml/SourcesPage.qml @@ -45,6 +45,10 @@ Connections { target: backendItem.backend onPassiveMessage: window.showPassiveNotification(message) + onProceedRequest: { + var dialog = sourceProceedDialog.createObject(window, {sourcesBackend: sourcesBackend, title: title, description: description}) + dialog.open() + } } anchors { @@ -106,6 +110,7 @@ action: modelData.action } } + onObjectAdded: { settingsMenu.insertItem(index, object) } @@ -137,6 +142,56 @@ componentTrue: sourceBackendDelegate componentFalse: sourceDelegate } + Component { + id: sourceProceedDialog + Kirigami.OverlaySheet { + id: sheet + showCloseButton: false + property QtObject sourcesBackend + property alias title: heading.text + property alias description: desc.text + property bool acted: false + ColumnLayout { + Kirigami.Heading { + id: heading + } + Label { + id: desc + Layout.fillWidth: true + textFormat: Text.StyledText + wrapMode: Text.WordWrap + } + RowLayout { + Layout.alignment: Qt.AlignRight + Button { + text: i18n("Proceed") + icon.name: "dialog-ok" + onClicked: { + sourcesBackend.proceed() + sheet.acted = true + sheet.close() + } + } + Button { + Layout.alignment: Qt.AlignRight + text: i18n("Cancel") + icon.name: "dialog-cancel" + onClicked: { + sourcesBackend.cancel() + sheet.acted = true + sheet.close() + } + } + } + } + onSheetOpenChanged: if(!sheetOpen) { + sheet.destroy(1000) + if (!sheet.acted) + sourcesBackend.cancel() + } + } + } + Component { id: sourceDelegate @@ -188,7 +243,7 @@ checked: modelChecked != Qt.Unchecked enabled: modelChecked !== undefined onClicked: { - sourcesView.model.setData(modelIndex, checkedState, Qt.CheckStateRole) + sourcesView.model.setData(modelIndex, checkState, Qt.CheckStateRole) } } Label { diff --git a/libdiscover/backends/CMakeLists.txt b/libdiscover/backends/CMakeLists.txt --- a/libdiscover/backends/CMakeLists.txt +++ b/libdiscover/backends/CMakeLists.txt @@ -39,3 +39,8 @@ if(BUILD_SnapBackend AND Snapd_FOUND) add_subdirectory(SnapBackend) endif() + +option(BUILD_FwupdBackend "Build Fwupd support." "ON") +if(BUILD_FwupdBackend AND LIBFWUPD_FOUND) + add_subdirectory(FwupdBackend) +endif() \ No newline at end of file diff --git a/libdiscover/backends/FwupdBackend/CMakeLists.txt b/libdiscover/backends/FwupdBackend/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/CMakeLists.txt @@ -0,0 +1,17 @@ +add_definitions( -DPROJECT_NAME=${PROJECT_NAME} -DPROJECT_VERSION=${PROJECT_VERSION}) + +set(fwupd-backend_SRCS + FwupdResource.cpp + FwupdBackend.cpp + FwupdTransaction.cpp + FwupdSourcesBackend.cpp +) + +find_package(GIO) + +include_directories(${LIBFWUPD_INCLUDE_DIRS} ) +add_library(fwupd-backend MODULE ${fwupd-backend_SRCS}) +target_link_libraries(fwupd-backend Qt5::Core Qt5::Widgets KF5::CoreAddons KF5::ConfigCore Discover::Common LIBFWUPD ${GIO} ) + +install(TARGETS fwupd-backend DESTINATION ${PLUGIN_INSTALL_DIR}/discover) + diff --git a/libdiscover/backends/FwupdBackend/FwupdBackend.h b/libdiscover/backends/FwupdBackend/FwupdBackend.h new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/FwupdBackend.h @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright © 2013 Aleix Pol Gonzalez * + * Copyright © 2018 Abhijeet Sharma * + * * + * 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 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * 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 FWUPDBACKEND_H +#define FWUPDBACKEND_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern "C" { +#include +} +#include + +class QAction; +class StandardBackendUpdater; +class FwupdResource; +class FwupdBackend : public AbstractResourcesBackend +{ +Q_OBJECT +Q_PROPERTY(int startElements MEMBER m_startElements) +Q_ENUMS(Modes) +public: + explicit FwupdBackend(QObject* parent = NULL); + ~FwupdBackend(); + + int updatesCount() const override; + AbstractBackendUpdater* backendUpdater() const override; + AbstractReviewsBackend* reviewsBackend() const override; + ResultsStream* search(const AbstractResourcesBackend::Filters & search) override; + ResultsStream * findResourceByPackageName(const QUrl& search) ; + QHash resources() const { return m_resources; } + bool isValid() const override { return true; } // No external file dependencies that could cause runtime errors + + Transaction* installApplication(AbstractResource* app) override; + Transaction* installApplication(AbstractResource* app, const AddonList& addons) override; + Transaction* removeApplication(AbstractResource* app) override; + bool isFetching() const override { return m_fetching; } + AbstractResource * resourceForFile(const QUrl & ) override; + void checkForUpdates() override; + QString displayName() const override; + bool hasApplications() const override; + FwupdClient *client; + + bool downloadFile(const QUrl &uri,const QString &filename); + bool refreshRemotes(uint cacheAge); + bool refreshRemote(FwupdRemote *remote,uint cacheAge); + const QUrl cacheFile(const QString &kind,const QFileInfo &resource); + FwupdResource * createDevice(FwupdDevice *device); + FwupdResource * createRelease(FwupdDevice *device); + FwupdResource * createApp(FwupdDevice *device); + QByteArray getChecksum(const QUrl filename,QCryptographicHash::Algorithm hashAlgorithm); + QString buildDeviceID(FwupdDevice* device); + void addUpdates(); + void addResourceToList(FwupdResource *res); + void addHistoricalUpdates(); + void setReleaseDetails(FwupdResource *res,FwupdRelease *rel); + void setDeviceDetails(FwupdResource *res,FwupdDevice *device); + void handleError(GError **perror); + QSet getAllUpdates(); + QString getAppName(QString ID); + QMap initHashMap(); + + +public Q_SLOTS: + void toggleFetching(); + +private Q_SLOTS: + void saveFile(QNetworkReply *reply); + +private: + void populate(const QString& name); + + QHash m_resources; + QMap m_downloadFile; + StandardBackendUpdater* m_updater; + bool m_fetching; + int m_startElements; + QList m_toUpdate; +}; + +#endif // FWUPDBACKEND_H diff --git a/libdiscover/backends/FwupdBackend/FwupdBackend.cpp b/libdiscover/backends/FwupdBackend/FwupdBackend.cpp new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/FwupdBackend.cpp @@ -0,0 +1,734 @@ +/*************************************************************************** + * Copyright © 2013 Aleix Pol Gonzalez * + * Copyright © 2018 Abhijeet Sharma * + * * + * 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 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * 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 "FwupdBackend.h" +#include "FwupdResource.h" +#include "FwupdTransaction.h" +#include "FwupdSourcesBackend.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +DISCOVER_BACKEND_PLUGIN(FwupdBackend) + +FwupdBackend::FwupdBackend(QObject* parent) + : AbstractResourcesBackend(parent) + , m_updater(new StandardBackendUpdater(this)) + , m_fetching(true) +{ + + QTimer::singleShot(500, this, &FwupdBackend::toggleFetching); + connect(m_updater, &StandardBackendUpdater::updatesCountChanged, this, &FwupdBackend::updatesCountChanged); + + client = fwupd_client_new (); + + populate(QStringLiteral("Releases")); + SourcesModel::global()->addSourcesBackend(new FwupdSourcesBackend(this)); +} + +QMap FwupdBackend::initHashMap() +{ + QMap map; + + map.insert(G_CHECKSUM_SHA1,QCryptographicHash::Sha1); + map.insert(G_CHECKSUM_SHA256,QCryptographicHash::Sha256); + map.insert(G_CHECKSUM_SHA512,QCryptographicHash::Sha512); + map.insert(G_CHECKSUM_MD5,QCryptographicHash::Md5); + + return map; +} + +FwupdBackend::~FwupdBackend() +{ + g_object_unref (client); +} + +QString FwupdBackend::buildDeviceID(FwupdDevice* device) +{ + QString DeviceID = QString::fromUtf8(fwupd_device_get_id (device)); + DeviceID.replace(QLatin1Char('/'),QLatin1Char('_')); + return QStringLiteral("org.fwupd.%1.device").arg(DeviceID); +} + +void FwupdBackend::addResourceToList(FwupdResource* res) +{ + res->setState(FwupdResource::Upgradeable); + m_resources.insert(res->packageName().toLower(), res); +} + +FwupdResource * FwupdBackend::createDevice(FwupdDevice *device) +{ + const QString name = QString::fromUtf8(fwupd_device_get_name(device)); + FwupdResource* res = new FwupdResource(name, true, this); + res->setId(buildDeviceID(device)); + res->addCategories(QStringLiteral("Releases")); + res->setIconName(QString::fromUtf8((const gchar *)g_ptr_array_index (fwupd_device_get_icons(device),0)));// Implement a Better way to decide icon + + setDeviceDetails(res,device); + return res; +} + +FwupdResource * FwupdBackend::createRelease(FwupdDevice *device) +{ + FwupdRelease *rel = fwupd_device_get_release_default (device); + const QString name = QString::fromUtf8(fwupd_release_get_name(rel)); + FwupdResource* res = new FwupdResource(name, true, this); + + res->setDeviceID(QString::fromUtf8(fwupd_device_get_id (device))); + setReleaseDetails(res,rel); + setDeviceDetails(res,device); + + if (fwupd_release_get_appstream_id (rel)) + res->setId(QString::fromUtf8(fwupd_release_get_appstream_id (rel))); + + /* the same as we have already */ + if(QLatin1Literal(fwupd_device_get_version (device)) == QLatin1Literal(fwupd_release_get_version (rel))) + { + qWarning() << "Fwupd Error: same firmware version as installed"; + } + + return res; + +} +void FwupdBackend::setReleaseDetails(FwupdResource *res,FwupdRelease *rel) +{ + res->addCategories(QLatin1String("Releases")); + if(fwupd_release_get_summary(rel)) + res->setSummary(QString::fromUtf8(fwupd_release_get_summary(rel))); + if(fwupd_release_get_vendor(rel)) + res->setVendor(QString::fromUtf8(fwupd_release_get_vendor(rel))); + if(fwupd_release_get_version(rel)) + res->setVersion(QString::fromUtf8(fwupd_release_get_version(rel))); + if(fwupd_release_get_description(rel)) + res->setDescription(QString::fromUtf8((fwupd_release_get_description (rel)))); + if(fwupd_release_get_homepage(rel)) + res->setHomePage(QUrl(QString::fromUtf8(fwupd_release_get_homepage(rel)))); + if(fwupd_release_get_license(rel)) + res->setLicense(QString::fromUtf8(fwupd_release_get_license(rel))); + if (fwupd_release_get_uri (rel)) + res->m_updateURI = QString::fromUtf8(fwupd_release_get_uri (rel)); +} +void FwupdBackend::setDeviceDetails(FwupdResource *res,FwupdDevice *dev) +{ + res->isLiveUpdatable = fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_UPDATABLE); + res->isOnlyOffline = fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_ONLY_OFFLINE); + res->needsReboot = fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + res->isDeviceRemoval = !fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_INTERNAL); + + GPtrArray *guids = fwupd_device_get_guids (dev); + if(guids->len > 0) + { + QString guidStr = QString::fromUtf8((char *)g_ptr_array_index (guids, 0)); + for (uint i = 1; i < guids->len; i++) + { + guidStr += QLatin1Char(',') + QString::fromUtf8((char *)g_ptr_array_index (guids, i)); + } + res->guidString = guidStr; + } + if(fwupd_device_get_name (dev)) + { + QString vendorName; + vendorName.sprintf("%s",fwupd_device_get_name (dev)); + + if(vendorName.indexOf(QString::fromUtf8(fwupd_device_get_vendor (dev))) == 0) + vendorName.sprintf("%s %s",fwupd_device_get_vendor (dev), fwupd_device_get_name (dev)); + res->setName(vendorName); + } + if(fwupd_device_get_summary (dev)) + res->setSummary(QString::fromUtf8(fwupd_device_get_summary(dev))); + if(fwupd_device_get_vendor(dev)) + res->setVendor(QString::fromUtf8(fwupd_device_get_vendor(dev))); + if(fwupd_device_get_version(dev)) + res->setVersion(QString::fromUtf8(fwupd_device_get_version(dev))); + if(fwupd_device_get_description(dev)) + res->setDescription(QString::fromUtf8((fwupd_device_get_description(dev)))); +} + +void FwupdBackend::populate(const QString& n) +{ + /* get devices */ + g_autoptr(GPtrArray) devices = fwupd_client_get_devices (client, nullptr, nullptr); + + if (devices) + { + for (uint i = 0; i < devices->len; i++) + { + FwupdDevice *device = (FwupdDevice *)g_ptr_array_index (devices, i); + + /* Devices Which are not updatable */ + if (!fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)) + continue; + + /* add releases */ + FwupdResource * res = createDevice(device); + res->addCategories(n); + + + g_autoptr(GPtrArray) releases = fwupd_client_get_releases (client,res->m_deviceID.toUtf8().constData(),nullptr,nullptr); + + if (releases) + { + for (uint j = 0; j < releases->len; j++) + { + FwupdRelease *rel = (FwupdRelease *)g_ptr_array_index (releases, j); + const QString name = QString::fromUtf8(fwupd_release_get_name(rel)); + FwupdResource* res_ = new FwupdResource(name, true, this); + setReleaseDetails (res_, rel); + res->m_releases.append(res_); + } + } + /* add all Valid Resources */ + m_resources.insert(res->packageName().toLower(), res); + } + } +} + + +void FwupdBackend::addUpdates() +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + g_autoptr(GError) error2 = nullptr; + g_autoptr(GPtrArray) devices = fwupd_client_get_devices (client, cancellable, &error); + + if(!devices) + { + if (g_error_matches (error,FWUPD_ERROR,FWUPD_ERROR_NOTHING_TO_DO)) + { + qDebug() << "Fwupd Info: No Devices Found"; + handleError(&error); + } + } + else{ + for (uint i = 0; i < devices->len; i++) + { + FwupdDevice *device = (FwupdDevice *)g_ptr_array_index (devices, i); + FwupdResource* res; + + if (!fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_SUPPORTED)) + continue; + + /*Device is Locked Needs Unlocking*/ + if (fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) + { + res = createDevice(device); + res->setIsDeviceLocked(true); + addResourceToList(res); + connect(res, &FwupdResource::stateChanged, this, &FwupdBackend::updatesCountChanged); + continue; + } + + + g_autoptr(GPtrArray) rels = fwupd_client_get_upgrades (client,fwupd_device_get_id(device),cancellable, &error2); + + if (!rels) + { + if (g_error_matches (error2,FWUPD_ERROR,FWUPD_ERROR_NOTHING_TO_DO)) + { + qWarning() << "Fwupd Error: No Packages Found for "<< fwupd_device_get_id(device); + handleError(&error2); + continue; + } + } + else + { + fwupd_device_add_release(device,(FwupdRelease *)g_ptr_array_index(rels,0)); + res = createApp(device); + if(res == nullptr) + { + qWarning() << "Fwupd Error: Cannot Create App From Device"; + } + else + { + if(rels->len > 1) + { + QString longdescription; + for(uint j = 0; j < rels->len; j++) + { + FwupdRelease *rel = (FwupdRelease *)g_ptr_array_index (rels, j); + if (fwupd_release_get_description (rel) == nullptr) + continue; + longdescription += QStringLiteral("Version %1\n").arg(QString::fromUtf8(fwupd_release_get_version (rel))); + longdescription += QString::fromUtf8(fwupd_release_get_description (rel)) + QLatin1Char('\n'); + } + res->setDescription(longdescription); + } + addResourceToList(res); + } + } + } + } +} + +void FwupdBackend::addHistoricalUpdates() +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + g_autoptr(FwupdDevice) device = fwupd_client_get_results (client,FWUPD_DEVICE_ID_ANY,cancellable,&error); + if(!device) + { + handleError(&error); + } + else + { + FwupdResource* res = createRelease(device); + if(!res) + qWarning() << "Fwupd Error: Cannot Make App for" << fwupd_device_get_id(device); + else + { + addResourceToList(res); + } + } +} + + +QByteArray FwupdBackend::getChecksum(const QUrl filename,QCryptographicHash::Algorithm hashAlgorithm) +{ + QFile f(filename.toString()); + if (f.open(QFile::ReadOnly)) + { + QCryptographicHash hash(hashAlgorithm); + if (hash.addData(&f)) + { + return hash.result().toHex(); + } + } + return QByteArray(); +} + +FwupdResource* FwupdBackend::createApp(FwupdDevice *device) +{ + FwupdRelease *rel = fwupd_device_get_release_default (device); + GPtrArray *checksums; + FwupdResource* app = createRelease(device); + + /* update unsupported */ + if (!app->isLiveUpdatable) + { + qWarning() << "Fwupd Error: " << app->m_name << "[" << app->m_id << "]" << "cannot be updated "; + return nullptr; + } + + /* Important Attributes missing */ + if (app->m_id.isNull()) + { + qWarning() << "Fwupd Error: No id for firmware"; + return nullptr; + } + if (app->m_version.isNull()) + { + qWarning() << "Fwupd Error: No version! for " << app->m_id; + return nullptr; + } + if (app->m_updateVersion.isNull()) + { + qWarning() << "Fwupd Error: No update-version! for " << app->m_id; + return nullptr; + } + checksums = fwupd_release_get_checksums (rel); + if (checksums->len == 0) + { + qWarning() << "Fwupd Error: " << app->m_name << "[" << app->m_id << "]" << "(" << app->m_updateVersion << ")" "has no checksums, ignoring as unsafe"; + return nullptr; + } + const QUrl update_uri(QString::fromUtf8(fwupd_release_get_uri(rel))); + + if (!update_uri.isValid()) + { + qWarning() << "Fwupd Error: No Update URI available for" << app->m_name << "[" << app->m_id << "]"; + return nullptr; + } + + /* Checking for firmware in the cache? */ + QFileInfo basename = QFileInfo(update_uri.path()); + const QUrl filename_cache = cacheFile(QStringLiteral("fwupd"),basename); + if (filename_cache.isEmpty()) + return nullptr; + + if (filename_cache.isLocalFile()) + { + QByteArray checksum_tmp = QByteArray(fwupd_checksum_get_by_kind (checksums,G_CHECKSUM_SHA1)); + + /* Currently LVFS supports SHA1 only*/ + if (checksum_tmp.isNull()) + { + qWarning() << "Fwupd Error: No valid checksum for" << filename_cache; + } + QByteArray checksum = getChecksum(filename_cache,QCryptographicHash::Sha1); + if (checksum.isNull() || checksum_tmp.isNull()) + return nullptr; + if (checksum_tmp != checksum) + { + qWarning() << "Fwupd Error: " << filename_cache << " does not match checksum, expected" << checksum_tmp << "got" << checksum; + QFile::remove((filename_cache.toString())); + return nullptr; + } + } + + /* link file to application and return its reference */ + app->m_file = filename_cache.toString(); + return app; +} + +void FwupdBackend::saveFile(QNetworkReply *reply) +{ + QString filename = this->m_downloadFile[reply->url()]; + if (reply->error() == QNetworkReply::NoError) + { + QByteArray Data = reply->readAll(); + QFile file(filename); + if (file.open(QIODevice::WriteOnly)) + { + file.write(Data); + } + else + { + qWarning() << "Fwupd Error: Cannot Open File to write Data"; + } + file.close(); + delete reply; + } +} + +bool FwupdBackend::downloadFile(const QUrl &uri,const QString &filename) +{ + QNetworkAccessManager *manager = new QNetworkAccessManager(this); + this->m_downloadFile.insert(uri,filename); + QEventLoop loop; + QTimer getTimer; + QTimer::connect(&getTimer,SIGNAL(timeout()),&loop, SLOT(quit())); + connect(manager, SIGNAL(finished(QNetworkReply*)),&loop, SLOT(quit())); + QNetworkReply *reply = manager->get(QNetworkRequest(uri)); + getTimer.start(600000); // 60 Seconds TimeOout Period + loop.exec(); + if(!reply) + { + return false; + } + else if( QNetworkReply::NoError != reply->error() ) + { + return false; + } + else + { + saveFile(reply); + delete manager; + } + return true; +} + +bool FwupdBackend::refreshRemotes(uint cacheAge) +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + g_autoptr(GPtrArray) remotes = fwupd_client_get_remotes (client, cancellable, &error); + if (!remotes) + return false; + for (uint i = 0; i < remotes->len; i++) + { + FwupdRemote *remote = (FwupdRemote *)g_ptr_array_index (remotes, i); + /*Remotes disabled by user so ignore*/ + if (!fwupd_remote_get_enabled (remote)) + continue; + /*Local Remotes Ignore*/ + if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) + continue; + /*Refresh the left ones*/ + if (!refreshRemote(remote, cacheAge)) + return false; + } + return true; +} + +bool FwupdBackend::refreshRemote(FwupdRemote* remote,uint cacheAge) +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + + if (fwupd_remote_get_filename_cache_sig (remote) == nullptr) + { + qWarning() << "Fwupd Error: " << "Remote " << fwupd_remote_get_id (remote) << "has no cache signature!"; + return false; + } + + /* check cache age */ + if (cacheAge > 0) + { + quint64 age = fwupd_remote_get_age (remote); + uint tmp = age < std::numeric_limits::max() ? (uint) age : std::numeric_limits::max(); + if (tmp < cacheAge) + { + qDebug() << "Fwupd Info:" << fwupd_remote_get_id (remote) << "is only" << tmp << "seconds old, so ignoring refresh! "; + return true; + } + } + + QString cacheId = QStringLiteral("fwupd/remotes.d/%1").arg(QString::fromUtf8(fwupd_remote_get_id (remote))); + QFileInfo basenameSig = QFileInfo(QString::fromUtf8(g_path_get_basename(fwupd_remote_get_filename_cache_sig (remote)))); + const QUrl filenameSig = cacheFile(cacheId,basenameSig); + + if (filenameSig.isEmpty()) + return false; + + /* download the signature first*/ + const QUrl urlSig(QString::fromUtf8(fwupd_remote_get_metadata_uri_sig(remote))); + qDebug() << "Fwupd Info: downloading remotes signatures ..."; + + + const QUrl filenameSig_(filenameSig.toString() + QStringLiteral(".tmp")); + + if(!downloadFile(urlSig,filenameSig_.toString())) + { + qDebug() << "Fwupd Info: remote signature download failed ..."; + return false; + } + + QMap map = initHashMap(); + QCryptographicHash::Algorithm hashAlgorithm = map[(fwupd_checksum_guess_kind (fwupd_remote_get_checksum (remote)))]; + QByteArray hash = getChecksum(filenameSig_,hashAlgorithm); + + if ((fwupd_remote_get_checksum (remote) == hash) && filenameSig.isLocalFile()) + { + qDebug() << "Fwupd Info: signature of" << urlSig.toString() << "is unchanged"; + return true; + } + else + QFile::remove(filenameSig.toString()); + + /* save to a file */ + qDebug() << "Fwupd Info: saving new remote signature to:" << filenameSig.toString(); + + if (!(QFile::copy(filenameSig_.toString(),filenameSig.toString()))) + { + QFile::remove(filenameSig_.toString()); + qWarning() << "Fwupd Error: cannot save remote signature"; + return false; + } + QFile::remove(filenameSig_.toString()); + + QFileInfo basename = QFileInfo(QString::fromUtf8(g_path_get_basename (fwupd_remote_get_filename_cache (remote)))); + const QUrl filename = cacheFile(cacheId,basename); + + if (filename.isEmpty()) + return false; + + qDebug() << "Fwupd Info: saving new firmware metadata to:" << filename; + + const QUrl url(QString::fromUtf8(fwupd_remote_get_metadata_uri (remote))); + if (!downloadFile (url, filename.toString())) + { + qWarning() << "Fwupd Error: cannot download file : " << filename ; + return false; + } + /* Sending Metadata to fwupd Daemon*/ + if (!fwupd_client_update_metadata (client,fwupd_remote_get_id (remote),filename.toString().toUtf8().constData(),filenameSig.toString().toUtf8().constData(),cancellable,&error)) + { + handleError(&error); + return false; + } + return true; +} + +void FwupdBackend::handleError(GError **perror) +{ + GError *error = perror != nullptr ? *perror : nullptr; + + if(!error) + return; + switch (error->code) + { + case FWUPD_ERROR_ALREADY_PENDING: + qWarning() << "Fwupd Error: FWUPD_ERROR_ALREADY_PENDING"; + Q_EMIT passiveMessage(i18n("FWUPD ERROR ALREADY PENDING!")); + break; + case FWUPD_ERROR_INVALID_FILE: + qWarning() << "Fwupd Error: FWUPD_ERROR_INVALID_FILE"; + Q_EMIT passiveMessage(i18n("FWUPD ERROR INVALID FILE")); + break; + case FWUPD_ERROR_NOT_SUPPORTED: + qWarning() << "Fwupd Error: FWUPD_ERROR_NOT_SUPPORTED"; + Q_EMIT passiveMessage(i18n("FWUPD ERROR NOT SUPPORTED")); + break; + case FWUPD_ERROR_AUTH_FAILED: + qWarning() << "Fwupd Error: FWUPD_ERROR_AUTH_FAILED"; + Q_EMIT passiveMessage(i18n("FWUPD ERROR AUTH FAILED")); + break; + case FWUPD_ERROR_SIGNATURE_INVALID: + qWarning() << "Fwupd Error: FWUPD_ERROR_SIGNATURE_INVALID"; + Q_EMIT passiveMessage(i18n("FWUPD ERROR SIGNATURE INVALID")); + break; + case FWUPD_ERROR_AC_POWER_REQUIRED: + qWarning() << "Fwupd Error: FWUPD_ERROR_AC_POWER_REQUIRED"; + Q_EMIT passiveMessage(i18n("FWUPD ERROR AC POWER REQUIRED")); + break; + default: + qWarning() << "Fwupd Error: Unknown Error " << error->code; + break; + } +} + +const QUrl FwupdBackend::cacheFile(const QString &kind,const QFileInfo &resource) +{ + QString basename = resource.fileName(); + const QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + const QUrl cacheDirFile = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/') + kind); + const QUrl fileUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/') + kind + QLatin1Char('/') + basename); + + if(!QFileInfo::exists(cacheDirFile.toLocalFile()) && !cacheDir.mkpath(kind)) + { + qWarning() << "Fwupd Error: cannot make cache directory!"; + return QUrl(); + } + return QUrl(fileUrl.toString().remove(QStringLiteral("file://"))); +} + +void FwupdBackend::toggleFetching() +{ + m_fetching = !m_fetching; + refreshRemotes(30*24*60*60); // Nicer Way to put time? currently 30 days in seconds + addUpdates(); + addHistoricalUpdates(); + emit fetchingChanged(); +} + +int FwupdBackend::updatesCount() const +{ + return m_updater->updatesCount(); +} + +ResultsStream* FwupdBackend::search(const AbstractResourcesBackend::Filters& filter) +{ + QVector ret; + if (!filter.resourceUrl.isEmpty() && filter.resourceUrl.scheme() == QLatin1String("fwupd")) + { + return findResourceByPackageName(filter.resourceUrl); + } + else + { + foreach(AbstractResource* r, m_resources) + { + if(r->name().contains(filter.search, Qt::CaseInsensitive) || r->comment().contains(filter.search, Qt::CaseInsensitive)) + ret += r; + } + } + return new ResultsStream(QStringLiteral("FwupdStream"), ret); +} + +ResultsStream * FwupdBackend::findResourceByPackageName(const QUrl& search) +{ + auto res = search.scheme() == QLatin1String("fwupd") ? m_resources.value(search.host().replace(QLatin1Char('.'), QLatin1Char(' '))) : nullptr; + if (!res) { + return new ResultsStream(QStringLiteral("FwupdStream"), {}); + } else + return new ResultsStream(QStringLiteral("FwupdStream"), { res }); +} + +AbstractBackendUpdater* FwupdBackend::backendUpdater() const +{ + return m_updater; +} + +AbstractReviewsBackend* FwupdBackend::reviewsBackend() const +{ + return nullptr; +} + +Transaction* FwupdBackend::installApplication(AbstractResource* app, const AddonList& addons) +{ + return new FwupdTransaction(qobject_cast(app), this, addons, Transaction::InstallRole); +} + +Transaction* FwupdBackend::installApplication(AbstractResource* app) +{ + return new FwupdTransaction(qobject_cast(app), this, Transaction::InstallRole); +} + +Transaction* FwupdBackend::removeApplication(AbstractResource* app) +{ + return new FwupdTransaction(qobject_cast(app), this, Transaction::RemoveRole); +} + +void FwupdBackend::checkForUpdates() +{ + if(m_fetching) + return; + + populate(QStringLiteral("Releases")); + toggleFetching(); + QTimer::singleShot(500, this, &FwupdBackend::toggleFetching); +} + +AbstractResource * FwupdBackend::resourceForFile(const QUrl& path) +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + + QMimeDatabase db; + QMimeType type = db.mimeTypeForFile(path.fileName()); + FwupdResource* app = nullptr; + + if(type.isValid() && type.inherits(QStringLiteral("application/vnd.ms-cab-compressed"))) + { + g_autofree gchar *filename = path.fileName().toUtf8().data(); + g_autoptr(GPtrArray) devices = fwupd_client_get_details (client,filename,cancellable,&error); + + if (devices) + { + FwupdDevice *dev = (FwupdDevice *)g_ptr_array_index (devices, 0); + app = createRelease(dev); + app->setState(AbstractResource::None); + for (uint i = 1; i < devices->len; i++) + { + FwupdDevice *dev = (FwupdDevice *)g_ptr_array_index (devices, i); + FwupdResource* app_ = createRelease(dev); + app_->setState(AbstractResource::None); + if(!app->m_releases.contains(app_)) + app->m_releases.append(app_); + } + m_resources.insert(app->packageName(), app); + addResourceToList(app); + connect(app, &FwupdResource::stateChanged, this, &FwupdBackend::updatesCountChanged); + } + else + { + handleError(&error); + } + } + return app; +} + +QString FwupdBackend::displayName() const +{ + return QStringLiteral("Firmware Updates"); +} + +bool FwupdBackend::hasApplications() const +{ + return !m_resources.isEmpty() ? true : false; +} + +#include "FwupdBackend.moc" diff --git a/libdiscover/backends/FwupdBackend/FwupdResource.h b/libdiscover/backends/FwupdBackend/FwupdResource.h new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/FwupdResource.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright © 2013 Aleix Pol Gonzalez * + * Copyright © 2018 Abhijeet Sharma * + * * + * 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 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * 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 FWUPDRESOURCE_H +#define FWUPDRESOURCE_H + +#include "FwupdBackend.h" + +#include +#include + +class AddonList; +class FwupdResource : public AbstractResource +{ +Q_OBJECT +public: + explicit FwupdResource(QString name, bool isTechnical, AbstractResourcesBackend* parent); + + QList addonsInformation() override; + QString section() override; + QString origin() const override; + QString longDescription() override; + QString availableVersion() const override; + QString installedVersion() const override; + QString license() override; + int size() override; + QUrl homepage() override; + QUrl helpURL() override; + QUrl bugURL() override; + QUrl donationURL() override; + QStringList categories() override; + AbstractResource::State state() override; + QVariant icon() const override; + QString comment() override; + QString name() const override; + QString packageName() const override; + QString vendor() const; + bool isTechnical() const override { return m_isTechnical; } + bool canExecute() const override { return false; } + void invokeApplication() const override; + void fetchChangelog() override; + QUrl url() const override; + QString executeLabel() const override; + + void setState(State state); + void setSize(int size) { m_size = size; } + void setAddons(const AddonList& addons); + void setId(const QString &id){m_id = id;} + void setName(const QString &name){ m_name = name;} + void setSummary(const QString &summary){ m_summary = summary;} + void setDescription(const QString &description){ m_description = description;} + void setVersion(const QString &version){ m_version = version;} + void setVendor(const QString &vendor){ m_vendor = vendor;} + void addCategories(const QString &category); + void setHomePage(const QUrl &homepage){ m_homepage = homepage;} + void setLicense(const QString &license){ m_license = license;} + void setIconName(const QString &iconName){ m_iconName = iconName;} + virtual QStringList allResourceNames() const; + + void setIsDeviceLocked(bool status){ isDeviceLocked = status;} + void setDeviceID(const QString &deviceID){ m_deviceID = deviceID;} + void setUpdateURI(const QString &updateURI){m_updateURI = updateURI;} + + void setAddonInstalled(const QString& addon, bool installed); + QString sourceIcon() const override { return QStringLiteral("player-time"); } + QDate releaseDate() const override { return {}; } + +public: + QString m_id; + QString m_name; + QString m_summary; + QString m_description; + QString m_version; + QString m_updateVersion; + QString m_vendor; + QStringList m_categories; + QString m_license; + + AbstractResource::State m_state; + QUrl m_homepage; + QString m_iconName; + QList m_addons; + bool m_isTechnical; + int m_size; + + QString m_deviceID; + QString m_updateURI; + QString m_file; + bool isDeviceLocked = false; // True if device is locked! + bool isOnlyOffline = false; // True if only offline updates + bool isLiveUpdatable = false; // True if device is live updatable + bool needsReboot = false; // True if device needs Reboot + bool isDeviceRemoval = false; //True if device is Removal + QString guidString; + + + QVector m_releases; // A list of all references to releases of a device. +}; + +#endif // FWUPDRESOURCE_H diff --git a/libdiscover/backends/FwupdBackend/FwupdResource.cpp b/libdiscover/backends/FwupdBackend/FwupdResource.cpp new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/FwupdResource.cpp @@ -0,0 +1,198 @@ +/*************************************************************************** + * Copyright © 2013 Aleix Pol Gonzalez * + * Copyright © 2018 Abhijeet Sharma * + * * + * 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 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * 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 "FwupdResource.h" + + +#include +#include +#include +#include + +FwupdResource::FwupdResource(QString name, bool isTechnical, AbstractResourcesBackend* parent) + : AbstractResource(parent) + , m_name(std::move(name)) + , m_state(State::Broken) + , m_isTechnical(isTechnical) +{ + +} + +QList FwupdResource::addonsInformation() +{ + return m_addons; +} + +QString FwupdResource::availableVersion() const +{ + return m_version; +} + +QStringList FwupdResource::allResourceNames() const +{ + return { m_name }; +} + +QStringList FwupdResource::categories() +{ + return m_categories; +} + +void FwupdResource::addCategories(const QString &category){ + m_categories.append(category); +} + +QString FwupdResource::comment() +{ + return m_summary; +} + +int FwupdResource::size() +{ + return m_size; +} + +QUrl FwupdResource::homepage() +{ + return m_homepage; +} + +QUrl FwupdResource::helpURL() +{ + return m_homepage; +} + +QUrl FwupdResource::bugURL() +{ + return m_homepage; +} + +QUrl FwupdResource::donationURL() +{ + return m_homepage; +} + +QVariant FwupdResource::icon() const +{ + return m_iconName; +} + +QString FwupdResource::installedVersion() const +{ + return m_version; +} + +QString FwupdResource::license() +{ + return m_license; +} + +QString FwupdResource::longDescription() +{ + return m_description; +} + +QString FwupdResource::name() const +{ + return m_name; +} + +QString FwupdResource::vendor() const +{ + return m_vendor; +} + +QString FwupdResource::origin() const +{ + return m_homepage.toString(); +} + +QString FwupdResource::packageName() const +{ + return m_name; +} + +QString FwupdResource::section() +{ + return QStringLiteral("Firmware Updates"); +} + +AbstractResource::State FwupdResource::state() +{ + return m_state; +} + +void FwupdResource::fetchChangelog() +{ + QString log = longDescription(); + log.replace(QLatin1Char('\n'), QLatin1String("
")); + + emit changelogFetched(log); +} + +void FwupdResource::setState(AbstractResource::State state) +{ + if(m_state != state) + { + m_state = state; + emit stateChanged(); + } + +} + +void FwupdResource::setAddons(const AddonList& addons) +{ + Q_FOREACH (const QString& toInstall, addons.addonsToInstall()) + { + setAddonInstalled(toInstall, true); + } + Q_FOREACH (const QString& toRemove, addons.addonsToRemove()) + { + setAddonInstalled(toRemove, false); + } +} + +void FwupdResource::setAddonInstalled(const QString& addon, bool installed) +{ + for(auto & elem : m_addons) + { + if(elem.name() == addon) + { + elem.setInstalled(installed); + } + } +} + + +void FwupdResource::invokeApplication() const +{ + qWarning() << "Not Launchable"; +} + +QUrl FwupdResource::url() const +{ + return m_homepage; +} + +QString FwupdResource::executeLabel() const +{ + return i18n("Not Invokable"); +} diff --git a/libdiscover/backends/FwupdBackend/FwupdSourcesBackend.h b/libdiscover/backends/FwupdBackend/FwupdSourcesBackend.h new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/FwupdSourcesBackend.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright © 2014 Aleix Pol Gonzalez * + * Copyright © 2018 Abhijeet Sharma * + * * + * 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 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * 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 FWUPDSOURCESBACKEND_H +#define FWUPDSOURCESBACKEND_H + +#include +#include "FwupdBackend.h" +#include + +#include "FwupdBackend.h" + +class FwupdSourcesModel; + +class FwupdSourcesBackend : public AbstractSourcesBackend +{ + Q_OBJECT +public: + explicit FwupdSourcesBackend(AbstractResourcesBackend * parent); + + FwupdBackend* backend ; + QAbstractItemModel* sources() override; + bool addSource(const QString& id) override; + bool removeSource(const QString& id) override; + QString idDescription() override { return QString(); } + QList actions() const override; + bool supportsAdding() const override { return false; } + void eulaRequired(const QString& remoteName , const QString& licenseAgreement); + void populateSources(); +Q_SIGNALS: + void proceed() override; + void cancel() override; + +private: + FwupdSourcesModel* m_sources; + QList m_actions; +}; + +#endif // FWUPDSOURCESBACKEND_H diff --git a/libdiscover/backends/FwupdBackend/FwupdSourcesBackend.cpp b/libdiscover/backends/FwupdBackend/FwupdSourcesBackend.cpp new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/FwupdSourcesBackend.cpp @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright © 2014 Aleix Pol Gonzalez * + * Copyright © 2018 Abhijeet Sharma * + * * + * 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 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * 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 "FwupdSourcesBackend.h" + +#include +#include +#include + + +class FwupdSourcesModel : public QStandardItemModel +{ +Q_OBJECT +public: + FwupdSourcesModel(FwupdSourcesBackend* backend) + : QStandardItemModel(backend) + , m_backend(backend) {} + + QHash roleNames() const override + { + auto roles = QStandardItemModel::roleNames(); + roles[Qt::CheckStateRole] = "checked"; + return roles; + } + + bool setData(const QModelIndex & index, const QVariant & value, int role) override { + auto item = itemFromIndex(index); + if (!item) + return false; + remote = fwupd_client_get_remote_by_id(m_backend->backend->client,item->data(AbstractSourcesBackend::IdRole).toString().toUtf8().constData(),nullptr,nullptr); + status = fwupd_remote_get_enabled(remote); + switch(role) + { + case Qt::CheckStateRole: + { + if((value.toInt() == Qt::Checked) ) + { + #if FWUPD_CHECK_VERSION(1,0,7) + m_backend->eulaRequired(QString::fromUtf8(fwupd_remote_get_title(remote)),QString::fromUtf8(fwupd_remote_get_agreement(remote))); + #endif + connect(m_backend,&FwupdSourcesBackend::proceed,this, + [=]() + { + if(fwupd_client_modify_remote(m_backend->backend->client,fwupd_remote_get_id(remote),QString(QLatin1String("Enabled")).toUtf8().constData(),(QString(QLatin1String("true")).toUtf8().constData()),nullptr,nullptr)) + item->setData(value, role); + } + ); + connect(m_backend,&FwupdSourcesBackend::cancel,this, + [=]() + { + item->setCheckState(Qt::Unchecked); + Q_EMIT dataChanged(index,index,{}); + return false; + } + ); + } + else if(value.toInt() == Qt::Unchecked) + { + if(fwupd_client_modify_remote(m_backend->backend->client,fwupd_remote_get_id(remote),QString(QLatin1String("Enabled")).toUtf8().constData(),(QString(QLatin1String("false")).toUtf8().constData()),nullptr,nullptr)) + item->setData(value, role); + } + + } + } + Q_EMIT dataChanged(index,index,{}); + return true; + } + +private: + FwupdSourcesBackend* m_backend; + FwupdRemote* remote; + bool status; +}; + +FwupdSourcesBackend::FwupdSourcesBackend(AbstractResourcesBackend * parent) + : AbstractSourcesBackend(parent) + , backend(qobject_cast(parent)) + , m_sources(new FwupdSourcesModel(this)) +{ + populateSources(); +} + +void FwupdSourcesBackend::populateSources() +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + /* find all remotes */ + g_autoptr(GPtrArray) remotes = fwupd_client_get_remotes (backend->client,cancellable,&error); + if(remotes != nullptr) + { + for (uint i = 0; i < remotes->len; i++) + { + FwupdRemote *remote = (FwupdRemote *)g_ptr_array_index (remotes, i); + if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) + continue; + const QString id = QString::fromUtf8(fwupd_remote_get_id (remote)); + if (id.isEmpty()) + continue; + bool status = !fwupd_remote_get_enabled(remote); + QStandardItem* it = new QStandardItem(id); + it->setData(id, AbstractSourcesBackend::IdRole); + it->setData(QVariant(QString::fromUtf8(fwupd_remote_get_title (remote))), Qt::ToolTipRole); + it->setCheckable(true); + it->setCheckState(status ? Qt::Unchecked : Qt::Checked); + m_sources->appendRow(it); + } + } +} + +QAbstractItemModel* FwupdSourcesBackend::sources() +{ + return m_sources; +} + +void FwupdSourcesBackend::eulaRequired( const QString& remoteName , const QString& licenseAgreement) +{ + Q_EMIT proceedRequest(i18n("Accept EULA"), i18n("The remote %1 require that you accept their license:\n %2", + remoteName, licenseAgreement)); +} + +bool FwupdSourcesBackend::addSource(const QString& id) +{ + qWarning() << "Fwupd Error: Custom Addition of Sources Not Allowed" << "Remote-ID" << id; + return false; +} + +bool FwupdSourcesBackend::removeSource(const QString& id) +{ + qWarning() << "Fwupd Error: Removal of Sources Not Allowed" << "Remote-ID" << id; + return false; +} + +QList FwupdSourcesBackend::actions() const +{ + return m_actions ; +} + +#include "FwupdSourcesBackend.moc" + + diff --git a/libdiscover/backends/FwupdBackend/FwupdTransaction.h b/libdiscover/backends/FwupdBackend/FwupdTransaction.h new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/FwupdTransaction.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright © 2013 Aleix Pol Gonzalez * + * Copyright © 2018 Abhijeet Sharma * + * * + * 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 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * 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 FWUPDTRANSACTION_H +#define FWUPDTRANSACTION_H + +#include +#include "FwupdBackend.h" +#include "FwupdResource.h" + + +class FwupdResource; +class FwupdTransaction : public Transaction +{ + Q_OBJECT + public: + FwupdTransaction(FwupdResource* app, FwupdBackend* backend, Role role); + FwupdTransaction(FwupdResource* app, FwupdBackend* backend, const AddonList& list, Role role); + ~FwupdTransaction(); + bool check(); + bool install(); + bool remove(); + void cancel() override; + void proceed() override; + + private Q_SLOTS: + void iterateTransaction(); + void finishTransaction(); + void fwupdInstall(QNetworkReply* reply); + + private: + bool m_iterate = true; + FwupdResource* m_app; + FwupdBackend* m_backend; +}; + +#endif // FWUPDTRANSACTION_H diff --git a/libdiscover/backends/FwupdBackend/FwupdTransaction.cpp b/libdiscover/backends/FwupdBackend/FwupdTransaction.cpp new file mode 100644 --- /dev/null +++ b/libdiscover/backends/FwupdBackend/FwupdTransaction.cpp @@ -0,0 +1,206 @@ +/*************************************************************************** + * Copyright © 2013 Aleix Pol Gonzalez * + * Copyright © 2018 Abhijeet Sharma * + * * + * 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 2 of * + * the License or (at your option) version 3 or any later version * + * accepted by the membership of KDE e.V. (or its successor approved * + * by the membership of KDE e.V.), which shall act as a proxy * + * defined in Section 14 of version 3 of the license. * + * * + * 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 "FwupdTransaction.h" + +#include + + +#define TEST_PROCEED + +FwupdTransaction::FwupdTransaction(FwupdResource* app, FwupdBackend* backend, Role role) + : FwupdTransaction(app, backend,{}, role) +{ +} + +FwupdTransaction::FwupdTransaction(FwupdResource* app, FwupdBackend* backend, const AddonList& addons, Transaction::Role role) + : Transaction(app->backend(), app, role, addons) + , m_app(app) + , m_backend(backend) +{ + setCancellable(true); + if(role == InstallRole) + { + setStatus(QueuedStatus); + if(!check()) + qWarning() << "Fwupd Error: Error In Install!"; + } + else if(role == RemoveRole) + { + if(!remove()) + qWarning() << "Fwupd Error: Error in Remove!"; + } + + iterateTransaction(); +} + +FwupdTransaction::~FwupdTransaction() +{ + +} + +bool FwupdTransaction::check() +{ + g_autoptr(GCancellable) cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + + if(m_app->isDeviceLocked) + { + QString device_id = m_app->m_deviceID; + if(device_id.isNull()) + { + qWarning("Fwupd Error: No Device ID set, cannot unlock device "); + return false; + } + if (!fwupd_client_unlock (m_backend->client, device_id.toUtf8().constData(),cancellable, &error)) + { + m_backend->handleError(&error); + return false; + } + return true; + } + if(!install()) + { + qWarning("Fwupd Error: Cannot Install Application"); + return false; + } + return true; + +} + +bool FwupdTransaction::install() +{ + QString localFile = m_app->m_file; + if(localFile.isEmpty()) + { + qWarning("Fwupd Error: No Local File Set For this Resource"); + return false; + } + + if (!(QFileInfo::exists(localFile))) + { + const QUrl uri(m_app->m_updateURI); + setStatus(DownloadingStatus); + QNetworkAccessManager *manager = new QNetworkAccessManager(this); + connect(manager, SIGNAL(finished(QNetworkReply*)),this, SLOT(fwupdInstall(QNetworkReply*))); + manager->get(QNetworkRequest(uri)); + } + else + { + fwupdInstall(nullptr); + } + return true; +} + +void FwupdTransaction::fwupdInstall(QNetworkReply* reply) +{ + if(reply) + { + QString filename = m_app->m_file; + if (reply->error() == QNetworkReply::NoError) + { + QByteArray Data = reply->readAll(); + QFile file(filename); + if (file.open(QIODevice::WriteOnly)) + { + file.write(Data); + } + else + { + qWarning() << "Fwupd Error: Cannot Save File"; + } + file.close(); + delete reply; + } + } + FwupdInstallFlags install_flags = FWUPD_INSTALL_FLAG_NONE; + g_autoptr(GCancellable) cancellable = g_cancellable_new(); + g_autoptr(GError) error = nullptr; + + QString localFile = m_app->m_file; + QString deviceId = m_app->m_deviceID; + /* limit to single device? */ + if (deviceId.isNull()) + deviceId = QStringLiteral(FWUPD_DEVICE_ID_ANY); + + /* only offline supported */ + if (m_app->isOnlyOffline) + install_flags = static_cast(install_flags | FWUPD_INSTALL_FLAG_OFFLINE); + + m_iterate = true; + QTimer::singleShot(100, this, &FwupdTransaction::iterateTransaction); + if (!fwupd_client_install (m_backend->client, deviceId.toUtf8().constData(),localFile.toUtf8().constData(), install_flags,cancellable, &error)) + { + m_backend->handleError(&error); + m_iterate = false; + return; + } + finishTransaction(); +} + +bool FwupdTransaction::remove() +{ + m_app->setState(AbstractResource::State::None); + return true; +} + +void FwupdTransaction::iterateTransaction() +{ + if (!m_iterate) + return; + + setStatus(CommittingStatus); + if(progress()<100) + { + setProgress(fwupd_client_get_percentage (m_backend->client)); + QTimer::singleShot(100, this, &FwupdTransaction::iterateTransaction); + } +} + +void FwupdTransaction::proceed() +{ + finishTransaction(); +} + +void FwupdTransaction::cancel() +{ + m_iterate = false; + + setStatus(CancelledStatus); +} + +void FwupdTransaction::finishTransaction() +{ + AbstractResource::State newState; + switch(role()) { + case InstallRole: + case ChangeAddonsRole: + newState = AbstractResource::Installed; + break; + case RemoveRole: + newState = AbstractResource::None; + break; + } + m_app->setAddons(addons()); + m_app->setState(newState); + setStatus(DoneStatus); + deleteLater(); +} diff --git a/libdiscover/resources/AbstractSourcesBackend.h b/libdiscover/resources/AbstractSourcesBackend.h --- a/libdiscover/resources/AbstractSourcesBackend.h +++ b/libdiscover/resources/AbstractSourcesBackend.h @@ -67,10 +67,15 @@ QString firstSourceId() const; QString lastSourceId() const; + public Q_SLOTS: + virtual void cancel() {} + virtual void proceed() {} + Q_SIGNALS: void firstSourceIdChanged(); void lastSourceIdChanged(); void passiveMessage(const QString &message); + void proceedRequest(const QString &title, const QString &description); }; #endif // ABSTRACTRESOURCESBACKEND_H