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