00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "package.h"
00022
00023 #include <QDir>
00024 #include <QFile>
00025 #include <QRegExp>
00026
00027 #include <karchive.h>
00028 #include <kcomponentdata.h>
00029 #include <kdesktopfile.h>
00030 #include <kio/copyjob.h>
00031 #include <kio/deletejob.h>
00032 #include <kio/jobclasses.h>
00033 #include <kio/job.h>
00034 #include <kmimetype.h>
00035 #include <kplugininfo.h>
00036 #include <kstandarddirs.h>
00037 #include <ktar.h>
00038 #include <ktempdir.h>
00039 #include <ktemporaryfile.h>
00040 #include <kzip.h>
00041 #include <kdebug.h>
00042
00043 #include "packagemetadata.h"
00044
00045 namespace Plasma
00046 {
00047
00048 class PackagePrivate
00049 {
00050 public:
00051 PackagePrivate(const PackageStructure::Ptr st, const QString &p)
00052 : structure(st)
00053 {
00054 structure->setPath(p);
00055 valid = !structure->path().isEmpty();
00056 }
00057
00058 ~PackagePrivate()
00059 {
00060 }
00061
00062 PackageStructure::Ptr structure;
00063 bool valid;
00064 };
00065
00066 Package::Package(const QString &packageRoot, const QString &package,
00067 PackageStructure::Ptr structure)
00068 : d(new PackagePrivate(structure, packageRoot + '/' + package))
00069 {
00070 }
00071
00072 Package::Package(const QString &packagePath, PackageStructure::Ptr structure)
00073 : d(new PackagePrivate(structure, packagePath))
00074 {
00075 }
00076
00077 Package::~Package()
00078 {
00079 delete d;
00080 }
00081
00082 bool Package::isValid() const
00083 {
00084 if (!d->valid) {
00085 return false;
00086 }
00087
00088 foreach (const char *dir, d->structure->requiredDirectories()) {
00089 if (!QFile::exists(d->structure->path() + d->structure->contentsPrefix() + d->structure->path(dir))) {
00090 kWarning() << "Could not find required directory" << dir;
00091 d->valid = false;
00092 return false;
00093 }
00094 }
00095
00096 foreach (const char *file, d->structure->requiredFiles()) {
00097 if (!QFile::exists(d->structure->path() + d->structure->contentsPrefix() + d->structure->path(file))) {
00098 kWarning() << "Could not find required file" << file << ", look in"
00099 << d->structure->path() + d->structure->contentsPrefix() + d->structure->path(file) << endl;
00100 d->valid = false;
00101 return false;
00102 }
00103 }
00104
00105 return true;
00106 }
00107
00108 QString Package::filePath(const char *fileType, const QString &filename) const
00109 {
00110 if (!d->valid) {
00111 kDebug() << "package is not valid";
00112 return QString();
00113 }
00114
00115 QString path = d->structure->path(fileType);
00116
00117 if (path.isEmpty()) {
00118 kDebug() << "no matching path came of it, while looking for" << fileType << filename;
00119 return QString();
00120 }
00121
00122 path.prepend(d->structure->path() + d->structure->contentsPrefix());
00123
00124 if (!filename.isEmpty()) {
00125 path.append("/").append(filename);
00126 }
00127
00128 if (QFile::exists(path)) {
00129 if (d->structure->allowExternalPaths()) {
00130 return path;
00131 }
00132
00133
00134
00135 QDir dir(path);
00136 QString canonicalized = dir.canonicalPath() + QDir::separator();
00137 if (canonicalized.startsWith(d->structure->path())) {
00138 return path;
00139 }
00140 }
00141
00142 kDebug() << path << "does not exist";
00143 return QString();
00144 }
00145
00146 QString Package::filePath(const char *fileType) const
00147 {
00148 return filePath(fileType, QString());
00149 }
00150
00151 QStringList Package::entryList(const char *fileType) const
00152 {
00153 if (!d->valid) {
00154 return QStringList();
00155 }
00156
00157 return d->structure->entryList(fileType);
00158 }
00159
00160 PackageMetadata Package::metadata() const
00161 {
00162 return d->structure->metadata();
00163 }
00164
00165 void Package::setPath(const QString &path)
00166 {
00167 d->structure->setPath(path);
00168 d->valid = !d->structure->path().isEmpty();
00169 }
00170
00171 const QString Package::path() const
00172 {
00173 return d->structure->path();
00174 }
00175
00176 const PackageStructure::Ptr Package::structure() const
00177 {
00178 return d->structure;
00179 }
00180
00181
00182
00183 QStringList Package::listInstalled(const QString &packageRoot)
00184 {
00185 QDir dir(packageRoot);
00186
00187 if (!dir.exists()) {
00188 return QStringList();
00189 }
00190
00191 QStringList packages;
00192
00193 foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
00194 QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
00195 if (QFile::exists(metadata)) {
00196 PackageMetadata m(metadata);
00197 packages << m.pluginName();
00198 }
00199 }
00200
00201 return packages;
00202 }
00203
00204 QStringList Package::listInstalledPaths(const QString &packageRoot)
00205 {
00206 QDir dir(packageRoot);
00207
00208 if (!dir.exists()) {
00209 return QStringList();
00210 }
00211
00212 QStringList packages;
00213
00214 foreach (const QString &sdir, dir.entryList(QDir::AllDirs | QDir::Readable)) {
00215 QString metadata = packageRoot + '/' + sdir + "/metadata.desktop";
00216 if (QFile::exists(metadata)) {
00217 packages << sdir;
00218 }
00219 }
00220
00221 return packages;
00222 }
00223
00224 bool Package::installPackage(const QString &package,
00225 const QString &packageRoot,
00226 const QString &servicePrefix)
00227 {
00228
00229 QDir root(packageRoot);
00230
00231 if (!root.exists()) {
00232 KStandardDirs::makeDir(packageRoot);
00233 if (!root.exists()) {
00234 kWarning() << "Could not create package root directory:" << packageRoot;
00235 return false;
00236 }
00237 }
00238
00239 QFileInfo fileInfo(package);
00240 if (!fileInfo.exists()) {
00241 kWarning() << "No such file:" << package;
00242 return false;
00243 }
00244
00245 QString path;
00246 KTempDir tempdir;
00247 bool archivedPackage = false;
00248
00249 if (fileInfo.isDir()) {
00250
00251 path = package;
00252
00253
00254 if (path[path.size() - 1] != '/') {
00255 path.append('/');
00256 }
00257 } else {
00258 KArchive *archive = 0;
00259 KMimeType::Ptr mimetype = KMimeType::findByPath(package);
00260
00261 if (mimetype->is("application/zip")) {
00262 archive = new KZip(package);
00263 } else if (mimetype->is("application/x-compressed-tar") ||
00264 mimetype->is("application/x-tar")) {
00265 archive = new KTar(package);
00266 } else {
00267 kWarning() << "Could not open package file, unsupported archive format:" << package << mimetype->name();
00268 return false;
00269 }
00270
00271 if (!archive->open(QIODevice::ReadOnly)) {
00272 kWarning() << "Could not open package file:" << package;
00273 return false;
00274 }
00275
00276 archivedPackage = true;
00277 path = tempdir.name();
00278
00279 const KArchiveDirectory *source = archive->directory();
00280 source->copyTo(path);
00281
00282 QStringList entries = source->entries();
00283 if (entries.count() == 1) {
00284 const KArchiveEntry *entry = source->entry(entries[0]);
00285 if (entry->isDirectory()) {
00286 path.append(entry->name()).append("/");
00287 }
00288 }
00289 }
00290
00291 QString metadataPath = path + "metadata.desktop";
00292 if (!QFile::exists(metadataPath)) {
00293 kWarning() << "No metadata file in package" << package << metadataPath;
00294 return false;
00295 }
00296
00297 PackageMetadata meta(metadataPath);
00298 QString targetName = meta.pluginName();
00299
00300 if (targetName.isEmpty()) {
00301 kWarning() << "Package plugin name not specified";
00302 return false;
00303 }
00304
00305
00306
00307 QRegExp validatePluginName("^[\\w-\\.]+$");
00308 if (!validatePluginName.exactMatch(targetName)) {
00309 kWarning() << "Package plugin name " << targetName << "contains invalid characters";
00310 return false;
00311 }
00312
00313 targetName = packageRoot + '/' + targetName;
00314 if (QFile::exists(targetName)) {
00315 kWarning() << targetName << "already exists";
00316 return false;
00317 }
00318
00319 if (archivedPackage) {
00320
00321 KIO::CopyJob *job = KIO::move(KUrl(path), KUrl(targetName), KIO::HideProgressInfo);
00322 if (!job->exec()) {
00323 kWarning() << "Could not move package to destination:" << targetName << " : " << job->errorString();
00324 return false;
00325 }
00326 } else {
00327
00328
00329 KIO::CopyJob *job = KIO::copy(KUrl(path), KUrl(targetName), KIO::HideProgressInfo);
00330 if (!job->exec()) {
00331 kWarning() << "Could not copy package to destination:" << targetName << " : " << job->errorString();
00332 return false;
00333 }
00334 }
00335
00336 if (archivedPackage) {
00337
00338 tempdir.setAutoRemove(false);
00339 }
00340
00341 if (!servicePrefix.isEmpty()) {
00342
00343 QString metaPath = targetName + "/metadata.desktop";
00344 KDesktopFile df(metaPath);
00345 KConfigGroup cg = df.desktopGroup();
00346
00347
00348
00349
00350
00351
00352
00353
00354 QString serviceName = servicePrefix + meta.pluginName();
00355
00356 QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00357 KIO::FileCopyJob *job = KIO::file_copy(metaPath, service, -1, KIO::HideProgressInfo);
00358 if (job->exec()) {
00359
00360
00361 QString iconPath = targetName + '/' + cg.readEntry("Icon");
00362 QFile icon(iconPath);
00363 if (icon.exists()) {
00364 KDesktopFile df(service);
00365 KConfigGroup cg = df.desktopGroup();
00366 cg.writeEntry("Icon", iconPath);
00367 }
00368 } else {
00369 kWarning() << "Could not register package as service (this is not necessarily fatal):" << serviceName << " : " << job->errorString();
00370 }
00371 }
00372
00373 return true;
00374 }
00375
00376 bool Package::uninstallPackage(const QString &pluginName,
00377 const QString &packageRoot,
00378 const QString &servicePrefix)
00379 {
00380
00381 QString targetName = pluginName;
00382 targetName = packageRoot + '/' + targetName;
00383
00384 if (!QFile::exists(targetName)) {
00385 kWarning() << targetName << "does not exist";
00386 return false;
00387 }
00388
00389 QString serviceName = servicePrefix + pluginName;
00390
00391 QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00392 kDebug() << "Removing service file " << service;
00393 bool ok = QFile::remove(service);
00394
00395 if (!ok) {
00396 kWarning() << "Unable to remove " << service;
00397 return ok;
00398 }
00399
00400 KIO::DeleteJob *job = KIO::del(KUrl(targetName));
00401 if (!job->exec()) {
00402 kWarning() << "Could not delete package from:" << targetName << " : " << job->errorString();
00403 return false;
00404 }
00405
00406 return true;
00407 }
00408
00409 bool Package::registerPackage(const PackageMetadata &data, const QString &iconPath)
00410 {
00411 QString serviceName("plasma-applet-" + data.pluginName());
00412 QString service = KStandardDirs::locateLocal("services", serviceName + ".desktop");
00413
00414 if (data.pluginName().isEmpty()) {
00415 return false;
00416 }
00417
00418 data.write(service);
00419
00420 KDesktopFile config(service);
00421 KConfigGroup cg = config.desktopGroup();
00422 const QString type = data.type().isEmpty() ? "Service" : data.type();
00423 cg.writeEntry("Type", type);
00424 const QString serviceTypes = data.serviceType().isNull() ? "Plasma/Applet,Plasma/Containment" : data.serviceType();
00425 cg.writeEntry("X-KDE-ServiceTypes", serviceTypes);
00426 cg.writeEntry("X-KDE-PluginInfo-EnabledByDefault", true);
00427
00428 QFile icon(iconPath);
00429 if (icon.exists()) {
00430
00431 QString installedIcon("plasma_applet_" + data.pluginName() +
00432 iconPath.right(iconPath.length() - iconPath.lastIndexOf("/")));
00433 cg.writeEntry("Icon", installedIcon);
00434 installedIcon = KStandardDirs::locateLocal("icon", installedIcon);
00435 KIO::FileCopyJob *job = KIO::file_copy(iconPath, installedIcon, -1, KIO::HideProgressInfo);
00436 job->exec();
00437 }
00438
00439 return true;
00440 }
00441
00442 bool Package::createPackage(const PackageMetadata &metadata,
00443 const QString &source,
00444 const QString &destination,
00445 const QString &icon)
00446 {
00447 Q_UNUSED(icon)
00448 if (!metadata.isValid()) {
00449 kWarning() << "Metadata file is not complete";
00450 return false;
00451 }
00452
00453
00454 KTemporaryFile metadataFile;
00455 if (!metadataFile.open()) {
00456 return false;
00457 }
00458 metadata.write(metadataFile.fileName());
00459
00460
00461 KZip creation(destination);
00462 creation.setCompression(KZip::NoCompression);
00463 if (!creation.open(QIODevice::WriteOnly)) {
00464 return false;
00465 }
00466
00467 creation.addLocalFile(metadataFile.fileName(), "metadata.desktop");
00468 creation.addLocalDirectory(source, "contents");
00469 creation.close();
00470 return true;
00471 }
00472
00473 }