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>
47 #include "private/authorizationmanager_p.h"
48 #include "private/package_p.h"
49 #include "private/plasmoidservice_p.h"
50 #include "private/service_p.h"
57 QDir source(sourcePath);
61 QDir target(targetPath);
62 if(!target.exists()) {
63 QString targetName = target.dirName();
65 target.mkdir(targetName);
66 target = QDir(targetPath);
69 foreach (
const QString &fileName, source.entryList(QDir::Files)) {
70 QString sourceFilePath = sourcePath + QDir::separator() + fileName;
71 QString targetFilePath = targetPath + QDir::separator() + fileName;
73 if (!QFile::copy(sourceFilePath, targetFilePath)) {
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;
82 if (!
copyFolder(sourceSubFolderPath, targetSubFolderPath)) {
92 QDir folder(folderPath);
96 foreach (
const QString &fileName, folder.entryList(QDir::Files)) {
97 if (!QFile::remove(folderPath + QDir::separator() + fileName)) {
102 foreach (
const QString &subFolderName, folder.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) {
103 if (!
removeFolder(folderPath + QDir::separator() + subFolderName)) {
108 QString folderName = folder.dirName();
110 return folder.rmdir(folderName);
120 : d(new PackagePrivate(structure, packageRoot, package))
125 : d(new PackagePrivate(structure, packagePath))
130 : d(new PackagePrivate(*other.d))
153 bool PackagePrivate::isValid()
155 if (!valid || !structure) {
162 QStringList prefixes = structure->contentsPrefixPaths();
163 if (prefixes.isEmpty()) {
164 prefixes << QString();
167 foreach (
const char *dir, structure->requiredDirectories()) {
169 foreach (
const QString &path, structure->searchPath(dir)) {
170 foreach (
const QString &prefix, prefixes) {
171 if (QFile::exists(structure->path() + prefix + path)) {
182 kWarning() <<
"Could not find required directory" << dir;
188 foreach (
const char *file, structure->requiredFiles()) {
190 foreach (
const QString &path, structure->searchPath(file)) {
191 foreach (
const QString &prefix, prefixes) {
192 if (QFile::exists(structure->path() + prefix + path)) {
203 kWarning() <<
"Could not find required file" << file;
222 if (qstrlen(fileType) != 0) {
223 paths = d->structure->searchPath(fileType);
225 if (paths.isEmpty()) {
235 QStringList prefixes = d->structure->contentsPrefixPaths();
236 if (prefixes.isEmpty()) {
237 prefixes << QString();
241 foreach (
const QString &contentsPrefix, prefixes) {
242 const QString prefix(d->structure->path() + contentsPrefix);
244 foreach (
const QString &path, paths) {
245 QString file = prefix +
path;
247 if (!filename.isEmpty()) {
248 file.append(
"/").append(filename);
252 if (QFile::exists(file)) {
253 if (d->structure->allowExternalPaths()) {
261 QString canonicalized = dir.canonicalPath() + QDir::separator();
264 if (canonicalized.startsWith(d->structure->path())) {
278 return filePath(fileType, QString());
284 return QStringList();
287 return d->structure->entryList(fileType);
293 return d->structure->metadata();
301 d->setPathFromStructure(path);
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-compressed-tar") || mimetype->is(
"application/x-lzma-compressed-tar")) {
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);
587 if (!servicePrefix.isEmpty()) {
589 kDebug() <<
"************************** 1";
590 QString metaPath = targetName +
"/metadata.desktop";
591 kDebug() <<
"************************** 2";
592 KDesktopFile df(metaPath);
593 KConfigGroup cg = df.desktopGroup();
594 kDebug() <<
"************************** 3";
603 QString serviceName = servicePrefix + meta.
pluginName();
605 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
606 kDebug() <<
"************************** 4";
607 const bool ok = QFile::copy(metaPath, service);
608 kDebug() <<
"************************** 5";
612 QString iconPath = targetName +
'/' + cg.readEntry(
"Icon");
613 QFile icon(iconPath);
615 KDesktopFile df(service);
616 KConfigGroup cg = df.desktopGroup();
617 cg.writeEntry(
"Icon", iconPath);
620 kWarning() <<
"Could not register package as service (this is not necessarily fatal):" << serviceName;
622 kDebug() <<
"************************** 7";
629 const QString &packageRoot,
630 const QString &servicePrefix)
633 QString targetName = pluginName;
634 targetName = packageRoot +
'/' + targetName;
636 if (!QFile::exists(targetName)) {
637 kWarning() << targetName <<
"does not exist";
641 QString serviceName = servicePrefix + pluginName;
643 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
644 kDebug() <<
"Removing service file " << service;
645 bool ok = QFile::remove(service);
648 kWarning() <<
"Unable to remove " << service;
652 const QString errorString(
"unknown");
654 kWarning() <<
"Could not delete package from:" << targetName <<
" : " << errorString;
663 QString serviceName(
"plasma-applet-" + data.
pluginName());
664 QString service = KStandardDirs::locateLocal(
"services", serviceName +
".desktop");
672 KDesktopFile config(service);
673 KConfigGroup cg = config.desktopGroup();
674 const QString
type = data.
type().isEmpty() ?
"Service" : data.
type();
675 cg.writeEntry(
"Type", type);
676 const QString serviceTypes = data.
serviceType().isNull() ?
"Plasma/Applet,Plasma/Containment" : data.
serviceType();
677 cg.writeEntry(
"X-KDE-ServiceTypes", serviceTypes);
678 cg.writeEntry(
"X-KDE-PluginInfo-EnabledByDefault",
true);
680 QFile icon(iconPath);
683 QString installedIcon(
"plasma_applet_" + data.
pluginName() +
684 iconPath.right(iconPath.length() - iconPath.lastIndexOf(
"/")));
685 cg.writeEntry(
"Icon", installedIcon);
686 installedIcon = KStandardDirs::locateLocal(
"icon", installedIcon);
687 QFile::copy(iconPath, installedIcon);
694 const QString &source,
695 const QString &destination,
700 kWarning() <<
"Metadata file is not complete";
705 KTemporaryFile metadataFile;
706 if (!metadataFile.open()) {
709 metadata.
write(metadataFile.fileName());
712 KZip creation(destination);
713 creation.setCompression(KZip::NoCompression);
714 if (!creation.open(QIODevice::WriteOnly)) {
718 creation.addLocalFile(metadataFile.fileName(),
"metadata.desktop");
719 creation.addLocalDirectory(source,
"contents");
728 setPathFromStructure(p);
731 PackagePrivate::PackagePrivate(
const PackageStructure::Ptr st,
const QString &packageRoot,
const QString &path)
735 setPathFromStructure(packageRoot.isEmpty() ? path : packageRoot %
"/" %
path);
738 PackagePrivate::PackagePrivate(
const PackagePrivate &other)
739 : structure(other.structure),
740 service(other.service),
745 PackagePrivate::~PackagePrivate()
749 PackagePrivate &PackagePrivate::operator=(
const PackagePrivate &rhs)
751 structure = rhs.structure;
752 service = rhs.service;
757 void PackagePrivate::setPathFromStructure(
const QString &path)
765 if (path.isEmpty()) {
766 paths << structure->defaultPackageRoot();
767 }
else if (QDir::isRelativePath(path)) {
768 QString p = structure->defaultPackageRoot() %
"/" % path %
"/";
770 if (QDir::isRelativePath(p)) {
771 paths = KGlobal::dirs()->findDirs(
"data", p);
779 foreach (
const QString &p, paths) {
780 structure->setPath(p);
789 structure->setPath(QString());
792 void PackagePrivate::publish(AnnouncementMethods methods)
799 service =
new PlasmoidService(structure->path());
802 QString resourceName =
803 i18nc(
"%1 is the name of a plasmoid, %2 the name of the machine that plasmoid is published on",
804 "%1 on %2", structure->metadata().name(), QHostInfo::localHostName());
805 kDebug() <<
"publishing package under name " << resourceName;
806 service->d->publish(methods, resourceName, structure->metadata());
809 void PackagePrivate::unpublish()
812 service->d->unpublish();
816 bool PackagePrivate::isPublished()
const
819 return service->d->isPublished();
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
bool removeFolder(QString folderPath)
QStringList entryList(const char *fileType) const
Get the list of files of a given type.
PackageMetadata metadata() const
QString contentsHash() const
static QScriptValue type(QScriptContext *ctx, QScriptEngine *eng)
Package()
Default constructor that creates an invalid Package.
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.