• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 API Reference
  • KDE Home
  • Contact Us
 

Plasma

  • plasma
package.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 * Copyright 2007 by Aaron Seigo <aseigo@kde.org> *
3 * Copyright 2007 by Riccardo Iaconelli <riccardo@kde.org> *
4 * *
5 * This library is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU Library General Public *
7 * License as published by the Free Software Foundation; either *
8 * version 2 of the License, or (at your option) any later version. *
9 * *
10 * This library is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
13 * Library General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU Library General Public License *
16 * along with this library; see the file COPYING.LIB. If not, write to *
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
18 * Boston, MA 02110-1301, USA. *
19 *******************************************************************************/
20 
21 #include "package.h"
22 #include "config-plasma.h"
23 
24 #include <QDir>
25 #include <QFile>
26 #include <QRegExp>
27 #include <QtNetwork/QHostInfo>
28 
29 #ifdef QCA2_FOUND
30 #include <QtCrypto>
31 #endif
32 
33 #include <karchive.h>
34 #include <kcomponentdata.h>
35 #include <kdesktopfile.h>
36 #include <kmimetype.h>
37 #include <kplugininfo.h>
38 #include <kstandarddirs.h>
39 #include <ktar.h>
40 #include <ktempdir.h>
41 #include <ktemporaryfile.h>
42 #include <kzip.h>
43 #include <kdebug.h>
44 
45 #include "authorizationmanager.h"
46 #include "packagemetadata.h"
47 #include "private/authorizationmanager_p.h"
48 #include "private/package_p.h"
49 #include "private/plasmoidservice_p.h"
50 #include "private/service_p.h"
51 
52 namespace Plasma
53 {
54 
55 bool copyFolder(QString sourcePath, QString targetPath)
56 {
57  QDir source(sourcePath);
58  if(!source.exists())
59  return false;
60 
61  QDir target(targetPath);
62  if(!target.exists()) {
63  QString targetName = target.dirName();
64  target.cdUp();
65  target.mkdir(targetName);
66  target = QDir(targetPath);
67  }
68 
69  foreach (const QString &fileName, source.entryList(QDir::Files)) {
70  QString sourceFilePath = sourcePath + QDir::separator() + fileName;
71  QString targetFilePath = targetPath + QDir::separator() + fileName;
72 
73  if (!QFile::copy(sourceFilePath, targetFilePath)) {
74  return false;
75  }
76  }
77 
78  foreach (const QString &subFolderName, source.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
79  QString sourceSubFolderPath = sourcePath + QDir::separator() + subFolderName;
80  QString targetSubFolderPath = targetPath + QDir::separator() + subFolderName;
81 
82  if (!copyFolder(sourceSubFolderPath, targetSubFolderPath)) {
83  return false;
84  }
85  }
86 
87  return true;
88 }
89 
90 bool removeFolder(QString folderPath)
91 {
92  QDir folder(folderPath);
93  if(!folder.exists())
94  return false;
95 
96  foreach (const QString &fileName, folder.entryList(QDir::Files)) {
97  if (!QFile::remove(folderPath + QDir::separator() + fileName)) {
98  return false;
99  }
100  }
101 
102  foreach (const QString &subFolderName, folder.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
103  if (!removeFolder(folderPath + QDir::separator() + subFolderName)) {
104  return false;
105  }
106  }
107 
108  QString folderName = folder.dirName();
109  folder.cdUp();
110  return folder.rmdir(folderName);
111 }
112 
113 Package::Package()
114  : d(new PackagePrivate(PackageStructure::Ptr(0), QString()))
115 {
116 }
117 
118 Package::Package(const QString &packageRoot, const QString &package,
119  PackageStructure::Ptr structure)
120  : d(new PackagePrivate(structure, packageRoot, package))
121 {
122 }
123 
124 Package::Package(const QString &packagePath, PackageStructure::Ptr structure)
125  : d(new PackagePrivate(structure, packagePath))
126 {
127 }
128 
129 Package::Package(const Package &other)
130  : d(new PackagePrivate(*other.d))
131 {
132 }
133 
134 Package::~Package()
135 {
136  delete d;
137 }
138 
139 Package &Package::operator=(const Package &rhs)
140 {
141  if (&rhs != this) {
142  *d = *rhs.d;
143  }
144 
145  return *this;
146 }
147 
148 bool Package::isValid() const
149 {
150  if (!d->valid) {
151  return false;
152  }
153 
154  //search for the file in all prefixes and in all possible paths for each prefix
155  //even if it's a big nested loop, usually there is one prefix and one location
156  //so shouldn't cause too much disk access
157  QStringList prefixes = d->structure->contentsPrefixPaths();
158  if (prefixes.isEmpty()) {
159  prefixes << QString();
160  }
161 
162  foreach (const char *dir, d->structure->requiredDirectories()) {
163  bool failed = true;
164  foreach (const QString &path, d->structure->searchPath(dir)) {
165  foreach (const QString &prefix, prefixes) {
166  if (QFile::exists(d->structure->path() + prefix + path)) {
167  failed = false;
168  break;
169  }
170  }
171  if (!failed) {
172  break;
173  }
174  }
175 
176  if (failed) {
177  kWarning() << "Could not find required directory" << dir;
178  d->valid = false;
179  return false;
180  }
181  }
182 
183  foreach (const char *file, d->structure->requiredFiles()) {
184  bool failed = true;
185  foreach (const QString &path, d->structure->searchPath(file)) {
186  foreach (const QString &prefix, prefixes) {
187  if (QFile::exists(d->structure->path() + prefix + path)) {
188  failed = false;
189  break;
190  }
191  }
192  if (!failed) {
193  break;
194  }
195  }
196 
197  if (failed) {
198  kWarning() << "Could not find required file" << file;
199  d->valid = false;
200  return false;
201  }
202  }
203 
204  return true;
205 }
206 
207 QString Package::filePath(const char *fileType, const QString &filename) const
208 {
209  if (!d->valid) {
210  //kDebug() << "package is not valid";
211  return QString();
212  }
213 
214  QStringList paths;
215 
216  if (qstrlen(fileType) != 0) {
217  paths = d->structure->searchPath(fileType);
218 
219  if (paths.isEmpty()) {
220  //kDebug() << "no matching path came of it, while looking for" << fileType << filename;
221  return QString();
222  }
223  } else {
224  //when filetype is empty paths is always empty, so try with an empty string
225  paths << QString();
226  }
227 
228  //Nested loop, but in the medium case resolves to just one iteration
229  QStringList prefixes = d->structure->contentsPrefixPaths();
230  if (prefixes.isEmpty()) {
231  prefixes << QString();
232  }
233 
234  //kDebug() << "prefixes:" << prefixes.count() << prefixes;
235  foreach (const QString &contentsPrefix, prefixes) {
236  const QString prefix(d->structure->path() + contentsPrefix);
237 
238  foreach (const QString &path, paths) {
239  QString file = prefix + path;
240 
241  if (!filename.isEmpty()) {
242  file.append("/").append(filename);
243  }
244 
245  //kDebug() << "testing" << file << QFile::exists("/bin/ls") << QFile::exists(file);
246  if (QFile::exists(file)) {
247  if (d->structure->allowExternalPaths()) {
248  //kDebug() << "found" << file;
249  return file;
250  }
251 
252  // ensure that we don't return files outside of our base path
253  // due to symlink or ../ games
254  QDir dir(file);
255  QString canonicalized = dir.canonicalPath() + QDir::separator();
256 
257  //kDebug() << "testing that" << canonicalized << "is in" << d->structure->path();
258  if (canonicalized.startsWith(d->structure->path())) {
259  //kDebug() << "found" << file;
260  return file;
261  }
262  }
263  }
264  }
265 
266  //kDebug() << fileType << filename << "does not exist in" << prefixes << "at root" << d->structure->path();
267  return QString();
268 }
269 
270 QString Package::filePath(const char *fileType) const
271 {
272  return filePath(fileType, QString());
273 }
274 
275 QStringList Package::entryList(const char *fileType) const
276 {
277  if (!d->valid) {
278  return QStringList();
279  }
280 
281  return d->structure->entryList(fileType);
282 }
283 
284 PackageMetadata Package::metadata() const
285 {
286  if (d->structure) {
287  return d->structure->metadata();
288  }
289 
290  return PackageMetadata();
291 }
292 
293 void Package::setPath(const QString &path)
294 {
295  if (d->structure) {
296  d->structure->setPath(path);
297  d->valid = !d->structure->path().isEmpty();
298  }
299 }
300 
301 const QString Package::path() const
302 {
303  return d->structure ? d->structure->path() : QString();
304 }
305 
306 const PackageStructure::Ptr Package::structure() const
307 {
308  return d->structure;
309 }
310 
311 #ifdef QCA2_FOUND
312 void PackagePrivate::updateHash(const QString &basePath, const QString &subPath, const QDir &dir, QCA::Hash &hash)
313 {
314  // hash is calculated as a function of:
315  // * files ordered alphabetically by name, with each file's:
316  // * path relative to the content root
317  // * file data
318  // * directories ordered alphabetically by name, with each dir's:
319  // * path relative to the content root
320  // * file listing (recursing)
321  // symlinks (in both the file and dir case) are handled by adding
322  // the name of the symlink itself and the abs path of what it points to
323 
324  const QDir::SortFlags sorting = QDir::Name | QDir::IgnoreCase;
325  const QDir::Filters filters = QDir::Hidden | QDir::System | QDir::NoDotAndDotDot;
326  foreach (const QString &file, dir.entryList(QDir::Files | filters, sorting)) {
327  if (!subPath.isEmpty()) {
328  hash.update(subPath.toUtf8());
329  }
330 
331  hash.update(file.toUtf8());
332 
333  QFileInfo info(dir.path() + '/' + file);
334  if (info.isSymLink()) {
335  hash.update(info.symLinkTarget().toUtf8());
336  } else {
337  QFile f(info.filePath());
338  if (f.open(QIODevice::ReadOnly)) {
339  while (!f.atEnd()) {
340  hash.update(f.read(1024));
341  }
342  } else {
343  kWarning() << "could not add" << f.fileName() << "to the hash; file could not be opened for reading. "
344  << "permissions fail?" << info.permissions() << info.isFile();
345  }
346  }
347  }
348 
349  foreach (const QString &subDirPath, dir.entryList(QDir::Dirs | filters, sorting)) {
350  const QString relativePath = subPath + subDirPath + '/';
351  hash.update(relativePath.toUtf8());
352 
353  QDir subDir(dir.path());
354  subDir.cd(subDirPath);
355 
356  if (subDir.path() != subDir.canonicalPath()) {
357  hash.update(subDir.canonicalPath().toUtf8());
358  } else {
359  updateHash(basePath, relativePath, subDir, hash);
360  }
361  }
362 }
363 #endif
364 
365 QString Package::contentsHash() const
366 {
367 #ifdef QCA2_FOUND
368  if (!d->valid) {
369  kWarning() << "can not create hash due to Package being invalid";
370  return QString();
371  }
372 
373  //FIXME: the initializer should go somewhere global to be shared between all plasma uses?
374  QCA::Initializer init;
375  if (!QCA::isSupported("sha1")) {
376  kWarning() << "can not create hash for" << path() << "due to no SHA1 support in QCA2";
377  return QString();
378  }
379 
380  QCA::Hash hash("sha1");
381  QString metadataPath = d->structure->path() + "metadata.desktop";
382  if (QFile::exists(metadataPath)) {
383  QFile f(metadataPath);
384  if (f.open(QIODevice::ReadOnly)) {
385  while (!f.atEnd()) {
386  hash.update(f.read(1024));
387  }
388  } else {
389  kWarning() << "could not add" << f.fileName() << "to the hash; file could not be opened for reading.";
390  }
391  } else {
392  kWarning() << "no metadata at" << metadataPath;
393  }
394 
395  QStringList prefixes = d->structure->contentsPrefixPaths();
396  if (prefixes.isEmpty()) {
397  prefixes << QString();
398  }
399 
400  foreach (QString prefix, prefixes) {
401  const QString basePath = d->structure->path() + prefix;
402  QDir dir(basePath);
403 
404  if (!dir.exists()) {
405  return QString();
406  }
407 
408  d->updateHash(basePath, QString(), dir, hash);
409  }
410  return QCA::arrayToHex(hash.final().toByteArray());
411 #else
412  // no QCA2!
413  kWarning() << "can not create hash for" << path() << "due to no cryptographic support (QCA2)";
414  return QString();
415 #endif
416 }
417 
418 //TODO: provide a version of this that allows one to ask for certain types of packages, etc?
419 // should we be using KService here instead/as well?
420 QStringList Package::listInstalled(const QString &packageRoot) // static
421 {
422  QDir dir(packageRoot);
423 
424  if (!dir.exists()) {
425  return QStringList();
426  }
427 
428  QStringList packages;
429 
430  foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
431  QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
432  if (QFile::exists(metadata)) {
433  PackageMetadata m(metadata);
434  packages << m.pluginName();
435  }
436  }
437 
438  return packages;
439 }
440 
441 QStringList Package::listInstalledPaths(const QString &packageRoot) // static
442 {
443  QDir dir(packageRoot);
444 
445  if (!dir.exists()) {
446  return QStringList();
447  }
448 
449  QStringList packages;
450 
451  foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
452  QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
453  if (QFile::exists(metadata)) {
454  packages << sdir;
455  }
456  }
457 
458  return packages;
459 }
460 
461 bool Package::installPackage(const QString &package,
462  const QString &packageRoot,
463  const QString &servicePrefix) // static
464 {
465  //TODO: report *what* failed if something does fail
466  QDir root(packageRoot);
467 
468  if (!root.exists()) {
469  KStandardDirs::makeDir(packageRoot);
470  if (!root.exists()) {
471  kWarning() << "Could not create package root directory:" << packageRoot;
472  return false;
473  }
474  }
475 
476  QFileInfo fileInfo(package);
477  if (!fileInfo.exists()) {
478  kWarning() << "No such file:" << package;
479  return false;
480  }
481 
482  QString path;
483  KTempDir tempdir;
484  bool archivedPackage = false;
485 
486  if (fileInfo.isDir()) {
487  // we have a directory, so let's just install what is in there
488  path = package;
489 
490  // make sure we end in a slash!
491  if (path[path.size() - 1] != '/') {
492  path.append('/');
493  }
494  } else {
495  KArchive *archive = 0;
496  KMimeType::Ptr mimetype = KMimeType::findByPath(package);
497 
498  if (mimetype->is("application/zip")) {
499  archive = new KZip(package);
500  } else if (mimetype->is("application/x-compressed-tar") ||
501  mimetype->is("application/x-tar")|| mimetype->is("application/x-bzip-compressed-tar") ||
502  mimetype->is("application/x-xz") || mimetype->is("application/x-lzma")) {
503  archive = new KTar(package);
504  } else {
505  kWarning() << "Could not open package file, unsupported archive format:" << package << mimetype->name();
506  return false;
507  }
508 
509  if (!archive->open(QIODevice::ReadOnly)) {
510  kWarning() << "Could not open package file:" << package;
511  delete archive;
512  return false;
513  }
514 
515  archivedPackage = true;
516  path = tempdir.name();
517 
518  const KArchiveDirectory *source = archive->directory();
519  source->copyTo(path);
520 
521  QStringList entries = source->entries();
522  if (entries.count() == 1) {
523  const KArchiveEntry *entry = source->entry(entries[0]);
524  if (entry->isDirectory()) {
525  path.append(entry->name()).append("/");
526  }
527  }
528  delete archive;
529  }
530 
531  QString metadataPath = path + "metadata.desktop";
532  if (!QFile::exists(metadataPath)) {
533  kWarning() << "No metadata file in package" << package << metadataPath;
534  return false;
535  }
536 
537  PackageMetadata meta(metadataPath);
538  QString targetName = meta.pluginName();
539 
540  if (targetName.isEmpty()) {
541  kWarning() << "Package plugin name not specified";
542  return false;
543  }
544 
545  // Ensure that package names are safe so package uninstall can't inject
546  // bad characters into the paths used for removal.
547  QRegExp validatePluginName("^[\\w-\\.]+$"); // Only allow letters, numbers, underscore and period.
548  if (!validatePluginName.exactMatch(targetName)) {
549  kWarning() << "Package plugin name " << targetName << "contains invalid characters";
550  return false;
551  }
552 
553  targetName = packageRoot + '/' + targetName;
554  if (QFile::exists(targetName)) {
555  kWarning() << targetName << "already exists";
556  return false;
557  }
558 
559  if (archivedPackage) {
560  // it's in a temp dir, so just move it over.
561  const bool ok = copyFolder(path, targetName);
562  removeFolder(path);
563  if (!ok) {
564  kWarning() << "Could not move package to destination:" << targetName;
565  return false;
566  }
567  } else {
568  kDebug() << "************************** 12";
569  // it's a directory containing the stuff, so copy the contents rather
570  // than move them
571  const bool ok = copyFolder(path, targetName);
572  kDebug() << "************************** 13";
573  if (!ok) {
574  kWarning() << "Could not copy package to destination:" << targetName;
575  return false;
576  }
577  }
578 
579  if (archivedPackage) {
580  // no need to remove the temp dir (which has been successfully moved if it's an archive)
581  tempdir.setAutoRemove(false);
582  }
583 
584  if (!servicePrefix.isEmpty()) {
585  // and now we register it as a service =)
586  kDebug() << "************************** 1";
587  QString metaPath = targetName + "/metadata.desktop";
588  kDebug() << "************************** 2";
589  KDesktopFile df(metaPath);
590  KConfigGroup cg = df.desktopGroup();
591  kDebug() << "************************** 3";
592 
593  // Q: should not installing it as a service disqualify it?
594  // Q: i don't think so since KServiceTypeTrader may not be
595  // used by the installing app in any case, and the
596  // package is properly installed - aseigo
597 
598  //TODO: reduce code duplication with registerPackage below
599 
600  QString serviceName = servicePrefix + meta.pluginName();
601 
602  QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
603  kDebug() << "************************** 4";
604  const bool ok = QFile::copy(metaPath, service);
605  kDebug() << "************************** 5";
606  if (ok) {
607  // the icon in the installed file needs to point to the icon in the
608  // installation dir!
609  QString iconPath = targetName + '/' + cg.readEntry("Icon");
610  QFile icon(iconPath);
611  if (icon.exists()) {
612  KDesktopFile df(service);
613  KConfigGroup cg = df.desktopGroup();
614  cg.writeEntry("Icon", iconPath);
615  }
616  } else {
617  kWarning() << "Could not register package as service (this is not necessarily fatal):" << serviceName;
618  }
619  kDebug() << "************************** 7";
620  }
621 
622  return true;
623 }
624 
625 bool Package::uninstallPackage(const QString &pluginName,
626  const QString &packageRoot,
627  const QString &servicePrefix) // static
628 {
629  // We need to remove the package directory and its metadata file.
630  QString targetName = pluginName;
631  targetName = packageRoot + '/' + targetName;
632 
633  if (!QFile::exists(targetName)) {
634  kWarning() << targetName << "does not exist";
635  return false;
636  }
637 
638  QString serviceName = servicePrefix + pluginName;
639 
640  QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
641  kDebug() << "Removing service file " << service;
642  bool ok = QFile::remove(service);
643 
644  if (!ok) {
645  kWarning() << "Unable to remove " << service;
646  }
647 
648  ok = removeFolder(targetName);
649  const QString errorString("unknown");
650  if (!ok) {
651  kWarning() << "Could not delete package from:" << targetName << " : " << errorString;
652  return false;
653  }
654 
655  return true;
656 }
657 
658 bool Package::registerPackage(const PackageMetadata &data, const QString &iconPath)
659 {
660  QString serviceName("plasma-applet-" + data.pluginName());
661  QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
662 
663  if (data.pluginName().isEmpty()) {
664  return false;
665  }
666 
667  data.write(service);
668 
669  KDesktopFile config(service);
670  KConfigGroup cg = config.desktopGroup();
671  const QString type = data.type().isEmpty() ? "Service" : data.type();
672  cg.writeEntry("Type", type);
673  const QString serviceTypes = data.serviceType().isNull() ? "Plasma/Applet,Plasma/Containment" : data.serviceType();
674  cg.writeEntry("X-KDE-ServiceTypes", serviceTypes);
675  cg.writeEntry("X-KDE-PluginInfo-EnabledByDefault", true);
676 
677  QFile icon(iconPath);
678  if (icon.exists()) {
679  //FIXME: the '/' search will break on non-UNIX. do we care?
680  QString installedIcon("plasma_applet_" + data.pluginName() +
681  iconPath.right(iconPath.length() - iconPath.lastIndexOf("/")));
682  cg.writeEntry("Icon", installedIcon);
683  installedIcon = KStandardDirs::locateLocal("icon", installedIcon);
684  QFile::copy(iconPath, installedIcon);
685  }
686 
687  return true;
688 }
689 
690 bool Package::createPackage(const PackageMetadata &metadata,
691  const QString &source,
692  const QString &destination,
693  const QString &icon) // static
694 {
695  Q_UNUSED(icon)
696  if (!metadata.isValid()) {
697  kWarning() << "Metadata file is not complete";
698  return false;
699  }
700 
701  // write metadata in a temporary file
702  KTemporaryFile metadataFile;
703  if (!metadataFile.open()) {
704  return false;
705  }
706  metadata.write(metadataFile.fileName());
707 
708  // put everything into a zip archive
709  KZip creation(destination);
710  creation.setCompression(KZip::NoCompression);
711  if (!creation.open(QIODevice::WriteOnly)) {
712  return false;
713  }
714 
715  creation.addLocalFile(metadataFile.fileName(), "metadata.desktop");
716  creation.addLocalDirectory(source, "contents");
717  creation.close();
718  return true;
719 }
720 
721 PackagePrivate::PackagePrivate(const PackageStructure::Ptr st, const QString &p)
722  : structure(st),
723  service(0)
724 {
725  if (structure) {
726  if (p.isEmpty()) {
727  structure->setPath(structure->defaultPackageRoot());
728  } else {
729  structure->setPath(p);
730  }
731  }
732 
733  valid = structure && !structure->path().isEmpty();
734 }
735 
736 PackagePrivate::PackagePrivate(const PackageStructure::Ptr st, const QString &packageRoot, const QString &path)
737  : structure(st),
738  service(0)
739 {
740  if (structure) {
741  if (packageRoot.isEmpty()) {
742  structure->setPath(structure->defaultPackageRoot()%"/"%path);
743  } else {
744  structure->setPath(packageRoot%"/"%path);
745  }
746  }
747 
748  valid = structure && !structure->path().isEmpty();
749 }
750 
751 PackagePrivate::PackagePrivate(const PackagePrivate &other)
752  : structure(other.structure),
753  service(other.service),
754  valid(other.valid)
755 {
756 }
757 
758 PackagePrivate::~PackagePrivate()
759 {
760 }
761 
762 PackagePrivate &PackagePrivate::operator=(const PackagePrivate &rhs)
763 {
764  structure = rhs.structure;
765  service = rhs.service;
766  valid = rhs.valid;
767  return *this;
768 }
769 
770 void PackagePrivate::publish(AnnouncementMethods methods)
771 {
772  if (!structure) {
773  return;
774  }
775 
776  if (!service) {
777  service = new PlasmoidService(structure->path());
778  }
779 
780  QString resourceName =
781  i18nc("%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
782  "%1 on %2", structure->metadata().name(), QHostInfo::localHostName());
783  kDebug() << "publishing package under name " << resourceName;
784  service->d->publish(methods, resourceName, structure->metadata());
785 }
786 
787 void PackagePrivate::unpublish()
788 {
789  if (service) {
790  service->d->unpublish();
791  }
792 }
793 
794 bool PackagePrivate::isPublished() const
795 {
796  if (service) {
797  return service->d->isPublished();
798  } else {
799  return false;
800  }
801 }
802 
803 } // Namespace
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Thu Feb 21 2013 11:04:19 by doxygen 1.8.1 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.5 API Reference

Skip menu "kdelibs-4.8.5 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