00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "coreengine.h"
00023
00024 #include "entryhandler.h"
00025 #include "providerhandler.h"
00026 #include "entryloader.h"
00027 #include "providerloader.h"
00028 #include "installation.h"
00029 #include "security.h"
00030
00031 #include <kaboutdata.h>
00032 #include <kconfig.h>
00033 #include <kconfiggroup.h>
00034 #include <kcomponentdata.h>
00035 #include <kdebug.h>
00036 #include <kstandarddirs.h>
00037 #include <kcodecs.h>
00038 #include <kprocess.h>
00039 #include <kshell.h>
00040
00041 #include <kio/job.h>
00042 #include <kmimetype.h>
00043 #include <krandom.h>
00044 #include <ktar.h>
00045 #include <kzip.h>
00046
00047 #include <QtCore/QDir>
00048 #include <QtXml/qdom.h>
00049 #include <QtCore/Q_PID>
00050
00051 using namespace KNS;
00052
00053 CoreEngine::CoreEngine(QObject* parent)
00054 : QObject(parent), m_uploadedentry(NULL), m_uploadprovider(NULL), m_installation(NULL), m_activefeeds(0),
00055 m_initialized(false), m_cachepolicy(CacheNever), m_automationpolicy(AutomationOn)
00056 {
00057 }
00058
00059 CoreEngine::~CoreEngine()
00060 {
00061 shutdown();
00062 }
00063
00064 bool CoreEngine::init(const QString &configfile)
00065 {
00066 kDebug() << "Initializing KNS::CoreEngine from '" << configfile << "'";
00067
00068 KConfig conf(configfile);
00069 if (conf.accessMode() == KConfig::NoAccess) {
00070 kError() << "No knsrc file named '" << configfile << "' was found." << endl;
00071 return false;
00072 }
00073
00074
00075
00076
00077 if (KStandardDirs::locate("config", configfile).isEmpty()) {
00078 kError() << "No knsrc file named '" << configfile << "' was found." << endl;
00079 return false;
00080 }
00081
00082 if (!conf.hasGroup("KNewStuff2")) {
00083 kError() << "A knsrc file was found but it doesn't contain a KNewStuff2 section." << endl;
00084 return false;
00085 }
00086
00087 KConfigGroup group = conf.group("KNewStuff2");
00088 m_providersurl = group.readEntry("ProvidersUrl", QString());
00089
00090 m_componentname = QFileInfo(KStandardDirs::locate("config", configfile)).baseName() + ':';
00091
00092
00093
00094 m_installation = new Installation();
00095 QString uncompresssetting = group.readEntry("Uncompress", QString("never"));
00096
00097 if (uncompresssetting == "true") {
00098 uncompresssetting = "always";
00099 }
00100 if (uncompresssetting != "always" && uncompresssetting != "archive" && uncompresssetting != "never") {
00101 kError() << "invalid Uncompress setting chosen, must be one of: always, archive, or never" << endl;
00102 return false;
00103 }
00104 m_installation->setUncompression(uncompresssetting);
00105
00106 m_installation->setCommand(group.readEntry("InstallationCommand", QString()));
00107 m_installation->setStandardResourceDir(group.readEntry("StandardResource", QString()));
00108 m_installation->setTargetDir(group.readEntry("TargetDir", QString()));
00109 m_installation->setInstallPath(group.readEntry("InstallPath", QString()));
00110 m_installation->setAbsoluteInstallPath(group.readEntry("AbsoluteInstallPath", QString()));
00111 m_installation->setCustomName(group.readEntry("CustomName", false));
00112
00113 QString checksumpolicy = group.readEntry("ChecksumPolicy", QString());
00114 if (!checksumpolicy.isEmpty()) {
00115 if (checksumpolicy == "never")
00116 m_installation->setChecksumPolicy(Installation::CheckNever);
00117 else if (checksumpolicy == "ifpossible")
00118 m_installation->setChecksumPolicy(Installation::CheckIfPossible);
00119 else if (checksumpolicy == "always")
00120 m_installation->setChecksumPolicy(Installation::CheckAlways);
00121 else {
00122 kError() << "The checksum policy '" + checksumpolicy + "' is unknown." << endl;
00123 return false;
00124 }
00125 }
00126
00127 QString signaturepolicy = group.readEntry("SignaturePolicy", QString());
00128 if (!signaturepolicy.isEmpty()) {
00129 if (signaturepolicy == "never")
00130 m_installation->setSignaturePolicy(Installation::CheckNever);
00131 else if (signaturepolicy == "ifpossible")
00132 m_installation->setSignaturePolicy(Installation::CheckIfPossible);
00133 else if (signaturepolicy == "always")
00134 m_installation->setSignaturePolicy(Installation::CheckAlways);
00135 else {
00136 kError() << "The signature policy '" + signaturepolicy + "' is unknown." << endl;
00137 return false;
00138 }
00139 }
00140
00141 QString scope = group.readEntry("Scope", QString());
00142 if (!scope.isEmpty()) {
00143 if (scope == "user")
00144 m_installation->setScope(Installation::ScopeUser);
00145 else if (scope == "system")
00146 m_installation->setScope(Installation::ScopeSystem);
00147 else {
00148 kError() << "The scope '" + scope + "' is unknown." << endl;
00149 return false;
00150 }
00151
00152 if (m_installation->scope() == Installation::ScopeSystem) {
00153 if (!m_installation->installPath().isEmpty()) {
00154 kError() << "System installation cannot be mixed with InstallPath." << endl;
00155 return false;
00156 }
00157 }
00158 }
00159
00160 QString cachePolicy = group.readEntry("CachePolicy", QString());
00161 if (!cachePolicy.isEmpty()) {
00162 if (cachePolicy == "never") {
00163 m_cachepolicy = CacheNever;
00164 } else if (cachePolicy == "replaceable") {
00165 m_cachepolicy = CacheReplaceable;
00166 } else if (cachePolicy == "resident") {
00167 m_cachepolicy = CacheResident;
00168 } else if (cachePolicy == "only") {
00169 m_cachepolicy = CacheOnly;
00170 } else {
00171 kError() << "Cache policy '" + cachePolicy + "' is unknown." << endl;
00172 }
00173 }
00174 kDebug() << "cache policy: " << cachePolicy;
00175
00176 m_initialized = true;
00177
00178 return true;
00179 }
00180
00181 void CoreEngine::start()
00182 {
00183
00184
00185 if (!m_initialized) {
00186 kError() << "Must call KNS::CoreEngine::init() first." << endl;
00187 return;
00188 }
00189
00190
00191 loadRegistry();
00192
00193
00194 if (m_cachepolicy != CacheNever) {
00195 loadProvidersCache();
00196 }
00197
00198
00199 if (m_cachepolicy == CacheOnly) {
00200
00201 return;
00202 }
00203
00204 ProviderLoader *provider_loader = new ProviderLoader(this);
00205
00206
00207 connect(provider_loader,
00208 SIGNAL(signalProvidersLoaded(KNS::Provider::List)),
00209 SLOT(slotProvidersLoaded(KNS::Provider::List)));
00210 connect(provider_loader,
00211 SIGNAL(signalProvidersFailed()),
00212 SLOT(slotProvidersFailed()));
00213
00214 provider_loader->load(m_providersurl);
00215 }
00216
00217 void CoreEngine::loadEntries(Provider *provider)
00218 {
00219
00220
00221 if (m_cachepolicy == CacheOnly) {
00222 return;
00223 }
00224
00225
00226
00227
00228
00229
00230
00231
00232 QStringList feeds = provider->feeds();
00233 for (int i = 0; i < feeds.count(); i++) {
00234 Feed *feed = provider->downloadUrlFeed(feeds.at(i));
00235 if (feed) {
00236 ++m_activefeeds;
00237
00238 EntryLoader *entry_loader = new EntryLoader(this);
00239
00240 connect(entry_loader,
00241 SIGNAL(signalEntriesLoaded(KNS::Entry::List)),
00242 SLOT(slotEntriesLoaded(KNS::Entry::List)));
00243 connect(entry_loader,
00244 SIGNAL(signalEntriesFailed()),
00245 SLOT(slotEntriesFailed()));
00246 connect(entry_loader,
00247 SIGNAL(signalProgress(KJob*, unsigned long)),
00248 SLOT(slotProgress(KJob*, unsigned long)));
00249
00250 entry_loader->load(provider, feed);
00251 }
00252 }
00253 }
00254
00255 void CoreEngine::downloadPreview(Entry *entry)
00256 {
00257 if (m_previewfiles.contains(entry)) {
00258
00259
00260 emit signalPreviewLoaded(KUrl::fromPath(m_previewfiles[entry]));
00261 return;
00262 }
00263
00264 KUrl source = KUrl(entry->preview().representation());
00265
00266 if (!source.isValid()) {
00267 kError() << "The entry doesn't have a preview." << endl;
00268 return;
00269 }
00270
00271 KUrl destination = KGlobal::dirs()->saveLocation("tmp") + KRandom::randomString(10);
00272
00273
00274
00275 KIO::FileCopyJob *job = KIO::file_copy(source, destination, -1, KIO::Overwrite | KIO::HideProgressInfo);
00276 connect(job,
00277 SIGNAL(result(KJob*)),
00278 SLOT(slotPreviewResult(KJob*)));
00279 connect(job,
00280 SIGNAL(progress(KJob*, unsigned long)),
00281 SLOT(slotProgress(KJob*, unsigned long)));
00282
00283 m_entry_jobs[job] = entry;
00284 }
00285
00286 void CoreEngine::downloadPayload(Entry *entry)
00287 {
00288 if(!entry)
00289 return;
00290
00291 KUrl source = KUrl(entry->payload().representation());
00292
00293 if (!source.isValid()) {
00294 kError() << "The entry doesn't have a payload." << endl;
00295 return;
00296 }
00297
00298 if (m_installation->isRemote()) {
00299
00300
00301 entry->setStatus(Entry::Installed);
00302 m_payloadfiles[entry] = entry->payload().representation();
00303 install(source.pathOrUrl());
00304 emit signalPayloadLoaded(source);
00305
00306 return;
00307 }
00308
00309 KUrl destination = KGlobal::dirs()->saveLocation("tmp") + KRandom::randomString(10);
00310 kDebug() << "Downloading payload '" << source << "' to '" << destination << "'";
00311
00312
00313 KIO::FileCopyJob *job = KIO::file_copy(source, destination, -1, KIO::Overwrite | KIO::HideProgressInfo);
00314 connect(job,
00315 SIGNAL(result(KJob*)),
00316 SLOT(slotPayloadResult(KJob*)));
00317 connect(job,
00318 SIGNAL(percent(KJob*, unsigned long)),
00319 SLOT(slotProgress(KJob*, unsigned long)));
00320
00321 m_entry_jobs[job] = entry;
00322 }
00323
00324 bool CoreEngine::uploadEntry(Provider *provider, Entry *entry)
00325 {
00326
00327
00328 if (m_uploadedentry) {
00329 kError() << "Another upload is in progress!" << endl;
00330 return false;
00331 }
00332
00333 if (!provider->uploadUrl().isValid()) {
00334 kError() << "The provider doesn't support uploads." << endl;
00335 return false;
00336
00337
00338 }
00339
00340
00341
00342 m_uploadedentry = entry;
00343
00344 KUrl sourcepayload = KUrl(entry->payload().representation());
00345 KUrl destfolder = provider->uploadUrl();
00346
00347 destfolder.setFileName(sourcepayload.fileName());
00348
00349 KIO::FileCopyJob *fcjob = KIO::file_copy(sourcepayload, destfolder, -1, KIO::Overwrite | KIO::HideProgressInfo);
00350 connect(fcjob,
00351 SIGNAL(result(KJob*)),
00352 SLOT(slotUploadPayloadResult(KJob*)));
00353
00354 return true;
00355 }
00356
00357 void CoreEngine::slotProvidersLoaded(KNS::Provider::List list)
00358 {
00359
00360 ProviderLoader *loader = dynamic_cast<ProviderLoader*>(sender());
00361 delete loader;
00362
00363 mergeProviders(list);
00364 }
00365
00366 void CoreEngine::slotProvidersFailed()
00367 {
00368 kDebug() << "slotProvidersFailed";
00369 ProviderLoader *loader = dynamic_cast<ProviderLoader*>(sender());
00370 delete loader;
00371
00372 emit signalProvidersFailed();
00373 }
00374
00375 void CoreEngine::slotEntriesLoaded(KNS::Entry::List list)
00376 {
00377 EntryLoader *loader = dynamic_cast<EntryLoader*>(sender());
00378 if (!loader) return;
00379 const Provider *provider = loader->provider();
00380 Feed *feed = loader->feed();
00381 delete loader;
00382 m_activefeeds--;
00383
00384
00385
00386
00387
00388
00389 mergeEntries(list, feed, provider);
00390 }
00391
00392 void CoreEngine::slotEntriesFailed()
00393 {
00394 EntryLoader *loader = dynamic_cast<EntryLoader*>(sender());
00395 delete loader;
00396 m_activefeeds--;
00397
00398 emit signalEntriesFailed();
00399 }
00400
00401 void CoreEngine::slotProgress(KJob *job, unsigned long percent)
00402 {
00403 QString url;
00404 KIO::FileCopyJob * copyJob = qobject_cast<KIO::FileCopyJob*>(job);
00405 KIO::TransferJob * transferJob = qobject_cast<KIO::TransferJob*>(job);
00406 if (copyJob != NULL) {
00407 url = copyJob->srcUrl().fileName();
00408 } else if (transferJob != NULL) {
00409 url = transferJob->url().fileName();
00410 }
00411
00412 QString message = QString("loading %1").arg(url);
00413 emit signalProgress(message, percent);
00414 }
00415
00416 void CoreEngine::slotPayloadResult(KJob *job)
00417 {
00418
00419 if (m_entry_jobs.contains(job)) {
00420 Entry *entry = m_entry_jobs[job];
00421 m_entry_jobs.remove(job);
00422
00423 if (job->error()) {
00424 kError() << "Cannot load payload file." << endl;
00425 kError() << job->errorString() << endl;
00426
00427 emit signalPayloadFailed(entry);
00428 } else {
00429 KIO::FileCopyJob *fcjob = static_cast<KIO::FileCopyJob*>(job);
00430
00431 entry->setStatus(Entry::Installed);
00432 m_payloadfiles[entry] = fcjob->destUrl().path();
00433
00434 install(fcjob->destUrl().pathOrUrl());
00435
00436 emit signalPayloadLoaded(fcjob->destUrl());
00437 }
00438 }
00439 }
00440
00441
00442 void CoreEngine::slotPreviewResult(KJob *job)
00443 {
00444 if (job->error()) {
00445 kError() << "Cannot load preview file." << endl;
00446 kError() << job->errorString() << endl;
00447
00448 m_entry_jobs.remove(job);
00449 emit signalPreviewFailed();
00450 } else {
00451 KIO::FileCopyJob *fcjob = static_cast<KIO::FileCopyJob*>(job);
00452
00453 if (m_entry_jobs.contains(job)) {
00454
00455 Entry *entry = m_entry_jobs[job];
00456 m_entry_jobs.remove(job);
00457 m_previewfiles[entry] = fcjob->destUrl().path();
00458 cacheEntry(entry);
00459 }
00460
00461
00462 emit signalPreviewLoaded(fcjob->destUrl());
00463 }
00464 }
00465
00466 void CoreEngine::slotUploadPayloadResult(KJob *job)
00467 {
00468 if (job->error()) {
00469 kError() << "Cannot upload payload file." << endl;
00470 kError() << job->errorString() << endl;
00471
00472 m_uploadedentry = NULL;
00473 m_uploadprovider = NULL;
00474
00475 emit signalEntryFailed();
00476 return;
00477 }
00478
00479 if (m_uploadedentry->preview().isEmpty()) {
00480
00481 slotUploadPreviewResult(job);
00482 return;
00483 }
00484
00485 KUrl sourcepreview = KUrl(m_uploadedentry->preview().representation());
00486 KUrl destfolder = m_uploadprovider->uploadUrl();
00487
00488 KIO::FileCopyJob *fcjob = KIO::file_copy(sourcepreview, destfolder, -1, KIO::Overwrite | KIO::HideProgressInfo);
00489 connect(fcjob,
00490 SIGNAL(result(KJob*)),
00491 SLOT(slotUploadPreviewResult(KJob*)));
00492 }
00493
00494 void CoreEngine::slotUploadPreviewResult(KJob *job)
00495 {
00496 if (job->error()) {
00497 kError() << "Cannot upload preview file." << endl;
00498 kError() << job->errorString() << endl;
00499
00500 m_uploadedentry = NULL;
00501 m_uploadprovider = NULL;
00502
00503 emit signalEntryFailed();
00504 return;
00505 }
00506
00507
00508
00509
00510
00511 KUrl sourcemeta = KGlobal::dirs()->saveLocation("tmp") + KRandom::randomString(10) + ".meta";
00512 KUrl destfolder = m_uploadprovider->uploadUrl();
00513
00514 EntryHandler eh(*m_uploadedentry);
00515 QDomElement exml = eh.entryXML();
00516
00517 QFile f(sourcemeta.path());
00518 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
00519 kError() << "Cannot write meta information to '" << sourcemeta << "'." << endl;
00520
00521 m_uploadedentry = NULL;
00522 m_uploadprovider = NULL;
00523
00524 emit signalEntryFailed();
00525 return;
00526 }
00527 QTextStream metastream(&f);
00528 metastream << exml;
00529 f.close();
00530
00531 KIO::FileCopyJob *fcjob = KIO::file_copy(sourcemeta, destfolder, -1, KIO::Overwrite | KIO::HideProgressInfo);
00532 connect(fcjob,
00533 SIGNAL(result(KJob*)),
00534 SLOT(slotUploadMetaResult(KJob*)));
00535 }
00536
00537 void CoreEngine::slotUploadMetaResult(KJob *job)
00538 {
00539 if (job->error()) {
00540 kError() << "Cannot upload meta file." << endl;
00541 kError() << job->errorString() << endl;
00542
00543 m_uploadedentry = NULL;
00544 m_uploadprovider = NULL;
00545
00546 emit signalEntryFailed();
00547 return;
00548 } else {
00549 m_uploadedentry = NULL;
00550 m_uploadprovider = NULL;
00551
00552
00553 emit signalEntryUploaded();
00554 }
00555 }
00556
00557 void CoreEngine::loadRegistry()
00558 {
00559 KStandardDirs d;
00560
00561
00562
00563 QString realAppName = m_componentname.split(":")[0];
00564
00565
00566 const QStringList dirs = d.findDirs("data", "knewstuff2-entries.registry");
00567 for (QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
00568
00569 QDir dir((*it));
00570 const QStringList files = dir.entryList(QDir::Files | QDir::Readable);
00571 for (QStringList::const_iterator fit = files.begin(); fit != files.end(); ++fit) {
00572 QString filepath = (*it) + '/' + (*fit);
00573
00574
00575 bool ret;
00576 QFileInfo info(filepath);
00577 QFile f(filepath);
00578
00579
00580
00581
00582 QString thisAppName = QString::fromUtf8(QByteArray::fromBase64(info.baseName().toUtf8()));
00583
00584
00585
00586 thisAppName = thisAppName.split(":")[0];
00587
00588 if (thisAppName != realAppName) {
00589 continue;
00590 }
00591
00592 ret = f.open(QIODevice::ReadOnly);
00593 if (!ret) {
00594 kWarning() << "The file could not be opened.";
00595 continue;
00596 }
00597
00598 QDomDocument doc;
00599 ret = doc.setContent(&f);
00600 if (!ret) {
00601 kWarning() << "The file could not be parsed.";
00602 continue;
00603 }
00604
00605 QDomElement root = doc.documentElement();
00606 if (root.tagName() != "ghnsinstall") {
00607 kWarning() << "The file doesn't seem to be of interest.";
00608 continue;
00609 }
00610
00611 QDomElement stuff = root.firstChildElement("stuff");
00612 if (stuff.isNull()) {
00613 kWarning() << "Missing GHNS installation metadata.";
00614 continue;
00615 }
00616
00617 EntryHandler handler(stuff);
00618 if (!handler.isValid()) {
00619 kWarning() << "Invalid GHNS installation metadata.";
00620 continue;
00621 }
00622
00623 Entry *e = handler.entryptr();
00624 e->setStatus(Entry::Installed);
00625 e->setSource(Entry::Registry);
00626 m_entry_registry.insert(id(e), e);
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642 }
00643 }
00644 }
00645
00646 void CoreEngine::loadProvidersCache()
00647 {
00648 KStandardDirs d;
00649
00650
00651 QString cachefile = d.findResource("cache", m_componentname + "kns2providers.cache.xml");
00652 if (cachefile.isEmpty()) {
00653 kDebug() << "Cache not present, skip loading.";
00654 return;
00655 }
00656
00657 kDebug() << "Loading provider cache from file '" + cachefile + "'.";
00658
00659
00660 bool ret;
00661 QFile f(cachefile);
00662 ret = f.open(QIODevice::ReadOnly);
00663 if (!ret) {
00664 kWarning() << "The file could not be opened.";
00665 return;
00666 }
00667
00668
00669 QDomDocument doc;
00670 ret = doc.setContent(&f);
00671 if (!ret) {
00672 kWarning() << "The file could not be parsed.";
00673 return;
00674 }
00675
00676
00677 QDomElement root = doc.documentElement();
00678 if (root.tagName() != "ghnsproviders") {
00679 kWarning() << "The file doesn't seem to be of interest.";
00680 return;
00681 }
00682
00683
00684 QDomElement provider = root.firstChildElement("provider");
00685 if (provider.isNull()) {
00686 kWarning() << "Missing provider entries in the cache.";
00687 return;
00688 }
00689
00690
00691 while (!provider.isNull()) {
00692 ProviderHandler handler(provider);
00693 if (!handler.isValid()) {
00694 kWarning() << "Invalid provider metadata.";
00695 continue;
00696 }
00697
00698 Provider *p = handler.providerptr();
00699 m_provider_cache.append(p);
00700 m_provider_index[pid(p)] = p;
00701
00702 emit signalProviderLoaded(p);
00703
00704 loadFeedCache(p);
00705
00706
00707
00708
00709
00710
00711 provider = provider.nextSiblingElement("provider");
00712 }
00713
00714 if (m_cachepolicy == CacheOnly) {
00715 emit signalEntriesFinished();
00716 }
00717 }
00718
00719 void CoreEngine::loadFeedCache(Provider *provider)
00720 {
00721 KStandardDirs d;
00722
00723 kDebug() << "Loading feed cache.";
00724
00725 QStringList cachedirs = d.findDirs("cache", m_componentname + "kns2feeds.cache");
00726 if (cachedirs.size() == 0) {
00727 kDebug() << "Cache directory not present, skip loading.";
00728 return;
00729 }
00730 QString cachedir = cachedirs.first();
00731
00732 QStringList entrycachedirs = d.findDirs("cache", "knewstuff2-entries.cache/");
00733 if (entrycachedirs.size() == 0) {
00734 kDebug() << "Cache directory not present, skip loading.";
00735 return;
00736 }
00737 QString entrycachedir = entrycachedirs.first();
00738
00739 kDebug() << "Load from directory: " + cachedir;
00740
00741 QStringList feeds = provider->feeds();
00742 for (int i = 0; i < feeds.count(); i++) {
00743 Feed *feed = provider->downloadUrlFeed(feeds.at(i));
00744 QString feedname = feeds.at(i);
00745
00746 QString idbase64 = QString(pid(provider).toUtf8().toBase64() + '-' + feedname);
00747 QString cachefile = cachedir + '/' + idbase64 + ".xml";
00748
00749 kDebug() << " + Load from file: " + cachefile;
00750
00751 bool ret;
00752 QFile f(cachefile);
00753 ret = f.open(QIODevice::ReadOnly);
00754 if (!ret) {
00755 kWarning() << "The file could not be opened.";
00756 return;
00757 }
00758
00759 QDomDocument doc;
00760 ret = doc.setContent(&f);
00761 if (!ret) {
00762 kWarning() << "The file could not be parsed.";
00763 return;
00764 }
00765
00766 QDomElement root = doc.documentElement();
00767 if (root.tagName() != "ghnsfeeds") {
00768 kWarning() << "The file doesn't seem to be of interest.";
00769 return;
00770 }
00771
00772 QDomElement entryel = root.firstChildElement("entry-id");
00773 if (entryel.isNull()) {
00774 kWarning() << "Missing entries in the cache.";
00775 return;
00776 }
00777
00778 while (!entryel.isNull()) {
00779 QString idbase64 = entryel.text();
00780
00781
00782 QString filepath = entrycachedir + '/' + idbase64 + ".meta";
00783
00784
00785
00786
00787 Entry *entry = loadEntryCache(filepath);
00788 if (entry) {
00789 QString entryid = id(entry);
00790
00791 if (m_entry_registry.contains(entryid)) {
00792 Entry * registryEntry = m_entry_registry.value(entryid);
00793 entry->setStatus(registryEntry->status());
00794 entry->setInstalledFiles(registryEntry->installedFiles());
00795 }
00796
00797 feed->addEntry(entry);
00798
00799 emit signalEntryLoaded(entry, feed, provider);
00800 }
00801
00802 entryel = entryel.nextSiblingElement("entry-id");
00803 }
00804 }
00805 }
00806
00807 KNS::Entry *CoreEngine::loadEntryCache(const QString& filepath)
00808 {
00809 bool ret;
00810 QFile f(filepath);
00811 ret = f.open(QIODevice::ReadOnly);
00812 if (!ret) {
00813 kWarning() << "The file " << filepath << " could not be opened.";
00814 return NULL;
00815 }
00816
00817 QDomDocument doc;
00818 ret = doc.setContent(&f);
00819 if (!ret) {
00820 kWarning() << "The file could not be parsed.";
00821 return NULL;
00822 }
00823
00824 QDomElement root = doc.documentElement();
00825 if (root.tagName() != "ghnscache") {
00826 kWarning() << "The file doesn't seem to be of interest.";
00827 return NULL;
00828 }
00829
00830 QDomElement stuff = root.firstChildElement("stuff");
00831 if (stuff.isNull()) {
00832 kWarning() << "Missing GHNS cache metadata.";
00833 return NULL;
00834 }
00835
00836 EntryHandler handler(stuff);
00837 if (!handler.isValid()) {
00838 kWarning() << "Invalid GHNS installation metadata.";
00839 return NULL;
00840 }
00841
00842 Entry *e = handler.entryptr();
00843 e->setStatus(Entry::Downloadable);
00844 m_entry_cache.append(e);
00845 m_entry_index[id(e)] = e;
00846
00847 if (root.hasAttribute("previewfile")) {
00848 m_previewfiles[e] = root.attribute("previewfile");
00849
00850 }
00851
00852 if (root.hasAttribute("payloadfile")) {
00853 m_payloadfiles[e] = root.attribute("payloadfile");
00854
00855 }
00856
00857 e->setSource(Entry::Cache);
00858
00859 return e;
00860 }
00861
00862
00863 #if 0
00864 void CoreEngine::loadEntriesCache()
00865 {
00866 KStandardDirs d;
00867
00868
00869
00870 QStringList cachedirs = d.findDirs("cache", "knewstuff2-entries.cache/" + m_componentname);
00871 if (cachedirs.size() == 0) {
00872
00873 return;
00874 }
00875 QString cachedir = cachedirs.first();
00876
00877
00878
00879 QDir dir(cachedir);
00880 QStringList files = dir.entryList(QDir::Files | QDir::Readable);
00881 for (QStringList::iterator fit = files.begin(); fit != files.end(); ++fit) {
00882 QString filepath = cachedir + '/' + (*fit);
00883
00884
00885 Entry *e = loadEntryCache(filepath);
00886
00887 if (e) {
00888
00889 emit signalEntryLoaded(e, NULL, NULL);
00890 }
00891 }
00892 }
00893 #endif
00894
00895 void CoreEngine::shutdown()
00896 {
00897 m_entry_index.clear();
00898 m_provider_index.clear();
00899
00900 qDeleteAll(m_entry_cache);
00901 qDeleteAll(m_provider_cache);
00902
00903 m_entry_cache.clear();
00904 m_provider_cache.clear();
00905
00906 delete m_installation;
00907 }
00908
00909 bool CoreEngine::providerCached(Provider *provider)
00910 {
00911 if (m_cachepolicy == CacheNever) return false;
00912
00913 if (m_provider_index.contains(pid(provider)))
00914 return true;
00915 return false;
00916 }
00917
00918 bool CoreEngine::providerChanged(Provider *oldprovider, Provider *provider)
00919 {
00920 QStringList oldfeeds = oldprovider->feeds();
00921 QStringList feeds = provider->feeds();
00922 if (oldfeeds.count() != feeds.count())
00923 return true;
00924 for (int i = 0; i < feeds.count(); i++) {
00925 Feed *oldfeed = oldprovider->downloadUrlFeed(feeds.at(i));
00926 Feed *feed = provider->downloadUrlFeed(feeds.at(i));
00927 if (!oldfeed)
00928 return true;
00929 if (feed->feedUrl() != oldfeed->feedUrl())
00930 return true;
00931 }
00932 return false;
00933 }
00934
00935 void CoreEngine::mergeProviders(Provider::List providers)
00936 {
00937 for (Provider::List::Iterator it = providers.begin(); it != providers.end(); ++it) {
00938 Provider *p = (*it);
00939
00940 if (providerCached(p)) {
00941 kDebug() << "CACHE: hit provider " << p->name().representation();
00942 Provider *oldprovider = m_provider_index[pid(p)];
00943 if (providerChanged(oldprovider, p)) {
00944 kDebug() << "CACHE: update provider";
00945 cacheProvider(p);
00946 emit signalProviderChanged(p);
00947 }
00948
00949
00950
00951
00952 } else {
00953 if (m_cachepolicy != CacheNever) {
00954 kDebug() << "CACHE: miss provider " << p->name().representation();
00955 cacheProvider(p);
00956 }
00957 emit signalProviderLoaded(p);
00958
00959
00960
00961
00962
00963 }
00964
00965 m_provider_cache.append(p);
00966 m_provider_index[pid(p)] = p;
00967 }
00968
00969 emit signalProvidersFinished();
00970 }
00971
00972 bool CoreEngine::entryCached(Entry *entry)
00973 {
00974 if (m_cachepolicy == CacheNever) return false;
00975
00976
00977
00978 if (m_entry_index.contains(id(entry)) && m_entry_index[id(entry)]->source() == Entry::Cache) {
00979 return true;
00980 }
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990
00991 for (int i = 0; i < m_entry_cache.count(); i++) {
00992 Entry *oldentry = m_entry_cache.at(i);
00993 if (id(entry) == id(oldentry)) return true;
00994
00995
00996
00998
00999 }
01000
01001 return false;
01002 }
01003
01004 bool CoreEngine::entryChanged(Entry *oldentry, Entry *entry)
01005 {
01006
01007 if ((!oldentry) || (entry->releaseDate() > oldentry->releaseDate())
01008 || (entry->version() > oldentry->version())
01009 || (entry->release() > oldentry->release()))
01010 return true;
01011 return false;
01012 }
01013
01014 void CoreEngine::mergeEntries(Entry::List entries, Feed *feed, const Provider *provider)
01015 {
01016 for (Entry::List::Iterator it = entries.begin(); it != entries.end(); ++it) {
01017
01018
01019 Entry *e = (*it);
01020 QString thisId = id(e);
01021
01022
01023 if (m_entry_registry.contains(thisId)) {
01024
01025 Entry *registryentry = m_entry_registry[thisId];
01026 e->setInstalledFiles(registryentry->installedFiles());
01027
01028 if (entryChanged(registryentry, e)) {
01029 e->setStatus(Entry::Updateable);
01030 emit signalEntryChanged(e);
01031 } else {
01032
01033 e->setStatus(registryentry->status());
01034 }
01035
01036 if (entryCached(e)) {
01037
01038 Entry * cachedentry = m_entry_index[thisId];
01039 if (entryChanged(cachedentry, e)) {
01040
01041 cachedentry->setStatus(Entry::Updateable);
01042
01043 if (m_cachepolicy != CacheNever) {
01044 cacheEntry(e);
01045 }
01046 emit signalEntryChanged(e);
01047 }
01048
01049
01050 feed->removeEntry(cachedentry);
01051
01052 } else {
01053 emit signalEntryLoaded(e, feed, provider);
01054 }
01055
01056 } else {
01057 e->setStatus(Entry::Downloadable);
01058
01059 if (entryCached(e)) {
01060
01061
01062 Entry *cachedentry = m_entry_index[thisId];
01063 if (entryChanged(cachedentry, e)) {
01064
01065 e->setStatus(Entry::Updateable);
01066
01067 if (m_cachepolicy != CacheNever) {
01068 cacheEntry(e);
01069 }
01070 emit signalEntryChanged(e);
01071
01072
01073 }
01074
01075 feed->removeEntry(cachedentry);
01076
01077 } else {
01078 if (m_cachepolicy != CacheNever) {
01079
01080 cacheEntry(e);
01081 }
01082 emit signalEntryLoaded(e, feed, provider);
01083 }
01084
01085 m_entry_cache.append(e);
01086 m_entry_index[thisId] = e;
01087 }
01088 }
01089
01090 if (m_cachepolicy != CacheNever) {
01091
01092
01093
01094 QStringList feeds = provider->feeds();
01095 QString feedname;
01096 for (int i = 0; i < feeds.size(); ++i) {
01097 if (provider->downloadUrlFeed(feeds[i]) == feed) {
01098 feedname = feeds[i];
01099 }
01100 }
01101 cacheFeed(provider, feedname, feed, entries);
01102 }
01103
01104 emit signalEntriesFeedFinished(feed);
01105 if (m_activefeeds == 0) {
01106 emit signalEntriesFinished();
01107 }
01108 }
01109
01110 void CoreEngine::cacheProvider(Provider *provider)
01111 {
01112 KStandardDirs d;
01113
01114 kDebug() << "Caching provider.";
01115
01116 QString cachedir = d.saveLocation("cache");
01117 QString cachefile = cachedir + m_componentname + "kns2providers.cache.xml";
01118
01119 kDebug() << " + Save to file '" + cachefile + "'.";
01120
01121 QDomDocument doc;
01122 QDomElement root = doc.createElement("ghnsproviders");
01123
01124 for (Provider::List::Iterator it = m_provider_cache.begin(); it != m_provider_cache.end(); ++it) {
01125 Provider *p = (*it);
01126 ProviderHandler ph(*p);
01127 QDomElement pxml = ph.providerXML();
01128 root.appendChild(pxml);
01129 }
01130 ProviderHandler ph(*provider);
01131 QDomElement pxml = ph.providerXML();
01132 root.appendChild(pxml);
01133
01134 QFile f(cachefile);
01135 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
01136 kError() << "Cannot write meta information to '" << cachedir << "'." << endl;
01137
01138 return;
01139 }
01140 QTextStream metastream(&f);
01141 metastream << root;
01142 f.close();
01143
01144
01145
01146
01147
01148
01149 }
01150
01151 void CoreEngine::cacheFeed(const Provider *provider, const QString & feedname, const Feed *feed, Entry::List entries)
01152 {
01153
01154 KStandardDirs d;
01155
01156 Q_UNUSED(feed);
01157
01158 QString cachedir = d.saveLocation("cache", m_componentname + "kns2feeds.cache");
01159
01160 QString idbase64 = QString(pid(provider).toUtf8().toBase64() + '-' + feedname);
01161 QString cachefile = idbase64 + ".xml";
01162
01163 kDebug() << "Caching feed to file '" + cachefile + "'.";
01164
01165 QDomDocument doc;
01166 QDomElement root = doc.createElement("ghnsfeeds");
01167 for (int i = 0; i < entries.count(); i++) {
01168 QString idbase64 = id(entries.at(i)).toUtf8().toBase64();
01169 QDomElement entryel = doc.createElement("entry-id");
01170 root.appendChild(entryel);
01171 QDomText entrytext = doc.createTextNode(idbase64);
01172 entryel.appendChild(entrytext);
01173 }
01174
01175 QFile f(cachedir + cachefile);
01176 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
01177 kError() << "Cannot write meta information to '" << cachedir + cachefile << "'." << endl;
01178
01179 return;
01180 }
01181 QTextStream metastream(&f);
01182 metastream << root;
01183 f.close();
01184 }
01185
01186 void CoreEngine::cacheEntry(Entry *entry)
01187 {
01188 KStandardDirs d;
01189
01190 QString cachedir = d.saveLocation("cache", "knewstuff2-entries.cache/");
01191
01192 kDebug() << "Caching entry in directory '" + cachedir + "'.";
01193
01194
01195
01196 QString idbase64 = QString(id(entry).toUtf8().toBase64());
01197 QString cachefile = idbase64 + ".meta";
01198
01199 kDebug() << "Caching to file '" + cachefile + "'.";
01200
01201
01202
01203
01204 EntryHandler eh(*entry);
01205 QDomElement exml = eh.entryXML();
01206
01207 QDomDocument doc;
01208 QDomElement root = doc.createElement("ghnscache");
01209 root.appendChild(exml);
01210
01211 if (m_previewfiles.contains(entry)) {
01212 root.setAttribute("previewfile", m_previewfiles[entry]);
01213 }
01214
01215
01216
01217
01218 QFile f(cachedir + cachefile);
01219 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
01220 kError() << "Cannot write meta information to '" << cachedir + cachefile << "'." << endl;
01221
01222 return;
01223 }
01224 QTextStream metastream(&f);
01225 metastream << root;
01226 f.close();
01227 }
01228
01229 void CoreEngine::registerEntry(Entry *entry)
01230 {
01231 m_entry_registry.insert(id(entry), entry);
01232 KStandardDirs d;
01233
01234
01235
01236
01237 QString registrydir = d.saveLocation("data", "knewstuff2-entries.registry");
01238
01239
01240
01241
01242 QString registryfile = QString(id(entry).toUtf8().toBase64()) + ".meta";
01243
01244
01245
01246 EntryHandler eh(*entry);
01247 QDomElement exml = eh.entryXML();
01248
01249 QDomDocument doc;
01250 QDomElement root = doc.createElement("ghnsinstall");
01251 root.appendChild(exml);
01252
01253 if (m_payloadfiles.contains(entry)) {
01254 root.setAttribute("payloadfile", m_payloadfiles[entry]);
01255 }
01256
01257 QFile f(registrydir + registryfile);
01258 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) {
01259 kError() << "Cannot write meta information to '" << registrydir + registryfile << "'." << endl;
01260
01261 return;
01262 }
01263 QTextStream metastream(&f);
01264 metastream << root;
01265 f.close();
01266 }
01267
01268 void KNS::CoreEngine::unregisterEntry(Entry * entry)
01269 {
01270 KStandardDirs d;
01271
01272
01273 QString registrydir = d.saveLocation("data", "knewstuff2-entries.registry");
01274
01275
01276 QString registryfile = QString(id(entry).toUtf8().toBase64()) + ".meta";
01277
01278 QFile::remove(registrydir + registryfile);
01279
01280
01281 m_entry_registry.remove(id(entry));
01282 }
01283
01284 QString CoreEngine::id(Entry *e)
01285 {
01286
01287
01288
01289 return m_componentname + e->name().language() + ':' + e->name().representation();
01290 }
01291
01292 QString CoreEngine::pid(const Provider *p)
01293 {
01294
01295
01296
01297
01298
01299 QStringList feeds = p->feeds();
01300 for (int i = 0; i < feeds.count(); i++) {
01301 QString feedtype = feeds.at(i);
01302 Feed *f = p->downloadUrlFeed(feedtype);
01303 if (f->feedUrl().isValid())
01304 return m_componentname + f->feedUrl().url();
01305 }
01306 if (p->webService().isValid())
01307 return m_componentname + p->webService().url();
01308 return m_componentname;
01309 }
01310
01311 bool CoreEngine::install(const QString &payloadfile)
01312 {
01313 QList<Entry*> entries = m_payloadfiles.keys(payloadfile);
01314 if (entries.size() != 1) {
01315
01316 kError() << "ASSERT: payloadfile is not associated" << endl;
01317 return false;
01318 }
01319 Entry *entry = entries.first();
01320
01321
01322
01323
01324
01325 if (m_installation->checksumPolicy() != Installation::CheckNever) {
01326 if (entry->checksum().isEmpty()) {
01327 if (m_installation->checksumPolicy() == Installation::CheckIfPossible) {
01328
01329 } else {
01330 kError() << "Checksum verification not possible" << endl;
01331 return false;
01332 }
01333 } else {
01334
01335 }
01336 }
01337 if (m_installation->signaturePolicy() != Installation::CheckNever) {
01338 if (entry->signature().isEmpty()) {
01339 if (m_installation->signaturePolicy() == Installation::CheckIfPossible) {
01340
01341 } else {
01342 kError() << "Signature verification not possible" << endl;
01343 return false;
01344 }
01345 } else {
01346
01347 }
01348 }
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359 QStringList installedFiles;
01360 QString installpath(payloadfile);
01361 if (!m_installation->isRemote()) {
01362
01363 QString installdir;
01364
01365 int pathcounter = 0;
01366 if (!m_installation->standardResourceDir().isEmpty()) {
01367 if (m_installation->scope() == Installation::ScopeUser) {
01368 installdir = KStandardDirs::locateLocal(m_installation->standardResourceDir().toUtf8(), "/");
01369 } else {
01370 installdir = KStandardDirs::installPath(m_installation->standardResourceDir().toUtf8());
01371 }
01372 pathcounter++;
01373 }
01374 if (!m_installation->targetDir().isEmpty()) {
01375 if (m_installation->scope() == Installation::ScopeUser) {
01376 installdir = KStandardDirs::locateLocal("data", m_installation->targetDir() + '/');
01377 } else {
01378 installdir = KStandardDirs::installPath("data") + m_installation->targetDir() + '/';
01379 }
01380 pathcounter++;
01381 }
01382 if (!m_installation->installPath().isEmpty()) {
01383 installdir = QDir::home().path() + '/' + m_installation->installPath() + '/';
01384 pathcounter++;
01385 }
01386 if (!m_installation->absoluteInstallPath().isEmpty()) {
01387 installdir = m_installation->absoluteInstallPath() + '/';
01388 pathcounter++;
01389 }
01390 if (pathcounter != 1) {
01391 kError() << "Wrong number of installation directories given." << endl;
01392 return false;
01393 }
01394
01395 kDebug() << "installdir: " << installdir;
01396 bool isarchive = true;
01397
01398
01399 if (m_installation->uncompression() == "always" || m_installation->uncompression() == "archive") {
01400
01401 installpath = installdir;
01402 KMimeType::Ptr mimeType = KMimeType::findByPath(payloadfile);
01403
01404
01405
01406
01407 KArchive *archive = 0;
01408
01409 if (mimeType->name() == "application/zip") {
01410 archive = new KZip(payloadfile);
01411 } else if (mimeType->name() == "application/tar"
01412 || mimeType->name() == "application/x-gzip"
01413 || mimeType->name() == "application/x-bzip") {
01414 archive = new KTar(payloadfile);
01415 } else {
01416 delete archive;
01417 kError() << "Could not determine type of archive file '" << payloadfile << "'";
01418 if (m_installation->uncompression() == "always") {
01419 return false;
01420 }
01421 isarchive = false;
01422 }
01423
01424 if (isarchive) {
01425 bool success = archive->open(QIODevice::ReadOnly);
01426 if (!success) {
01427 kError() << "Cannot open archive file '" << payloadfile << "'";
01428 if (m_installation->uncompression() == "always") {
01429 return false;
01430 }
01431
01432 isarchive = false;
01433 }
01434
01435 if (isarchive) {
01436 const KArchiveDirectory *dir = archive->directory();
01437 dir->copyTo(installdir);
01438
01439 installedFiles << archiveEntries(installdir, dir);
01440 installedFiles << installdir + '/';
01441
01442 archive->close();
01443 QFile::remove(payloadfile);
01444 delete archive;
01445 }
01446 }
01447 }
01448
01449 kDebug() << "isarchive: " << isarchive;
01450
01451 if (m_installation->uncompression() == "never" || (m_installation->uncompression() == "archive" && !isarchive)) {
01452
01453
01455
01456 KUrl source = KUrl(entry->payload().representation());
01457 kDebug() << "installing non-archive from " << source.url();
01458 QString installfile;
01459 QString ext = source.fileName().section('.', -1);
01460 if (m_installation->customName()) {
01461 installfile = entry->name().representation();
01462 installfile += '-' + entry->version();
01463 if (!ext.isEmpty()) installfile += '.' + ext;
01464 } else {
01465 installfile = source.fileName();
01466 }
01467 installpath = installdir + '/' + installfile;
01468
01469
01470
01471
01472
01473
01474
01475 QFile file(payloadfile);
01476
01477 bool success = file.rename(installpath);
01478 if (!success) {
01479 kError() << "Cannot move file '" << payloadfile << "' to destination '" << installpath << "'";
01480 return false;
01481 }
01482 installedFiles << installpath;
01483 installedFiles << installdir + '/';
01484 }
01485 }
01486
01487 entry->setInstalledFiles(installedFiles);
01488
01489 if (!m_installation->command().isEmpty()) {
01490 KProcess process;
01491 QString command(m_installation->command());
01492 QString fileArg(KShell::quoteArg(installpath));
01493 command.replace("%f", fileArg);
01494
01495
01496
01497
01498 process.setShellCommand(command);
01499 int exitcode = process.execute();
01500
01501 if (exitcode) {
01502 kError() << "Command failed" << endl;
01503 } else {
01504
01505 }
01506 }
01507
01508
01509
01510
01511 Security *sec = Security::ref();
01512
01513 connect(sec,
01514 SIGNAL(validityResult(int)),
01515 SLOT(slotInstallationVerification(int)));
01516
01517
01518 sec->checkValidity(QString());
01519
01520 m_payloadfiles[entry] = installpath;
01521 registerEntry(entry);
01522
01523
01524
01525 emit signalEntryChanged(entry);
01526
01527 return true;
01528 }
01529
01530 bool CoreEngine::uninstall(KNS::Entry *entry)
01531 {
01532 entry->setStatus(Entry::Deleted);
01533
01534 foreach(const QString &file, entry->installedFiles()) {
01535 if (file.endsWith('/')) {
01536 QDir dir;
01537 bool worked = dir.rmdir(file);
01538 if (!worked) {
01539
01540 continue;
01541 }
01542 } else {
01543 bool worked = QFile::remove(file);
01544 if (!worked) {
01545 kWarning() << "unable to delete file " << file;
01546 return false;
01547 }
01548 }
01549 }
01550 entry->setUnInstalledFiles(entry->installedFiles());
01551 entry->setInstalledFiles(QStringList());
01552 unregisterEntry(entry);
01553
01554 emit signalEntryChanged(entry);
01555
01556 return true;
01557 }
01558
01559 void CoreEngine::slotInstallationVerification(int result)
01560 {
01561
01562
01563 if (result & Security::SIGNED_OK)
01564 emit signalInstallationFinished();
01565 else
01566 emit signalInstallationFailed();
01567 }
01568
01569 void CoreEngine::setAutomationPolicy(AutomationPolicy policy)
01570 {
01571 m_automationpolicy = policy;
01572 }
01573
01574 void CoreEngine::setCachePolicy(CachePolicy policy)
01575 {
01576 m_cachepolicy = policy;
01577 }
01578
01579 QStringList KNS::CoreEngine::archiveEntries(const QString& path, const KArchiveDirectory * dir)
01580 {
01581 QStringList files;
01582 foreach(const QString &entry, dir->entries()) {
01583 QString childPath = path + '/' + entry;
01584 if (dir->entry(entry)->isFile()) {
01585 files << childPath;
01586 }
01587
01588 if (dir->entry(entry)->isDirectory()) {
01589 const KArchiveDirectory* childDir = static_cast<const KArchiveDirectory*>(dir->entry(entry));
01590 files << archiveEntries(childPath, childDir);
01591 files << childPath + '/';
01592 }
01593 }
01594 return files;
01595 }
01596
01597
01598 #include "coreengine.moc"