22 #include "config-plasma.h"
27 #include <QtNetwork/QHostInfo>
34 #include <kcomponentdata.h>
35 #include <kdesktopfile.h>
36 #include <kmimetype.h>
37 #include <kplugininfo.h>
38 #include <kstandarddirs.h>
41 #include <ktemporaryfile.h>
49 #include "private/authorizationmanager_p.h"
50 #include "private/componentinstaller_p.h"
51 #include "private/package_p.h"
52 #include "private/plasmoidservice_p.h"
53 #include "private/service_p.h"
60 QDir source(sourcePath);
64 QDir target(targetPath);
65 if(!target.exists()) {
66 QString targetName = target.dirName();
68 target.mkdir(targetName);
69 target = QDir(targetPath);
72 foreach (
const QString &fileName, source.entryList(QDir::Files)) {
73 QString sourceFilePath = sourcePath + QDir::separator() + fileName;
74 QString targetFilePath = targetPath + QDir::separator() + fileName;
76 if (!QFile::copy(sourceFilePath, targetFilePath)) {
81 foreach (
const QString &subFolderName, source.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
82 QString sourceSubFolderPath = sourcePath + QDir::separator() + subFolderName;
83 QString targetSubFolderPath = targetPath + QDir::separator() + subFolderName;
85 if (!
copyFolder(sourceSubFolderPath, targetSubFolderPath)) {
95 QDir folder(folderPath);
99 foreach (
const QString &fileName, folder.entryList(QDir::Files)) {
100 if (!QFile::remove(folderPath + QDir::separator() + fileName)) {
105 foreach (
const QString &subFolderName, folder.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
106 if (!
removeFolder(folderPath + QDir::separator() + subFolderName)) {
111 QString folderName = folder.dirName();
113 return folder.rmdir(folderName);
123 : d(new PackagePrivate(structure, packageRoot, package))
128 : d(new PackagePrivate(structure, packagePath))
133 : d(new PackagePrivate(*other.d))
156 bool PackagePrivate::isValid()
158 if (!valid || !structure) {
165 QStringList prefixes = structure->contentsPrefixPaths();
166 if (prefixes.isEmpty()) {
167 prefixes << QString();
170 foreach (
const char *dir, structure->requiredDirectories()) {
172 foreach (
const QString &path, structure->searchPath(dir)) {
173 foreach (
const QString &prefix, prefixes) {
174 if (QFile::exists(structure->path() + prefix + path)) {
185 kWarning() <<
"Could not find required directory" << dir;
191 foreach (
const char *file, structure->requiredFiles()) {
193 foreach (
const QString &path, structure->searchPath(file)) {
194 foreach (
const QString &prefix, prefixes) {
195 if (QFile::exists(structure->path() + prefix + path)) {
206 kWarning() <<
"Could not find required file" << file;
225 if (qstrlen(fileType) != 0) {
226 paths = d->structure->searchPath(fileType);
228 if (paths.isEmpty()) {
238 QStringList prefixes = d->structure->contentsPrefixPaths();
239 if (prefixes.isEmpty()) {
240 prefixes << QString();
244 foreach (
const QString &contentsPrefix, prefixes) {
245 const QString prefix(d->structure->path() + contentsPrefix);
247 foreach (
const QString &path, paths) {
248 QString file = prefix +
path;
250 if (!filename.isEmpty()) {
251 file.append(
"/").append(filename);
255 if (QFile::exists(file)) {
256 if (d->structure->allowExternalPaths()) {
264 QString canonicalized = dir.canonicalPath() + QDir::separator();
267 if (canonicalized.startsWith(d->structure->path())) {
281 return filePath(fileType, QString());
287 return QStringList();
290 return d->structure->entryList(fileType);
296 return d->structure->metadata();
304 d->setPathFromStructure(path);
309 return d->structure ? d->structure->path() : QString();
318 void PackagePrivate::updateHash(
const QString &basePath,
const QString &subPath,
const QDir &dir, QCA::Hash &hash)
330 const QDir::SortFlags sorting = QDir::Name | QDir::IgnoreCase;
331 const QDir::Filters filters = QDir::Hidden | QDir::System | QDir::NoDotAndDotDot;
332 foreach (
const QString &file, dir.entryList(QDir::Files | filters, sorting)) {
333 if (!subPath.isEmpty()) {
334 hash.update(subPath.toUtf8());
337 hash.update(file.toUtf8());
339 QFileInfo info(dir.path() +
'/' + file);
340 if (info.isSymLink()) {
341 hash.update(info.symLinkTarget().toUtf8());
343 QFile f(info.filePath());
344 if (f.open(QIODevice::ReadOnly)) {
346 hash.update(f.read(1024));
349 kWarning() <<
"could not add" << f.fileName() <<
"to the hash; file could not be opened for reading. "
350 <<
"permissions fail?" << info.permissions() << info.isFile();
355 foreach (
const QString &subDirPath, dir.entryList(QDir::Dirs | filters, sorting)) {
356 const QString relativePath = subPath + subDirPath +
'/';
357 hash.update(relativePath.toUtf8());
359 QDir subDir(dir.path());
360 subDir.cd(subDirPath);
362 if (subDir.path() != subDir.canonicalPath()) {
363 hash.update(subDir.canonicalPath().toUtf8());
365 updateHash(basePath, relativePath, subDir, hash);
375 kWarning() <<
"can not create hash due to Package being invalid";
380 QCA::Initializer init;
381 if (!QCA::isSupported(
"sha1")) {
382 kWarning() <<
"can not create hash for" <<
path() <<
"due to no SHA1 support in QCA2";
386 QCA::Hash hash(
"sha1");
387 QString metadataPath = d->structure->path() +
"metadata.desktop";
388 if (QFile::exists(metadataPath)) {
389 QFile f(metadataPath);
390 if (f.open(QIODevice::ReadOnly)) {
392 hash.update(f.read(1024));
395 kWarning() <<
"could not add" << f.fileName() <<
"to the hash; file could not be opened for reading.";
398 kWarning() <<
"no metadata at" << metadataPath;
401 QStringList prefixes = d->structure->contentsPrefixPaths();
402 if (prefixes.isEmpty()) {
403 prefixes << QString();
406 foreach (QString prefix, prefixes) {
407 const QString basePath = d->structure->path() + prefix;
414 d->updateHash(basePath, QString(), dir, hash);
416 return QCA::arrayToHex(hash.final().toByteArray());
419 kWarning() <<
"can not create hash for" <<
path() <<
"due to no cryptographic support (QCA2)";
428 QDir dir(packageRoot);
431 return QStringList();
434 QStringList packages;
436 foreach (
const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
437 QString
metadata = packageRoot +
'/' + sdir +
"/metadata.desktop";
438 if (QFile::exists(metadata)) {
449 QDir dir(packageRoot);
452 return QStringList();
455 QStringList packages;
457 foreach (
const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
458 QString
metadata = packageRoot +
'/' + sdir +
"/metadata.desktop";
459 if (QFile::exists(metadata)) {
468 const QString &packageRoot,
469 const QString &servicePrefix)
472 QDir root(packageRoot);
474 if (!root.exists()) {
475 KStandardDirs::makeDir(packageRoot);
476 if (!root.exists()) {
477 kWarning() <<
"Could not create package root directory:" << packageRoot;
482 QFileInfo fileInfo(package);
483 if (!fileInfo.exists()) {
484 kWarning() <<
"No such file:" << package;
490 bool archivedPackage =
false;
492 if (fileInfo.isDir()) {
497 if (path[path.size() - 1] !=
'/') {
501 KArchive *archive = 0;
502 KMimeType::Ptr mimetype = KMimeType::findByPath(package);
504 if (mimetype->is(
"application/zip")) {
505 archive =
new KZip(package);
506 }
else if (mimetype->is(
"application/x-compressed-tar") ||
507 mimetype->is(
"application/x-tar")|| mimetype->is(
"application/x-bzip-compressed-tar") ||
508 mimetype->is(
"application/x-xz") || mimetype->is(
"application/x-lzma")) {
509 archive =
new KTar(package);
511 kWarning() <<
"Could not open package file, unsupported archive format:" <<
package << mimetype->name();
515 if (!archive->open(QIODevice::ReadOnly)) {
516 kWarning() <<
"Could not open package file:" << package;
521 archivedPackage =
true;
522 path = tempdir.name();
524 const KArchiveDirectory *source = archive->directory();
525 source->copyTo(path);
527 QStringList entries = source->entries();
528 if (entries.count() == 1) {
529 const KArchiveEntry *entry = source->entry(entries[0]);
530 if (entry->isDirectory()) {
531 path.append(entry->name()).append(
"/");
537 QString metadataPath = path +
"metadata.desktop";
538 if (!QFile::exists(metadataPath)) {
539 kWarning() <<
"No metadata file in package" <<
package << metadataPath;
546 if (targetName.isEmpty()) {
547 kWarning() <<
"Package plugin name not specified";
553 QRegExp validatePluginName(
"^[\\w-\\.]+$");
554 if (!validatePluginName.exactMatch(targetName)) {
555 kWarning() <<
"Package plugin name " << targetName <<
"contains invalid characters";
559 targetName = packageRoot +
'/' + targetName;
560 if (QFile::exists(targetName)) {
561 kWarning() << targetName <<
"already exists";
565 if (archivedPackage) {
570 kWarning() <<
"Could not move package to destination:" << targetName;
574 kDebug() <<
"************************** 12";
578 kDebug() <<
"************************** 13";
580 kWarning() <<
"Could not copy package to destination:" << targetName;
585 if (archivedPackage) {
587 tempdir.setAutoRemove(
false);
591 if (!requiredScriptEngine.isEmpty()) {
593 ComponentTypes componentTypes =
static_cast<ComponentTypes
>(0);
594 QStringList serviceTypes = meta.
serviceType().split(
',');
595 if (serviceTypes.contains(
"Plasma/Applet")) {
598 if (serviceTypes.contains(
"Plasma/DataEngine")) {
601 if (serviceTypes.contains(
"Plasma/Runner")) {
604 if (serviceTypes.contains(
"Plasma/Wallpaper")) {
607 if (!
knownLanguages(componentTypes).contains(requiredScriptEngine)) {
610 ComponentInstaller::self()->installMissingComponent(
"scriptengine", requiredScriptEngine, 0,
true);
614 if (requiredDataEngines.isEmpty()) {
616 QString metaPath = targetName +
"/metadata.desktop";
617 KDesktopFile df(metaPath);
618 KConfigGroup cg = df.desktopGroup();
619 if (!cg.hasKey(
"X-Plasma-RequiredDataEngines")) {
621 requiredDataEngines = ComponentInstaller::self()->extractDataEngineDependencies(targetName,
622 requiredScriptEngine);
625 if (!requiredDataEngines.isEmpty()) {
627 foreach (
const QString &requiredDataEngine, requiredDataEngines) {
628 if (!knownDataEngines.contains(requiredDataEngine)) {
631 ComponentInstaller::self()->installMissingComponent(
"dataengine", requiredDataEngine, 0,
true);
636 if (!servicePrefix.isEmpty()) {
638 kDebug() <<
"************************** 1";
639 QString metaPath = targetName +
"/metadata.desktop";
640 kDebug() <<
"************************** 2";
641 KDesktopFile df(metaPath);
642 KConfigGroup cg = df.desktopGroup();
643 kDebug() <<
"************************** 3";
652 QString serviceName = servicePrefix + meta.
pluginName();
654 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
655 kDebug() <<
"************************** 4";
656 const bool ok = QFile::copy(metaPath, service);
657 kDebug() <<
"************************** 5";
661 QString iconPath = targetName +
'/' + cg.readEntry(
"Icon");
662 QFile icon(iconPath);
664 KDesktopFile df(service);
665 KConfigGroup cg = df.desktopGroup();
666 cg.writeEntry(
"Icon", iconPath);
669 kWarning() <<
"Could not register package as service (this is not necessarily fatal):" << serviceName;
671 kDebug() <<
"************************** 7";
678 const QString &packageRoot,
679 const QString &servicePrefix)
682 QString targetName = pluginName;
683 targetName = packageRoot +
'/' + targetName;
685 if (!QFile::exists(targetName)) {
686 kWarning() << targetName <<
"does not exist";
690 QString serviceName = servicePrefix + pluginName;
692 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
693 kDebug() <<
"Removing service file " << service;
694 bool ok = QFile::remove(service);
697 kWarning() <<
"Unable to remove " << service;
701 const QString errorString(
"unknown");
703 kWarning() <<
"Could not delete package from:" << targetName <<
" : " << errorString;
712 QString serviceName(
"plasma-applet-" + data.
pluginName());
713 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
721 KDesktopFile config(service);
722 KConfigGroup cg = config.desktopGroup();
723 const QString
type = data.
type().isEmpty() ?
"Service" : data.
type();
724 cg.writeEntry(
"Type", type);
725 const QString serviceTypes = data.
serviceType().isNull() ?
"Plasma/Applet,Plasma/Containment" : data.
serviceType();
726 cg.writeEntry(
"X-KDE-ServiceTypes", serviceTypes);
727 cg.writeEntry(
"X-KDE-PluginInfo-EnabledByDefault",
true);
729 QFile icon(iconPath);
732 QString installedIcon(
"plasma_applet_" + data.
pluginName() +
733 iconPath.right(iconPath.length() - iconPath.lastIndexOf(
"/")));
734 cg.writeEntry(
"Icon", installedIcon);
735 installedIcon = KStandardDirs::locateLocal(
"icon", installedIcon);
736 QFile::copy(iconPath, installedIcon);
743 const QString &source,
744 const QString &destination,
749 kWarning() <<
"Metadata file is not complete";
754 KTemporaryFile metadataFile;
755 if (!metadataFile.open()) {
758 metadata.
write(metadataFile.fileName());
761 KZip creation(destination);
762 creation.setCompression(KZip::NoCompression);
763 if (!creation.open(QIODevice::WriteOnly)) {
767 creation.addLocalFile(metadataFile.fileName(),
"metadata.desktop");
768 creation.addLocalDirectory(source,
"contents");
777 setPathFromStructure(p);
780 PackagePrivate::PackagePrivate(
const PackageStructure::Ptr st,
const QString &packageRoot,
const QString &path)
784 setPathFromStructure(packageRoot.isEmpty() ? path : packageRoot %
"/" %
path);
787 PackagePrivate::PackagePrivate(
const PackagePrivate &other)
788 : structure(other.structure),
789 service(other.service),
794 PackagePrivate::~PackagePrivate()
798 PackagePrivate &PackagePrivate::operator=(
const PackagePrivate &rhs)
800 structure = rhs.structure;
801 service = rhs.service;
806 void PackagePrivate::setPathFromStructure(
const QString &path)
814 if (path.isEmpty()) {
815 paths << structure->defaultPackageRoot();
816 }
else if (QDir::isRelativePath(path)) {
817 QString p = structure->defaultPackageRoot() %
"/" % path %
"/";
819 if (QDir::isRelativePath(p)) {
820 paths = KGlobal::dirs()->findDirs(
"data", p);
828 foreach (
const QString &p, paths) {
829 structure->setPath(p);
838 structure->setPath(QString());
841 void PackagePrivate::publish(AnnouncementMethods methods)
848 service =
new PlasmoidService(structure->path());
851 QString resourceName =
852 i18nc(
"%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
853 "%1 on %2", structure->metadata().name(), QHostInfo::localHostName());
854 kDebug() <<
"publishing package under name " << resourceName;
855 service->d->publish(methods, resourceName, structure->metadata());
858 void PackagePrivate::unpublish()
861 service->d->unpublish();
865 bool PackagePrivate::isPublished()
const
868 return service->d->isPublished();
Plasma::Wallpaper based plugins.
static QStringList listInstalled(const QString &packageRoot)
Returns a list of all installed packages by name.
static QStringList listInstalledPaths(const QString &packageRoot)
Returns a list of all paths of installed packages in the given root.
const PackageStructure::Ptr structure() const
static bool registerPackage(const PackageMetadata &data, const QString &iconPath)
Registers a package described by the given desktop file.
static bool uninstallPackage(const QString &package, const QString &packageRoot, const QString &servicePrefix)
Uninstalls a package.
static bool installPackage(const QString &package, const QString &packageRoot, const QString &servicePrefix)
Installs a package.
const QString path() const
bool copyFolder(QString sourcePath, QString targetPath)
Package & operator=(const Package &rhs)
Assignment operator.
KSharedPtr< PackageStructure > Ptr
object representing an installed Plasmagik package
static DataEngineManager * self()
Singleton pattern accessor.
bool removeFolder(QString folderPath)
Plasma::AbstractRunner based plugsin.
static QStringList listAllEngines(const QString &parentApp=QString())
QStringList entryList(const char *fileType) const
Get the list of files of a given type.
PackageMetadata metadata() const
QString contentsHash() const
Plasma::Applet based plugins.
static QScriptValue type(QScriptContext *ctx, QScriptEngine *eng)
Plasma::DataEngine based plugins.
Package()
Default constructor that creates an invalid Package.
QStringList knownLanguages(ComponentTypes types)
void setPath(const QString &path)
Sets the path to the root of this package.
A description of the expected file structure of a given package type.
QString filePath(const char *fileType, const QString &filename) const
Get the path to a given file.
static bool createPackage(const PackageMetadata &metadata, const QString &source, const QString &destination, const QString &icon=QString())
Creates a package based on the metadata from the files contained in the source directory.