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))
160 QStringList prefixes = d->structure->contentsPrefixPaths();
161 if (prefixes.isEmpty()) {
162 prefixes << QString();
165 foreach (
const char *dir, d->structure->requiredDirectories()) {
167 foreach (
const QString &
path, d->structure->searchPath(dir)) {
168 foreach (
const QString &prefix, prefixes) {
169 if (QFile::exists(d->structure->path() + prefix +
path)) {
180 kWarning() <<
"Could not find required directory" << dir;
186 foreach (
const char *file, d->structure->requiredFiles()) {
188 foreach (
const QString &
path, d->structure->searchPath(file)) {
189 foreach (
const QString &prefix, prefixes) {
190 if (QFile::exists(d->structure->path() + prefix +
path)) {
201 kWarning() <<
"Could not find required file" << file;
219 if (qstrlen(fileType) != 0) {
220 paths = d->structure->searchPath(fileType);
222 if (paths.isEmpty()) {
232 QStringList prefixes = d->structure->contentsPrefixPaths();
233 if (prefixes.isEmpty()) {
234 prefixes << QString();
238 foreach (
const QString &contentsPrefix, prefixes) {
239 const QString prefix(d->structure->path() + contentsPrefix);
241 foreach (
const QString &
path, paths) {
242 QString file = prefix +
path;
244 if (!filename.isEmpty()) {
245 file.append(
"/").append(filename);
249 if (QFile::exists(file)) {
250 if (d->structure->allowExternalPaths()) {
258 QString canonicalized = dir.canonicalPath() + QDir::separator();
261 if (canonicalized.startsWith(d->structure->path())) {
275 return filePath(fileType, QString());
281 return QStringList();
284 return d->structure->entryList(fileType);
290 return d->structure->metadata();
299 d->structure->setPath(path);
300 d->valid = !d->structure->path().isEmpty();
306 return d->structure ? d->structure->path() : QString();
315 void PackagePrivate::updateHash(
const QString &basePath,
const QString &subPath,
const QDir &dir, QCA::Hash &hash)
327 const QDir::SortFlags sorting = QDir::Name | QDir::IgnoreCase;
328 const QDir::Filters filters = QDir::Hidden | QDir::System | QDir::NoDotAndDotDot;
329 foreach (
const QString &file, dir.entryList(QDir::Files | filters, sorting)) {
330 if (!subPath.isEmpty()) {
331 hash.update(subPath.toUtf8());
334 hash.update(file.toUtf8());
336 QFileInfo info(dir.path() +
'/' + file);
337 if (info.isSymLink()) {
338 hash.update(info.symLinkTarget().toUtf8());
340 QFile f(info.filePath());
341 if (f.open(QIODevice::ReadOnly)) {
343 hash.update(f.read(1024));
346 kWarning() <<
"could not add" << f.fileName() <<
"to the hash; file could not be opened for reading. "
347 <<
"permissions fail?" << info.permissions() << info.isFile();
352 foreach (
const QString &subDirPath, dir.entryList(QDir::Dirs | filters, sorting)) {
353 const QString relativePath = subPath + subDirPath +
'/';
354 hash.update(relativePath.toUtf8());
356 QDir subDir(dir.path());
357 subDir.cd(subDirPath);
359 if (subDir.path() != subDir.canonicalPath()) {
360 hash.update(subDir.canonicalPath().toUtf8());
362 updateHash(basePath, relativePath, subDir, hash);
372 kWarning() <<
"can not create hash due to Package being invalid";
377 QCA::Initializer init;
378 if (!QCA::isSupported(
"sha1")) {
379 kWarning() <<
"can not create hash for" <<
path() <<
"due to no SHA1 support in QCA2";
383 QCA::Hash hash(
"sha1");
384 QString metadataPath = d->structure->path() +
"metadata.desktop";
385 if (QFile::exists(metadataPath)) {
386 QFile f(metadataPath);
387 if (f.open(QIODevice::ReadOnly)) {
389 hash.update(f.read(1024));
392 kWarning() <<
"could not add" << f.fileName() <<
"to the hash; file could not be opened for reading.";
395 kWarning() <<
"no metadata at" << metadataPath;
398 QStringList prefixes = d->structure->contentsPrefixPaths();
399 if (prefixes.isEmpty()) {
400 prefixes << QString();
403 foreach (QString prefix, prefixes) {
404 const QString basePath = d->structure->path() + prefix;
411 d->updateHash(basePath, QString(), dir, hash);
413 return QCA::arrayToHex(hash.final().toByteArray());
416 kWarning() <<
"can not create hash for" <<
path() <<
"due to no cryptographic support (QCA2)";
425 QDir dir(packageRoot);
428 return QStringList();
431 QStringList packages;
433 foreach (
const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
434 QString
metadata = packageRoot +
'/' + sdir +
"/metadata.desktop";
435 if (QFile::exists(metadata)) {
446 QDir dir(packageRoot);
449 return QStringList();
452 QStringList packages;
454 foreach (
const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
455 QString
metadata = packageRoot +
'/' + sdir +
"/metadata.desktop";
456 if (QFile::exists(metadata)) {
465 const QString &packageRoot,
466 const QString &servicePrefix)
469 QDir root(packageRoot);
471 if (!root.exists()) {
472 KStandardDirs::makeDir(packageRoot);
473 if (!root.exists()) {
474 kWarning() <<
"Could not create package root directory:" << packageRoot;
479 QFileInfo fileInfo(package);
480 if (!fileInfo.exists()) {
481 kWarning() <<
"No such file:" << package;
487 bool archivedPackage =
false;
489 if (fileInfo.isDir()) {
494 if (path[path.size() - 1] !=
'/') {
498 KArchive *archive = 0;
499 KMimeType::Ptr mimetype = KMimeType::findByPath(package);
501 if (mimetype->is(
"application/zip")) {
502 archive =
new KZip(package);
503 }
else if (mimetype->is(
"application/x-compressed-tar") ||
504 mimetype->is(
"application/x-tar")|| mimetype->is(
"application/x-bzip-compressed-tar") ||
505 mimetype->is(
"application/x-xz") || mimetype->is(
"application/x-lzma")) {
506 archive =
new KTar(package);
508 kWarning() <<
"Could not open package file, unsupported archive format:" <<
package << mimetype->name();
512 if (!archive->open(QIODevice::ReadOnly)) {
513 kWarning() <<
"Could not open package file:" << package;
518 archivedPackage =
true;
519 path = tempdir.name();
521 const KArchiveDirectory *source = archive->directory();
522 source->copyTo(path);
524 QStringList entries = source->entries();
525 if (entries.count() == 1) {
526 const KArchiveEntry *entry = source->entry(entries[0]);
527 if (entry->isDirectory()) {
528 path.append(entry->name()).append(
"/");
534 QString metadataPath = path +
"metadata.desktop";
535 if (!QFile::exists(metadataPath)) {
536 kWarning() <<
"No metadata file in package" <<
package << metadataPath;
543 if (targetName.isEmpty()) {
544 kWarning() <<
"Package plugin name not specified";
550 QRegExp validatePluginName(
"^[\\w-\\.]+$");
551 if (!validatePluginName.exactMatch(targetName)) {
552 kWarning() <<
"Package plugin name " << targetName <<
"contains invalid characters";
556 targetName = packageRoot +
'/' + targetName;
557 if (QFile::exists(targetName)) {
558 kWarning() << targetName <<
"already exists";
562 if (archivedPackage) {
567 kWarning() <<
"Could not move package to destination:" << targetName;
571 kDebug() <<
"************************** 12";
575 kDebug() <<
"************************** 13";
577 kWarning() <<
"Could not copy package to destination:" << targetName;
582 if (archivedPackage) {
584 tempdir.setAutoRemove(
false);
588 if (!requiredScriptEngine.isEmpty()) {
590 ComponentTypes componentTypes =
static_cast<ComponentTypes
>(0);
591 QStringList serviceTypes = meta.
serviceType().split(
',');
592 if (serviceTypes.contains(
"Plasma/Applet")) {
595 if (serviceTypes.contains(
"Plasma/DataEngine")) {
598 if (serviceTypes.contains(
"Plasma/Runner")) {
601 if (serviceTypes.contains(
"Plasma/Wallpaper")) {
604 if (!
knownLanguages(componentTypes).contains(requiredScriptEngine)) {
607 ComponentInstaller::self()->installMissingComponent(
"scriptengine", requiredScriptEngine, 0,
true);
611 if (requiredDataEngines.isEmpty()) {
613 QString metaPath = targetName +
"/metadata.desktop";
614 KDesktopFile df(metaPath);
615 KConfigGroup cg = df.desktopGroup();
616 if (!cg.hasKey(
"X-Plasma-RequiredDataEngines")) {
618 requiredDataEngines = ComponentInstaller::self()->extractDataEngineDependencies(targetName,
619 requiredScriptEngine);
622 if (!requiredDataEngines.isEmpty()) {
624 foreach (
const QString &requiredDataEngine, requiredDataEngines) {
625 if (!knownDataEngines.contains(requiredDataEngine)) {
628 ComponentInstaller::self()->installMissingComponent(
"dataengine", requiredDataEngine, 0,
true);
633 if (!servicePrefix.isEmpty()) {
635 kDebug() <<
"************************** 1";
636 QString metaPath = targetName +
"/metadata.desktop";
637 kDebug() <<
"************************** 2";
638 KDesktopFile df(metaPath);
639 KConfigGroup cg = df.desktopGroup();
640 kDebug() <<
"************************** 3";
649 QString serviceName = servicePrefix + meta.
pluginName();
651 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
652 kDebug() <<
"************************** 4";
653 const bool ok = QFile::copy(metaPath, service);
654 kDebug() <<
"************************** 5";
658 QString iconPath = targetName +
'/' + cg.readEntry(
"Icon");
659 QFile icon(iconPath);
661 KDesktopFile df(service);
662 KConfigGroup cg = df.desktopGroup();
663 cg.writeEntry(
"Icon", iconPath);
666 kWarning() <<
"Could not register package as service (this is not necessarily fatal):" << serviceName;
668 kDebug() <<
"************************** 7";
675 const QString &packageRoot,
676 const QString &servicePrefix)
679 QString targetName = pluginName;
680 targetName = packageRoot +
'/' + targetName;
682 if (!QFile::exists(targetName)) {
683 kWarning() << targetName <<
"does not exist";
687 QString serviceName = servicePrefix + pluginName;
689 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
690 kDebug() <<
"Removing service file " << service;
691 bool ok = QFile::remove(service);
694 kWarning() <<
"Unable to remove " << service;
698 const QString errorString(
"unknown");
700 kWarning() <<
"Could not delete package from:" << targetName <<
" : " << errorString;
709 QString serviceName(
"plasma-applet-" + data.
pluginName());
710 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
718 KDesktopFile config(service);
719 KConfigGroup cg = config.desktopGroup();
720 const QString
type = data.
type().isEmpty() ?
"Service" : data.
type();
721 cg.writeEntry(
"Type", type);
722 const QString serviceTypes = data.
serviceType().isNull() ?
"Plasma/Applet,Plasma/Containment" : data.
serviceType();
723 cg.writeEntry(
"X-KDE-ServiceTypes", serviceTypes);
724 cg.writeEntry(
"X-KDE-PluginInfo-EnabledByDefault",
true);
726 QFile icon(iconPath);
729 QString installedIcon(
"plasma_applet_" + data.
pluginName() +
730 iconPath.right(iconPath.length() - iconPath.lastIndexOf(
"/")));
731 cg.writeEntry(
"Icon", installedIcon);
732 installedIcon = KStandardDirs::locateLocal(
"icon", installedIcon);
733 QFile::copy(iconPath, installedIcon);
740 const QString &source,
741 const QString &destination,
746 kWarning() <<
"Metadata file is not complete";
751 KTemporaryFile metadataFile;
752 if (!metadataFile.open()) {
755 metadata.
write(metadataFile.fileName());
758 KZip creation(destination);
759 creation.setCompression(KZip::NoCompression);
760 if (!creation.open(QIODevice::WriteOnly)) {
764 creation.addLocalFile(metadataFile.fileName(),
"metadata.desktop");
765 creation.addLocalDirectory(source,
"contents");
785 PackagePrivate::PackagePrivate(
const PackageStructure::Ptr st,
const QString &packageRoot,
const QString &path)
790 if (packageRoot.isEmpty()) {
793 structure->setPath(packageRoot%
"/"%path);
800 PackagePrivate::PackagePrivate(
const PackagePrivate &other)
801 : structure(other.structure),
802 service(other.service),
807 PackagePrivate::~PackagePrivate()
811 PackagePrivate &PackagePrivate::operator=(
const PackagePrivate &rhs)
813 structure = rhs.structure;
814 service = rhs.service;
819 void PackagePrivate::publish(AnnouncementMethods methods)
826 service =
new PlasmoidService(structure->path());
829 QString resourceName =
830 i18nc(
"%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
831 "%1 on %2", structure->metadata().name(), QHostInfo::localHostName());
832 kDebug() <<
"publishing package under name " << resourceName;
833 service->d->publish(methods, resourceName, structure->metadata());
836 void PackagePrivate::unpublish()
839 service->d->unpublish();
843 bool PackagePrivate::isPublished()
const
846 return service->d->isPublished();