Changeset View
Changeset View
Standalone View
Standalone View
src/core/installation.cpp
Context not available. | |||||
24 | #include <QTemporaryFile> | 24 | #include <QTemporaryFile> | ||
---|---|---|---|---|---|
25 | #include <QtCore/QProcess> | 25 | #include <QtCore/QProcess> | ||
26 | #include <QUrlQuery> | 26 | #include <QUrlQuery> | ||
27 | #include <QDesktopServices> | ||||
27 | 28 | | |||
28 | #include "qmimedatabase.h" | 29 | #include "qmimedatabase.h" | ||
29 | #include "karchive.h" | 30 | #include "karchive.h" | ||
30 | #include "kzip.h" | 31 | #include "kzip.h" | ||
31 | #include "ktar.h" | 32 | #include "ktar.h" | ||
32 | #include "kio/job.h" | | |||
33 | #include "krandom.h" | 33 | #include "krandom.h" | ||
34 | #include "kshell.h" | 34 | #include "kshell.h" | ||
35 | #include "kmessagebox.h" // TODO get rid of message box | 35 | | ||
36 | #include <KRun> | | |||
37 | #include <qstandardpaths.h> | 36 | #include <qstandardpaths.h> | ||
38 | #include "klocalizedstring.h" | 37 | #include "klocalizedstring.h" | ||
39 | #include <knewstuff_debug.h> | 38 | #include <knewstuffcore_debug.h> | ||
40 | 39 | | |||
41 | #include "core/security_p.h" | 40 | #include "jobs/filecopyjob.h" | ||
41 | #include "security_p.h" | ||||
42 | #include "question.h" | ||||
42 | #ifdef Q_OS_WIN | 43 | #ifdef Q_OS_WIN | ||
43 | #include <windows.h> | 44 | #include <windows.h> | ||
44 | #include <shlobj.h> | 45 | #include <shlobj.h> | ||
45 | #endif | 46 | #endif | ||
46 | 47 | | |||
47 | using namespace KNS3; | 48 | using namespace KNSCore; | ||
48 | 49 | | |||
49 | Installation::Installation(QObject *parent) | 50 | Installation::Installation(QObject *parent) | ||
50 | : QObject(parent) | 51 | : QObject(parent) | ||
Context not available. | |||||
54 | , customName(false) | 55 | , customName(false) | ||
55 | , acceptHtml(false) | 56 | , acceptHtml(false) | ||
56 | { | 57 | { | ||
58 | Security *sec = Security::ref(); | ||||
59 | | ||||
60 | connect(sec, | ||||
61 | &Security::validityResult, | ||||
62 | this, &Installation::slotInstallationVerification); | ||||
63 | connect(sec, | ||||
64 | &Security::signalInformation, | ||||
65 | this, &Installation::signalInformation); | ||||
66 | connect(sec, | ||||
67 | &Security::signalError, | ||||
68 | this, &Installation::signalError); | ||||
57 | } | 69 | } | ||
58 | 70 | | |||
59 | bool Installation::readConfig(const KConfigGroup &group) | 71 | bool Installation::readConfig(const KConfigGroup &group) | ||
Context not available. | |||||
164 | return true; | 176 | return true; | ||
165 | } | 177 | } | ||
166 | 178 | | |||
167 | void Installation::install(EntryInternal entry) | 179 | void Installation::install(const EntryInternal& entry) | ||
168 | { | 180 | { | ||
169 | downloadPayload(entry); | 181 | downloadPayload(entry); | ||
170 | } | 182 | } | ||
171 | 183 | | |||
172 | void Installation::downloadPayload(const KNS3::EntryInternal &entry) | 184 | void Installation::downloadPayload(const KNSCore::EntryInternal &entry) | ||
173 | { | 185 | { | ||
174 | if (!entry.isValid()) { | 186 | if (!entry.isValid()) { | ||
175 | emit signalInstallationFailed(i18n("Invalid item.")); | 187 | emit signalInstallationFailed(i18n("Invalid item.")); | ||
Context not available. | |||||
186 | // FIXME no clue what this is supposed to do | 198 | // FIXME no clue what this is supposed to do | ||
187 | if (isRemote()) { | 199 | if (isRemote()) { | ||
188 | // Remote resource | 200 | // Remote resource | ||
189 | qCDebug(KNEWSTUFF) << "Relaying remote payload '" << source << "'"; | 201 | qCDebug(KNEWSTUFFCORE) << "Relaying remote payload '" << source << "'"; | ||
190 | install(entry, source.toDisplayString(QUrl::PreferLocalFile)); | 202 | install(entry, source.toDisplayString(QUrl::PreferLocalFile)); | ||
191 | emit signalPayloadLoaded(source); | 203 | emit signalPayloadLoaded(source); | ||
192 | // FIXME: we still need registration for eventual deletion | 204 | // FIXME: we still need registration for eventual deletion | ||
Context not available. | |||||
199 | return; // ERROR | 211 | return; // ERROR | ||
200 | } | 212 | } | ||
201 | QUrl destination = QUrl::fromLocalFile(tempFile.fileName()); | 213 | QUrl destination = QUrl::fromLocalFile(tempFile.fileName()); | ||
202 | qCDebug(KNEWSTUFF) << "Downloading payload" << source << "to" << destination; | 214 | qCDebug(KNEWSTUFFCORE) << "Downloading payload" << source << "to" << destination; | ||
203 | 215 | | |||
204 | // FIXME: check for validity | 216 | // FIXME: check for validity | ||
205 | KIO::FileCopyJob *job = KIO::file_copy(source, destination, -1, KIO::Overwrite | KIO::HideProgressInfo); | 217 | FileCopyJob *job = FileCopyJob::file_copy(source, destination, -1, JobFlag::Overwrite | JobFlag::HideProgressInfo); | ||
206 | connect(job, | 218 | connect(job, | ||
207 | &KJob::result, | 219 | &KJob::result, | ||
208 | this, &Installation::slotPayloadResult); | 220 | this, &Installation::slotPayloadResult); | ||
Context not available. | |||||
220 | if (job->error()) { | 232 | if (job->error()) { | ||
221 | emit signalInstallationFailed(i18n("Download of \"%1\" failed, error: %2", entry.name(), job->errorString())); | 233 | emit signalInstallationFailed(i18n("Download of \"%1\" failed, error: %2", entry.name(), job->errorString())); | ||
222 | } else { | 234 | } else { | ||
223 | KIO::FileCopyJob *fcjob = static_cast<KIO::FileCopyJob *>(job); | 235 | FileCopyJob *fcjob = static_cast<FileCopyJob *>(job); | ||
224 | 236 | | |||
225 | // check if the app likes html files - disabled by default as too many bad links have been submitted to opendesktop.org | 237 | // check if the app likes html files - disabled by default as too many bad links have been submitted to opendesktop.org | ||
226 | if (!acceptHtml) { | 238 | if (!acceptHtml) { | ||
227 | QMimeDatabase db; | 239 | QMimeDatabase db; | ||
228 | QMimeType mimeType = db.mimeTypeForFile(fcjob->destUrl().toLocalFile()); | 240 | QMimeType mimeType = db.mimeTypeForFile(fcjob->destUrl().toLocalFile()); | ||
229 | if (mimeType.inherits(QStringLiteral("text/html")) || mimeType.inherits(QStringLiteral("application/x-php"))) { | 241 | if (mimeType.inherits(QStringLiteral("text/html")) || mimeType.inherits(QStringLiteral("application/x-php"))) { | ||
230 | if (KMessageBox::questionYesNo(0, i18n("The downloaded file is a html file. This indicates a link to a website instead of the actual download. Would you like to open the site with a browser instead?"), i18n("Possibly bad download link")) | 242 | Question question; | ||
231 | == KMessageBox::Yes) { | 243 | question.setQuestion(i18n("The downloaded file is a html file. This indicates a link to a website instead of the actual download. Would you like to open the site with a browser instead?")); | ||
232 | KRun::runUrl(fcjob->srcUrl(), QStringLiteral("text/html"), Q_NULLPTR); | 244 | question.setTitle(i18n("Possibly bad download link")); | ||
245 | if(question.ask() == Question::YesResponse) { | ||||
246 | QDesktopServices::openUrl(fcjob->srcUrl()); | ||||
233 | emit signalInstallationFailed(i18n("Downloaded file was a HTML file. Opened in browser.")); | 247 | emit signalInstallationFailed(i18n("Downloaded file was a HTML file. Opened in browser.")); | ||
234 | entry.setStatus(Entry::Invalid); | 248 | entry.setStatus(KNS3::Entry::Invalid); | ||
235 | emit signalEntryChanged(entry); | 249 | emit signalEntryChanged(entry); | ||
236 | return; | 250 | return; | ||
237 | } | 251 | } | ||
Context not available. | |||||
244 | } | 258 | } | ||
245 | } | 259 | } | ||
246 | 260 | | |||
247 | void Installation::install(KNS3::EntryInternal entry, const QString &downloadedFile) | 261 | void KNSCore::Installation::install(KNSCore::EntryInternal entry, const QString& downloadedFile) | ||
248 | { | 262 | { | ||
249 | qCDebug(KNEWSTUFF) << "Install: " << entry.name() << " from " << downloadedFile; | 263 | qCDebug(KNEWSTUFFCORE) << "Install: " << entry.name() << " from " << downloadedFile; | ||
250 | 264 | | |||
251 | if (entry.payload().isEmpty()) { | 265 | if (entry.payload().isEmpty()) { | ||
252 | qCDebug(KNEWSTUFF) << "No payload associated with: " << entry.name(); | 266 | qCDebug(KNEWSTUFFCORE) << "No payload associated with: " << entry.name(); | ||
253 | return; | 267 | return; | ||
254 | } | 268 | } | ||
255 | 269 | | |||
Context not available. | |||||
260 | if (checksumPolicy() != Installation::CheckNever) { | 274 | if (checksumPolicy() != Installation::CheckNever) { | ||
261 | if (entry.checksum().isEmpty()) { | 275 | if (entry.checksum().isEmpty()) { | ||
262 | if (checksumPolicy() == Installation::CheckIfPossible) { | 276 | if (checksumPolicy() == Installation::CheckIfPossible) { | ||
263 | qCDebug(KNEWSTUFF) << "Skip checksum verification"; | 277 | qCDebug(KNEWSTUFFCORE) << "Skip checksum verification"; | ||
264 | } else { | 278 | } else { | ||
265 | qCritical() << "Checksum verification not possible" << endl; | 279 | qCritical() << "Checksum verification not possible" << endl; | ||
266 | return false; | 280 | return false; | ||
267 | } | 281 | } | ||
268 | } else { | 282 | } else { | ||
269 | qCDebug(KNEWSTUFF) << "Verify checksum..."; | 283 | qCDebug(KNEWSTUFFCORE) << "Verify checksum..."; | ||
270 | } | 284 | } | ||
271 | } | 285 | } | ||
272 | if (signaturePolicy() != Installation::CheckNever) { | 286 | if (signaturePolicy() != Installation::CheckNever) { | ||
273 | if (entry.signature().isEmpty()) { | 287 | if (entry.signature().isEmpty()) { | ||
274 | if (signaturePolicy() == Installation::CheckIfPossible) { | 288 | if (signaturePolicy() == Installation::CheckIfPossible) { | ||
275 | qCDebug(KNEWSTUFF) << "Skip signature verification"; | 289 | qCDebug(KNEWSTUFFCORE) << "Skip signature verification"; | ||
276 | } else { | 290 | } else { | ||
277 | qCritical() << "Signature verification not possible" << endl; | 291 | qCritical() << "Signature verification not possible" << endl; | ||
278 | return false; | 292 | return false; | ||
279 | } | 293 | } | ||
280 | } else { | 294 | } else { | ||
281 | qCDebug(KNEWSTUFF) << "Verify signature..."; | 295 | qCDebug(KNEWSTUFFCORE) << "Verify signature..."; | ||
282 | } | 296 | } | ||
283 | } | 297 | } | ||
284 | */ | 298 | */ | ||
Context not available. | |||||
287 | QStringList installedFiles = installDownloadedFileAndUncompress(entry, downloadedFile, targetPath); | 301 | QStringList installedFiles = installDownloadedFileAndUncompress(entry, downloadedFile, targetPath); | ||
288 | 302 | | |||
289 | if (installedFiles.isEmpty()) { | 303 | if (installedFiles.isEmpty()) { | ||
290 | if (entry.status() == Entry::Installing) { | 304 | if (entry.status() == KNS3::Entry::Installing) { | ||
291 | entry.setStatus(Entry::Downloadable); | 305 | entry.setStatus(KNS3::Entry::Downloadable); | ||
292 | } else if (entry.status() == Entry::Updating) { | 306 | } else if (entry.status() == KNS3::Entry::Updating) { | ||
293 | entry.setStatus(Entry::Updateable); | 307 | entry.setStatus(KNS3::Entry::Updateable); | ||
294 | } | 308 | } | ||
295 | emit signalEntryChanged(entry); | 309 | emit signalEntryChanged(entry); | ||
296 | emit signalInstallationFailed(i18n("Could not install \"%1\": file not found.", entry.name())); | 310 | emit signalInstallationFailed(i18n("Could not install \"%1\": file not found.", entry.name())); | ||
Context not available. | |||||
312 | // FIXME: security object lifecycle - it is a singleton! | 326 | // FIXME: security object lifecycle - it is a singleton! | ||
313 | Security *sec = Security::ref(); | 327 | Security *sec = Security::ref(); | ||
314 | 328 | | |||
315 | connect(sec, | | |||
316 | &Security::validityResult, | | |||
317 | this, &Installation::slotInstallationVerification); | | |||
318 | | ||||
319 | // FIXME: change to accept filename + signature | 329 | // FIXME: change to accept filename + signature | ||
320 | sec->checkValidity(QString()); | 330 | sec->checkValidity(QString()); | ||
321 | 331 | | |||
322 | // update version and release date to the new ones | 332 | // update version and release date to the new ones | ||
323 | if (entry.status() == Entry::Updating) { | 333 | if (entry.status() == KNS3::Entry::Updating) { | ||
324 | if (!entry.updateVersion().isEmpty()) { | 334 | if (!entry.updateVersion().isEmpty()) { | ||
325 | entry.setVersion(entry.updateVersion()); | 335 | entry.setVersion(entry.updateVersion()); | ||
326 | } | 336 | } | ||
Context not available. | |||||
329 | } | 339 | } | ||
330 | } | 340 | } | ||
331 | 341 | | |||
332 | entry.setStatus(Entry::Installed); | 342 | entry.setStatus(KNS3::Entry::Installed); | ||
333 | emit signalEntryChanged(entry); | 343 | emit signalEntryChanged(entry); | ||
334 | emit signalInstallationFinished(); | 344 | emit signalInstallationFinished(); | ||
335 | } | 345 | } | ||
Context not available. | |||||
408 | return QString(); | 418 | return QString(); | ||
409 | } | 419 | } | ||
410 | 420 | | |||
411 | qCDebug(KNEWSTUFF) << "installdir: " << installdir; | 421 | qCDebug(KNEWSTUFFCORE) << "installdir: " << installdir; | ||
412 | 422 | | |||
413 | // create the dir if it doesn't exist (QStandardPaths doesn't create it, unlike KStandardDirs!) | 423 | // create the dir if it doesn't exist (QStandardPaths doesn't create it, unlike KStandardDirs!) | ||
414 | QDir().mkpath(installdir); | 424 | QDir().mkpath(installdir); | ||
Context not available. | |||||
417 | return installdir; | 427 | return installdir; | ||
418 | } | 428 | } | ||
419 | 429 | | |||
420 | QStringList Installation::installDownloadedFileAndUncompress(const KNS3::EntryInternal &entry, const QString &payloadfile, const QString installdir) | 430 | QStringList Installation::installDownloadedFileAndUncompress(const KNSCore::EntryInternal &entry, const QString &payloadfile, const QString installdir) | ||
421 | { | 431 | { | ||
422 | QString installpath(payloadfile); | 432 | QString installpath(payloadfile); | ||
423 | // Collect all files that were installed | 433 | // Collect all files that were installed | ||
Context not available. | |||||
432 | installpath = installdir; | 442 | installpath = installdir; | ||
433 | QMimeDatabase db; | 443 | QMimeDatabase db; | ||
434 | QMimeType mimeType = db.mimeTypeForFile(payloadfile); | 444 | QMimeType mimeType = db.mimeTypeForFile(payloadfile); | ||
435 | qCDebug(KNEWSTUFF) << "Postinstallation: uncompress the file"; | 445 | qCDebug(KNEWSTUFFCORE) << "Postinstallation: uncompress the file"; | ||
436 | 446 | | |||
437 | // FIXME: check for overwriting, malicious archive entries (../foo) etc. | 447 | // FIXME: check for overwriting, malicious archive entries (../foo) etc. | ||
438 | // FIXME: KArchive should provide "safe mode" for this! | 448 | // FIXME: KArchive should provide "safe mode" for this! | ||
Context not available. | |||||
485 | } | 495 | } | ||
486 | } | 496 | } | ||
487 | 497 | | |||
488 | qCDebug(KNEWSTUFF) << "isarchive: " << isarchive; | 498 | qCDebug(KNEWSTUFFCORE) << "isarchive: " << isarchive; | ||
489 | 499 | | |||
490 | if (uncompression == QLatin1String("never") || (uncompression == QLatin1String("archive") && !isarchive)) { | 500 | if (uncompression == QLatin1String("never") || (uncompression == QLatin1String("archive") && !isarchive)) { | ||
491 | // no decompress but move to target | 501 | // no decompress but move to target | ||
Context not available. | |||||
493 | /// @todo when using KIO::get the http header can be accessed and it contains a real file name. | 503 | /// @todo when using KIO::get the http header can be accessed and it contains a real file name. | ||
494 | // FIXME: make naming convention configurable through *.knsrc? e.g. for kde-look.org image names | 504 | // FIXME: make naming convention configurable through *.knsrc? e.g. for kde-look.org image names | ||
495 | QUrl source = QUrl(entry.payload()); | 505 | QUrl source = QUrl(entry.payload()); | ||
496 | qCDebug(KNEWSTUFF) << "installing non-archive from " << source.url(); | 506 | qCDebug(KNEWSTUFFCORE) << "installing non-archive from " << source.url(); | ||
497 | QString installfile; | 507 | QString installfile; | ||
498 | QString ext = source.fileName().section('.', -1); | 508 | QString ext = source.fileName().section('.', -1); | ||
499 | if (customName) { | 509 | if (customName) { | ||
Context not available. | |||||
519 | } | 529 | } | ||
520 | installpath = installdir + QLatin1Char('/') + installfile; | 530 | installpath = installdir + QLatin1Char('/') + installfile; | ||
521 | 531 | | |||
522 | qCDebug(KNEWSTUFF) << "Install to file " << installpath; | 532 | qCDebug(KNEWSTUFFCORE) << "Install to file " << installpath; | ||
523 | // FIXME: copy goes here (including overwrite checking) | 533 | // FIXME: copy goes here (including overwrite checking) | ||
524 | // FIXME: what must be done now is to update the cache *again* | 534 | // FIXME: what must be done now is to update the cache *again* | ||
525 | // in order to set the new payload filename (on root tag only) | 535 | // in order to set the new payload filename (on root tag only) | ||
Context not available. | |||||
527 | // FIXME: for updates, we might need to force an overwrite (that is, deleting before) | 537 | // FIXME: for updates, we might need to force an overwrite (that is, deleting before) | ||
528 | QFile file(payloadfile); | 538 | QFile file(payloadfile); | ||
529 | bool success = true; | 539 | bool success = true; | ||
530 | const bool update = ((entry.status() == Entry::Updateable) || (entry.status() == Entry::Updating)); | 540 | const bool update = ((entry.status() == KNS3::Entry::Updateable) || (entry.status() == KNS3::Entry::Updating)); | ||
531 | 541 | | |||
532 | if (QFile::exists(installpath)) { | 542 | if (QFile::exists(installpath)) { | ||
533 | if (!update) { | 543 | if (!update) { | ||
534 | if (KMessageBox::warningContinueCancel(0, i18n("Overwrite existing file?") + "\n'" + installpath + '\'', i18n("Download File")) == KMessageBox::Cancel) { | 544 | Question question(Question::ContinueCancelQuestion); | ||
545 | question.setQuestion(i18n("Overwrite existing file?") + "\n'" + installpath + '\''); | ||||
546 | question.setTitle(i18n("Download File")); | ||||
547 | if(question.ask() == Question::CancelResponse) { | ||||
535 | return QStringList(); | 548 | return QStringList(); | ||
536 | } | 549 | } | ||
537 | } | 550 | } | ||
Context not available. | |||||
539 | } | 552 | } | ||
540 | if (success) { | 553 | if (success) { | ||
541 | success = file.rename(installpath); | 554 | success = file.rename(installpath); | ||
542 | qCDebug(KNEWSTUFF) << "move: " << file.fileName() << " to " << installpath; | 555 | qCDebug(KNEWSTUFFCORE) << "move: " << file.fileName() << " to " << installpath; | ||
543 | } | 556 | } | ||
544 | if (!success) { | 557 | if (!success) { | ||
545 | qCritical() << "Cannot move file '" << payloadfile << "' to destination '" << installpath << "'"; | 558 | qCritical() << "Cannot move file '" << payloadfile << "' to destination '" << installpath << "'"; | ||
Context not available. | |||||
557 | QString fileArg(KShell::quoteArg(installPath)); | 570 | QString fileArg(KShell::quoteArg(installPath)); | ||
558 | command.replace(QLatin1String("%f"), fileArg); | 571 | command.replace(QLatin1String("%f"), fileArg); | ||
559 | 572 | | |||
560 | qCDebug(KNEWSTUFF) << "Run command: " << command; | 573 | qCDebug(KNEWSTUFFCORE) << "Run command: " << command; | ||
561 | 574 | | |||
562 | int exitcode = QProcess::execute(command); | 575 | int exitcode = QProcess::execute(command); | ||
563 | 576 | | |||
Context not available. | |||||
568 | 581 | | |||
569 | void Installation::uninstall(EntryInternal entry) | 582 | void Installation::uninstall(EntryInternal entry) | ||
570 | { | 583 | { | ||
571 | entry.setStatus(Entry::Deleted); | 584 | entry.setStatus(KNS3::Entry::Deleted); | ||
572 | 585 | | |||
573 | if (!uninstallCommand.isEmpty()) { | 586 | if (!uninstallCommand.isEmpty()) { | ||
574 | foreach (const QString &file, entry.installedFiles()) { | 587 | foreach (const QString &file, entry.installedFiles()) { | ||
Context not available. | |||||
583 | if (exitcode) { | 596 | if (exitcode) { | ||
584 | qCritical() << "Command failed" << endl; | 597 | qCritical() << "Command failed" << endl; | ||
585 | } else { | 598 | } else { | ||
586 | qCDebug(KNEWSTUFF) << "Command executed successfully"; | 599 | qCDebug(KNEWSTUFFCORE) << "Command executed successfully"; | ||
587 | } | 600 | } | ||
588 | } | 601 | } | ||
589 | } | 602 | } | ||
Context not available. | |||||
618 | 631 | | |||
619 | void Installation::slotInstallationVerification(int result) | 632 | void Installation::slotInstallationVerification(int result) | ||
620 | { | 633 | { | ||
621 | qCDebug(KNEWSTUFF) << "SECURITY result " << result; | 634 | qCDebug(KNEWSTUFFCORE) << "SECURITY result " << result; | ||
622 | 635 | | |||
623 | //FIXME do something here ??? and get the right entry again | 636 | //FIXME do something here ??? and get the right entry again | ||
624 | EntryInternal entry; | 637 | EntryInternal entry; | ||
Context not available. |