Plasma
packagestructure.cpp
Go to the documentation of this file.
00001 /****************************************************************************** 00002 * Copyright 2007 by Aaron Seigo <aseigo@kde.org> * 00003 * * 00004 * This library is free software; you can redistribute it and/or * 00005 * modify it under the terms of the GNU Library General Public * 00006 * License as published by the Free Software Foundation; either * 00007 * version 2 of the License, or (at your option) any later version. * 00008 * * 00009 * This library is distributed in the hope that it will be useful, * 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 00012 * Library General Public License for more details. * 00013 * * 00014 * You should have received a copy of the GNU Library General Public License * 00015 * along with this library; see the file COPYING.LIB. If not, write to * 00016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 00017 * Boston, MA 02110-1301, USA. * 00018 *******************************************************************************/ 00019 00020 #include "packagestructure.h" 00021 00022 #include "config-plasma.h" 00023 00024 #include <QDir> 00025 #include <QMap> 00026 #include <QFileInfo> 00027 00028 #include <kconfiggroup.h> 00029 #include <kdebug.h> 00030 #ifndef PLASMA_NO_KIO 00031 #include <kio/job.h> 00032 #endif 00033 #include <kmimetype.h> 00034 #include <kstandarddirs.h> 00035 #include <kservicetypetrader.h> 00036 #include <ktar.h> 00037 #include <ktemporaryfile.h> 00038 #include <ktempdir.h> 00039 #include <kurl.h> 00040 #include <kzip.h> 00041 00042 #include "package.h" 00043 #include "private/packages_p.h" 00044 #include "theme.h" 00045 00046 namespace Plasma 00047 { 00048 00049 class ContentStructure 00050 { 00051 public: 00052 ContentStructure() 00053 : directory(false), 00054 required(false) 00055 { 00056 } 00057 00058 ContentStructure(const ContentStructure &other) 00059 { 00060 paths = other.paths; 00061 name = other.name; 00062 mimetypes = other.mimetypes; 00063 directory = other.directory; 00064 required = other.required; 00065 } 00066 00067 QStringList paths; 00068 QString name; 00069 QStringList mimetypes; 00070 bool directory : 1; 00071 bool required : 1; 00072 }; 00073 00074 class PackageStructurePrivate 00075 { 00076 public: 00077 PackageStructurePrivate(const QString &t) 00078 : type(t), 00079 packageRoot("plasma/plasmoids"), 00080 servicePrefix("plasma-applet-"), 00081 metadata(0), 00082 externalPaths(false) 00083 { 00084 contentsPrefixPaths << "contents/"; 00085 } 00086 00087 ~PackageStructurePrivate() 00088 { 00089 delete metadata; 00090 } 00091 00092 void createPackageMetadata(const QString &path); 00093 QStringList entryList(const QString &prefix, const QString &requestedPath); 00094 00095 static QHash<QString, PackageStructure::Ptr> structures; 00096 00097 QString type; 00098 QString path; 00099 QStringList contentsPrefixPaths; 00100 QString packageRoot; 00101 QString servicePrefix; 00102 QMap<QByteArray, ContentStructure> contents; 00103 QStringList mimetypes; 00104 PackageMetadata *metadata; 00105 bool externalPaths; 00106 }; 00107 00108 QHash<QString, PackageStructure::Ptr> PackageStructurePrivate::structures; 00109 00110 PackageStructure::PackageStructure(QObject *parent, const QString &type) 00111 : QObject(parent), 00112 d(new PackageStructurePrivate(type)) 00113 { 00114 } 00115 00116 PackageStructure::~PackageStructure() 00117 { 00118 delete d; 00119 } 00120 00121 PackageStructure::Ptr PackageStructure::load(const QString &packageFormat) 00122 { 00123 if (packageFormat.isEmpty()) { 00124 return Ptr(new PackageStructure()); 00125 } 00126 00127 PackageStructure::Ptr structure = PackageStructurePrivate::structures[packageFormat]; 00128 00129 if (structure) { 00130 return structure; 00131 } 00132 00133 if (packageFormat == "Plasma/Applet") { 00134 structure = defaultPackageStructure(AppletComponent); 00135 structure->d->type = "Plasma/Applet"; 00136 } else if (packageFormat == "Plasma/DataEngine") { 00137 structure = defaultPackageStructure(DataEngineComponent); 00138 structure->d->type = "Plasma/DataEngine"; 00139 } else if (packageFormat == "Plasma/Runner") { 00140 structure = defaultPackageStructure(RunnerComponent); 00141 structure->d->type = "Plasma/Runner"; 00142 } else if (packageFormat == "Plasma/Wallpaper") { 00143 structure = defaultPackageStructure(WallpaperComponent); 00144 structure->d->type = "Plasma/Wallpaper"; 00145 } else if (packageFormat == "Plasma/Theme") { 00146 structure = Theme::packageStructure(); 00147 structure->d->type = "Plasma/Theme"; 00148 } 00149 00150 if (structure) { 00151 PackageStructurePrivate::structures[packageFormat] = structure; 00152 return structure; 00153 } 00154 00155 // first we check for plugins in sycoca 00156 QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(packageFormat); 00157 KService::List offers = 00158 KServiceTypeTrader::self()->query("Plasma/PackageStructure", constraint); 00159 00160 QVariantList args; 00161 QString error; 00162 foreach (const KService::Ptr &offer, offers) { 00163 PackageStructure::Ptr structure( 00164 offer->createInstance<Plasma::PackageStructure>(0, args, &error)); 00165 00166 if (structure) { 00167 return structure; 00168 } 00169 00170 kDebug() << "Couldn't load PackageStructure for" << packageFormat 00171 << "! reason given: " << error; 00172 } 00173 00174 // if that didn't give us any love, then we try to load from a config file 00175 structure = new PackageStructure(); 00176 QString configPath("plasma/packageformats/%1rc"); 00177 configPath = KStandardDirs::locate("data", configPath.arg(packageFormat)); 00178 00179 if (!configPath.isEmpty()) { 00180 KConfig config(configPath); 00181 structure->read(&config); 00182 PackageStructurePrivate::structures[packageFormat] = structure; 00183 return structure; 00184 } 00185 00186 // try to load from absolute file path 00187 KUrl url(packageFormat); 00188 if (url.isLocalFile()) { 00189 KConfig config(url.toLocalFile(), KConfig::SimpleConfig); 00190 structure->read(&config); 00191 PackageStructurePrivate::structures[structure->type()] = structure; 00192 } 00193 #ifndef PLASMA_NO_KIO 00194 else { 00195 KTemporaryFile tmp; 00196 if (tmp.open()) { 00197 KIO::Job *job = KIO::file_copy(url, KUrl(tmp.fileName()), 00198 -1, KIO::Overwrite | KIO::HideProgressInfo); 00199 if (job->exec()) { 00200 KConfig config(tmp.fileName(), KConfig::SimpleConfig); 00201 structure->read(&config); 00202 PackageStructurePrivate::structures[structure->type()] = structure; 00203 } 00204 } 00205 } 00206 #endif 00207 00208 return structure; 00209 } 00210 00211 PackageStructure &PackageStructure::operator=(const PackageStructure &rhs) 00212 { 00213 if (this == &rhs) { 00214 return *this; 00215 } 00216 00217 *d = *rhs.d; 00218 return *this; 00219 } 00220 00221 QString PackageStructure::type() const 00222 { 00223 return d->type; 00224 } 00225 00226 QList<const char*> PackageStructure::directories() const 00227 { 00228 QList<const char*> dirs; 00229 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constBegin(); 00230 while (it != d->contents.constEnd()) { 00231 if (it.value().directory) { 00232 dirs << it.key(); 00233 } 00234 ++it; 00235 } 00236 return dirs; 00237 } 00238 00239 QList<const char*> PackageStructure::requiredDirectories() const 00240 { 00241 QList<const char*> dirs; 00242 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constBegin(); 00243 while (it != d->contents.constEnd()) { 00244 if (it.value().directory && 00245 it.value().required) { 00246 dirs << it.key(); 00247 } 00248 ++it; 00249 } 00250 return dirs; 00251 } 00252 00253 QList<const char*> PackageStructure::files() const 00254 { 00255 QList<const char*> files; 00256 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constBegin(); 00257 while (it != d->contents.constEnd()) { 00258 if (!it.value().directory) { 00259 files << it.key(); 00260 } 00261 ++it; 00262 } 00263 return files; 00264 } 00265 00266 QList<const char*> PackageStructure::requiredFiles() const 00267 { 00268 QList<const char*> files; 00269 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constBegin(); 00270 while (it != d->contents.constEnd()) { 00271 if (!it.value().directory && it.value().required) { 00272 files << it.key(); 00273 } 00274 ++it; 00275 } 00276 return files; 00277 } 00278 00279 QStringList PackageStructure::entryList(const char *key) 00280 { 00281 QString p = path(key); 00282 00283 if (p.isEmpty()) { 00284 return QStringList(); 00285 } 00286 00287 QStringList list; 00288 if (d->contentsPrefixPaths.isEmpty()) { 00289 // no prefixes is the same as d->contentsPrefixPths with QStringList() << QString() 00290 list << d->entryList(QString(), p); 00291 } else { 00292 foreach (QString prefix, d->contentsPrefixPaths) { 00293 list << d->entryList(prefix, p); 00294 } 00295 } 00296 00297 return list; 00298 } 00299 00300 QStringList PackageStructurePrivate::entryList(const QString &prefix, const QString &requestedPath) 00301 { 00302 QDir dir(path + prefix + requestedPath); 00303 00304 if (externalPaths) { 00305 return dir.entryList(QDir::Files | QDir::Readable); 00306 } 00307 00308 // ensure that we don't return files outside of our base path 00309 // due to symlink or ../ games 00310 QString canonicalized = dir.canonicalPath(); 00311 if (canonicalized.startsWith(path)) { 00312 return dir.entryList(QDir::Files | QDir::Readable); 00313 } 00314 00315 return QStringList(); 00316 } 00317 00318 void PackageStructure::addDirectoryDefinition(const char *key, 00319 const QString &path, const QString &name) 00320 { 00321 ContentStructure s; 00322 00323 if (d->contents.contains(key)) { 00324 s = d->contents[key]; 00325 } 00326 00327 if (!name.isEmpty()) { 00328 s.name = name; 00329 } 00330 00331 s.paths.append(path); 00332 s.directory = true; 00333 00334 d->contents[key] = s; 00335 } 00336 00337 void PackageStructure::addFileDefinition(const char *key, const QString &path, const QString &name) 00338 { 00339 ContentStructure s; 00340 00341 if (d->contents.contains(key)) { 00342 s = d->contents[key]; 00343 } 00344 00345 if (!name.isEmpty()) { 00346 s.name = name; 00347 } 00348 00349 s.paths.append(path); 00350 s.directory = false; 00351 00352 d->contents[key] = s; 00353 } 00354 00355 void PackageStructure::removeDefinition(const char *key) 00356 { 00357 d->contents.remove(key); 00358 } 00359 00360 QString PackageStructure::path(const char *key) const 00361 { 00362 //kDebug() << "looking for" << key; 00363 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constFind(key); 00364 if (it == d->contents.constEnd()) { 00365 return QString(); 00366 } 00367 00368 //kDebug() << "found" << key << "and the value is" << it.value().paths.first(); 00369 return it.value().paths.first(); 00370 } 00371 00372 QStringList PackageStructure::searchPath(const char *key) const 00373 { 00374 //kDebug() << "looking for" << key; 00375 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constFind(key); 00376 if (it == d->contents.constEnd()) { 00377 return QStringList(); 00378 } 00379 00380 //kDebug() << "found" << key << "and the value is" << it.value().paths; 00381 return it.value().paths; 00382 } 00383 00384 QString PackageStructure::name(const char *key) const 00385 { 00386 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constFind(key); 00387 if (it == d->contents.constEnd()) { 00388 return QString(); 00389 } 00390 00391 return it.value().name; 00392 } 00393 00394 void PackageStructure::setRequired(const char *key, bool required) 00395 { 00396 QMap<QByteArray, ContentStructure>::iterator it = d->contents.find(key); 00397 if (it == d->contents.end()) { 00398 return; 00399 } 00400 00401 it.value().required = required; 00402 } 00403 00404 bool PackageStructure::isRequired(const char *key) const 00405 { 00406 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constFind(key); 00407 if (it == d->contents.constEnd()) { 00408 return false; 00409 } 00410 00411 return it.value().required; 00412 } 00413 00414 void PackageStructure::setDefaultMimetypes(QStringList mimetypes) 00415 { 00416 d->mimetypes = mimetypes; 00417 } 00418 00419 void PackageStructure::setMimetypes(const char *key, QStringList mimetypes) 00420 { 00421 QMap<QByteArray, ContentStructure>::iterator it = d->contents.find(key); 00422 if (it == d->contents.end()) { 00423 return; 00424 } 00425 00426 it.value().mimetypes = mimetypes; 00427 } 00428 00429 QStringList PackageStructure::mimetypes(const char *key) const 00430 { 00431 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constFind(key); 00432 if (it == d->contents.constEnd()) { 00433 return QStringList(); 00434 } 00435 00436 if (it.value().mimetypes.isEmpty()) { 00437 return d->mimetypes; 00438 } 00439 00440 return it.value().mimetypes; 00441 } 00442 00443 void PackageStructure::setPath(const QString &path) 00444 { 00445 KUrl url(path); 00446 QDir dir(url.toLocalFile()); 00447 QString basePath = dir.canonicalPath(); 00448 bool valid = QFile::exists(basePath); 00449 00450 if (valid) { 00451 QFileInfo info(basePath); 00452 if (info.isDir() && !basePath.endsWith('/')) { 00453 basePath.append('/'); 00454 } 00455 //kDebug() << "basePath is" << basePath; 00456 } else { 00457 kDebug() << path << "invalid, basePath is" << basePath; 00458 return; 00459 } 00460 00461 if (d->path == basePath) { 00462 return; 00463 } 00464 00465 d->path = basePath; 00466 delete d->metadata; 00467 d->metadata = 0; 00468 pathChanged(); 00469 } 00470 00471 QString PackageStructure::path() const 00472 { 00473 return d->path; 00474 } 00475 00476 void PackageStructure::pathChanged() 00477 { 00478 // default impl does nothing, this is a hook for subclasses. 00479 } 00480 00481 void PackageStructure::read(const KConfigBase *config) 00482 { 00483 d->contents.clear(); 00484 d->mimetypes.clear(); 00485 KConfigGroup general(config, QString()); 00486 d->type = general.readEntry("Type", QString()); 00487 d->contentsPrefixPaths = general.readEntry("ContentsPrefixPaths", d->contentsPrefixPaths); 00488 d->packageRoot = general.readEntry("DefaultPackageRoot", d->packageRoot); 00489 d->externalPaths = general.readEntry("AllowExternalPaths", d->externalPaths); 00490 00491 QStringList groups = config->groupList(); 00492 foreach (const QString &group, groups) { 00493 KConfigGroup entry(config, group); 00494 QByteArray key = group.toAscii(); 00495 00496 QString path = entry.readEntry("Path", QString()); 00497 QString name = entry.readEntry("Name", QString()); 00498 QStringList mimetypes = entry.readEntry("Mimetypes", QStringList()); 00499 bool directory = entry.readEntry("Directory", false); 00500 bool required = entry.readEntry("Required", false); 00501 00502 if (directory) { 00503 addDirectoryDefinition(key, path, name); 00504 } else { 00505 addFileDefinition(key, path, name); 00506 } 00507 00508 setMimetypes(key, mimetypes); 00509 setRequired(key, required); 00510 } 00511 } 00512 00513 void PackageStructure::write(KConfigBase *config) const 00514 { 00515 KConfigGroup general = KConfigGroup(config, ""); 00516 general.writeEntry("Type", type()); 00517 general.writeEntry("ContentsPrefixPaths", d->contentsPrefixPaths); 00518 general.writeEntry("DefaultPackageRoot", d->packageRoot); 00519 general.writeEntry("AllowExternalPaths", d->externalPaths); 00520 00521 QMap<QByteArray, ContentStructure>::const_iterator it = d->contents.constBegin(); 00522 while (it != d->contents.constEnd()) { 00523 KConfigGroup group = config->group(it.key()); 00524 group.writeEntry("Path", it.value().paths); 00525 group.writeEntry("Name", it.value().name); 00526 if (!it.value().mimetypes.isEmpty()) { 00527 group.writeEntry("Mimetypes", it.value().mimetypes); 00528 } 00529 if (it.value().directory) { 00530 group.writeEntry("Directory", true); 00531 } 00532 if (it.value().required) { 00533 group.writeEntry("Required", true); 00534 } 00535 00536 ++it; 00537 } 00538 } 00539 00540 QString PackageStructure::contentsPrefix() const 00541 { 00542 return d->contentsPrefixPaths.isEmpty() ? QString() : d->contentsPrefixPaths.first(); 00543 } 00544 00545 void PackageStructure::setContentsPrefix(const QString &prefix) 00546 { 00547 d->contentsPrefixPaths.clear(); 00548 d->contentsPrefixPaths << prefix; 00549 } 00550 00551 QStringList PackageStructure::contentsPrefixPaths() const 00552 { 00553 return d->contentsPrefixPaths; 00554 } 00555 00556 void PackageStructure::setContentsPrefixPaths(const QStringList &prefixPaths) 00557 { 00558 d->contentsPrefixPaths = prefixPaths; 00559 } 00560 00561 bool PackageStructure::installPackage(const QString &package, const QString &packageRoot) 00562 { 00563 return Package::installPackage(package, packageRoot, d->servicePrefix); 00564 } 00565 00566 bool PackageStructure::uninstallPackage(const QString &packageName, const QString &packageRoot) 00567 { 00568 return Package::uninstallPackage(packageName, packageRoot, d->servicePrefix); 00569 } 00570 00571 void PackageStructure::createNewWidgetBrowser(QWidget *parent) 00572 { 00573 Q_UNUSED(parent) 00574 emit newWidgetBrowserFinished(); 00575 } 00576 00577 QString PackageStructure::defaultPackageRoot() const 00578 { 00579 return d->packageRoot; 00580 } 00581 00582 QString PackageStructure::servicePrefix() const 00583 { 00584 return d->servicePrefix; 00585 } 00586 00587 void PackageStructure::setDefaultPackageRoot(const QString &packageRoot) 00588 { 00589 d->packageRoot = packageRoot; 00590 } 00591 00592 void PackageStructure::setServicePrefix(const QString &servicePrefix) 00593 { 00594 d->servicePrefix = servicePrefix; 00595 } 00596 00597 void PackageStructurePrivate::createPackageMetadata(const QString &path) 00598 { 00599 delete metadata; 00600 metadata = 0; 00601 00602 QString metadataPath(path + "/metadata.desktop"); 00603 if (!QFile::exists(metadataPath)) { 00604 metadataPath.clear(); 00605 kWarning() << "No metadata file in the package, expected it at:" << metadataPath; 00606 } 00607 00608 metadata = new PackageMetadata(metadataPath); 00609 } 00610 00611 //FIXME KDE5: should be const 00612 PackageMetadata PackageStructure::metadata() 00613 { 00614 if (!d->metadata && !d->path.isEmpty()) { 00615 QFileInfo fileInfo(d->path); 00616 00617 if (fileInfo.isDir()) { 00618 d->createPackageMetadata(d->path); 00619 } else if (fileInfo.exists()) { 00620 KArchive *archive = 0; 00621 KMimeType::Ptr mimetype = KMimeType::findByPath(d->path); 00622 00623 if (mimetype->is("application/zip")) { 00624 archive = new KZip(d->path); 00625 } else if (mimetype->is("application/x-compressed-tar") || 00626 mimetype->is("application/x-tar")|| mimetype->is("application/x-bzip-compressed-tar")) { 00627 archive = new KTar(d->path); 00628 } else { 00629 kWarning() << "Could not open package file, unsupported archive format:" << d->path << mimetype->name(); 00630 } 00631 00632 if (archive->open(QIODevice::ReadOnly)) { 00633 const KArchiveDirectory *source = archive->directory(); 00634 KTempDir tempdir; 00635 source->copyTo(tempdir.name()); 00636 d->createPackageMetadata(tempdir.name()); 00637 } else { 00638 kWarning() << "Could not open package file:" << d->path; 00639 } 00640 00641 delete archive; 00642 } 00643 } 00644 00645 if (!d->metadata) { 00646 d->metadata = new PackageMetadata(); 00647 } 00648 00649 return *d->metadata; 00650 } 00651 00652 bool PackageStructure::allowExternalPaths() const 00653 { 00654 return d->externalPaths; 00655 } 00656 00657 void PackageStructure::setAllowExternalPaths(bool allow) 00658 { 00659 d->externalPaths = allow; 00660 } 00661 00662 } // Plasma namespace 00663 00664 #include "packagestructure.moc" 00665
KDE 4.6 API Reference