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-compressed-tar") || mimetype->is(
"application/x-lzma-compressed-tar")) {
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")) {
608 && !
knownLanguages(componentTypes).contains(requiredScriptEngine)) {
611 ComponentInstaller::self()->installMissingComponent(
"scriptengine", requiredScriptEngine, 0,
true);
615 if (requiredDataEngines.isEmpty()) {
617 QString metaPath = targetName +
"/metadata.desktop";
618 KDesktopFile df(metaPath);
619 KConfigGroup cg = df.desktopGroup();
620 if (!cg.hasKey(
"X-Plasma-RequiredDataEngines")) {
622 requiredDataEngines = ComponentInstaller::self()->extractDataEngineDependencies(targetName,
623 requiredScriptEngine);
626 if (!requiredDataEngines.isEmpty()) {
628 foreach (
const QString &requiredDataEngine, requiredDataEngines) {
629 if (!knownDataEngines.contains(requiredDataEngine)) {
632 ComponentInstaller::self()->installMissingComponent(
"dataengine", requiredDataEngine, 0,
true);
637 if (!servicePrefix.isEmpty()) {
639 kDebug() <<
"************************** 1";
640 QString metaPath = targetName +
"/metadata.desktop";
641 kDebug() <<
"************************** 2";
642 KDesktopFile df(metaPath);
643 KConfigGroup cg = df.desktopGroup();
644 kDebug() <<
"************************** 3";
653 QString serviceName = servicePrefix + meta.
pluginName();
655 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
656 kDebug() <<
"************************** 4";
657 const bool ok = QFile::copy(metaPath, service);
658 kDebug() <<
"************************** 5";
662 QString iconPath = targetName +
'/' + cg.readEntry(
"Icon");
663 QFile icon(iconPath);
665 KDesktopFile df(service);
666 KConfigGroup cg = df.desktopGroup();
667 cg.writeEntry(
"Icon", iconPath);
670 kWarning() <<
"Could not register package as service (this is not necessarily fatal):" << serviceName;
672 kDebug() <<
"************************** 7";
679 const QString &packageRoot,
680 const QString &servicePrefix)
683 QString targetName = pluginName;
684 targetName = packageRoot +
'/' + targetName;
686 if (!QFile::exists(targetName)) {
687 kWarning() << targetName <<
"does not exist";
691 QString serviceName = servicePrefix + pluginName;
693 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
694 kDebug() <<
"Removing service file " << service;
695 bool ok = QFile::remove(service);
698 kWarning() <<
"Unable to remove " << service;
702 const QString errorString(
"unknown");
704 kWarning() <<
"Could not delete package from:" << targetName <<
" : " << errorString;
713 QString serviceName(
"plasma-applet-" + data.
pluginName());
714 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
722 KDesktopFile config(service);
723 KConfigGroup cg = config.desktopGroup();
724 const QString
type = data.
type().isEmpty() ?
"Service" : data.
type();
725 cg.writeEntry(
"Type", type);
726 const QString serviceTypes = data.
serviceType().isNull() ?
"Plasma/Applet,Plasma/Containment" : data.
serviceType();
727 cg.writeEntry(
"X-KDE-ServiceTypes", serviceTypes);
728 cg.writeEntry(
"X-KDE-PluginInfo-EnabledByDefault",
true);
730 QFile icon(iconPath);
733 QString installedIcon(
"plasma_applet_" + data.
pluginName() +
734 iconPath.right(iconPath.length() - iconPath.lastIndexOf(
"/")));
735 cg.writeEntry(
"Icon", installedIcon);
736 installedIcon = KStandardDirs::locateLocal(
"icon", installedIcon);
737 QFile::copy(iconPath, installedIcon);
744 const QString &source,
745 const QString &destination,
750 kWarning() <<
"Metadata file is not complete";
755 KTemporaryFile metadataFile;
756 if (!metadataFile.open()) {
759 metadata.
write(metadataFile.fileName());
762 KZip creation(destination);
763 creation.setCompression(KZip::NoCompression);
764 if (!creation.open(QIODevice::WriteOnly)) {
768 creation.addLocalFile(metadataFile.fileName(),
"metadata.desktop");
769 creation.addLocalDirectory(source,
"contents");
778 setPathFromStructure(p);
781 PackagePrivate::PackagePrivate(
const PackageStructure::Ptr st,
const QString &packageRoot,
const QString &path)
785 setPathFromStructure(packageRoot.isEmpty() ? path : packageRoot %
"/" %
path);
788 PackagePrivate::PackagePrivate(
const PackagePrivate &other)
789 : structure(other.structure),
790 service(other.service),
795 PackagePrivate::~PackagePrivate()
799 PackagePrivate &PackagePrivate::operator=(
const PackagePrivate &rhs)
801 structure = rhs.structure;
802 service = rhs.service;
807 void PackagePrivate::setPathFromStructure(
const QString &path)
815 if (path.isEmpty()) {
816 paths << structure->defaultPackageRoot();
817 }
else if (QDir::isRelativePath(path)) {
818 QString p = structure->defaultPackageRoot() %
"/" % path %
"/";
820 if (QDir::isRelativePath(p)) {
821 paths = KGlobal::dirs()->findDirs(
"data", p);
829 foreach (
const QString &p, paths) {
830 structure->setPath(p);
839 structure->setPath(QString());
842 void PackagePrivate::publish(AnnouncementMethods methods)
849 service =
new PlasmoidService(structure->path());
852 QString resourceName =
853 i18nc(
"%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
854 "%1 on %2", structure->metadata().name(), QHostInfo::localHostName());
855 kDebug() <<
"publishing package under name " << resourceName;
856 service->d->publish(methods, resourceName, structure->metadata());
859 void PackagePrivate::unpublish()
862 service->d->unpublish();
866 bool PackagePrivate::isPublished()
const
869 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.