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 Wed May 2 2012 17:36:12 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 17:36:12 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.