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