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