Index: kget/transfer-plugins/metalink/metalink.cpp =================================================================== --- kget/transfer-plugins/metalink/metalink.cpp (revision 1124973) +++ kget/transfer-plugins/metalink/metalink.cpp (revision 1124974) @@ -99,6 +99,7 @@ void Metalink::metalinkInit(const KUrl &src, const QByteArray &data) { kDebug(5001); + bool justDownloaded = !m_localMetalinkLocation.isValid(); if (!src.isEmpty()) { @@ -121,7 +122,9 @@ //error if (!m_metalink.isValid()) { - kDebug(5001) << "Unknown error when trying to load the .metalink-file"; + kError(5001) << "Unknown error when trying to load the .metalink-file. Metalink is not valid."; + setStatus(Job::Aborted); + setTransferChange(Tc_Status, true); return; } @@ -202,7 +205,7 @@ if (!m_dataSourceFactory.size()) { KMessageBox::error(0, i18n("Download failed, no working URLs were found."), i18n("Error")); - setStatus(Job::Aborted, i18n("An error occurred...."), SmallIcon("document-preview")); + setStatus(Job::Aborted); setTransferChange(Tc_Status, true); return; } @@ -227,16 +230,29 @@ ui.treeView->hideColumn(FileItem::SignatureVerified); dialog->setMainWidget(widget); dialog->setCaption(i18n("File Selection")); - dialog->setButtons(KDialog::Ok); - connect(dialog, SIGNAL(finished()), this, SLOT(filesSelected())); + dialog->setButtons(KDialog::Ok | KDialog::Cancel); + connect(dialog, SIGNAL(finished(int)), this, SLOT(fileDlgFinished(int))); dialog->show(); } } -void Metalink::filesSelected() +void Metalink::fileDlgFinished(int result) { + //BEGIN HACK if the dialog was not accepted untick every file, so that the download does not start + //generally setStatus should do the job as well, but does not as it appears + if (result != QDialog::Accepted) { + for (int row = 0; row < fileModel()->rowCount(); ++row) { + QModelIndex index = fileModel()->index(row, FileItem::File); + if (index.isValid()) { + fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); + } + } + } + //END + QModelIndexList files = fileModel()->fileIndexes(FileItem::File); + int numFilesSelected = 0; foreach (const QModelIndex &index, files) { const KUrl dest = fileModel()->getUrl(index); @@ -244,6 +260,9 @@ if (m_dataSourceFactory.contains(dest)) { m_dataSourceFactory[dest]->setDoDownload(doDownload); + if (doDownload) { + ++numFilesSelected; + } } } @@ -252,9 +271,15 @@ processedSizeChanged(); speedChanged(); + //no files selected to download or dialog rejected, stop the download + if (!numFilesSelected || (result != QDialog::Accepted)) { + setStatus(Job::Stopped);//FIXME + setTransferChange(Tc_Status, true); + return; + } + //some files may be set to download, so start them as long as the transfer is not stopped - if (status() != Job::Stopped) - { + if (status() != Job::Stopped) { startMetalink(); } } Index: kget/transfer-plugins/metalink/metalink.h =================================================================== --- kget/transfer-plugins/metalink/metalink.h (revision 1124973) +++ kget/transfer-plugins/metalink/metalink.h (revision 1124974) @@ -81,7 +81,7 @@ private Q_SLOTS: void metalinkInit(const KUrl &url = KUrl(), const QByteArray &data = QByteArray()); - void filesSelected(); + void fileDlgFinished(int result); void totalSizeChanged(KIO::filesize_t size); void processedSizeChanged(); void speedChanged(); Index: kget/ui/metalinkcreator/metalinker.h =================================================================== --- kget/ui/metalinkcreator/metalinker.h (revision 1124973) +++ kget/ui/metalinkcreator/metalinker.h (revision 1124974) @@ -259,6 +259,14 @@ KIO::filesize_t size; CommonData data; Resources resources; + + private: + /** + * Controlls if the name attribute is valid, i.e. it is not empty and + * does not contain any directory traversal directives or information, + * as described in the Metalink 4.0 specification 4.1.2.1. + */ + bool isValidNameAttribute() const; }; class Files Index: kget/ui/metalinkcreator/metalinker.cpp =================================================================== --- kget/ui/metalinkcreator/metalinker.cpp (revision 1124973) +++ kget/ui/metalinkcreator/metalinker.cpp (revision 1124974) @@ -528,14 +528,14 @@ bool KGetMetalink::File::isValid() const { - return !name.isEmpty() && resources.isValid(); + return isValidNameAttribute() && resources.isValid(); } void KGetMetalink::File::load(const QDomElement &e) { data.load(e); - name = e.attribute("name"); + name = QUrl::fromPercentEncoding(e.attribute("name").toAscii()); size = e.firstChildElement("size").text().toULongLong(); verification.load(e); @@ -575,6 +575,22 @@ resources.clear(); } + +bool KGetMetalink::File::isValidNameAttribute() const +{ + if (name.isEmpty()) { + kError(5001) << "Name attribute of Metalink::File is empty."; + return false; + } + + if (name.contains(QRegExp("$(\\.\\.?)?/")) || name.contains("/../") || name.endsWith("/..")) { + kError(5001) << "Name attribute of Metalink::File contains directory traversal directives:" << name; + return false; + } + + return true; +} + #ifdef HAVE_NEPOMUK QHash KGetMetalink::File::properties() const { @@ -584,13 +600,28 @@ bool KGetMetalink::Files::isValid() const { - bool isValid = !files.empty(); - foreach (const File &file, files) - { - isValid &= file.isValid(); + if (files.isEmpty()) { + return false; } - return isValid; + QStringList fileNames; + foreach (const File &file, files) { + fileNames << file.name; + if (!file.isValid()) { + return false; + } + } + + //The value of name must be unique for each file + while (!fileNames.isEmpty()) { + const QString fileName = fileNames.takeFirst(); + if (fileNames.contains(fileName)) { + kError(5001) << "Metalink::File name" << fileName << "exists multiple times."; + return false; + } + } + + return true; } void KGetMetalink::Files::load(const QDomElement &e) @@ -751,7 +782,7 @@ for (QDomElement elem = filesElem.firstChildElement("file"); !elem.isNull(); elem = elem.nextSiblingElement("file")) { File file; - file.name = elem.attribute("name"); + file.name = QUrl::fromPercentEncoding(elem.attribute("name").toAscii()); file.size = elem.firstChildElement("size").text().toULongLong(); file.data = parseCommonData(elem);