• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Plasma

package.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002 *   Copyright 2007 by Aaron Seigo <aseigo@kde.org>                            *
00003 *   Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org>                   *
00004 *                                                                             *
00005 *   This library is free software; you can redistribute it and/or             *
00006 *   modify it under the terms of the GNU Library General Public               *
00007 *   License as published by the Free Software Foundation; either              *
00008 *   version 2 of the License, or (at your option) any later version.          *
00009 *                                                                             *
00010 *   This library is distributed in the hope that it will be useful,           *
00011 *   but WITHOUT ANY WARRANTY; without even the implied warranty of            *
00012 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          *
00013 *   Library General Public License for more details.                          *
00014 *                                                                             *
00015 *   You should have received a copy of the GNU Library General Public License *
00016 *   along with this library; see the file COPYING.LIB.  If not, write to      *
00017 *   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,      *
00018 *   Boston, MA 02110-1301, USA.                                               *
00019 *******************************************************************************/
00020 
00021 #include "package.h"
00022 
00023 #include <QDir>
00024 #include <QFile>
00025 #include <QRegExp>
00026 
00027 #include <karchive.h>
00028 #include <kcomponentdata.h>
00029 #include <kdesktopfile.h>
00030 #include <kio/copyjob.h>
00031 #include <kio/deletejob.h>
00032 #include <kio/jobclasses.h>
00033 #include <kio/job.h>
00034 #include <kplugininfo.h>
00035 #include <kstandarddirs.h>
00036 #include <ktempdir.h>
00037 #include <ktemporaryfile.h>
00038 #include <kzip.h>
00039 #include <kdebug.h>
00040 
00041 #include "packagemetadata.h"
00042 
00043 namespace Plasma
00044 {
00045 
00046 class PackagePrivate
00047 {
00048 public:
00049     PackagePrivate(const PackageStructure::Ptr st, const QString &p)
00050         : structure(st),
00051           basePath(p)
00052     {
00053         QDir dir(basePath);
00054         basePath = dir.canonicalPath();
00055         valid = QFile::exists(basePath);
00056 
00057         if (valid) {
00058             QFileInfo info(basePath);
00059             if (info.isDir()) {
00060                 basePath.append(QDir::separator());
00061             }
00062             kDebug() << "basePath is" << basePath;
00063         } else {
00064             kDebug() << p << "invalid, basePath is" << basePath;
00065         }
00066     }
00067 
00068     ~PackagePrivate()
00069     {
00070     }
00071 
00072     PackageStructure::Ptr structure;
00073     QString basePath;
00074     bool valid;
00075 };
00076 
00077 Package::Package(const QString &packageRoot, const QString &package,
00078                  PackageStructure::Ptr structure)
00079     : d(new PackagePrivate(structure, packageRoot + '/' + package))
00080 {
00081     structure->setPath(d->basePath);
00082 }
00083 
00084 Package::Package(const QString &packagePath, PackageStructure::Ptr structure)
00085     : d(new PackagePrivate(structure, packagePath))
00086 {
00087     structure->setPath(d->basePath);
00088 }
00089 
00090 Package::~Package()
00091 {
00092     delete d;
00093 }
00094 
00095 bool Package::isValid() const
00096 {
00097     if (!d->valid) {
00098         return false;
00099     }
00100 
00101     foreach (const char *dir, d->structure->requiredDirectories()) {
00102         if (!QFile::exists(d->basePath + d->structure->contentsPrefix() + d->structure->path(dir))) {
00103             kWarning() << "Could not find required directory" << dir;
00104             d->valid = false;
00105             return false;
00106         }
00107     }
00108 
00109     foreach (const char *file, d->structure->requiredFiles()) {
00110         if (!QFile::exists(d->basePath + d->structure->contentsPrefix() + d->structure->path(file))) {
00111             kWarning() << "Could not find required file" << file << ", look in"
00112                        << d->basePath + d->structure->contentsPrefix() + d->structure->path(file) << endl;
00113             d->valid = false;
00114             return false;
00115         }
00116     }
00117 
00118     return true;
00119 }
00120 
00121 QString Package::filePath(const char *fileType, const QString &filename) const
00122 {
00123     if (!d->valid) {
00124         kDebug() << "package is not valid";
00125         return QString();
00126     }
00127 
00128     QString path = d->structure->path(fileType);
00129 
00130     if (path.isEmpty()) {
00131         kDebug() << "no matching path came of it, while looking for" << fileType << filename;
00132         return QString();
00133     }
00134 
00135     path.prepend(d->basePath + d->structure->contentsPrefix());
00136 
00137     if (!filename.isEmpty()) {
00138         path.append("/").append(filename);
00139     }
00140 
00141     if (QFile::exists(path)) {
00142         if (d->structure->allowExternalPaths()) {
00143             return path;
00144         }
00145 
00146         // ensure that we don't return files outside of our base path
00147         // due to symlink or ../ games
00148         QDir dir(path);
00149         QString canonicalized = dir.canonicalPath() + QDir::separator();
00150         if (canonicalized.startsWith(d->basePath)) {
00151             return path;
00152         }
00153     }
00154 
00155     kDebug() << path << "does not exist";
00156     return QString();
00157 }
00158 
00159 QString Package::filePath(const char *fileType) const
00160 {
00161     return filePath(fileType, QString());
00162 }
00163 
00164 QStringList Package::entryList(const char *fileType) const
00165 {
00166     if (!d->valid) {
00167         return QStringList();
00168     }
00169 
00170     QString path = d->structure->path(fileType);
00171     if (path.isEmpty()) {
00172         return QStringList();
00173     }
00174 
00175     QDir dir(d->basePath + d->structure->contentsPrefix() + path);
00176 
00177     if (dir.exists()) {
00178         if (d->structure->allowExternalPaths()) {
00179             return dir.entryList(QDir::Files | QDir::Readable);
00180         }
00181 
00182         // ensure that we don't return files outside of our base path
00183         // due to symlink or ../ games
00184         QString canonicalized = dir.canonicalPath();
00185         if (canonicalized.startsWith(d->basePath)) {
00186             return dir.entryList(QDir::Files | QDir::Readable);
00187         }
00188     }
00189 
00190     return QStringList();
00191 }
00192 
00193 PackageMetadata Package::metadata() const
00194 {
00195     return d->structure->metadata();
00196 }
00197 
00198 const QString Package::path() const
00199 {
00200     return d->basePath;
00201 }
00202 
00203 const PackageStructure::Ptr Package::structure() const
00204 {
00205     return d->structure;
00206 }
00207 
00208 //TODO: provide a version of this that allows one to ask for certain types of packages, etc?
00209 //      should we be using KService here instead/as well?
00210 QStringList Package::listInstalled(const QString &packageRoot) // static
00211 {
00212     QDir dir(packageRoot);
00213 
00214     if (!dir.exists()) {
00215         return QStringList();
00216     }
00217 
00218     QStringList packages;
00219 
00220     foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
00221         QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
00222         if (QFile::exists(metadata)) {
00223             PackageMetadata m(metadata);
00224             packages << m.pluginName();
00225         }
00226     }
00227 
00228     return packages;
00229 }
00230 
00231 QStringList Package::listInstalledPaths(const QString &packageRoot) // static
00232 {
00233     QDir dir(packageRoot);
00234 
00235     if (!dir.exists()) {
00236         return QStringList();
00237     }
00238 
00239     QStringList packages;
00240 
00241     foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
00242         QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
00243         if (QFile::exists(metadata)) {
00244             packages << sdir;
00245         }
00246     }
00247 
00248     return packages;
00249 }
00250 
00251 bool Package::installPackage(const QString &package,
00252                              const QString &packageRoot,
00253                              const QString &servicePrefix) // static
00254 {
00255     //TODO: report *what* failed if something does fail
00256     QDir root(packageRoot);
00257 
00258     if (!root.exists()) {
00259         KStandardDirs::makeDir(packageRoot);
00260         if (!root.exists()) {
00261             kWarning() << "Could not create package root directory:" << packageRoot;
00262             return false;
00263         }
00264     }
00265 
00266     QFileInfo fileInfo(package);
00267     if (!fileInfo.exists()) {
00268         kWarning() << "No such file:" << package;
00269         return false;
00270     }
00271 
00272     QString path;
00273     KTempDir tempdir;
00274     bool archivedPackage = false;
00275 
00276     if (fileInfo.isDir()) {
00277         // we have a directory, so let's just install what is in there
00278         path = package;
00279 
00280         // make sure we end in a slash!
00281         if (path[path.size() - 1] != '/') {
00282             path.append('/');
00283         }
00284     } else {
00285         KZip archive(package);
00286         if (!archive.open(QIODevice::ReadOnly)) {
00287             kWarning() << "Could not open package file:" << package;
00288             return false;
00289         }
00290 
00291         archivedPackage = true;
00292         const KArchiveDirectory *source = archive.directory();
00293         const KArchiveEntry *metadata = source->entry("metadata.desktop");
00294 
00295         if (!metadata) {
00296             kWarning() << "No metadata file in package" << package;
00297             return false;
00298         }
00299 
00300         path = tempdir.name();
00301         source->copyTo(path);
00302     }
00303 
00304     QString metadataPath = path + "metadata.desktop";
00305     if (!QFile::exists(metadataPath)) {
00306         kWarning() << "No metadata file in package" << package;
00307         return false;
00308     }
00309 
00310     PackageMetadata meta(metadataPath);
00311     QString targetName = meta.pluginName();
00312 
00313     if (targetName.isEmpty()) {
00314         kWarning() << "Package plugin name not specified";
00315         return false;
00316     }
00317 
00318     // Ensure that package names are safe so package uninstall can't inject
00319     // bad characters into the paths used for removal.
00320     QRegExp validatePluginName("^[\\w-\\.]+$"); // Only allow letters, numbers, underscore and period.
00321     if (!validatePluginName.exactMatch(targetName)) {
00322         kWarning() << "Package plugin name " << targetName << "contains invalid characters";
00323         return false;
00324     }
00325 
00326     targetName = packageRoot + '/' + targetName;
00327     if (QFile::exists(targetName)) {
00328         kWarning() << targetName << "already exists";
00329         return false;
00330     }
00331 
00332     if (archivedPackage) {
00333         // it's in a temp dir, so just move it over.
00334         KIO::CopyJob *job = KIO::move(KUrl(path), KUrl(targetName), KIO::HideProgressInfo);
00335         if (!job->exec()) {
00336             kWarning() << "Could not move package to destination:" << targetName << " : " << job->errorString();
00337             return false;
00338         }
00339     } else {
00340         // it's a directory containing the stuff, so copy the contents rather
00341         // than move them
00342         KIO::CopyJob *job = KIO::copy(KUrl(path), KUrl(targetName), KIO::HideProgressInfo);
00343         if (!job->exec()) {
00344             kWarning() << "Could not copy package to destination:" << targetName << " : " << job->errorString();
00345             return false;
00346         }
00347     }
00348 
00349     if (archivedPackage) {
00350         // no need to remove the temp dir (which has been successfully moved if it's an archive)
00351         tempdir.setAutoRemove(false);
00352     }
00353 
00354     // and now we register it as a service =)
00355     QString metaPath = targetName + "/metadata.desktop";
00356     KDesktopFile df(metaPath);
00357     KConfigGroup cg = df.desktopGroup();
00358 
00359     // Q: should not installing it as a service disqualify it?
00360     // Q: i don't think so since KServiceTypeTrader may not be
00361     // used by the installing app in any case, and the
00362     // package is properly installed - aseigo
00363 
00364     //TODO: reduce code duplication with registerPackage below
00365 
00366     QString serviceName = servicePrefix + meta.pluginName();
00367 
00368     QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00369     KIO::FileCopyJob *job = KIO::file_copy(metaPath, service, -1, KIO::HideProgressInfo);
00370     if (job->exec()) {
00371         // the icon in the installed file needs to point to the icon in the
00372         // installation dir!
00373         QString iconPath = targetName + '/' + cg.readEntry("Icon");
00374         QFile icon(iconPath);
00375         if (icon.exists()) {
00376             KDesktopFile df(service);
00377             KConfigGroup cg = df.desktopGroup();
00378             cg.writeEntry("Icon", iconPath);
00379         }
00380     }
00381 
00382     return true;
00383 }
00384 
00385 bool Package::uninstallPackage(const QString &pluginName,
00386                                const QString &packageRoot,
00387                                const QString &servicePrefix) // static
00388 {
00389     // We need to remove the package directory and its metadata file.
00390     QString targetName = pluginName;
00391     targetName = packageRoot + '/' + targetName;
00392 
00393     if (!QFile::exists(targetName)) {
00394         kWarning() << targetName << "does not exist";
00395         return false;
00396     }
00397 
00398     QString serviceName = servicePrefix + pluginName;
00399 
00400     QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00401     kDebug() << "Removing service file " << service;
00402     bool ok = QFile::remove(service);
00403 
00404     if (!ok) {
00405         kWarning() << "Unable to remove " << service;
00406         return ok;
00407     }
00408 
00409     KIO::DeleteJob *job = KIO::del(KUrl(targetName));
00410     if (!job->exec()) {
00411         kWarning() << "Could not delete package from:" << targetName << " : " << job->errorString();
00412         return false;
00413     }
00414 
00415     return true;
00416 }
00417 
00418 bool Package::registerPackage(const PackageMetadata &data, const QString &iconPath)
00419 {
00420     QString serviceName("plasma-applet-" + data.pluginName());
00421     QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00422 
00423     if (data.pluginName().isEmpty()) {
00424         return false;
00425     }
00426 
00427     data.write(service);
00428 
00429     KDesktopFile config(service);
00430     KConfigGroup cg = config.desktopGroup();
00431     const QString type = data.type().isEmpty() ? "Service" : data.type();
00432     cg.writeEntry("Type", type);
00433     const QString serviceTypes = data.serviceType().isNull() ? "Plasma/Applet,Plasma/Containment" : data.serviceType();
00434     cg.writeEntry("X-KDE-ServiceTypes", serviceTypes);
00435     cg.writeEntry("X-KDE-PluginInfo-EnabledByDefault", true);
00436 
00437     QFile icon(iconPath);
00438     if (icon.exists()) {
00439         //FIXME: the '/' search will break on non-UNIX. do we care?
00440         QString installedIcon("plasma_applet_" + data.pluginName() +
00441                               iconPath.right(iconPath.length() - iconPath.lastIndexOf("/")));
00442         cg.writeEntry("Icon", installedIcon);
00443         installedIcon = KStandardDirs::locateLocal("icon", installedIcon);
00444         KIO::FileCopyJob *job = KIO::file_copy(iconPath, installedIcon, -1, KIO::HideProgressInfo);
00445         job->exec();
00446     }
00447 
00448     return true;
00449 }
00450 
00451 bool Package::createPackage(const PackageMetadata &metadata,
00452                             const QString &source,
00453                             const QString &destination,
00454                             const QString &icon) // static
00455 {
00456     Q_UNUSED(icon)
00457     if (!metadata.isValid()) {
00458         kWarning() << "Metadata file is not complete";
00459         return false;
00460     }
00461 
00462     // write metadata in a temporary file
00463     KTemporaryFile metadataFile;
00464     if (!metadataFile.open()) {
00465         return false;
00466     }
00467     metadata.write(metadataFile.fileName());
00468 
00469     // put everything into a zip archive
00470     KZip creation(destination);
00471     creation.setCompression(KZip::NoCompression);
00472     if (!creation.open(QIODevice::WriteOnly)) {
00473         return false;
00474     }
00475 
00476     creation.addLocalFile(metadataFile.fileName(), "metadata.desktop");
00477     creation.addLocalDirectory(source, "contents");
00478     creation.close();
00479     return true;
00480 }
00481 
00482 } // Namespace

Plasma

Skip menu "Plasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal