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