Commit a4d75750 authored by Caio Jordão Carvalho's avatar Caio Jordão Carvalho

Merge branch 'master' into raid-support

parents a57bd884 5da3eacd
# Copyright (C) 2008 by Volker Lanz <vl@fidra.de>
# Copyright (C) 2014-2017 by Andrius Štikonas <andrius@stikonas.eu>
# Copyright (C) 2014-2019 by Andrius Štikonas <andrius@stikonas.eu>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
......@@ -23,7 +23,7 @@ set(CMAKE_BUILD_WITH_INSTALL_RPATH ON)
# Dependencies
set(QT_MIN_VERSION "5.10.0")
set(KF5_MIN_VERSION "5.25")
set(KF5_MIN_VERSION "5.56")
set(BLKID_MIN_VERSION "2.32")
# Qca-qt5 (tested with botan and ossl backends)
......@@ -32,7 +32,7 @@ set(BLKID_MIN_VERSION "2.32")
# Qca plugin (botan or ossl)
set(VERSION_MAJOR "3")
set(VERSION_MINOR "50")
set(VERSION_MINOR "80")
set(VERSION_RELEASE "0")
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE})
set(SOVERSION "8")
......
......@@ -3,20 +3,20 @@ Building and installing KDE Partition Manager Core Library from source
## Dependencies
* [util-linux](https://github.com/karelzak/util-linux) 2.32
* [util-linux](https://github.com/karelzak/util-linux) 2.34
* [Qt](https://www.qt.io/) 5.10
* QCA
* Tier 2 [KDE Frameworks](https://www.kde.org/products/frameworks/) 5.25
* Tier 2 [KDE Frameworks](https://www.kde.org/products/frameworks/) 5.56
## Configure
KPMcore is built with [cmake](https://cmake.org/). It is recommended to build out of tree:
After unpacking the source, create a separate build directory and run cmake there:
```
```bash
$ tar xf kpmcore-x.y.z.tar.xz
$ cd kpmcore-x.y.z
$ mkdir build
......@@ -35,7 +35,7 @@ configure a different install path by passing
`-DCMAKE_INSTALL_PREFIX=<your_path>` to cmake when configuring. To change the
install path after configuring and building, run
```
```bash
$ ccmake .
```
......
......@@ -28,7 +28,7 @@ KPMcore supports CMake as (meta-)build system and installs suitable
CMake support files. Typical use of of KPMcore in a `CMakeLists.txt`
looks like this:
```
```cmake
find_package( KPMcore 3.2 REQUIRED )
include_directories( ${KPMCORE_INCLUDE_DIR} )
target_link_libraries( target kpmcore )
......@@ -44,7 +44,7 @@ environment variable `KPMCORE_BACKEND` names a backend,
and typical initialization code will look like this (or use the
class `KPMCoreInitializer` from `test/helpers.h`):
```
```cpp
#include <backend/corebackendmanager.h>
#include <QDebug>
......@@ -76,7 +76,7 @@ result in undefined behavior.
After the backend is initialized you can scan for available devices.
If you only want devices from the loaded backend you can call
```
```cpp
QList<Device*> devices = backend->scanDevices( excludeReadOnly );
```
......@@ -87,7 +87,7 @@ read only devices.
Alternatively, you can use KPMcore device scanner
```
```cpp
#include <core/device.h>
#include <core/devicescanner.h>
#include <core/operationstack.h>
......
......@@ -49,7 +49,7 @@ target_link_libraries( kpmcore PUBLIC
KF5::I18n
KF5::CoreAddons
KF5::WidgetsAddons
KF5::Auth
KF5::AuthCore
)
install(TARGETS kpmcore EXPORT KPMcoreTargets ${INSTALL_TARGETS_DEFAULT_ARGS})
......
......@@ -36,6 +36,13 @@ class PartitionTable;
class QString;
enum class ScanFlag : uint8_t {
includeReadOnly = 0x1, /**< devices that are read-only according to the kernel */
includeLoopback = 0x2,
};
Q_DECLARE_FLAGS(ScanFlags, ScanFlag)
Q_DECLARE_OPERATORS_FOR_FLAGS(ScanFlags)
/**
* Interface class for backend plugins.
* @author Volker Lanz <vl@fidra.de>
......@@ -86,8 +93,7 @@ public:
/**
* Scan for devices in the system.
* @param excludeReadOnly when true, devices that are read-only
* according to the kernel are left out of the list.
* @param excludeReadOnly when true, are left out of the list.
* When false (the default) all devices, writable or
* not, including CD ROM devices, are returned.
* @return a QList of pointers to Device instances. The caller is responsible
......@@ -95,7 +101,17 @@ public:
* @note A Device object is a description of the device, not
* an object to operate on. See openDevice().
*/
virtual QList<Device*> scanDevices(bool excludeReadOnly = false) = 0;
[[deprecated("port to scanDevices(ScanFlags)")]] virtual QList<Device*> scanDevices(bool excludeReadOnly = false) = 0;
/**
* Scan for devices in the system.
* @param scanFlags can be used to expand the list of scanned devices.
* @return a QList of pointers to Device instances. The caller is responsible
* for deleting these objects.
* @note A Device object is a description of the device, not
* an object to operate on. See openDevice().
*/
virtual QList<Device*> scanDevices(const ScanFlags scanFlags) = 0;
/**
* Scan a single device in the system.
......
......@@ -63,7 +63,7 @@ void DeviceScanner::scan()
clear();
const QList<Device*> deviceList = CoreBackendManager::self()->backend()->scanDevices();
const QList<Device*> deviceList = CoreBackendManager::self()->backend()->scanDevices(ScanFlag::includeLoopback);
for (const auto &d : deviceList)
operationStack().addDevice(d);
......
......@@ -19,6 +19,7 @@
#include "core/fstab.h"
#include "util/externalcommand.h"
#include "util/report.h"
#if defined(Q_OS_LINUX)
#include <blkid/blkid.h>
......@@ -228,9 +229,8 @@ static void parseFsSpec(const QString& m_fsSpec, FstabEntry::Type& m_entryType,
}
}
static void writeEntry(QFile& output, const FstabEntry& entry)
static void writeEntry(QTextStream& s, const FstabEntry& entry)
{
QTextStream s(&output);
if (entry.entryType() == FstabEntry::Type::comment) {
s << entry.comment() << "\n";
return;
......@@ -256,32 +256,13 @@ static void writeEntry(QFile& output, const FstabEntry& entry)
bool writeMountpoints(const FstabEntryList& fstabEntries, const QString& filename)
{
QTemporaryFile out;
out.setAutoRemove(false);
if (!out.open()) {
qWarning() << "could not open output file " << out.fileName();
return false;
} else {
for (const auto &e : fstabEntries)
writeEntry(out, e);
out.close();
const QString bakFilename = QStringLiteral("%1.bak").arg(filename);
ExternalCommand mvCmd(QStringLiteral("mv"), { filename, bakFilename } );
if ( !(mvCmd.run(-1) && mvCmd.exitCode() == 0) ) {
qWarning() << "could not backup " << filename << " to " << bakFilename;
return false;
}
ExternalCommand mvCmd2(QStringLiteral("mv"), { out.fileName(), filename } );
Report report(nullptr);
QByteArray fstabContents;
QTextStream out(&fstabContents);
if ( !(mvCmd2.run(-1) && mvCmd2.exitCode() == 0) ) {
qWarning() << "could not move " << out.fileName() << " to " << filename;
return false;
}
}
for (const auto &e : fstabEntries)
writeEntry(out, e);
return true;
ExternalCommand cmd;
return cmd.writeData(report, fstabContents, filename, 0);
}
......@@ -192,7 +192,7 @@ Partition* LvmDevice::scanPartition(const QString& lvPath, PartitionTable* pTabl
startSector,
endSector,
lvPath,
PartitionTable::Flag::FlagNone,
PartitionTable::Flag::None,
mountPoint,
mounted);
return part;
......
......@@ -45,6 +45,8 @@ class LIBKPMCORE_EXPORT LvmDevice : public VolumeManagerDevice
{
Q_DISABLE_COPY(LvmDevice)
friend class VolumeManagerDevice;
public:
LvmDevice(const QString& name, const QString& iconName = QString());
~LvmDevice();
......@@ -57,8 +59,6 @@ public:
static QVector<const Partition*> s_DirtyPVs;
static QVector<const Partition*> s_OrphanPVs;
static void scanSystemLVM(QList<Device*>& devices);
static const QStringList getVGs();
static const QStringList getLVs(const QString& vgName);
......@@ -103,6 +103,9 @@ public:
protected:
std::unique_ptr<QHash<QString, qint64>>& LVSizeMap() const;
private:
static void scanSystemLVM(QList<Device*>& devices);
};
#endif
......@@ -44,7 +44,7 @@ public:
OperationRunner(QObject* parent, OperationStack& ostack);
public:
void run();
void run() override;
qint32 numJobs() const;
qint32 numOperations() const;
qint32 numProgressSub() const;
......
......@@ -86,7 +86,7 @@ public:
StateRestore [[deprecated("Use Partition::State::Restore")]] = Restore
};
Partition(PartitionNode* parent, const Device& device, const PartitionRole& role, FileSystem* fs, qint64 sectorStart, qint64 sectorEnd, QString partitionPath, PartitionTable::Flags availableFlags = PartitionTable::FlagNone, const QString& mountPoint = QString(), bool mounted = false, PartitionTable::Flags activeFlags = PartitionTable::FlagNone, State state = State::None);
Partition(PartitionNode* parent, const Device& device, const PartitionRole& role, FileSystem* fs, qint64 sectorStart, qint64 sectorEnd, QString partitionPath, PartitionTable::Flags availableFlags = PartitionTable::Flag::None, const QString& mountPoint = QString(), bool mounted = false, PartitionTable::Flags activeFlags = PartitionTable::Flag::None, State state = State::None);
~Partition() override;
Partition(const Partition& other, PartitionNode* parent = nullptr);
......@@ -238,10 +238,10 @@ public:
void setMounted(bool b);
void setFlag(PartitionTable::Flag f) {
m_ActiveFlags |= f;
m_ActiveFlags = m_ActiveFlags.setFlag(f);
}
void unsetFlag(PartitionTable::Flag f) {
m_ActiveFlags &= ~f;
m_ActiveFlags = m_ActiveFlags.setFlag(f, false);
}
void setParent(PartitionNode* p) {
m_Parent = p;
......
......@@ -182,39 +182,39 @@ void PartitionTable::append(Partition* partition)
QString PartitionTable::flagName(Flag f)
{
switch (f) {
case PartitionTable::FlagBoot:
case PartitionTable::Flag::Boot:
return xi18nc("@item partition flag", "boot");
case PartitionTable::FlagRoot:
case PartitionTable::Flag::Root:
return xi18nc("@item partition flag", "root");
case PartitionTable::FlagSwap:
case PartitionTable::Flag::Swap:
return xi18nc("@item partition flag", "swap");
case PartitionTable::FlagHidden:
case PartitionTable::Flag::Hidden:
return xi18nc("@item partition flag", "hidden");
case PartitionTable::FlagRaid:
case PartitionTable::Flag::Raid:
return xi18nc("@item partition flag", "raid");
case PartitionTable::FlagLvm:
case PartitionTable::Flag::Lvm:
return xi18nc("@item partition flag", "lvm");
case PartitionTable::FlagLba:
case PartitionTable::Flag::Lba:
return xi18nc("@item partition flag", "lba");
case PartitionTable::FlagHpService:
case PartitionTable::Flag::HpService:
return xi18nc("@item partition flag", "hpservice");
case PartitionTable::FlagPalo:
case PartitionTable::Flag::Palo:
return xi18nc("@item partition flag", "palo");
case PartitionTable::FlagPrep:
case PartitionTable::Flag::Prep:
return xi18nc("@item partition flag", "prep");
case PartitionTable::FlagMsftReserved:
case PartitionTable::Flag::MsftReserved:
return xi18nc("@item partition flag", "msft-reserved");
case PartitionTable::FlagBiosGrub:
case PartitionTable::Flag::BiosGrub:
return xi18nc("@item partition flag", "bios-grub");
case PartitionTable::FlagAppleTvRecovery:
case PartitionTable::Flag::AppleTvRecovery:
return xi18nc("@item partition flag", "apple-tv-recovery");
case PartitionTable::FlagDiag:
case PartitionTable::Flag::Diag:
return xi18nc("@item partition flag", "diag");
case PartitionTable::FlagLegacyBoot:
case PartitionTable::Flag::LegacyBoot:
return xi18nc("@item partition flag", "legacy-boot");
case PartitionTable::FlagMsftData:
case PartitionTable::Flag::MsftData:
return xi18nc("@item partition flag", "msft-data");
case PartitionTable::FlagIrst:
case PartitionTable::Flag::Irst:
return xi18nc("@item partition flag", "irst");
default:
break;
......@@ -228,23 +228,23 @@ const QList<PartitionTable::Flag> PartitionTable::flagList()
{
QList<PartitionTable::Flag> rval;
rval.append(PartitionTable::FlagBoot);
rval.append(PartitionTable::FlagRoot);
rval.append(PartitionTable::FlagSwap);
rval.append(PartitionTable::FlagHidden);
rval.append(PartitionTable::FlagRaid);
rval.append(PartitionTable::FlagLvm);
rval.append(PartitionTable::FlagLba);
rval.append(PartitionTable::FlagHpService);
rval.append(PartitionTable::FlagPalo);
rval.append(PartitionTable::FlagPrep);
rval.append(PartitionTable::FlagMsftReserved);
rval.append(PartitionTable::FlagBiosGrub);
rval.append(PartitionTable::FlagAppleTvRecovery);
rval.append(PartitionTable::FlagDiag);
rval.append(PartitionTable::FlagLegacyBoot);
rval.append(PartitionTable::FlagMsftData);
rval.append(PartitionTable::FlagIrst);
rval.append(PartitionTable::Flag::Boot);
rval.append(PartitionTable::Flag::Root);
rval.append(PartitionTable::Flag::Swap);
rval.append(PartitionTable::Flag::Hidden);
rval.append(PartitionTable::Flag::Raid);
rval.append(PartitionTable::Flag::Lvm);
rval.append(PartitionTable::Flag::Lba);
rval.append(PartitionTable::Flag::HpService);
rval.append(PartitionTable::Flag::Palo);
rval.append(PartitionTable::Flag::Prep);
rval.append(PartitionTable::Flag::MsftReserved);
rval.append(PartitionTable::Flag::BiosGrub);
rval.append(PartitionTable::Flag::AppleTvRecovery);
rval.append(PartitionTable::Flag::Diag);
rval.append(PartitionTable::Flag::LegacyBoot);
rval.append(PartitionTable::Flag::MsftData);
rval.append(PartitionTable::Flag::Irst);
return rval;
}
......@@ -269,6 +269,20 @@ QStringList PartitionTable::flagNames(Flags flags)
return rval;
}
/** @param list QStringList of the flags' names
@returns flags corresponding to names
*/
PartitionTable::Flags PartitionTable::flagsFromList(const QStringList list)
{
Flags flags;
for (const auto &flag : flagList())
if (list.contains(flagName(flag)))
flags.setFlag(flag);
return flags;
}
bool PartitionTable::getUnallocatedRange(const Device& d, PartitionNode& parent, qint64& start, qint64& end)
{
if (d.type() == Device::Type::Disk_Device) {
......
......@@ -16,8 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.*
*************************************************************************/
#if !defined(KPMCORE_PARTITIONTABLE_H)
#ifndef KPMCORE_PARTITIONTABLE_H
#define KPMCORE_PARTITIONTABLE_H
#include "util/libpartitionmanagerexport.h"
......@@ -50,7 +49,7 @@ class LIBKPMCORE_EXPORT PartitionTable : public PartitionNode
friend LIBKPMCORE_EXPORT QTextStream& operator<<(QTextStream& stream, const PartitionTable& ptable);
public:
enum TableType : qint8 {
enum TableType : int8_t {
unknownTableType = -1,
aix,
......@@ -69,30 +68,47 @@ public:
};
/** Partition flags */
enum Flag : qint32 {
FlagNone = 0,
FlagBoot = 1,
FlagRoot = 2,
FlagSwap = 4,
FlagHidden = 8,
FlagRaid = 16,
FlagLvm = 32,
FlagLba = 64,
FlagHpService = 128,
FlagPalo = 256,
FlagPrep = 512,
FlagMsftReserved = 1024,
FlagBiosGrub = 2048,
FlagAppleTvRecovery = 4096,
FlagDiag = 8192,
FlagLegacyBoot = 16384,
FlagMsftData = 32768,
FlagIrst = 65536,
FlagEsp [[deprecated]] = FlagBoot
enum Flag : uint32_t {
None = 0x0,
Boot = 0x1,
Root = 0x2,
Swap = 0x4,
Hidden = 0x8,
Raid = 0x10,
Lvm = 0x20,
Lba = 0x40,
HpService = 0x80,
Palo = 0x100,
Prep = 0x200,
MsftReserved = 0x400,
BiosGrub = 0x800,
AppleTvRecovery = 0x1000,
Diag = 0x2000,
LegacyBoot = 0x4000,
MsftData = 0x8000,
Irst = 0x100000,
FlagNone [[deprecated("Use PartitionTable::Flag::None")]] = None,
FlagBoot [[deprecated("Use PartitionTable::Flag::Boot")]] = Boot,
FlagRoot [[deprecated("Use PartitionTable::Flag::Root")]] = Root,
FlagSwap [[deprecated("Use PartitionTable::Flag::Swap")]] = Swap,
FlagHidden [[deprecated("Use PartitionTable::Flag::Hidden")]] = Hidden,
FlagRaid [[deprecated("Use PartitionTable::Flag::Raid")]] = Raid,
FlagLvm [[deprecated("Use PartitionTable::Flag::Lvm")]] = Lvm,
FlagLba [[deprecated("Use PartitionTable::Flag::Lba")]] = Lba,
FlagHpService [[deprecated("Use PartitionTable::Flag::HpService")]] = HpService,
FlagPalo [[deprecated("Use PartitionTable::Flag::Palo")]] = Palo,
FlagPrep [[deprecated("Use PartitionTable::Flag::Prep")]] = Prep,
FlagMsftReserved [[deprecated("Use PartitionTable::Flag::MsftReserved")]] = MsftReserved,
FlagBiosGrub [[deprecated("Use PartitionTable::Flag::BiosGrub")]] = BiosGrub,
FlagAppleTvRecovery [[deprecated("Use PartitionTable::Flag::AppleTvRecovery")]] = AppleTvRecovery,
FlagDiag [[deprecated("Use PartitionTable::Flag::Diag")]] = Diag,
FlagLegacyBoot [[deprecated("Use PartitionTable::Flag::LegacyBoot")]] = LegacyBoot,
FlagMsftData [[deprecated("Use PartitionTable::Flag::MsftData")]] = MsftData,
FlagIrst [[deprecated("Use PartitionTable::Flag::Irst")]] = Irst,
FlagEsp [[deprecated("Use PartitionTable::Flag::Boot")]] = Boot
};
Q_DECLARE_FLAGS(Flags, Flag)
Q_FLAG(Flag)
public:
PartitionTable(TableType type, qint64 firstUsable, qint64 lastUsable);
......@@ -168,6 +184,7 @@ public:
static const QList<Flag> flagList();
static QString flagName(Flag f);
static QStringList flagNames(Flags f);
static PartitionTable::Flags flagsFromList(const QStringList list);
static bool getUnallocatedRange(const Device& device, PartitionNode& parent, qint64& start, qint64& end);
......
......@@ -26,6 +26,8 @@ class LIBKPMCORE_EXPORT SoftwareRAID : public VolumeManagerDevice
{
Q_DISABLE_COPY(SoftwareRAID)
friend class VolumeManagerDevice;
public:
enum class Status {
Active,
......@@ -60,8 +62,6 @@ public:
void setStatus(SoftwareRAID::Status status);
public:
static void scanSoftwareRAID(QList<Device*>& devices);
static qint32 getRaidLevel(const QString& path);
static qint64 getChunkSize(const QString& path);
static qint64 getTotalChunk(const QString& path);
......@@ -109,6 +109,8 @@ private:
static bool eraseDeviceMDSuperblock(const QString& path);
static bool updateConfigurationFile(const QString& path);
static void scanSoftwareRAID(QList<Device*>& devices);
static QString getDetail(const QString& path);
......
......@@ -20,6 +20,8 @@
#include "core/partition.h"
#include "core/volumemanagerdevice.h"
#include "core/volumemanagerdevice_p.h"
#include "core/lvmdevice.h"
#include "core/raid/softwareraid.h"
#define d_ptr std::static_pointer_cast<VolumeManagerDevicePrivate>(d)
......@@ -40,6 +42,12 @@ VolumeManagerDevice::VolumeManagerDevice(std::shared_ptr<VolumeManagerDevicePriv
{
}
void VolumeManagerDevice::scanDevices(QList<Device*>& devices)
{
SoftwareRAID::scanSoftwareRAID(devices);
LvmDevice::scanSystemLVM(devices); // LVM scanner needs all other devices, so should be last
}
QString VolumeManagerDevice::prettyDeviceNodeList() const
{
return deviceNodes().join(QStringLiteral(", "));
......
......@@ -81,6 +81,8 @@ protected:
public:
static void scanDevices(QList<Device*>& devices);
/** join deviceNodes together into comma-separated list
*
* @return comma-separated list of deviceNodes
......
set(FS_SRC
fs/apfs.cpp
fs/bitlocker.cpp
fs/btrfs.cpp
fs/exfat.cpp
fs/ext2.cpp
......@@ -35,6 +37,8 @@ set(FS_SRC
)
set(FS_LIB_HDRS
fs/apfs.h
fs/bitlocker.h
fs/btrfs.h
fs/exfat.h
fs/ext2.h
......
/*************************************************************************
* Copyright (C) 2019 by Andrius Štikonas <stikonas@kde.org> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#include "fs/apfs.h"
namespace FS
{
FileSystem::CommandSupportType apfs::m_Move = FileSystem::cmdSupportCore;
FileSystem::CommandSupportType apfs::m_Copy = FileSystem::cmdSupportCore;
FileSystem::CommandSupportType apfs::m_Backup = FileSystem::cmdSupportCore;
apfs::apfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label) :
FileSystem(firstsector, lastsector, sectorsused, label, FileSystem::Type::Apfs)
{
}
}
/*************************************************************************
* Copyright (C) 2019 by Andrius Štikonas <stikonas@kde.org> *
* *
* 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 <http://www.gnu.org/licenses/>.*
*************************************************************************/
#ifndef KPMCORE_APFS_H
#define KPMCORE_APFS_H
#include "util/libpartitionmanagerexport.h"
#include "fs/filesystem.h"
#include <QtGlobal>
class QString;
namespace FS
{
/** An APFS file system.
@author Andrius Štikonas <stikonas@kde.org>
*/
class LIBKPMCORE_EXPORT apfs : public FileSystem
{
public:
apfs(qint64 firstsector, qint64 lastsector, qint64 sectorsused, const QString& label);
public:
CommandSupportType supportMove() const override {
return m_Move;
}
CommandSupportType supportCopy() const override {
return m_Copy;
}
CommandSupportType supportBackup() const override {
return m_Backup;
}
bool supportToolFound() const override {
return true;
}
public:
static CommandSupportType m_Move;
static CommandSupportType m_Copy;
static CommandSupportType m_Backup;
};
}
#endif
/*************************************************************************
* Copyright (C) 2019 by Andrius Štikonas <stikonas@kde.org> *
* *
* 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 <http://www.gnu.org/licenses/>.*