• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 API Reference
  • KDE Home
  • 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 #include "config-plasma.h"
00023 
00024 #include <QDir>
00025 #include <QFile>
00026 #include <QRegExp>
00027 #include <QtNetwork/QHostInfo>
00028 
00029 #ifdef QCA2_FOUND
00030 #include <QtCrypto>
00031 #endif
00032 
00033 #include <karchive.h>
00034 #include <kcomponentdata.h>
00035 #include <kdesktopfile.h>
00036 #ifndef PLASMA_NO_KIO
00037 #include <kio/copyjob.h>
00038 #include <kio/deletejob.h>
00039 #include <kio/jobclasses.h>
00040 #include <kio/job.h>
00041 #endif
00042 #include <kmimetype.h>
00043 #include <kplugininfo.h>
00044 #include <kstandarddirs.h>
00045 #include <ktar.h>
00046 #include <ktempdir.h>
00047 #include <ktemporaryfile.h>
00048 #include <kzip.h>
00049 #include <kdebug.h>
00050 
00051 #include "authorizationmanager.h"
00052 #include "dataenginemanager.h"
00053 #include "packagemetadata.h"
00054 #include "scripting/scriptengine.h"
00055 #include "private/authorizationmanager_p.h"
00056 #include "private/componentinstaller_p.h"
00057 #include "private/package_p.h"
00058 #include "private/plasmoidservice_p.h"
00059 #include "private/service_p.h"
00060 
00061 namespace Plasma
00062 {
00063 
00064 #ifdef PLASMA_NO_KIO // Provide some convenience for dealing with folders
00065 
00066 bool copyFolder(QString sourcePath, QString targetPath)
00067 {
00068     QDir source(sourcePath);
00069     if(!source.exists())
00070         return false;
00071 
00072     QDir target(targetPath);
00073     if(!target.exists()) {
00074         QString targetName = target.dirName();
00075         target.cdUp();
00076         target.mkdir(targetName);
00077         target = QDir(targetPath);
00078     }
00079 
00080     foreach (const QString &fileName, source.entryList(QDir::Files)) {
00081         QString sourceFilePath = sourcePath + QDir::separator() + fileName;
00082         QString targetFilePath = targetPath + QDir::separator() + fileName;
00083 
00084         if (!QFile::copy(sourceFilePath, targetFilePath)) {
00085             return false;
00086         }
00087     }
00088 
00089     foreach (const QString &subFolderName, source.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
00090         QString sourceSubFolderPath = sourcePath + QDir::separator() + subFolderName;
00091         QString targetSubFolderPath = targetPath + QDir::separator() + subFolderName;
00092 
00093         if (!copyFolder(sourceSubFolderPath, targetSubFolderPath)) {
00094             return false;
00095         }
00096     }
00097 
00098     return true;
00099 }
00100 
00101 bool removeFolder(QString folderPath)
00102 {
00103     QDir folder(folderPath);
00104     if(!folder.exists())
00105         return false;
00106 
00107     foreach (const QString &fileName, folder.entryList(QDir::Files)) {
00108         if (!QFile::remove(folderPath + QDir::separator() + fileName)) {
00109             return false;
00110         }
00111     }
00112 
00113     foreach (const QString &subFolderName, folder.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
00114         if (!removeFolder(folderPath + QDir::separator() + subFolderName)) {
00115             return false;
00116         }
00117     }
00118 
00119     QString folderName = folder.dirName();
00120     folder.cdUp();
00121     return folder.rmdir(folderName);
00122 }
00123 
00124 #endif // PLASMA_NO_KIO
00125 
00126 
00127 Package::Package()
00128     : d(new PackagePrivate(PackageStructure::Ptr(0), QString()))
00129 {
00130 }
00131 
00132 Package::Package(const QString &packageRoot, const QString &package,
00133                  PackageStructure::Ptr structure)
00134     : d(new PackagePrivate(structure, packageRoot, package))
00135 {
00136 }
00137 
00138 Package::Package(const QString &packagePath, PackageStructure::Ptr structure)
00139     : d(new PackagePrivate(structure, packagePath))
00140 {
00141 }
00142 
00143 Package::Package(const Package &other)
00144     : d(new PackagePrivate(*other.d))
00145 {
00146 }
00147 
00148 Package::~Package()
00149 {
00150     delete d;
00151 }
00152 
00153 Package &Package::operator=(const Package &rhs)
00154 {
00155     if (&rhs != this) {
00156         *d = *rhs.d;
00157     }
00158 
00159     return *this;
00160 }
00161 
00162 bool Package::isValid() const
00163 {
00164     if (!d->valid) {
00165         return false;
00166     }
00167 
00168     //search for the file in all prefixes and in all possible paths for each prefix
00169     //even if it's a big nested loop, usually there is one prefix and one location
00170     //so shouldn't cause too much disk access
00171     QStringList prefixes = d->structure->contentsPrefixPaths();
00172     if (prefixes.isEmpty()) {
00173         prefixes << QString();
00174     }
00175 
00176     foreach (const char *dir, d->structure->requiredDirectories()) {
00177         bool failed = true;
00178         foreach (const QString &path, d->structure->searchPath(dir)) {
00179             foreach (const QString &prefix, prefixes) {
00180                 if (QFile::exists(d->structure->path() + prefix + path)) {
00181                     failed = false;
00182                     break;
00183                 }
00184             }
00185             if (!failed) {
00186                 break;
00187             }
00188         }
00189 
00190         if (failed) {
00191             kWarning() << "Could not find required directory" << dir;
00192             d->valid = false;
00193             return false;
00194         }
00195     }
00196 
00197     foreach (const char *file, d->structure->requiredFiles()) {
00198         bool failed = true;
00199         foreach (const QString &path, d->structure->searchPath(file)) {
00200             foreach (const QString &prefix, prefixes) {
00201                 if (QFile::exists(d->structure->path() + prefix + path)) {
00202                     failed = false;
00203                     break;
00204                 }
00205             }
00206             if (!failed) {
00207                 break;
00208             }
00209         }
00210 
00211         if (failed) {
00212             kWarning() << "Could not find required file" << file;
00213             d->valid = false;
00214             return false;
00215         }
00216     }
00217 
00218     return true;
00219 }
00220 
00221 QString Package::filePath(const char *fileType, const QString &filename) const
00222 {
00223     if (!d->valid) {
00224         //kDebug() << "package is not valid";
00225         return QString();
00226     }
00227 
00228     QStringList paths;
00229 
00230     if (qstrlen(fileType) != 0) {
00231         paths = d->structure->searchPath(fileType);
00232 
00233         if (paths.isEmpty()) {
00234             //kDebug() << "no matching path came of it, while looking for" << fileType << filename;
00235             return QString();
00236         }
00237     } else {
00238         //when filetype is empty paths is always empty, so try with an empty string
00239         paths << QString();
00240     }
00241 
00242     //Nested loop, but in the medium case resolves to just one iteration
00243     QStringList prefixes = d->structure->contentsPrefixPaths();
00244     if (prefixes.isEmpty()) {
00245         prefixes << QString();
00246     }
00247 
00248     //kDebug() << "prefixes:" << prefixes.count() << prefixes;
00249     foreach (const QString &contentsPrefix, prefixes) {
00250         const QString prefix(d->structure->path() + contentsPrefix);
00251 
00252         foreach (const QString &path, paths) {
00253             QString file = prefix + path;
00254 
00255             if (!filename.isEmpty()) {
00256                 file.append("/").append(filename);
00257             }
00258 
00259             //kDebug() << "testing" << file << QFile::exists("/bin/ls") << QFile::exists(file);
00260             if (QFile::exists(file)) {
00261                 if (d->structure->allowExternalPaths()) {
00262                     //kDebug() << "found" << file;
00263                     return file;
00264                 }
00265 
00266                 // ensure that we don't return files outside of our base path
00267                 // due to symlink or ../ games
00268                 QDir dir(file);
00269                 QString canonicalized = dir.canonicalPath() + QDir::separator();
00270 
00271                 //kDebug() << "testing that" << canonicalized << "is in" << d->structure->path();
00272                 if (canonicalized.startsWith(d->structure->path())) {
00273                     //kDebug() << "found" << file;
00274                     return file;
00275                 }
00276             }
00277         }
00278     }
00279 
00280     //kDebug() << fileType << filename << "does not exist in" << prefixes << "at root" << d->structure->path();
00281     return QString();
00282 }
00283 
00284 QString Package::filePath(const char *fileType) const
00285 {
00286     return filePath(fileType, QString());
00287 }
00288 
00289 QStringList Package::entryList(const char *fileType) const
00290 {
00291     if (!d->valid) {
00292         return QStringList();
00293     }
00294 
00295     return d->structure->entryList(fileType);
00296 }
00297 
00298 PackageMetadata Package::metadata() const
00299 {
00300     if (d->structure) {
00301         return d->structure->metadata();
00302     }
00303 
00304     return PackageMetadata();
00305 }
00306 
00307 void Package::setPath(const QString &path)
00308 {
00309     if (d->structure) {
00310         d->structure->setPath(path);
00311         d->valid = !d->structure->path().isEmpty();
00312     }
00313 }
00314 
00315 const QString Package::path() const
00316 {
00317     return d->structure ? d->structure->path() : QString();
00318 }
00319 
00320 const PackageStructure::Ptr Package::structure() const
00321 {
00322     return d->structure;
00323 }
00324 
00325 #ifdef QCA2_FOUND
00326 void PackagePrivate::updateHash(const QString &basePath, const QString &subPath, const QDir &dir, QCA::Hash &hash)
00327 {
00328     // hash is calculated as a function of:
00329     // * files ordered alphabetically by name, with each file's:
00330     //      * path relative to the content root
00331     //      * file data
00332     // * directories ordered alphabetically by name, with each dir's:
00333     //      * path relative to the content root
00334     //      * file listing (recursing)
00335     // symlinks (in both the file and dir case) are handled by adding
00336     // the name of the symlink itself and the abs path of what it points to
00337 
00338     const QDir::SortFlags sorting = QDir::Name | QDir::IgnoreCase;
00339     const QDir::Filters filters = QDir::Hidden | QDir::System | QDir::NoDotAndDotDot;
00340     foreach (const QString &file, dir.entryList(QDir::Files | filters, sorting)) {
00341         if (!subPath.isEmpty()) {
00342             hash.update(subPath.toUtf8());
00343         }
00344 
00345         hash.update(file.toUtf8());
00346 
00347         QFileInfo info(dir.path() + '/' + file);
00348         if (info.isSymLink()) {
00349             hash.update(info.symLinkTarget().toUtf8());
00350         } else {
00351             QFile f(info.filePath());
00352             if (f.open(QIODevice::ReadOnly)) {
00353                 while (!f.atEnd()) {
00354                     hash.update(f.read(1024));
00355                 }
00356             } else {
00357                 kWarning() << "could not add" << f.fileName() << "to the hash; file could not be opened for reading. "
00358                            << "permissions fail?" << info.permissions() << info.isFile();
00359             }
00360         }
00361     }
00362 
00363     foreach (const QString &subDirPath, dir.entryList(QDir::Dirs | filters, sorting)) {
00364         const QString relativePath = subPath + subDirPath + '/';
00365         hash.update(relativePath.toUtf8());
00366 
00367         QDir subDir(dir.path());
00368         subDir.cd(subDirPath);
00369 
00370         if (subDir.path() != subDir.canonicalPath()) {
00371             hash.update(subDir.canonicalPath().toUtf8());
00372         } else {
00373             updateHash(basePath, relativePath, subDir, hash);
00374         }
00375     }
00376 }
00377 #endif
00378 
00379 QString Package::contentsHash() const
00380 {
00381 #ifdef QCA2_FOUND
00382     if (!d->valid) {
00383         kWarning() << "can not create hash due to Package being invalid";
00384         return QString();
00385     }
00386 
00387     //FIXME: the initializer should go somewhere global to be shared between all plasma uses?
00388     QCA::Initializer init;
00389     if (!QCA::isSupported("sha1")) {
00390         kWarning() << "can not create hash for" << path() << "due to no SHA1 support in QCA2";
00391         return QString();
00392     }
00393 
00394     QCA::Hash hash("sha1");
00395     QString metadataPath = d->structure->path() + "metadata.desktop";
00396     if (QFile::exists(metadataPath)) {
00397         QFile f(metadataPath);
00398         if (f.open(QIODevice::ReadOnly)) {
00399             while (!f.atEnd()) {
00400                 hash.update(f.read(1024));
00401             }
00402         } else {
00403             kWarning() << "could not add" << f.fileName() << "to the hash; file could not be opened for reading.";
00404         }
00405     } else {
00406         kWarning() << "no metadata at" << metadataPath;
00407     }
00408 
00409     QStringList prefixes = d->structure->contentsPrefixPaths();
00410     if (prefixes.isEmpty()) {
00411         prefixes << QString();
00412     }
00413 
00414     foreach (QString prefix, prefixes) {
00415         const QString basePath = d->structure->path() + prefix;
00416         QDir dir(basePath);
00417 
00418         if (!dir.exists()) {
00419             return QString();
00420         }
00421 
00422         d->updateHash(basePath, QString(), dir, hash);
00423     }
00424     return QCA::arrayToHex(hash.final().toByteArray());
00425 #else
00426     // no QCA2!
00427     kWarning() << "can not create hash for" << path() << "due to no cryptographic support (QCA2)";
00428     return QString();
00429 #endif
00430 }
00431 
00432 //TODO: provide a version of this that allows one to ask for certain types of packages, etc?
00433 //      should we be using KService here instead/as well?
00434 QStringList Package::listInstalled(const QString &packageRoot) // static
00435 {
00436     QDir dir(packageRoot);
00437 
00438     if (!dir.exists()) {
00439         return QStringList();
00440     }
00441 
00442     QStringList packages;
00443 
00444     foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
00445         QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
00446         if (QFile::exists(metadata)) {
00447             PackageMetadata m(metadata);
00448             packages << m.pluginName();
00449         }
00450     }
00451 
00452     return packages;
00453 }
00454 
00455 QStringList Package::listInstalledPaths(const QString &packageRoot) // static
00456 {
00457     QDir dir(packageRoot);
00458 
00459     if (!dir.exists()) {
00460         return QStringList();
00461     }
00462 
00463     QStringList packages;
00464 
00465     foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
00466         QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
00467         if (QFile::exists(metadata)) {
00468             packages << sdir;
00469         }
00470     }
00471 
00472     return packages;
00473 }
00474 
00475 bool Package::installPackage(const QString &package,
00476                              const QString &packageRoot,
00477                              const QString &servicePrefix) // static
00478 {
00479     //TODO: report *what* failed if something does fail
00480     QDir root(packageRoot);
00481 
00482     if (!root.exists()) {
00483         KStandardDirs::makeDir(packageRoot);
00484         if (!root.exists()) {
00485             kWarning() << "Could not create package root directory:" << packageRoot;
00486             return false;
00487         }
00488     }
00489 
00490     QFileInfo fileInfo(package);
00491     if (!fileInfo.exists()) {
00492         kWarning() << "No such file:" << package;
00493         return false;
00494     }
00495 
00496     QString path;
00497     KTempDir tempdir;
00498     bool archivedPackage = false;
00499 
00500     if (fileInfo.isDir()) {
00501         // we have a directory, so let's just install what is in there
00502         path = package;
00503 
00504         // make sure we end in a slash!
00505         if (path[path.size() - 1] != '/') {
00506             path.append('/');
00507         }
00508     } else {
00509         KArchive *archive = 0;
00510         KMimeType::Ptr mimetype = KMimeType::findByPath(package);
00511 
00512         if (mimetype->is("application/zip")) {
00513             archive = new KZip(package);
00514         } else if (mimetype->is("application/x-compressed-tar") ||
00515                    mimetype->is("application/x-tar")|| mimetype->is("application/x-bzip-compressed-tar") ||
00516                    mimetype->is("application/x-xz") || mimetype->is("application/x-lzma")) {
00517             archive = new KTar(package);
00518         } else {
00519             kWarning() << "Could not open package file, unsupported archive format:" << package << mimetype->name();
00520             return false;
00521         }
00522 
00523         if (!archive->open(QIODevice::ReadOnly)) {
00524             kWarning() << "Could not open package file:" << package;
00525         delete archive;
00526             return false;
00527         }
00528 
00529         archivedPackage = true;
00530         path = tempdir.name();
00531 
00532         const KArchiveDirectory *source = archive->directory();
00533         source->copyTo(path);
00534 
00535         QStringList entries = source->entries();
00536         if (entries.count() == 1) {
00537             const KArchiveEntry *entry = source->entry(entries[0]);
00538             if (entry->isDirectory()) {
00539                 path.append(entry->name()).append("/");
00540             }
00541         }
00542     delete archive;
00543     }
00544 
00545     QString metadataPath = path + "metadata.desktop";
00546     if (!QFile::exists(metadataPath)) {
00547         kWarning() << "No metadata file in package" << package << metadataPath;
00548         return false;
00549     }
00550 
00551     PackageMetadata meta(metadataPath);
00552     QString targetName = meta.pluginName();
00553 
00554     if (targetName.isEmpty()) {
00555         kWarning() << "Package plugin name not specified";
00556         return false;
00557     }
00558 
00559     // Ensure that package names are safe so package uninstall can't inject
00560     // bad characters into the paths used for removal.
00561     QRegExp validatePluginName("^[\\w-\\.]+$"); // Only allow letters, numbers, underscore and period.
00562     if (!validatePluginName.exactMatch(targetName)) {
00563         kWarning() << "Package plugin name " << targetName << "contains invalid characters";
00564         return false;
00565     }
00566 
00567     targetName = packageRoot + '/' + targetName;
00568     if (QFile::exists(targetName)) {
00569         kWarning() << targetName << "already exists";
00570         return false;
00571     }
00572 
00573     if (archivedPackage) {
00574         // it's in a temp dir, so just move it over.
00575 #ifndef PLASMA_NO_KIO
00576         KIO::CopyJob *job = KIO::move(KUrl(path), KUrl(targetName), KIO::HideProgressInfo);
00577         const bool ok = job->exec();
00578         const QString errorString = job->errorString();
00579 #else
00580         const bool ok = copyFolder(path, targetName);
00581         removeFolder(path);
00582         const QString errorString("unknown");
00583 #endif
00584         if (!ok) {
00585             kWarning() << "Could not move package to destination:" << targetName << " : " << errorString;
00586             return false;
00587         }
00588     } else {
00589         // it's a directory containing the stuff, so copy the contents rather
00590         // than move them
00591 #ifndef PLASMA_NO_KIO
00592         KIO::CopyJob *job = KIO::copy(KUrl(path), KUrl(targetName), KIO::HideProgressInfo);
00593         const bool ok = job->exec();
00594         const QString errorString = job->errorString();
00595 #else
00596         const bool ok = copyFolder(path, targetName);
00597         const QString errorString("unknown");
00598 #endif
00599         if (!ok) {
00600             kWarning() << "Could not copy package to destination:" << targetName << " : " << errorString;
00601             return false;
00602         }
00603     }
00604 
00605     if (archivedPackage) {
00606         // no need to remove the temp dir (which has been successfully moved if it's an archive)
00607         tempdir.setAutoRemove(false);
00608     }
00609 
00610     // check for missing dependencies
00611     QString requiredScriptEngine = meta.implementationApi();
00612     if (!requiredScriptEngine.isEmpty()) {
00613         // figure out the component type to query for
00614         ComponentTypes componentTypes = static_cast<ComponentTypes>(0);
00615         QStringList serviceTypes = meta.serviceType().split(',');
00616         if (serviceTypes.contains("Plasma/Applet")) {
00617             componentTypes |= AppletComponent;
00618         }
00619         if (serviceTypes.contains("Plasma/DataEngine")) {
00620             componentTypes |= DataEngineComponent;
00621         }
00622         if (serviceTypes.contains("Plasma/Runner")) {
00623             componentTypes |= RunnerComponent;
00624         }
00625         if (serviceTypes.contains("Plasma/Wallpaper")) {
00626             componentTypes |= WallpaperComponent;
00627         }
00628         if (!knownLanguages(componentTypes).contains(requiredScriptEngine)) {
00629             // install the missing script engine
00630             // force prompting because the user has just explicitly installed a widget
00631             ComponentInstaller::self()->installMissingComponent("scriptengine", requiredScriptEngine, 0, true);
00632         }
00633     }
00634     QStringList requiredDataEngines = meta.requiredDataEngines();
00635     if (requiredDataEngines.isEmpty()) {
00636         // check whether this was explicitly specified as empty
00637         QString metaPath = targetName + "/metadata.desktop";
00638         KDesktopFile df(metaPath);
00639         KConfigGroup cg = df.desktopGroup();
00640         if (!cg.hasKey("X-Plasma-RequiredDataEngines")) {
00641             // not specified at all, try running the dependency extraction
00642             requiredDataEngines = ComponentInstaller::self()->extractDataEngineDependencies(targetName,
00643                                                                                             requiredScriptEngine);
00644         }
00645     }
00646     if (!requiredDataEngines.isEmpty()) {
00647         QStringList knownDataEngines = DataEngineManager::self()->listAllEngines(meta.application());
00648         foreach (const QString &requiredDataEngine, requiredDataEngines) {
00649             if (!knownDataEngines.contains(requiredDataEngine)) {
00650                 // install the missing data engine
00651                 // force prompting because the user has just explicitly installed a widget
00652                 ComponentInstaller::self()->installMissingComponent("dataengine", requiredDataEngine, 0, true);
00653             }
00654         }
00655     }
00656 
00657     if (!servicePrefix.isEmpty()) {
00658         // and now we register it as a service =)
00659         QString metaPath = targetName + "/metadata.desktop";
00660         KDesktopFile df(metaPath);
00661         KConfigGroup cg = df.desktopGroup();
00662 
00663         // Q: should not installing it as a service disqualify it?
00664         // Q: i don't think so since KServiceTypeTrader may not be
00665         // used by the installing app in any case, and the
00666         // package is properly installed - aseigo
00667 
00668         //TODO: reduce code duplication with registerPackage below
00669 
00670         QString serviceName = servicePrefix + meta.pluginName();
00671 
00672         QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00673 #ifndef PLASMA_NO_KIO
00674         KIO::FileCopyJob *job = KIO::file_copy(metaPath, service, -1, KIO::HideProgressInfo);
00675         const bool ok = job->exec();
00676         const QString errorString = job->errorString();
00677 #else
00678         const bool ok = QFile::copy(metaPath, service);
00679         const QString errorString("unknown");
00680 #endif
00681         if (ok) {
00682             // the icon in the installed file needs to point to the icon in the
00683             // installation dir!
00684             QString iconPath = targetName + '/' + cg.readEntry("Icon");
00685             QFile icon(iconPath);
00686             if (icon.exists()) {
00687                 KDesktopFile df(service);
00688                 KConfigGroup cg = df.desktopGroup();
00689                 cg.writeEntry("Icon", iconPath);
00690             }
00691         } else {
00692             kWarning() << "Could not register package as service (this is not necessarily fatal):" << serviceName << " : " << errorString;
00693         }
00694     }
00695 
00696     return true;
00697 }
00698 
00699 bool Package::uninstallPackage(const QString &pluginName,
00700                                const QString &packageRoot,
00701                                const QString &servicePrefix) // static
00702 {
00703     // We need to remove the package directory and its metadata file.
00704     QString targetName = pluginName;
00705     targetName = packageRoot + '/' + targetName;
00706 
00707     if (!QFile::exists(targetName)) {
00708         kWarning() << targetName << "does not exist";
00709         return false;
00710     }
00711 
00712     QString serviceName = servicePrefix + pluginName;
00713 
00714     QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00715     kDebug() << "Removing service file " << service;
00716     bool ok = QFile::remove(service);
00717 
00718     if (!ok) {
00719         kWarning() << "Unable to remove " << service;
00720     }
00721 
00722 #ifndef PLASMA_NO_KIO
00723     KIO::DeleteJob *job = KIO::del(KUrl(targetName));
00724     ok = job->exec();
00725     const QString errorString = job->errorString();
00726 #else
00727     ok = removeFolder(targetName);
00728     const QString errorString("unknown");
00729 #endif
00730     if (!ok) {
00731         kWarning() << "Could not delete package from:" << targetName << " : " << errorString;
00732         return false;
00733     }
00734 
00735     return true;
00736 }
00737 
00738 bool Package::registerPackage(const PackageMetadata &data, const QString &iconPath)
00739 {
00740     QString serviceName("plasma-applet-" + data.pluginName());
00741     QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00742 
00743     if (data.pluginName().isEmpty()) {
00744         return false;
00745     }
00746 
00747     data.write(service);
00748 
00749     KDesktopFile config(service);
00750     KConfigGroup cg = config.desktopGroup();
00751     const QString type = data.type().isEmpty() ? "Service" : data.type();
00752     cg.writeEntry("Type", type);
00753     const QString serviceTypes = data.serviceType().isNull() ? "Plasma/Applet,Plasma/Containment" : data.serviceType();
00754     cg.writeEntry("X-KDE-ServiceTypes", serviceTypes);
00755     cg.writeEntry("X-KDE-PluginInfo-EnabledByDefault", true);
00756 
00757     QFile icon(iconPath);
00758     if (icon.exists()) {
00759         //FIXME: the '/' search will break on non-UNIX. do we care?
00760         QString installedIcon("plasma_applet_" + data.pluginName() +
00761                               iconPath.right(iconPath.length() - iconPath.lastIndexOf("/")));
00762         cg.writeEntry("Icon", installedIcon);
00763         installedIcon = KStandardDirs::locateLocal("icon", installedIcon);
00764 #ifndef PLASMA_NO_KIO
00765         KIO::FileCopyJob *job = KIO::file_copy(iconPath, installedIcon, -1, KIO::HideProgressInfo);
00766         job->exec();
00767 #else
00768         QFile::copy(iconPath, installedIcon);
00769 #endif
00770     }
00771 
00772     return true;
00773 }
00774 
00775 bool Package::createPackage(const PackageMetadata &metadata,
00776                             const QString &source,
00777                             const QString &destination,
00778                             const QString &icon) // static
00779 {
00780     Q_UNUSED(icon)
00781     if (!metadata.isValid()) {
00782         kWarning() << "Metadata file is not complete";
00783         return false;
00784     }
00785 
00786     // write metadata in a temporary file
00787     KTemporaryFile metadataFile;
00788     if (!metadataFile.open()) {
00789         return false;
00790     }
00791     metadata.write(metadataFile.fileName());
00792 
00793     // put everything into a zip archive
00794     KZip creation(destination);
00795     creation.setCompression(KZip::NoCompression);
00796     if (!creation.open(QIODevice::WriteOnly)) {
00797         return false;
00798     }
00799 
00800     creation.addLocalFile(metadataFile.fileName(), "metadata.desktop");
00801     creation.addLocalDirectory(source, "contents");
00802     creation.close();
00803     return true;
00804 }
00805 
00806 PackagePrivate::PackagePrivate(const PackageStructure::Ptr st, const QString &p)
00807         : structure(st),
00808           service(0)
00809 {
00810     if (structure) {
00811         if (p.isEmpty()) {
00812             structure->setPath(structure->defaultPackageRoot());
00813         } else {
00814             structure->setPath(p);
00815         }
00816     }
00817 
00818     valid = structure && !structure->path().isEmpty();
00819 }
00820 
00821 PackagePrivate::PackagePrivate(const PackageStructure::Ptr st, const QString &packageRoot, const QString &path)
00822         : structure(st),
00823           service(0)
00824 {
00825     if (structure) {
00826         if (packageRoot.isEmpty()) {
00827             structure->setPath(structure->defaultPackageRoot()%"/"%path);
00828         } else {
00829             structure->setPath(packageRoot%"/"%path);
00830         }
00831     }
00832 
00833     valid = structure && !structure->path().isEmpty();
00834 }
00835 
00836 PackagePrivate::PackagePrivate(const PackagePrivate &other)
00837         : structure(other.structure),
00838           service(other.service),
00839           valid(other.valid)
00840 {
00841 }
00842 
00843 PackagePrivate::~PackagePrivate()
00844 {
00845 }
00846 
00847 PackagePrivate &PackagePrivate::operator=(const PackagePrivate &rhs)
00848 {
00849     structure = rhs.structure;
00850     service = rhs.service;
00851     valid = rhs.valid;
00852     return *this;
00853 }
00854 
00855 void PackagePrivate::publish(AnnouncementMethods methods)
00856 {
00857     if (!structure) {
00858         return;
00859     }
00860 
00861     if (!service) {
00862         service = new PlasmoidService(structure->path());
00863     }
00864 
00865     QString resourceName =
00866     i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
00867           "%1 on %2", structure->metadata().name(), QHostInfo::localHostName());
00868     kDebug() << "publishing package under name " << resourceName;
00869     service->d->publish(methods, resourceName, structure->metadata());
00870 }
00871 
00872 void PackagePrivate::unpublish()
00873 {
00874     if (service) {
00875         service->d->unpublish();
00876     }
00877 }
00878 
00879 bool PackagePrivate::isPublished() const
00880 {
00881     if (service) {
00882         return service->d->isPublished();
00883     } else {
00884         return false;
00885     }
00886 }
00887 
00888 } // Namespace
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 20:51:36 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

Plasma

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

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal