Commit a3085090 authored by Abhijeet  sharma's avatar Abhijeet sharma

Fwupd-Backend Integration

Summary:
This patch brings the initial integration of Fwupd Backend.
    * This backend was implemented as the part of my GSoC"18 Project, its link can be found here:

    https://summerofcode.withgoogle.com/projects/?sp-search=abhijeet#5140246090481664

    * The status report for this backend can be found here:

    https://community.kde.org/GSoC/2018/StatusReports/AbhijeetSharma

Reviewers: apol, davidedmundson

Reviewed By: apol

Subscribers: zzag, anthonyfieroni, plasma-devel

Tags: #plasma, #discover_software_store

Differential Revision: https://phabricator.kde.org/D14050
parent 57e58f7c
......@@ -30,6 +30,7 @@ find_package(KF5NewStuff 5.23 CONFIG)
set(CMAKE_AUTORCC ON)
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.
......@@ -77,5 +78,10 @@ set_package_properties(FLATPAK PROPERTIES
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)
# FindGLib.cmake
# <https://github.com/nemequ/gnome-cmake>
#
# CMake support for GLib/GObject/GIO.
#
# License:
#
# Copyright (c) 2016 Evan Nemerson <evan@nemerson.com>
#
# 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)
# FindGObject.cmake
# <https://github.com/nemequ/gnome-cmake>
#
# CMake support for GObject.
#
# License:
#
# Copyright (c) 2016 Evan Nemerson <evan@nemerson.com>
#
# 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)
# - 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 <sharma.abhijeet2096@gmail.com>
#
# 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)
......@@ -40,6 +40,10 @@ DiscoverPage {
Connections {
target: backendItem.backend
onPassiveMessage: window.showPassiveNotification(message)
onProceedRequest: {
var dialog = sourceProceedDialog.createObject(window, {sourcesBackend: sourcesBackend, title: title, description: description})
dialog.open()
}
}
Kirigami.Heading {
......@@ -110,6 +114,56 @@ DiscoverPage {
}
}
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()
}
}
}
delegate: Kirigami.SwipeListItem {
Layout.fillWidth: true
enabled: display.length>0
......
......@@ -39,3 +39,10 @@ option(BUILD_SnapBackend "Build Snap support." "ON")
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()
add_definitions( -DPROJECT_NAME=${PROJECT_NAME} -DPROJECT_VERSION=${PROJECT_VERSION})
set(fwupd-backend_SRCS
FwupdResource.cpp
FwupdBackend.cpp
FwupdTransaction.cpp
FwupdSourcesBackend.cpp
)
find_package(GObject)
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 ${GObject})
install(TARGETS fwupd-backend DESTINATION ${PLUGIN_INSTALL_DIR}/discover)
This diff is collapsed.
/***************************************************************************
* Copyright © 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com> *
* Copyright © 2018 Abhijeet Sharma <sharma.abhijeet2096@gmail.com> *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#ifndef FWUPDBACKEND_H
#define FWUPDBACKEND_H
#include <resources/AbstractResourcesBackend.h>
#include <QString>
#include <QDir>
#include <QDebug>
#include <QThread>
#include <QTimer>
#include <QAction>
#include <QMimeDatabase>
#include <QVariantList>
#include <QSet>
#include <QFileInfo>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QCryptographicHash>
#include <QMap>
#include <QEventLoop>
extern "C" {
#include <fwupd.h>
}
#include <glib-2.0/glib-object.h>
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<QString, FwupdResource*> 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<AbstractResource*> getAllUpdates();
QString getAppName(QString ID);
QMap<GChecksumType,QCryptographicHash::Algorithm> initHashMap();
public Q_SLOTS:
void toggleFetching();
private Q_SLOTS:
void saveFile(QNetworkReply *reply);
private:
void populate(const QString& name);
QHash<QString, FwupdResource*> m_resources;
QMap<QUrl,QString> m_downloadFile;
StandardBackendUpdater* m_updater;
bool m_fetching;
int m_startElements;
QList<AbstractResource*> m_toUpdate;
};
#endif // FWUPDBACKEND_H
/***************************************************************************
* Copyright © 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com> *
* Copyright © 2018 Abhijeet Sharma <sharma.abhijeet2096@gmail.com> *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/
#include "FwupdResource.h"
#include <Transaction/AddonList.h>
#include <QDesktopServices>
#include <QStringList>
#include <QTimer>
FwupdResource::FwupdResource(QString name, bool isTechnical, AbstractResourcesBackend* parent)
: AbstractResource(parent)
, m_name(std::move(name))
, m_state(State::Broken)
, m_isTechnical(isTechnical)
{
}
QList<PackageState> 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("<br />"));
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");
}
/***************************************************************************
* Copyright © 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com> *
* Copyright © 2018 Abhijeet Sharma <sharma.abhijeet2096@gmail.com> *
* *
* 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 <http://www.gnu.org/licenses/>. *
***************************************************************************/